https://github.com/LucasChollet updated https://github.com/llvm/llvm-project/pull/194883
>From cb9f2707198fb5c036273ba1125e2d5ccd8d39a8 Mon Sep 17 00:00:00 2001 From: Lucas Chollet <[email protected]> Date: Wed, 29 Apr 2026 15:00:27 +0200 Subject: [PATCH 1/6] [RISCV] Add partial support for -fzero-call-used-regs This implements the "-fzero-call-used-regs" option on RISCV for the "skip" and "*gpr*" arguments. Zeroing floating points and vector registers will be implemented later. --- clang/include/clang/Options/Options.td | 2 +- clang/lib/Driver/ToolChains/Clang.cpp | 10 +- llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 21 ++ llvm/lib/Target/RISCV/RISCVFrameLowering.h | 4 + llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 16 ++ llvm/lib/Target/RISCV/RISCVInstrInfo.h | 4 + llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp | 10 + llvm/lib/Target/RISCV/RISCVRegisterInfo.h | 3 + llvm/lib/Target/RISCV/RISCVRegisterInfo.td | 8 + .../test/CodeGen/RISCV/zero-call-used-regs.ll | 225 ++++++++++++++++++ 10 files changed, 298 insertions(+), 5 deletions(-) create mode 100644 llvm/test/CodeGen/RISCV/zero-call-used-regs.ll diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index 6fc8806ba683c..ea2cc964fc5f7 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -4967,7 +4967,7 @@ defm raw_string_literals : BoolFOption<"raw-string-literals", def fzero_call_used_regs_EQ : Joined<["-"], "fzero-call-used-regs=">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>, - HelpText<"Clear call-used registers upon function return (AArch64/x86 only)">, + HelpText<"Clear call-used registers upon function return (AArch64/RISCV/x86 only)">, Values<"skip,used-gpr-arg,used-gpr,used-arg,used,all-gpr-arg,all-gpr,all-arg,all">, NormalizedValues<["Skip", "UsedGPRArg", "UsedGPR", "UsedArg", "Used", "AllGPRArg", "AllGPR", "AllArg", "All"]>, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 1bdcbc4e7620b..362a5ef90ea60 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6867,10 +6867,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_check_new); if (Arg *A = Args.getLastArg(options::OPT_fzero_call_used_regs_EQ)) { - // FIXME: There's no reason for this to be restricted to X86. The backend - // code needs to be changed to include the appropriate function calls - // automatically. - if (!Triple.isX86() && !Triple.isAArch64()) + // FIXME: There's no reason for this to be restricted to some backend. + // The backend code needs to be changed to include the appropriate function + // calls automatically. + StringRef Value = A->getValue(); + if (!Triple.isX86() && !Triple.isAArch64() && + !(Triple.isRISCV() && (Value == "skip" || Value.contains("gpr")))) D.Diag(diag::err_drv_unsupported_opt_for_target) << A->getAsString(Args) << TripleStr; } diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp index 3e2f2450753ca..aecebfb83898a 100644 --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -1433,6 +1433,27 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF, emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL); } +void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero, + MachineBasicBlock &MBB) const { + // Insertion point. + MachineBasicBlock::iterator MBBI = MBB.getFirstTerminator(); + + // Fake a debug loc. + DebugLoc DL; + if (MBBI != MBB.end()) + DL = MBBI->getDebugLoc(); + + const MachineFunction &MF = *MBB.getParent(); + const RISCVSubtarget &STI = MF.getSubtarget<RISCVSubtarget>(); + const RISCVRegisterInfo &TRI = STI.getTargetABI(); + const RISCVInstrInfo &TII = *STI.getInstrInfo(); + + for (MCRegister Reg : RegsToZero.set_bits()) { + if (TRI.isGeneralPurposeRegister(MF, Reg)) + TII.buildClearRegister(Reg, MBB, MBBI, DL); + } +} + StackOffset RISCVFrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI, Register &FrameReg) const { diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.h b/llvm/lib/Target/RISCV/RISCVFrameLowering.h index 84e48dbc05d67..496af9b9fd9cc 100644 --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.h +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.h @@ -115,6 +115,10 @@ class RISCVFrameLowering : public TargetFrameLowering { const DebugLoc &DL, int64_t Amount, MachineInstr::MIFlag Flag, bool EmitCFI, bool DynAllocation) const; + + /// Emit target zero call-used regs. + void emitZeroCallUsedRegs(BitVector RegsToZero, + MachineBasicBlock &MBB) const override; }; } // namespace llvm #endif diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index f2cfee8477883..098a7a61b82d4 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -3936,6 +3936,22 @@ MachineBasicBlock::iterator RISCVInstrInfo::insertOutlinedCall( return It; } +void RISCVInstrInfo::buildClearRegister(Register Reg, MachineBasicBlock &MBB, + MachineBasicBlock::iterator Iter, + DebugLoc &DL, + bool AllowSideEffects) const { + + const MachineFunction &MF = *MBB.getParent(); + const RISCVSubtarget &STI = MF.getSubtarget<RISCVSubtarget>(); + const RISCVRegisterInfo &TRI = *STI.getRegisterInfo(); + + if (TRI.isGeneralPurposeRegister(MF, Reg)) { + BuildMI(MBB, Iter, DL, get(RISCV::PseudoLI), Reg).addImm(0); + } else { + llvm_unreachable("Implement buildClearRegister for non-GPR registers"); + } +} + std::optional<RegImmPair> RISCVInstrInfo::isAddImmediate(const MachineInstr &MI, Register Reg) const { // TODO: Handle cases where Reg is a super- or sub-register of the diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h index 273ed5248f7cd..c75335ba7d145 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h @@ -257,6 +257,10 @@ class RISCVInstrInfo : public RISCVGenInstrInfo { MachineBasicBlock::iterator &It, MachineFunction &MF, outliner::Candidate &C) const override; + void buildClearRegister(Register Reg, MachineBasicBlock &MBB, + MachineBasicBlock::iterator Iter, DebugLoc &DL, + bool AllowSideEffects = true) const override; + std::optional<RegImmPair> isAddImmediate(const MachineInstr &MI, Register Reg) const override; diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp index 57b10686d8a1f..1b1b4a3042dd2 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -819,6 +819,16 @@ Register RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const { return TFI->hasFP(MF) ? RISCV::X8 : RISCV::X2; } +bool RISCVRegisterInfo::isArgumentRegister(const MachineFunction &MF, + MCRegister Reg) const { + auto const &STI = MF.getSubtarget<RISCVSubtarget>(); + if (!STI.getRegisterInfo()->isGeneralPurposeRegister(MF, Reg)) + llvm_unreachable("Implement isArgumentRegister for non-GPR registers"); + + return llvm::any_of(RISCV::getArgGPRs(STI.getTargetABI()), + [&](MCPhysReg R) { return Reg == R; }); +} + StringRef RISCVRegisterInfo::getRegAsmName(MCRegister Reg) const { if (Reg == RISCV::SF_VCIX_STATE) return "sf.vcix_state"; diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h index 3a77820d28bbd..df203dc073786 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h @@ -125,6 +125,9 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo { Register getFrameRegister(const MachineFunction &MF) const override; + bool isArgumentRegister(const MachineFunction &MF, + MCRegister Reg) const override; + StringRef getRegAsmName(MCRegister Reg) const override; bool requiresRegisterScavenging(const MachineFunction &MF) const override { diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td index 58af1f863f7bf..bd2f3bb4b411e 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td @@ -1018,3 +1018,11 @@ def MR : RISCVRegisterClass<[i8, v8i1], 8, } def MR0 : RISCVRegisterClass<[i8, v8i1], 8, (add M0)>; + +//===----------------------------------------------------------------------===// +// Register categories. +// + +def GeneralPurposeRegisters : RegisterCategory<[GPR]>; + +def FixedRegisters : RegisterCategory<[GPRX0]>; diff --git a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll new file mode 100644 index 0000000000000..e962f3fe6724c --- /dev/null +++ b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll @@ -0,0 +1,225 @@ +; RUN: llc < %s -verify-machineinstrs -mtriple=riscv64-unknown-unknown | FileCheck %s +; RUN: llc < %s -verify-machineinstrs -mtriple=riscv32-unknown-unknown | FileCheck %s + +target triple = "riscv64-unknown-linux-gnu" + +define dso_local i32 @skip(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 "zero-call-used-regs"="skip" { +; CHECK-LABEL: skip: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mul{{w?}} a0, a1, a0 +; CHECK-NEXT: or a0, a0, a2 +; CHECK-NEXT: ret + +entry: + %mul = mul nsw i32 %b, %a + %or = or i32 %mul, %c + ret i32 %or +} + +define dso_local i32 @used_gpr_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-gpr-arg" { +; CHECK-LABEL: used_gpr_arg: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mul{{w?}} a0, a1, a0 +; CHECK-NEXT: or a0, a0, a2 +; CHECK-NEXT: li a1, 0 +; CHECK-NEXT: li a2, 0 +; CHECK-NEXT: ret + +entry: + %mul = mul nsw i32 %b, %a + %or = or i32 %mul, %c + ret i32 %or +} + +define dso_local i32 @used_gpr(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-gpr" { +; CHECK-LABEL: used_gpr: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mul{{w?}} a0, a1, a0 +; CHECK-NEXT: or a0, a0, a2 +; CHECK-NEXT: li a1, 0 +; CHECK-NEXT: li a2, 0 +; CHECK-NEXT: ret + +entry: + %mul = mul nsw i32 %b, %a + %or = or i32 %mul, %c + ret i32 %or +} + +define dso_local i32 @used_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-arg" { +; CHECK-LABEL: used_arg: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mul{{w?}} a0, a1, a0 +; CHECK-NEXT: or a0, a0, a2 +; CHECK-NEXT: li a1, 0 +; CHECK-NEXT: li a2, 0 +; CHECK-NEXT: ret + +entry: + %mul = mul nsw i32 %b, %a + %or = or i32 %mul, %c + ret i32 %or +} + +define dso_local i32 @used(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used" { +; CHECK-LABEL: used: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mul{{w?}} a0, a1, a0 +; CHECK-NEXT: or a0, a0, a2 +; CHECK-NEXT: li a1, 0 +; CHECK-NEXT: li a2, 0 +; CHECK-NEXT: ret + +entry: + %mul = mul nsw i32 %b, %a + %or = or i32 %mul, %c + ret i32 %or +} + +define dso_local i32 @all_gpr_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 "zero-call-used-regs"="all-gpr-arg" { +; CHECK-LABEL: all_gpr_arg: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mul{{w?}} a0, a1, a0 +; CHECK-NEXT: or a0, a0, a2 +; CHECK-NEXT: li a1, 0 +; CHECK-NEXT: li a2, 0 +; CHECK-NEXT: li a3, 0 +; CHECK-NEXT: li a4, 0 +; CHECK-NEXT: li a5, 0 +; CHECK-NEXT: li a6, 0 +; CHECK-NEXT: li a7, 0 +; CHECK-NEXT: ret + +entry: + %mul = mul nsw i32 %b, %a + %or = or i32 %mul, %c + ret i32 %or +} + +define dso_local i32 @all_gpr(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 "zero-call-used-regs"="all-gpr" { +; CHECK-LABEL: all_gpr: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: mul{{w?}} a0, a1, a0 +; CHECK-NEXT: or a0, a0, a2 +; CHECK-NEXT: li t0, 0 +; CHECK-NEXT: li t1, 0 +; CHECK-NEXT: li t2, 0 +; CHECK-NEXT: li a1, 0 +; CHECK-NEXT: li a2, 0 +; CHECK-NEXT: li a3, 0 +; CHECK-NEXT: li a4, 0 +; CHECK-NEXT: li a5, 0 +; CHECK-NEXT: li a6, 0 +; CHECK-NEXT: li a7, 0 +; CHECK-NEXT: li t3, 0 +; CHECK-NEXT: li t4, 0 +; CHECK-NEXT: li t5, 0 +; CHECK-NEXT: li t6, 0 +; CHECK-NEXT: ret + +entry: + %mul = mul nsw i32 %b, %a + %or = or i32 %mul, %c + ret i32 %or +} + +define dso_local double @skip_float(double noundef %a, float noundef %b) local_unnamed_addr #0 "zero-call-used-regs"="skip" { +; CHECK-LABEL: skip_float: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: fcvt.d.s fa5, fa1 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: ret + +entry: + %conv = fpext float %b to double + %mul = fmul double %conv, %a + ret double %mul +} + +define dso_local double @used_gpr_arg_float(double noundef %a, float noundef %b) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-gpr-arg" { +; CHECK-LABEL: used_gpr_arg_float: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: fcvt.d.s fa5, fa1 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: ret + +entry: + %conv = fpext float %b to double + %mul = fmul double %conv, %a + ret double %mul +} + +define dso_local double @used_gpr_float(double noundef %a, float noundef %b) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-gpr" { +; CHECK-LABEL: used_gpr_float: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: fcvt.d.s fa5, fa1 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: ret + +entry: + %conv = fpext float %b to double + %mul = fmul double %conv, %a + ret double %mul +} + +define dso_local double @all_gpr_arg_float(double noundef %a, float noundef %b) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="all-gpr-arg" { +; CHECK-LABEL: all_gpr_arg_float: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: fcvt.d.s fa5, fa1 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: li a0, 0 +; CHECK-NEXT: li a1, 0 +; CHECK-NEXT: li a2, 0 +; CHECK-NEXT: li a3, 0 +; CHECK-NEXT: li a4, 0 +; CHECK-NEXT: li a5, 0 +; CHECK-NEXT: li a6, 0 +; CHECK-NEXT: li a7, 0 +; CHECK-NEXT: ret + +entry: + %conv = fpext float %b to double + %mul = fmul double %conv, %a + ret double %mul +} + +define dso_local double @all_gpr_float(double noundef %a, float noundef %b) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="all-gpr" { +; CHECK-LABEL: all_gpr_float: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: fcvt.d.s fa5, fa1 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: li t0, 0 +; CHECK-NEXT: li t1, 0 +; CHECK-NEXT: li t2, 0 +; CHECK-NEXT: li a0, 0 +; CHECK-NEXT: li a1, 0 +; CHECK-NEXT: li a2, 0 +; CHECK-NEXT: li a3, 0 +; CHECK-NEXT: li a4, 0 +; CHECK-NEXT: li a5, 0 +; CHECK-NEXT: li a6, 0 +; CHECK-NEXT: li a7, 0 +; CHECK-NEXT: li t3, 0 +; CHECK-NEXT: li t4, 0 +; CHECK-NEXT: li t5, 0 +; CHECK-NEXT: li t6, 0 +; CHECK-NEXT: ret + +entry: + %conv = fpext float %b to double + %mul = fmul double %conv, %a + ret double %mul +} + +; Don't emit zeroing registers in "main" function. +define dso_local i32 @main() local_unnamed_addr #0 { +; CHECK-LABEL: main: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: li a0, 0 +; CHECK-NEXT: ret + +entry: + ret i32 0 +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+m,+f,+d" } >From 3e54c23555c169df8dd78fca7e40c2000cfbc045 Mon Sep 17 00:00:00 2001 From: Lucas Chollet <[email protected]> Date: Wed, 29 Apr 2026 20:29:58 +0200 Subject: [PATCH 2/6] fix comments #1 --- clang/include/clang/Options/Options.td | 2 +- llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 2 +- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 3 +- llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp | 3 +- .../test/CodeGen/RISCV/zero-call-used-regs.ll | 28 +++++++++---------- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index ea2cc964fc5f7..8339415e88dd7 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -4967,7 +4967,7 @@ defm raw_string_literals : BoolFOption<"raw-string-literals", def fzero_call_used_regs_EQ : Joined<["-"], "fzero-call-used-regs=">, Group<f_Group>, Visibility<[ClangOption, CC1Option]>, - HelpText<"Clear call-used registers upon function return (AArch64/RISCV/x86 only)">, + HelpText<"Clear call-used registers upon function return (AArch64/RISC-V/x86 only)">, Values<"skip,used-gpr-arg,used-gpr,used-arg,used,all-gpr-arg,all-gpr,all-arg,all">, NormalizedValues<["Skip", "UsedGPRArg", "UsedGPR", "UsedArg", "Used", "AllGPRArg", "AllGPR", "AllArg", "All"]>, diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp index aecebfb83898a..37be434dfdcf9 100644 --- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp @@ -1445,7 +1445,7 @@ void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero, const MachineFunction &MF = *MBB.getParent(); const RISCVSubtarget &STI = MF.getSubtarget<RISCVSubtarget>(); - const RISCVRegisterInfo &TRI = STI.getTargetABI(); + const RISCVRegisterInfo &TRI = *STI.getRegisterInfo(); const RISCVInstrInfo &TII = *STI.getInstrInfo(); for (MCRegister Reg : RegsToZero.set_bits()) { diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index 098a7a61b82d4..e60399a7a0af0 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -3948,7 +3948,8 @@ void RISCVInstrInfo::buildClearRegister(Register Reg, MachineBasicBlock &MBB, if (TRI.isGeneralPurposeRegister(MF, Reg)) { BuildMI(MBB, Iter, DL, get(RISCV::PseudoLI), Reg).addImm(0); } else { - llvm_unreachable("Implement buildClearRegister for non-GPR registers"); + llvm::report_fatal_error( + "buildClearRegister is not implemented for non-GPR registers"); } } diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp index 1b1b4a3042dd2..d77cb25f5ed82 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -823,7 +823,8 @@ bool RISCVRegisterInfo::isArgumentRegister(const MachineFunction &MF, MCRegister Reg) const { auto const &STI = MF.getSubtarget<RISCVSubtarget>(); if (!STI.getRegisterInfo()->isGeneralPurposeRegister(MF, Reg)) - llvm_unreachable("Implement isArgumentRegister for non-GPR registers"); + llvm::report_fatal_error( + "isArgumentRegister is not implemented for non-GPR registers"); return llvm::any_of(RISCV::getArgGPRs(STI.getTargetABI()), [&](MCPhysReg R) { return Reg == R; }); diff --git a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll index e962f3fe6724c..fa10fbf4213db 100644 --- a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll +++ b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll @@ -3,7 +3,7 @@ target triple = "riscv64-unknown-linux-gnu" -define dso_local i32 @skip(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 "zero-call-used-regs"="skip" { +define i32 @skip(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="skip" { ; CHECK-LABEL: skip: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: mul{{w?}} a0, a1, a0 @@ -16,7 +16,7 @@ entry: ret i32 %or } -define dso_local i32 @used_gpr_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-gpr-arg" { +define i32 @used_gpr_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used-gpr-arg" { ; CHECK-LABEL: used_gpr_arg: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: mul{{w?}} a0, a1, a0 @@ -31,7 +31,7 @@ entry: ret i32 %or } -define dso_local i32 @used_gpr(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-gpr" { +define i32 @used_gpr(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used-gpr" { ; CHECK-LABEL: used_gpr: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: mul{{w?}} a0, a1, a0 @@ -46,7 +46,7 @@ entry: ret i32 %or } -define dso_local i32 @used_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-arg" { +define i32 @used_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used-arg" { ; CHECK-LABEL: used_arg: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: mul{{w?}} a0, a1, a0 @@ -61,7 +61,7 @@ entry: ret i32 %or } -define dso_local i32 @used(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used" { +define i32 @used(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used" { ; CHECK-LABEL: used: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: mul{{w?}} a0, a1, a0 @@ -76,7 +76,7 @@ entry: ret i32 %or } -define dso_local i32 @all_gpr_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 "zero-call-used-regs"="all-gpr-arg" { +define i32 @all_gpr_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="all-gpr-arg" { ; CHECK-LABEL: all_gpr_arg: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: mul{{w?}} a0, a1, a0 @@ -96,7 +96,7 @@ entry: ret i32 %or } -define dso_local i32 @all_gpr(i32 noundef %a, i32 noundef %b, i32 noundef %c) local_unnamed_addr #0 "zero-call-used-regs"="all-gpr" { +define i32 @all_gpr(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="all-gpr" { ; CHECK-LABEL: all_gpr: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: mul{{w?}} a0, a1, a0 @@ -123,7 +123,7 @@ entry: ret i32 %or } -define dso_local double @skip_float(double noundef %a, float noundef %b) local_unnamed_addr #0 "zero-call-used-regs"="skip" { +define double @skip_float(double noundef %a, float noundef %b) #0 "zero-call-used-regs"="skip" { ; CHECK-LABEL: skip_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 @@ -136,7 +136,7 @@ entry: ret double %mul } -define dso_local double @used_gpr_arg_float(double noundef %a, float noundef %b) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-gpr-arg" { +define double @used_gpr_arg_float(double noundef %a, float noundef %b) #0 "zero-call-used-regs"="used-gpr-arg" { ; CHECK-LABEL: used_gpr_arg_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 @@ -149,7 +149,7 @@ entry: ret double %mul } -define dso_local double @used_gpr_float(double noundef %a, float noundef %b) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="used-gpr" { +define double @used_gpr_float(double noundef %a, float noundef %b) #0 "zero-call-used-regs"="used-gpr" { ; CHECK-LABEL: used_gpr_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 @@ -162,7 +162,7 @@ entry: ret double %mul } -define dso_local double @all_gpr_arg_float(double noundef %a, float noundef %b) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="all-gpr-arg" { +define double @all_gpr_arg_float(double noundef %a, float noundef %b) #0 "zero-call-used-regs"="all-gpr-arg" { ; CHECK-LABEL: all_gpr_arg_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 @@ -183,7 +183,7 @@ entry: ret double %mul } -define dso_local double @all_gpr_float(double noundef %a, float noundef %b) local_unnamed_addr #0 noinline optnone "zero-call-used-regs"="all-gpr" { +define double @all_gpr_float(double noundef %a, float noundef %b) #0 "zero-call-used-regs"="all-gpr" { ; CHECK-LABEL: all_gpr_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 @@ -212,7 +212,7 @@ entry: } ; Don't emit zeroing registers in "main" function. -define dso_local i32 @main() local_unnamed_addr #0 { +define i32 @main() #0 { ; CHECK-LABEL: main: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: li a0, 0 @@ -222,4 +222,4 @@ entry: ret i32 0 } -attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone willreturn uwtable "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+m,+f,+d" } +attributes #0 = { "target-cpu"="generic" "target-features"="+m,+f,+d" } >From 1d005b18c124d6468305b66a18d543efc30acafe Mon Sep 17 00:00:00 2001 From: Lucas Chollet <[email protected]> Date: Thu, 30 Apr 2026 15:55:59 +0200 Subject: [PATCH 3/6] Add changelog entry --- clang/docs/ReleaseNotes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index f3865672120e0..5baf80b5942d2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -356,6 +356,9 @@ Modified Compiler Flags - The `-mno-outline` and `-moutline` compiler flags are now allowed on RISC-V and X86, which both support the machine outliner. - The `-mno-outline` flag will now add the `nooutline` IR attribute, so that `-mno-outline` and `-moutline` objects can be mixed correctly during LTO. +- The `-fzero-call-used-regs` compiler flag is now allowed on RISC-V, only the + "skip", "used-gpr", "used-gpr-arg", "all-gpr" and "all-gpr-arg" options are + supported for the moment. - Slightly changed hash id generation to get the unique linkage symbols names by ``-unique-internal-linkage-names`` option. Now it uses a path that >From 6230e4d04b505e5f342116e27e3f6b6d3c445c9b Mon Sep 17 00:00:00 2001 From: Lucas Chollet <[email protected]> Date: Thu, 30 Apr 2026 15:56:23 +0200 Subject: [PATCH 4/6] Use utils/update_llc_test_checks.py to generate the test case --- .../test/CodeGen/RISCV/zero-call-used-regs.ll | 214 ++++++++++++------ 1 file changed, 143 insertions(+), 71 deletions(-) diff --git a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll index fa10fbf4213db..44dd85e700534 100644 --- a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll +++ b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll @@ -1,14 +1,21 @@ -; RUN: llc < %s -verify-machineinstrs -mtriple=riscv64-unknown-unknown | FileCheck %s -; RUN: llc < %s -verify-machineinstrs -mtriple=riscv32-unknown-unknown | FileCheck %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6 +; RUN: llc < %s -verify-machineinstrs -mtriple=riscv64-unknown-unknown | FileCheck %s --check-prefixes=CHECK,64-BITS +; RUN: llc < %s -verify-machineinstrs -mtriple=riscv32-unknown-unknown | FileCheck %s --check-prefixes=CHECK,32-BITS target triple = "riscv64-unknown-linux-gnu" define i32 @skip(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="skip" { -; CHECK-LABEL: skip: -; CHECK: # %bb.0: # %entry -; CHECK-NEXT: mul{{w?}} a0, a1, a0 -; CHECK-NEXT: or a0, a0, a2 -; CHECK-NEXT: ret +; 64-BITS-LABEL: skip: +; 64-BITS: # %bb.0: # %entry +; 64-BITS-NEXT: mulw a0, a1, a0 +; 64-BITS-NEXT: or a0, a0, a2 +; 64-BITS-NEXT: ret +; +; 32-BITS-LABEL: skip: +; 32-BITS: # %bb.0: # %entry +; 32-BITS-NEXT: mul a0, a1, a0 +; 32-BITS-NEXT: or a0, a0, a2 +; 32-BITS-NEXT: ret entry: %mul = mul nsw i32 %b, %a @@ -17,13 +24,21 @@ entry: } define i32 @used_gpr_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used-gpr-arg" { -; CHECK-LABEL: used_gpr_arg: -; CHECK: # %bb.0: # %entry -; CHECK-NEXT: mul{{w?}} a0, a1, a0 -; CHECK-NEXT: or a0, a0, a2 -; CHECK-NEXT: li a1, 0 -; CHECK-NEXT: li a2, 0 -; CHECK-NEXT: ret +; 64-BITS-LABEL: used_gpr_arg: +; 64-BITS: # %bb.0: # %entry +; 64-BITS-NEXT: mulw a0, a1, a0 +; 64-BITS-NEXT: or a0, a0, a2 +; 64-BITS-NEXT: li a1, 0 +; 64-BITS-NEXT: li a2, 0 +; 64-BITS-NEXT: ret +; +; 32-BITS-LABEL: used_gpr_arg: +; 32-BITS: # %bb.0: # %entry +; 32-BITS-NEXT: mul a0, a1, a0 +; 32-BITS-NEXT: or a0, a0, a2 +; 32-BITS-NEXT: li a1, 0 +; 32-BITS-NEXT: li a2, 0 +; 32-BITS-NEXT: ret entry: %mul = mul nsw i32 %b, %a @@ -32,13 +47,21 @@ entry: } define i32 @used_gpr(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used-gpr" { -; CHECK-LABEL: used_gpr: -; CHECK: # %bb.0: # %entry -; CHECK-NEXT: mul{{w?}} a0, a1, a0 -; CHECK-NEXT: or a0, a0, a2 -; CHECK-NEXT: li a1, 0 -; CHECK-NEXT: li a2, 0 -; CHECK-NEXT: ret +; 64-BITS-LABEL: used_gpr: +; 64-BITS: # %bb.0: # %entry +; 64-BITS-NEXT: mulw a0, a1, a0 +; 64-BITS-NEXT: or a0, a0, a2 +; 64-BITS-NEXT: li a1, 0 +; 64-BITS-NEXT: li a2, 0 +; 64-BITS-NEXT: ret +; +; 32-BITS-LABEL: used_gpr: +; 32-BITS: # %bb.0: # %entry +; 32-BITS-NEXT: mul a0, a1, a0 +; 32-BITS-NEXT: or a0, a0, a2 +; 32-BITS-NEXT: li a1, 0 +; 32-BITS-NEXT: li a2, 0 +; 32-BITS-NEXT: ret entry: %mul = mul nsw i32 %b, %a @@ -47,13 +70,21 @@ entry: } define i32 @used_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used-arg" { -; CHECK-LABEL: used_arg: -; CHECK: # %bb.0: # %entry -; CHECK-NEXT: mul{{w?}} a0, a1, a0 -; CHECK-NEXT: or a0, a0, a2 -; CHECK-NEXT: li a1, 0 -; CHECK-NEXT: li a2, 0 -; CHECK-NEXT: ret +; 64-BITS-LABEL: used_arg: +; 64-BITS: # %bb.0: # %entry +; 64-BITS-NEXT: mulw a0, a1, a0 +; 64-BITS-NEXT: or a0, a0, a2 +; 64-BITS-NEXT: li a1, 0 +; 64-BITS-NEXT: li a2, 0 +; 64-BITS-NEXT: ret +; +; 32-BITS-LABEL: used_arg: +; 32-BITS: # %bb.0: # %entry +; 32-BITS-NEXT: mul a0, a1, a0 +; 32-BITS-NEXT: or a0, a0, a2 +; 32-BITS-NEXT: li a1, 0 +; 32-BITS-NEXT: li a2, 0 +; 32-BITS-NEXT: ret entry: %mul = mul nsw i32 %b, %a @@ -62,13 +93,21 @@ entry: } define i32 @used(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used" { -; CHECK-LABEL: used: -; CHECK: # %bb.0: # %entry -; CHECK-NEXT: mul{{w?}} a0, a1, a0 -; CHECK-NEXT: or a0, a0, a2 -; CHECK-NEXT: li a1, 0 -; CHECK-NEXT: li a2, 0 -; CHECK-NEXT: ret +; 64-BITS-LABEL: used: +; 64-BITS: # %bb.0: # %entry +; 64-BITS-NEXT: mulw a0, a1, a0 +; 64-BITS-NEXT: or a0, a0, a2 +; 64-BITS-NEXT: li a1, 0 +; 64-BITS-NEXT: li a2, 0 +; 64-BITS-NEXT: ret +; +; 32-BITS-LABEL: used: +; 32-BITS: # %bb.0: # %entry +; 32-BITS-NEXT: mul a0, a1, a0 +; 32-BITS-NEXT: or a0, a0, a2 +; 32-BITS-NEXT: li a1, 0 +; 32-BITS-NEXT: li a2, 0 +; 32-BITS-NEXT: ret entry: %mul = mul nsw i32 %b, %a @@ -77,18 +116,31 @@ entry: } define i32 @all_gpr_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="all-gpr-arg" { -; CHECK-LABEL: all_gpr_arg: -; CHECK: # %bb.0: # %entry -; CHECK-NEXT: mul{{w?}} a0, a1, a0 -; CHECK-NEXT: or a0, a0, a2 -; CHECK-NEXT: li a1, 0 -; CHECK-NEXT: li a2, 0 -; CHECK-NEXT: li a3, 0 -; CHECK-NEXT: li a4, 0 -; CHECK-NEXT: li a5, 0 -; CHECK-NEXT: li a6, 0 -; CHECK-NEXT: li a7, 0 -; CHECK-NEXT: ret +; 64-BITS-LABEL: all_gpr_arg: +; 64-BITS: # %bb.0: # %entry +; 64-BITS-NEXT: mulw a0, a1, a0 +; 64-BITS-NEXT: or a0, a0, a2 +; 64-BITS-NEXT: li a1, 0 +; 64-BITS-NEXT: li a2, 0 +; 64-BITS-NEXT: li a3, 0 +; 64-BITS-NEXT: li a4, 0 +; 64-BITS-NEXT: li a5, 0 +; 64-BITS-NEXT: li a6, 0 +; 64-BITS-NEXT: li a7, 0 +; 64-BITS-NEXT: ret +; +; 32-BITS-LABEL: all_gpr_arg: +; 32-BITS: # %bb.0: # %entry +; 32-BITS-NEXT: mul a0, a1, a0 +; 32-BITS-NEXT: or a0, a0, a2 +; 32-BITS-NEXT: li a1, 0 +; 32-BITS-NEXT: li a2, 0 +; 32-BITS-NEXT: li a3, 0 +; 32-BITS-NEXT: li a4, 0 +; 32-BITS-NEXT: li a5, 0 +; 32-BITS-NEXT: li a6, 0 +; 32-BITS-NEXT: li a7, 0 +; 32-BITS-NEXT: ret entry: %mul = mul nsw i32 %b, %a @@ -97,25 +149,45 @@ entry: } define i32 @all_gpr(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="all-gpr" { -; CHECK-LABEL: all_gpr: -; CHECK: # %bb.0: # %entry -; CHECK-NEXT: mul{{w?}} a0, a1, a0 -; CHECK-NEXT: or a0, a0, a2 -; CHECK-NEXT: li t0, 0 -; CHECK-NEXT: li t1, 0 -; CHECK-NEXT: li t2, 0 -; CHECK-NEXT: li a1, 0 -; CHECK-NEXT: li a2, 0 -; CHECK-NEXT: li a3, 0 -; CHECK-NEXT: li a4, 0 -; CHECK-NEXT: li a5, 0 -; CHECK-NEXT: li a6, 0 -; CHECK-NEXT: li a7, 0 -; CHECK-NEXT: li t3, 0 -; CHECK-NEXT: li t4, 0 -; CHECK-NEXT: li t5, 0 -; CHECK-NEXT: li t6, 0 -; CHECK-NEXT: ret +; 64-BITS-LABEL: all_gpr: +; 64-BITS: # %bb.0: # %entry +; 64-BITS-NEXT: mulw a0, a1, a0 +; 64-BITS-NEXT: or a0, a0, a2 +; 64-BITS-NEXT: li t0, 0 +; 64-BITS-NEXT: li t1, 0 +; 64-BITS-NEXT: li t2, 0 +; 64-BITS-NEXT: li a1, 0 +; 64-BITS-NEXT: li a2, 0 +; 64-BITS-NEXT: li a3, 0 +; 64-BITS-NEXT: li a4, 0 +; 64-BITS-NEXT: li a5, 0 +; 64-BITS-NEXT: li a6, 0 +; 64-BITS-NEXT: li a7, 0 +; 64-BITS-NEXT: li t3, 0 +; 64-BITS-NEXT: li t4, 0 +; 64-BITS-NEXT: li t5, 0 +; 64-BITS-NEXT: li t6, 0 +; 64-BITS-NEXT: ret +; +; 32-BITS-LABEL: all_gpr: +; 32-BITS: # %bb.0: # %entry +; 32-BITS-NEXT: mul a0, a1, a0 +; 32-BITS-NEXT: or a0, a0, a2 +; 32-BITS-NEXT: li t0, 0 +; 32-BITS-NEXT: li t1, 0 +; 32-BITS-NEXT: li t2, 0 +; 32-BITS-NEXT: li a1, 0 +; 32-BITS-NEXT: li a2, 0 +; 32-BITS-NEXT: li a3, 0 +; 32-BITS-NEXT: li a4, 0 +; 32-BITS-NEXT: li a5, 0 +; 32-BITS-NEXT: li a6, 0 +; 32-BITS-NEXT: li a7, 0 +; 32-BITS-NEXT: li t3, 0 +; 32-BITS-NEXT: li t4, 0 +; 32-BITS-NEXT: li t5, 0 +; 32-BITS-NEXT: li t6, 0 +; 32-BITS-NEXT: ret entry: %mul = mul nsw i32 %b, %a @@ -127,7 +199,7 @@ define double @skip_float(double noundef %a, float noundef %b) #0 "zero-call-use ; CHECK-LABEL: skip_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 -; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 ; CHECK-NEXT: ret entry: @@ -140,7 +212,7 @@ define double @used_gpr_arg_float(double noundef %a, float noundef %b) #0 "zero- ; CHECK-LABEL: used_gpr_arg_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 -; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 ; CHECK-NEXT: ret entry: @@ -153,7 +225,7 @@ define double @used_gpr_float(double noundef %a, float noundef %b) #0 "zero-call ; CHECK-LABEL: used_gpr_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 -; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 ; CHECK-NEXT: ret entry: @@ -166,7 +238,7 @@ define double @all_gpr_arg_float(double noundef %a, float noundef %b) #0 "zero-c ; CHECK-LABEL: all_gpr_arg_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 -; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 ; CHECK-NEXT: li a0, 0 ; CHECK-NEXT: li a1, 0 ; CHECK-NEXT: li a2, 0 @@ -187,7 +259,7 @@ define double @all_gpr_float(double noundef %a, float noundef %b) #0 "zero-call- ; CHECK-LABEL: all_gpr_float: ; CHECK: # %bb.0: # %entry ; CHECK-NEXT: fcvt.d.s fa5, fa1 -; CHECK-NEXT: fmul.d fa0, fa5, fa0 +; CHECK-NEXT: fmul.d fa0, fa5, fa0 ; CHECK-NEXT: li t0, 0 ; CHECK-NEXT: li t1, 0 ; CHECK-NEXT: li t2, 0 >From 7a25176f3f033d2db3d13e2674409802edcd4bd7 Mon Sep 17 00:00:00 2001 From: Lucas Chollet <[email protected]> Date: Wed, 27 May 2026 16:23:37 +0200 Subject: [PATCH 5/6] Use pseudo instruction PseudoClearGPR --- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 2 +- llvm/lib/Target/RISCV/RISCVInstrInfo.td | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index e60399a7a0af0..b2e78c14de041 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -3946,7 +3946,7 @@ void RISCVInstrInfo::buildClearRegister(Register Reg, MachineBasicBlock &MBB, const RISCVRegisterInfo &TRI = *STI.getRegisterInfo(); if (TRI.isGeneralPurposeRegister(MF, Reg)) { - BuildMI(MBB, Iter, DL, get(RISCV::PseudoLI), Reg).addImm(0); + BuildMI(MBB, Iter, DL, get(RISCV::PseudoClearGPR), Reg); } else { llvm::report_fatal_error( "buildClearRegister is not implemented for non-GPR registers"); diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index 4f60a367b711a..5976f0cfaeb3f 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -2128,6 +2128,13 @@ def SetFCSRImm : SetSysRegImm<SysRegFCSR, [FRM, FFLAGS]>; /// Other pseudo-instructions +// Used by -fzero-call-used-regs to zero out registers. +let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 8, + isCodeGenOnly = 0 in +def PseudoClearGPR : Pseudo<(outs GPR:$rd), (ins), [], + "clear_gpr", "$rd">, + PseudoInstExpansion<(ADDI GPR:$rd, X0, 0)>; + // Pessimistically assume the stack pointer will be clobbered let Defs = [X2], Uses = [X2] in { def ADJCALLSTACKDOWN : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), >From 0a31c0ffd20269cb866550bd8fcc1d3e706211d5 Mon Sep 17 00:00:00 2001 From: Lucas Chollet <[email protected]> Date: Fri, 5 Jun 2026 11:32:11 +0200 Subject: [PATCH 6/6] Resolve comments --- llvm/lib/Target/RISCV/RISCVInstrInfo.cpp | 3 +-- llvm/lib/Target/RISCV/RISCVInstrInfo.td | 5 ++--- llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp | 5 ++--- llvm/test/CodeGen/RISCV/zero-call-used-regs.ll | 15 +-------------- 4 files changed, 6 insertions(+), 22 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp index b2e78c14de041..b4d4debc8e8d4 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -3942,13 +3942,12 @@ void RISCVInstrInfo::buildClearRegister(Register Reg, MachineBasicBlock &MBB, bool AllowSideEffects) const { const MachineFunction &MF = *MBB.getParent(); - const RISCVSubtarget &STI = MF.getSubtarget<RISCVSubtarget>(); const RISCVRegisterInfo &TRI = *STI.getRegisterInfo(); if (TRI.isGeneralPurposeRegister(MF, Reg)) { BuildMI(MBB, Iter, DL, get(RISCV::PseudoClearGPR), Reg); } else { - llvm::report_fatal_error( + llvm::reportFatalInternalError( "buildClearRegister is not implemented for non-GPR registers"); } } diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index 5976f0cfaeb3f..804a4ca027958 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -2130,9 +2130,8 @@ def SetFCSRImm : SetSysRegImm<SysRegFCSR, [FRM, FFLAGS]>; // Used by -fzero-call-used-regs to zero out registers. let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 8, - isCodeGenOnly = 0 in -def PseudoClearGPR : Pseudo<(outs GPR:$rd), (ins), [], - "clear_gpr", "$rd">, + isCodeGenOnly = true in +def PseudoClearGPR : Pseudo<(outs GPR:$rd), (ins), []>, PseudoInstExpansion<(ADDI GPR:$rd, X0, 0)>; // Pessimistically assume the stack pointer will be clobbered diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp index d77cb25f5ed82..971b4fe84b0b1 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -823,11 +823,10 @@ bool RISCVRegisterInfo::isArgumentRegister(const MachineFunction &MF, MCRegister Reg) const { auto const &STI = MF.getSubtarget<RISCVSubtarget>(); if (!STI.getRegisterInfo()->isGeneralPurposeRegister(MF, Reg)) - llvm::report_fatal_error( + llvm::reportFatalInternalError( "isArgumentRegister is not implemented for non-GPR registers"); - return llvm::any_of(RISCV::getArgGPRs(STI.getTargetABI()), - [&](MCPhysReg R) { return Reg == R; }); + return llvm::is_contained(RISCV::getArgGPRs(STI.getTargetABI()), Reg); } StringRef RISCVRegisterInfo::getRegAsmName(MCRegister Reg) const { diff --git a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll index 44dd85e700534..28e6c792b9dfd 100644 --- a/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll +++ b/llvm/test/CodeGen/RISCV/zero-call-used-regs.ll @@ -2,8 +2,6 @@ ; RUN: llc < %s -verify-machineinstrs -mtriple=riscv64-unknown-unknown | FileCheck %s --check-prefixes=CHECK,64-BITS ; RUN: llc < %s -verify-machineinstrs -mtriple=riscv32-unknown-unknown | FileCheck %s --check-prefixes=CHECK,32-BITS -target triple = "riscv64-unknown-linux-gnu" - define i32 @skip(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="skip" { ; 64-BITS-LABEL: skip: ; 64-BITS: # %bb.0: # %entry @@ -69,7 +67,7 @@ entry: ret i32 %or } -define i32 @used_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c) #0 "zero-call-used-regs"="used-arg" { +define i32 @used_arg(i32 noundef %a, i32 noundef %b, i32 noundef %c, i32 noundef %d) #0 "zero-call-used-regs"="used-arg" { ; 64-BITS-LABEL: used_arg: ; 64-BITS: # %bb.0: # %entry ; 64-BITS-NEXT: mulw a0, a1, a0 @@ -283,15 +281,4 @@ entry: ret double %mul } -; Don't emit zeroing registers in "main" function. -define i32 @main() #0 { -; CHECK-LABEL: main: -; CHECK: # %bb.0: # %entry -; CHECK-NEXT: li a0, 0 -; CHECK-NEXT: ret - -entry: - ret i32 0 -} - attributes #0 = { "target-cpu"="generic" "target-features"="+m,+f,+d" } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
