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

Reply via email to