[llvm] [RISCV] Support floating point registers with -fzero-call-used-regs (PR #204929)

Lucas Chollet via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 26 14:04:02 PDT 2026


https://github.com/LucasChollet updated https://github.com/llvm/llvm-project/pull/204929

>From 1d7a1c944844337c1c4abec22bb33512e6d184ad Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Tue, 9 Jun 2026 14:31:12 +0200
Subject: [PATCH 1/7] [RISCV] Support floating point registers with
 -fzero-call-used-regs

This patch adds support for clearing floating-point registers with the
`zero-call-used-regs` attribute. It supports all combinations of
floating-point sizes (fp16, fp32, fp64, fp128) and extensions (F, D, Q).

Like #194883, this uses pseudo-instructions to clear the registers. Note
that `PseudoClearFPR64` is manually expanded in `riscv-expand-pseudo`.
For RV32, we expand it to `fcvt.d.w reg, x0, rtz`, but for RV64 we use
the more efficient `fmv.d.x reg, x0`. This also matches GCC behavior.

Finally, using non-GPR values for `zero-call-used-regs` in the Clang
driver is still rejected as the backend doesn't support clearing
vector registers.
---
 .../Target/RISCV/RISCVExpandPseudoInsts.cpp   |  21 +
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp  |  40 +-
 llvm/lib/Target/RISCV/RISCVInstrInfo.cpp      |   8 +-
 llvm/lib/Target/RISCV/RISCVInstrInfoD.td      |   4 +
 llvm/lib/Target/RISCV/RISCVInstrInfoF.td      |   6 +
 llvm/lib/Target/RISCV/RISCVInstrInfoQ.td      |   6 +
 llvm/lib/Target/RISCV/RISCVRegisterInfo.h     |   7 +
 llvm/lib/Target/RISCV/RISCVSubtarget.h        |  10 +
 .../CodeGen/RISCV/zero-call-used-regs-fp.ll   | 469 ++++++++++++++++++
 9 files changed, 568 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll

diff --git a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
index fdd98d03a77f1..5e2882b066752 100644
--- a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
@@ -60,6 +60,8 @@ class RISCVExpandPseudo : public MachineFunctionPass {
                            MachineBasicBlock::iterator MBBI);
   bool expandPseudoReadVLENBViaVSETVLIX0(MachineBasicBlock &MBB,
                                          MachineBasicBlock::iterator MBBI);
+  bool expandPseudoClearFPR64(MachineBasicBlock &MBB,
+                              MachineBasicBlock::iterator MBBI);
 #ifndef NDEBUG
   unsigned getInstSizeInBytes(const MachineFunction &MF) const {
     unsigned Size = 0;
@@ -192,6 +194,8 @@ bool RISCVExpandPseudo::expandMI(MachineBasicBlock &MBB,
     return expandVMSET_VMCLR(MBB, MBBI, RISCV::VMXNOR_MM);
   case RISCV::PseudoReadVLENBViaVSETVLIX0:
     return expandPseudoReadVLENBViaVSETVLIX0(MBB, MBBI);
+  case RISCV::PseudoClearFPR64:
+    return expandPseudoClearFPR64(MBB, MBBI);
   }
 
   return false;
@@ -621,6 +625,23 @@ bool RISCVExpandPseudo::expandPseudoReadVLENBViaVSETVLIX0(
   return true;
 }
 
+bool RISCVExpandPseudo::expandPseudoClearFPR64(
+    MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) {
+  DebugLoc DL = MBBI->getDebugLoc();
+  Register Dst = MBBI->getOperand(0).getReg();
+
+  if (STI->is64Bit()) {
+    BuildMI(MBB, MBBI, DL, TII->get(RISCV::FMV_D_X), Dst).addReg(RISCV::X0);
+  } else {
+    BuildMI(MBB, MBBI, DL, TII->get(RISCV::FCVT_D_W), Dst)
+        .addReg(RISCV::X0)
+        .addImm(RISCVFPRndMode::RTZ);
+  }
+
+  MBBI->eraseFromParent();
+  return true;
+}
+
 class RISCVPreRAExpandPseudo : public MachineFunctionPass {
 public:
   const RISCVSubtarget *STI;
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 50184f74f9634..754462d22eb8a 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -1432,6 +1432,34 @@ void RISCVFrameLowering::emitEpilogue(MachineFunction &MF,
   emitSiFiveCLICStackSwap(MF, MBB, MBBI, DL);
 }
 
+static MCRegister getLargestFPRegisterOrZero(const RISCVSubtarget &STI,
+                                             const TargetRegisterInfo &TRI,
+                                             MCRegister Reg) {
+  if (!STI.hasStdExtF())
+    return 0;
+
+  TargetRegisterClass LargestFPRegClass = STI.getLargestFPRegClass().value();
+
+  if (LargestFPRegClass.contains(Reg))
+    return Reg;
+
+  std::array<TargetRegisterClass, 3> RegisterClasses = {
+      RISCV::FPR16RegClass, RISCV::FPR32RegClass, RISCV::FPR64RegClass};
+  std::array<int, 3> SubIdx = {RISCV::sub_16, RISCV::sub_32, RISCV::sub_64};
+
+  for (unsigned i = 0; i < RegisterClasses.size(); ++i) {
+    if (RegisterClasses[i].contains(Reg)) {
+      if (MCRegister Super =
+              TRI.getMatchingSuperReg(Reg, SubIdx[i], &LargestFPRegClass))
+        return Super;
+    }
+  }
+
+  // Reg is bigger than what's currently available for the target, we can ignore
+  // it.
+  return 0;
+}
+
 void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
                                               MachineBasicBlock &MBB) const {
   // Insertion point.
@@ -1443,14 +1471,22 @@ void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
     DL = MBBI->getDebugLoc();
 
   const MachineFunction &MF = *MBB.getParent();
-  const RISCVSubtarget &STI = MF.getSubtarget<RISCVSubtarget>();
   const RISCVRegisterInfo &TRI = *STI.getRegisterInfo();
   const RISCVInstrInfo &TII = *STI.getInstrInfo();
 
+  BitVector FinalRegsToZero(TRI.getNumRegs());
+
   for (MCRegister Reg : RegsToZero.set_bits()) {
     if (TRI.isGeneralPurposeRegister(MF, Reg))
-      TII.buildClearRegister(Reg, MBB, MBBI, DL);
+      FinalRegsToZero.set(Reg);
+    else if (TRI.isFPRegister(Reg)) {
+      if (MCRegister MaybeReg = getLargestFPRegisterOrZero(STI, TRI, Reg))
+        FinalRegsToZero.set(MaybeReg);
+    }
   }
+
+  for (MCRegister Reg : FinalRegsToZero.set_bits())
+    TII.buildClearRegister(Reg, MBB, MBBI, DL);
 }
 
 StackOffset
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
index b31a3a7760d3b..d95d18981f4b9 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp
@@ -3954,9 +3954,15 @@ void RISCVInstrInfo::buildClearRegister(Register Reg, MachineBasicBlock &MBB,
 
   if (TRI.isGeneralPurposeRegister(MF, Reg)) {
     BuildMI(MBB, Iter, DL, get(RISCV::PseudoClearGPR), Reg);
+  } else if (RISCV::FPR32RegClass.contains(Reg)) {
+    BuildMI(MBB, Iter, DL, get(RISCV::PseudoClearFPR32), Reg);
+  } else if (RISCV::FPR64RegClass.contains(Reg)) {
+    BuildMI(MBB, Iter, DL, get(RISCV::PseudoClearFPR64), Reg);
+  } else if (RISCV::FPR128RegClass.contains(Reg)) {
+    BuildMI(MBB, Iter, DL, get(RISCV::PseudoClearFPR128), Reg);
   } else {
     llvm::reportFatalInternalError(
-        "buildClearRegister is not implemented for non-GPR registers");
+        "buildClearRegister is not implemented for vector registers");
   }
 }
 
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoD.td b/llvm/lib/Target/RISCV/RISCVInstrInfoD.td
index bc4a99f64ddb7..acac861b57b65 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoD.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoD.td
@@ -666,3 +666,7 @@ def : Pat<(i64 (any_llround FPR64INX:$rs1)), (FCVT_L_D_INX $rs1, FRM_RMM)>;
 def : Pat<(any_sint_to_fp (i64 GPR:$rs1)), (FCVT_D_L_INX GPR:$rs1, FRM_DYN)>;
 def : Pat<(any_uint_to_fp (i64 GPR:$rs1)), (FCVT_D_LU_INX GPR:$rs1, FRM_DYN)>;
 } // Predicates = [HasStdExtZdinx, IsRV64]
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 8,
+    isCodeGenOnly = true in
+def PseudoClearFPR64 : Pseudo<(outs FPR64:$rd), (ins), []>;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoF.td b/llvm/lib/Target/RISCV/RISCVInstrInfoF.td
index 20d062ba7c7f2..a7660bb6b1a5d 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoF.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoF.td
@@ -866,3 +866,9 @@ def : Pat<(any_uint_to_fp (i64 (zexti32 (i64 GPR:$rs1)))), (FCVT_S_WU_INX $rs1,
 def : Pat<(any_sint_to_fp (i64 GPR:$rs1)), (FCVT_S_L_INX $rs1, FRM_DYN)>;
 def : Pat<(any_uint_to_fp (i64 GPR:$rs1)), (FCVT_S_LU_INX $rs1, FRM_DYN)>;
 } // Predicates = [HasStdExtZfinx, IsRV64]
+
+// Used by -fzero-call-used-regs to zero out registers.
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 8,
+    isCodeGenOnly = true in
+def PseudoClearFPR32 : Pseudo<(outs FPR32:$rd), (ins), []>,
+                       PseudoInstExpansion<(FMV_W_X FPR32:$rd, X0)>;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoQ.td b/llvm/lib/Target/RISCV/RISCVInstrInfoQ.td
index da78c13c0edcc..efcee6db91e22 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoQ.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoQ.td
@@ -165,3 +165,9 @@ let Predicates = [HasStdExtQ] in {
   def PseudoFLQ : PseudoFloatLoad<"flq", FPR128>;
   def PseudoFSQ : PseudoStore<"fsq", FPR128>;
 } // Predicates = [HasStdExtQ]
+
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 8,
+    isCodeGenOnly = true in
+def PseudoClearFPR128 : Pseudo<(outs FPR128:$rd), (ins), []>,
+                        PseudoInstExpansion<(FCVT_Q_W FPR128:$rd, X0, FRM_RTZ)>;
+
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
index df203dc073786..625c19b3a02fd 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.h
@@ -177,6 +177,13 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo {
   static bool isRVVRegClass(const TargetRegisterClass *RC) {
     return RISCVRI::isVRegClass(RC->TSFlags);
   }
+
+  static bool isFPRegister(MCRegister Reg) {
+    return RISCV::FPR16RegClass.contains(Reg) ||
+           RISCV::FPR32RegClass.contains(Reg) ||
+           RISCV::FPR64RegClass.contains(Reg) ||
+           RISCV::FPR128RegClass.contains(Reg);
+  }
 };
 } // namespace llvm
 
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index 3669e14a04beb..f1f20e932c6fe 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -297,6 +297,16 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
     return UserReservedRegister[i.id()];
   }
 
+  std::optional<TargetRegisterClass> getLargestFPRegClass() const {
+    if (HasStdExtQ)
+      return RISCV::FPR128RegClass;
+    if (HasStdExtD)
+      return RISCV::FPR64RegClass;
+    if (HasStdExtF)
+      return RISCV::FPR32RegClass;
+    return std::nullopt;
+  };
+
   // XRay support - require D and C extensions.
   bool isXRaySupported() const override { return hasStdExtD() && hasStdExtC(); }
 
diff --git a/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll b/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll
new file mode 100644
index 0000000000000..b955455fb280f
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll
@@ -0,0 +1,469 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc < %s -verify-machineinstrs -mtriple=riscv32-unknown-unknown -mattr="+i,+m,+Zfh,+f" | FileCheck %s --check-prefixes=32-BITS-F
+; RUN: llc < %s -verify-machineinstrs -mtriple=riscv32-unknown-unknown -mattr="+i,+m,+Zfh,+d" | FileCheck %s --check-prefixes=32-BITS-D
+; RUN: llc < %s -verify-machineinstrs -mtriple=riscv32-unknown-unknown -mattr="+i,+m,+Zfh,+q" | FileCheck %s --check-prefixes=32-BITS-Q
+; RUN: llc < %s -verify-machineinstrs -mtriple=riscv64-unknown-unknown -mattr="+i,+m,+Zfh,+f" | FileCheck %s  --check-prefixes=64-BITS-F
+; RUN: llc < %s -verify-machineinstrs -mtriple=riscv64-unknown-unknown -mattr="+i,+m,+Zfh,+d" | FileCheck %s  --check-prefixes=64-BITS-D
+; RUN: llc < %s -verify-machineinstrs -mtriple=riscv64-unknown-unknown -mattr="+i,+m,+Zfh,+q" | FileCheck %s  --check-prefixes=64-BITS-Q
+
+define double @used(double noundef %a, float noundef %b) "zero-call-used-regs"="used" {
+; 32-BITS-F-LABEL: used:
+; 32-BITS-F:       # %bb.0: # %entry
+; 32-BITS-F-NEXT:    addi sp, sp, -16
+; 32-BITS-F-NEXT:    .cfi_def_cfa_offset 16
+; 32-BITS-F-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; 32-BITS-F-NEXT:    sw s0, 8(sp) # 4-byte Folded Spill
+; 32-BITS-F-NEXT:    sw s1, 4(sp) # 4-byte Folded Spill
+; 32-BITS-F-NEXT:    .cfi_offset ra, -4
+; 32-BITS-F-NEXT:    .cfi_offset s0, -8
+; 32-BITS-F-NEXT:    .cfi_offset s1, -12
+; 32-BITS-F-NEXT:    mv s0, a1
+; 32-BITS-F-NEXT:    mv s1, a0
+; 32-BITS-F-NEXT:    call __extendsfdf2
+; 32-BITS-F-NEXT:    mv a2, s1
+; 32-BITS-F-NEXT:    mv a3, s0
+; 32-BITS-F-NEXT:    call __muldf3
+; 32-BITS-F-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; 32-BITS-F-NEXT:    lw s0, 8(sp) # 4-byte Folded Reload
+; 32-BITS-F-NEXT:    lw s1, 4(sp) # 4-byte Folded Reload
+; 32-BITS-F-NEXT:    .cfi_restore ra
+; 32-BITS-F-NEXT:    .cfi_restore s0
+; 32-BITS-F-NEXT:    .cfi_restore s1
+; 32-BITS-F-NEXT:    addi sp, sp, 16
+; 32-BITS-F-NEXT:    .cfi_def_cfa_offset 0
+; 32-BITS-F-NEXT:    li a2, 0
+; 32-BITS-F-NEXT:    li a3, 0
+; 32-BITS-F-NEXT:    ret
+;
+; 32-BITS-D-LABEL: used:
+; 32-BITS-D:       # %bb.0: # %entry
+; 32-BITS-D-NEXT:    fcvt.d.s fa5, fa1
+; 32-BITS-D-NEXT:    fmul.d fa0, fa5, fa0
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa5, zero, rtz
+; 32-BITS-D-NEXT:    ret
+;
+; 32-BITS-Q-LABEL: used:
+; 32-BITS-Q:       # %bb.0: # %entry
+; 32-BITS-Q-NEXT:    fcvt.d.s fa5, fa1
+; 32-BITS-Q-NEXT:    fmul.d fa0, fa5, fa0
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa5, zero, rtz
+; 32-BITS-Q-NEXT:    ret
+;
+; 64-BITS-F-LABEL: used:
+; 64-BITS-F:       # %bb.0: # %entry
+; 64-BITS-F-NEXT:    addi sp, sp, -16
+; 64-BITS-F-NEXT:    .cfi_def_cfa_offset 16
+; 64-BITS-F-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; 64-BITS-F-NEXT:    sd s0, 0(sp) # 8-byte Folded Spill
+; 64-BITS-F-NEXT:    .cfi_offset ra, -8
+; 64-BITS-F-NEXT:    .cfi_offset s0, -16
+; 64-BITS-F-NEXT:    mv s0, a0
+; 64-BITS-F-NEXT:    call __extendsfdf2
+; 64-BITS-F-NEXT:    mv a1, s0
+; 64-BITS-F-NEXT:    call __muldf3
+; 64-BITS-F-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; 64-BITS-F-NEXT:    ld s0, 0(sp) # 8-byte Folded Reload
+; 64-BITS-F-NEXT:    .cfi_restore ra
+; 64-BITS-F-NEXT:    .cfi_restore s0
+; 64-BITS-F-NEXT:    addi sp, sp, 16
+; 64-BITS-F-NEXT:    .cfi_def_cfa_offset 0
+; 64-BITS-F-NEXT:    li a1, 0
+; 64-BITS-F-NEXT:    ret
+;
+; 64-BITS-D-LABEL: used:
+; 64-BITS-D:       # %bb.0: # %entry
+; 64-BITS-D-NEXT:    fcvt.d.s fa5, fa1
+; 64-BITS-D-NEXT:    fmul.d fa0, fa5, fa0
+; 64-BITS-D-NEXT:    fmv.d.x fa1, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa5, zero
+; 64-BITS-D-NEXT:    ret
+;
+; 64-BITS-Q-LABEL: used:
+; 64-BITS-Q:       # %bb.0: # %entry
+; 64-BITS-Q-NEXT:    fcvt.d.s fa5, fa1
+; 64-BITS-Q-NEXT:    fmul.d fa0, fa5, fa0
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa5, zero, rtz
+; 64-BITS-Q-NEXT:    ret
+entry:
+  %conv = fpext float %b to double
+  %mul = fmul double %conv, %a
+  ret double %mul
+}
+
+define half @used_arg_half(half noundef %a, half noundef %b) "zero-call-used-regs"="used-arg" {
+; 32-BITS-F-LABEL: used_arg_half:
+; 32-BITS-F:       # %bb.0: # %entry
+; 32-BITS-F-NEXT:    fmul.h fa0, fa0, fa1
+; 32-BITS-F-NEXT:    fmv.w.x fa1, zero
+; 32-BITS-F-NEXT:    ret
+;
+; 32-BITS-D-LABEL: used_arg_half:
+; 32-BITS-D:       # %bb.0: # %entry
+; 32-BITS-D-NEXT:    fmul.h fa0, fa0, fa1
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
+; 32-BITS-D-NEXT:    ret
+;
+; 32-BITS-Q-LABEL: used_arg_half:
+; 32-BITS-Q:       # %bb.0: # %entry
+; 32-BITS-Q-NEXT:    fmul.h fa0, fa0, fa1
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 32-BITS-Q-NEXT:    ret
+;
+; 64-BITS-F-LABEL: used_arg_half:
+; 64-BITS-F:       # %bb.0: # %entry
+; 64-BITS-F-NEXT:    fmul.h fa0, fa0, fa1
+; 64-BITS-F-NEXT:    fmv.w.x fa1, zero
+; 64-BITS-F-NEXT:    ret
+;
+; 64-BITS-D-LABEL: used_arg_half:
+; 64-BITS-D:       # %bb.0: # %entry
+; 64-BITS-D-NEXT:    fmul.h fa0, fa0, fa1
+; 64-BITS-D-NEXT:    fmv.d.x fa1, zero
+; 64-BITS-D-NEXT:    ret
+;
+; 64-BITS-Q-LABEL: used_arg_half:
+; 64-BITS-Q:       # %bb.0: # %entry
+; 64-BITS-Q-NEXT:    fmul.h fa0, fa0, fa1
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 64-BITS-Q-NEXT:    ret
+entry:
+  %mul = fmul half %a, %b
+  ret half %mul
+}
+
+define float @used_arg_float(float noundef %a, float noundef %b) "zero-call-used-regs"="used-arg" {
+; 32-BITS-F-LABEL: used_arg_float:
+; 32-BITS-F:       # %bb.0: # %entry
+; 32-BITS-F-NEXT:    fmul.s fa0, fa0, fa1
+; 32-BITS-F-NEXT:    fmv.w.x fa1, zero
+; 32-BITS-F-NEXT:    ret
+;
+; 32-BITS-D-LABEL: used_arg_float:
+; 32-BITS-D:       # %bb.0: # %entry
+; 32-BITS-D-NEXT:    fmul.s fa0, fa0, fa1
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
+; 32-BITS-D-NEXT:    ret
+;
+; 32-BITS-Q-LABEL: used_arg_float:
+; 32-BITS-Q:       # %bb.0: # %entry
+; 32-BITS-Q-NEXT:    fmul.s fa0, fa0, fa1
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 32-BITS-Q-NEXT:    ret
+;
+; 64-BITS-F-LABEL: used_arg_float:
+; 64-BITS-F:       # %bb.0: # %entry
+; 64-BITS-F-NEXT:    fmul.s fa0, fa0, fa1
+; 64-BITS-F-NEXT:    fmv.w.x fa1, zero
+; 64-BITS-F-NEXT:    ret
+;
+; 64-BITS-D-LABEL: used_arg_float:
+; 64-BITS-D:       # %bb.0: # %entry
+; 64-BITS-D-NEXT:    fmul.s fa0, fa0, fa1
+; 64-BITS-D-NEXT:    fmv.d.x fa1, zero
+; 64-BITS-D-NEXT:    ret
+;
+; 64-BITS-Q-LABEL: used_arg_float:
+; 64-BITS-Q:       # %bb.0: # %entry
+; 64-BITS-Q-NEXT:    fmul.s fa0, fa0, fa1
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 64-BITS-Q-NEXT:    ret
+entry:
+  %mul = fmul float %a, %b
+  ret float %mul
+}
+
+define double @used_arg_double(double noundef %a, double noundef %b) "zero-call-used-regs"="used-arg" {
+; 32-BITS-F-LABEL: used_arg_double:
+; 32-BITS-F:       # %bb.0: # %entry
+; 32-BITS-F-NEXT:    addi sp, sp, -16
+; 32-BITS-F-NEXT:    .cfi_def_cfa_offset 16
+; 32-BITS-F-NEXT:    sw ra, 12(sp) # 4-byte Folded Spill
+; 32-BITS-F-NEXT:    .cfi_offset ra, -4
+; 32-BITS-F-NEXT:    call __muldf3
+; 32-BITS-F-NEXT:    lw ra, 12(sp) # 4-byte Folded Reload
+; 32-BITS-F-NEXT:    .cfi_restore ra
+; 32-BITS-F-NEXT:    addi sp, sp, 16
+; 32-BITS-F-NEXT:    .cfi_def_cfa_offset 0
+; 32-BITS-F-NEXT:    ret
+;
+; 32-BITS-D-LABEL: used_arg_double:
+; 32-BITS-D:       # %bb.0: # %entry
+; 32-BITS-D-NEXT:    fmul.d fa0, fa0, fa1
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
+; 32-BITS-D-NEXT:    ret
+;
+; 32-BITS-Q-LABEL: used_arg_double:
+; 32-BITS-Q:       # %bb.0: # %entry
+; 32-BITS-Q-NEXT:    fmul.d fa0, fa0, fa1
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 32-BITS-Q-NEXT:    ret
+;
+; 64-BITS-F-LABEL: used_arg_double:
+; 64-BITS-F:       # %bb.0: # %entry
+; 64-BITS-F-NEXT:    addi sp, sp, -16
+; 64-BITS-F-NEXT:    .cfi_def_cfa_offset 16
+; 64-BITS-F-NEXT:    sd ra, 8(sp) # 8-byte Folded Spill
+; 64-BITS-F-NEXT:    .cfi_offset ra, -8
+; 64-BITS-F-NEXT:    call __muldf3
+; 64-BITS-F-NEXT:    ld ra, 8(sp) # 8-byte Folded Reload
+; 64-BITS-F-NEXT:    .cfi_restore ra
+; 64-BITS-F-NEXT:    addi sp, sp, 16
+; 64-BITS-F-NEXT:    .cfi_def_cfa_offset 0
+; 64-BITS-F-NEXT:    ret
+;
+; 64-BITS-D-LABEL: used_arg_double:
+; 64-BITS-D:       # %bb.0: # %entry
+; 64-BITS-D-NEXT:    fmul.d fa0, fa0, fa1
+; 64-BITS-D-NEXT:    fmv.d.x fa1, zero
+; 64-BITS-D-NEXT:    ret
+;
+; 64-BITS-Q-LABEL: used_arg_double:
+; 64-BITS-Q:       # %bb.0: # %entry
+; 64-BITS-Q-NEXT:    fmul.d fa0, fa0, fa1
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 64-BITS-Q-NEXT:    ret
+entry:
+  %mul = fmul double %a, %b
+  ret double %mul
+}
+
+define void @all_fp() "zero-call-used-regs"="all" {
+; 32-BITS-F-LABEL: all_fp:
+; 32-BITS-F:       # %bb.0: # %entry
+; 32-BITS-F-NEXT:    li t0, 0
+; 32-BITS-F-NEXT:    li t1, 0
+; 32-BITS-F-NEXT:    li t2, 0
+; 32-BITS-F-NEXT:    li a0, 0
+; 32-BITS-F-NEXT:    li a1, 0
+; 32-BITS-F-NEXT:    li a2, 0
+; 32-BITS-F-NEXT:    li a3, 0
+; 32-BITS-F-NEXT:    li a4, 0
+; 32-BITS-F-NEXT:    li a5, 0
+; 32-BITS-F-NEXT:    li a6, 0
+; 32-BITS-F-NEXT:    li a7, 0
+; 32-BITS-F-NEXT:    li t3, 0
+; 32-BITS-F-NEXT:    li t4, 0
+; 32-BITS-F-NEXT:    li t5, 0
+; 32-BITS-F-NEXT:    li t6, 0
+; 32-BITS-F-NEXT:    fmv.w.x ft0, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft1, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft2, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft3, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft4, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft5, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft6, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft7, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa0, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa1, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa2, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa3, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa4, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa5, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa6, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa7, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft8, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft9, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft10, zero
+; 32-BITS-F-NEXT:    fmv.w.x ft11, zero
+; 32-BITS-F-NEXT:    ret
+;
+; 32-BITS-D-LABEL: all_fp:
+; 32-BITS-D:       # %bb.0: # %entry
+; 32-BITS-D-NEXT:    li t0, 0
+; 32-BITS-D-NEXT:    li t1, 0
+; 32-BITS-D-NEXT:    li t2, 0
+; 32-BITS-D-NEXT:    li a0, 0
+; 32-BITS-D-NEXT:    li a1, 0
+; 32-BITS-D-NEXT:    li a2, 0
+; 32-BITS-D-NEXT:    li a3, 0
+; 32-BITS-D-NEXT:    li a4, 0
+; 32-BITS-D-NEXT:    li a5, 0
+; 32-BITS-D-NEXT:    li a6, 0
+; 32-BITS-D-NEXT:    li a7, 0
+; 32-BITS-D-NEXT:    li t3, 0
+; 32-BITS-D-NEXT:    li t4, 0
+; 32-BITS-D-NEXT:    li t5, 0
+; 32-BITS-D-NEXT:    li t6, 0
+; 32-BITS-D-NEXT:    fcvt.d.w ft0, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft1, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft2, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft3, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft4, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft5, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft6, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft7, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa0, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa2, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa3, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa4, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa5, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa6, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa7, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft8, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft9, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft10, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft11, zero, rtz
+; 32-BITS-D-NEXT:    ret
+;
+; 32-BITS-Q-LABEL: all_fp:
+; 32-BITS-Q:       # %bb.0: # %entry
+; 32-BITS-Q-NEXT:    li t0, 0
+; 32-BITS-Q-NEXT:    li t1, 0
+; 32-BITS-Q-NEXT:    li t2, 0
+; 32-BITS-Q-NEXT:    li a0, 0
+; 32-BITS-Q-NEXT:    li a1, 0
+; 32-BITS-Q-NEXT:    li a2, 0
+; 32-BITS-Q-NEXT:    li a3, 0
+; 32-BITS-Q-NEXT:    li a4, 0
+; 32-BITS-Q-NEXT:    li a5, 0
+; 32-BITS-Q-NEXT:    li a6, 0
+; 32-BITS-Q-NEXT:    li a7, 0
+; 32-BITS-Q-NEXT:    li t3, 0
+; 32-BITS-Q-NEXT:    li t4, 0
+; 32-BITS-Q-NEXT:    li t5, 0
+; 32-BITS-Q-NEXT:    li t6, 0
+; 32-BITS-Q-NEXT:    fcvt.q.w ft0, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft1, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft2, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft3, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft4, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft5, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft6, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft7, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa0, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa2, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa3, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa4, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa5, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa6, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa7, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft8, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft9, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft10, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft11, zero, rtz
+; 32-BITS-Q-NEXT:    ret
+;
+; 64-BITS-F-LABEL: all_fp:
+; 64-BITS-F:       # %bb.0: # %entry
+; 64-BITS-F-NEXT:    li t0, 0
+; 64-BITS-F-NEXT:    li t1, 0
+; 64-BITS-F-NEXT:    li t2, 0
+; 64-BITS-F-NEXT:    li a0, 0
+; 64-BITS-F-NEXT:    li a1, 0
+; 64-BITS-F-NEXT:    li a2, 0
+; 64-BITS-F-NEXT:    li a3, 0
+; 64-BITS-F-NEXT:    li a4, 0
+; 64-BITS-F-NEXT:    li a5, 0
+; 64-BITS-F-NEXT:    li a6, 0
+; 64-BITS-F-NEXT:    li a7, 0
+; 64-BITS-F-NEXT:    li t3, 0
+; 64-BITS-F-NEXT:    li t4, 0
+; 64-BITS-F-NEXT:    li t5, 0
+; 64-BITS-F-NEXT:    li t6, 0
+; 64-BITS-F-NEXT:    fmv.w.x ft0, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft1, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft2, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft3, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft4, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft5, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft6, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft7, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa0, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa1, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa2, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa3, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa4, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa5, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa6, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa7, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft8, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft9, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft10, zero
+; 64-BITS-F-NEXT:    fmv.w.x ft11, zero
+; 64-BITS-F-NEXT:    ret
+;
+; 64-BITS-D-LABEL: all_fp:
+; 64-BITS-D:       # %bb.0: # %entry
+; 64-BITS-D-NEXT:    li t0, 0
+; 64-BITS-D-NEXT:    li t1, 0
+; 64-BITS-D-NEXT:    li t2, 0
+; 64-BITS-D-NEXT:    li a0, 0
+; 64-BITS-D-NEXT:    li a1, 0
+; 64-BITS-D-NEXT:    li a2, 0
+; 64-BITS-D-NEXT:    li a3, 0
+; 64-BITS-D-NEXT:    li a4, 0
+; 64-BITS-D-NEXT:    li a5, 0
+; 64-BITS-D-NEXT:    li a6, 0
+; 64-BITS-D-NEXT:    li a7, 0
+; 64-BITS-D-NEXT:    li t3, 0
+; 64-BITS-D-NEXT:    li t4, 0
+; 64-BITS-D-NEXT:    li t5, 0
+; 64-BITS-D-NEXT:    li t6, 0
+; 64-BITS-D-NEXT:    fmv.d.x ft0, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft1, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft2, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft3, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft4, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft5, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft6, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft7, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa0, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa1, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa2, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa3, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa4, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa5, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa6, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa7, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft8, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft9, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft10, zero
+; 64-BITS-D-NEXT:    fmv.d.x ft11, zero
+; 64-BITS-D-NEXT:    ret
+;
+; 64-BITS-Q-LABEL: all_fp:
+; 64-BITS-Q:       # %bb.0: # %entry
+; 64-BITS-Q-NEXT:    li t0, 0
+; 64-BITS-Q-NEXT:    li t1, 0
+; 64-BITS-Q-NEXT:    li t2, 0
+; 64-BITS-Q-NEXT:    li a0, 0
+; 64-BITS-Q-NEXT:    li a1, 0
+; 64-BITS-Q-NEXT:    li a2, 0
+; 64-BITS-Q-NEXT:    li a3, 0
+; 64-BITS-Q-NEXT:    li a4, 0
+; 64-BITS-Q-NEXT:    li a5, 0
+; 64-BITS-Q-NEXT:    li a6, 0
+; 64-BITS-Q-NEXT:    li a7, 0
+; 64-BITS-Q-NEXT:    li t3, 0
+; 64-BITS-Q-NEXT:    li t4, 0
+; 64-BITS-Q-NEXT:    li t5, 0
+; 64-BITS-Q-NEXT:    li t6, 0
+; 64-BITS-Q-NEXT:    fcvt.q.w ft0, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft1, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft2, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft3, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft4, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft5, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft6, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft7, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa0, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa2, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa3, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa4, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa5, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa6, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa7, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft8, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft9, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft10, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft11, zero, rtz
+; 64-BITS-Q-NEXT:    ret
+entry:
+  ret void
+}

>From e3599044b6a4dc87127866b3a75dc3e8af92aad0 Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Mon, 22 Jun 2026 09:08:30 +0200
Subject: [PATCH 2/7] Fix comments

---
 .../Target/RISCV/RISCVExpandPseudoInsts.cpp   |  2 +-
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp  | 26 ++++++++++---------
 llvm/lib/Target/RISCV/RISCVInstrInfoD.td      |  3 +--
 llvm/lib/Target/RISCV/RISCVInstrInfoF.td      |  3 +--
 llvm/lib/Target/RISCV/RISCVInstrInfoQ.td      |  5 ++--
 llvm/lib/Target/RISCV/RISCVSubtarget.h        | 10 +++----
 6 files changed, 24 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
index 5e2882b066752..bc86a60934813 100644
--- a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
@@ -635,7 +635,7 @@ bool RISCVExpandPseudo::expandPseudoClearFPR64(
   } else {
     BuildMI(MBB, MBBI, DL, TII->get(RISCV::FCVT_D_W), Dst)
         .addReg(RISCV::X0)
-        .addImm(RISCVFPRndMode::RTZ);
+        .addImm(RISCVFPRndMode::RNE);
   }
 
   MBBI->eraseFromParent();
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 754462d22eb8a..c35f0cb7d99a2 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -1436,28 +1436,30 @@ static MCRegister getLargestFPRegisterOrZero(const RISCVSubtarget &STI,
                                              const TargetRegisterInfo &TRI,
                                              MCRegister Reg) {
   if (!STI.hasStdExtF())
-    return 0;
+    return MCRegister();
 
-  TargetRegisterClass LargestFPRegClass = STI.getLargestFPRegClass().value();
+  TargetRegisterClass const *LargestFPRegClass = STI.getLargestFPRegClass();
+  assert(LargestFPRegClass);
 
-  if (LargestFPRegClass.contains(Reg))
+  if (LargestFPRegClass->contains(Reg))
     return Reg;
 
-  std::array<TargetRegisterClass, 3> RegisterClasses = {
-      RISCV::FPR16RegClass, RISCV::FPR32RegClass, RISCV::FPR64RegClass};
-  std::array<int, 3> SubIdx = {RISCV::sub_16, RISCV::sub_32, RISCV::sub_64};
+  std::array<TargetRegisterClass const *, 3> RegisterClasses = {
+      &RISCV::FPR16RegClass, &RISCV::FPR32RegClass, &RISCV::FPR64RegClass};
+  std::array<unsigned, 3> SubIdx = {RISCV::sub_16, RISCV::sub_32,
+                                    RISCV::sub_64};
 
-  for (unsigned i = 0; i < RegisterClasses.size(); ++i) {
-    if (RegisterClasses[i].contains(Reg)) {
+  for (auto [RegClass, SubReg] : zip(RegisterClasses, SubIdx)) {
+    if (RegClass->contains(Reg)) {
       if (MCRegister Super =
-              TRI.getMatchingSuperReg(Reg, SubIdx[i], &LargestFPRegClass))
+              TRI.getMatchingSuperReg(Reg, SubReg, LargestFPRegClass))
         return Super;
     }
   }
 
   // Reg is bigger than what's currently available for the target, we can ignore
   // it.
-  return 0;
+  return MCRegister();
 }
 
 void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
@@ -1478,10 +1480,10 @@ void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
 
   for (MCRegister Reg : RegsToZero.set_bits()) {
     if (TRI.isGeneralPurposeRegister(MF, Reg))
-      FinalRegsToZero.set(Reg);
+      FinalRegsToZero.set(Reg.id());
     else if (TRI.isFPRegister(Reg)) {
       if (MCRegister MaybeReg = getLargestFPRegisterOrZero(STI, TRI, Reg))
-        FinalRegsToZero.set(MaybeReg);
+        FinalRegsToZero.set(MaybeReg.id());
     }
   }
 
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoD.td b/llvm/lib/Target/RISCV/RISCVInstrInfoD.td
index acac861b57b65..d8f9c551d9e5d 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoD.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoD.td
@@ -667,6 +667,5 @@ def : Pat<(any_sint_to_fp (i64 GPR:$rs1)), (FCVT_D_L_INX GPR:$rs1, FRM_DYN)>;
 def : Pat<(any_uint_to_fp (i64 GPR:$rs1)), (FCVT_D_LU_INX GPR:$rs1, FRM_DYN)>;
 } // Predicates = [HasStdExtZdinx, IsRV64]
 
-let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 8,
-    isCodeGenOnly = true in
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
 def PseudoClearFPR64 : Pseudo<(outs FPR64:$rd), (ins), []>;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoF.td b/llvm/lib/Target/RISCV/RISCVInstrInfoF.td
index a7660bb6b1a5d..1ccbc7e76de30 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoF.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoF.td
@@ -868,7 +868,6 @@ def : Pat<(any_uint_to_fp (i64 GPR:$rs1)), (FCVT_S_LU_INX $rs1, FRM_DYN)>;
 } // Predicates = [HasStdExtZfinx, IsRV64]
 
 // Used by -fzero-call-used-regs to zero out registers.
-let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 8,
-    isCodeGenOnly = true in
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
 def PseudoClearFPR32 : Pseudo<(outs FPR32:$rd), (ins), []>,
                        PseudoInstExpansion<(FMV_W_X FPR32:$rd, X0)>;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoQ.td b/llvm/lib/Target/RISCV/RISCVInstrInfoQ.td
index efcee6db91e22..4d0d71b93a055 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoQ.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoQ.td
@@ -166,8 +166,7 @@ let Predicates = [HasStdExtQ] in {
   def PseudoFSQ : PseudoStore<"fsq", FPR128>;
 } // Predicates = [HasStdExtQ]
 
-let hasSideEffects = 0, mayLoad = 0, mayStore = 0, Size = 8,
-    isCodeGenOnly = true in
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
 def PseudoClearFPR128 : Pseudo<(outs FPR128:$rd), (ins), []>,
-                        PseudoInstExpansion<(FCVT_Q_W FPR128:$rd, X0, FRM_RTZ)>;
+                        PseudoInstExpansion<(FCVT_Q_W FPR128:$rd, X0, FRM_RNE)>;
 
diff --git a/llvm/lib/Target/RISCV/RISCVSubtarget.h b/llvm/lib/Target/RISCV/RISCVSubtarget.h
index f1f20e932c6fe..0971ae85a5634 100644
--- a/llvm/lib/Target/RISCV/RISCVSubtarget.h
+++ b/llvm/lib/Target/RISCV/RISCVSubtarget.h
@@ -297,14 +297,14 @@ class RISCVSubtarget : public RISCVGenSubtargetInfo {
     return UserReservedRegister[i.id()];
   }
 
-  std::optional<TargetRegisterClass> getLargestFPRegClass() const {
+  TargetRegisterClass const *getLargestFPRegClass() const {
     if (HasStdExtQ)
-      return RISCV::FPR128RegClass;
+      return &RISCV::FPR128RegClass;
     if (HasStdExtD)
-      return RISCV::FPR64RegClass;
+      return &RISCV::FPR64RegClass;
     if (HasStdExtF)
-      return RISCV::FPR32RegClass;
-    return std::nullopt;
+      return &RISCV::FPR32RegClass;
+    return nullptr;
   };
 
   // XRay support - require D and C extensions.

>From eb81ac79948c78132273ff68de50e7bbb88e510b Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Mon, 22 Jun 2026 09:18:04 +0200
Subject: [PATCH 3/7] Update test case

---
 .../CodeGen/RISCV/zero-call-used-regs-fp.ll   | 150 +++++++++---------
 1 file changed, 75 insertions(+), 75 deletions(-)

diff --git a/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll b/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll
index b955455fb280f..282fe6c93011a 100644
--- a/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll
+++ b/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll
@@ -39,16 +39,16 @@ define double @used(double noundef %a, float noundef %b) "zero-call-used-regs"="
 ; 32-BITS-D:       # %bb.0: # %entry
 ; 32-BITS-D-NEXT:    fcvt.d.s fa5, fa1
 ; 32-BITS-D-NEXT:    fmul.d fa0, fa5, fa0
-; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa5, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa5, zero
 ; 32-BITS-D-NEXT:    ret
 ;
 ; 32-BITS-Q-LABEL: used:
 ; 32-BITS-Q:       # %bb.0: # %entry
 ; 32-BITS-Q-NEXT:    fcvt.d.s fa5, fa1
 ; 32-BITS-Q-NEXT:    fmul.d fa0, fa5, fa0
-; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa5, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa5, zero
 ; 32-BITS-Q-NEXT:    ret
 ;
 ; 64-BITS-F-LABEL: used:
@@ -84,8 +84,8 @@ define double @used(double noundef %a, float noundef %b) "zero-call-used-regs"="
 ; 64-BITS-Q:       # %bb.0: # %entry
 ; 64-BITS-Q-NEXT:    fcvt.d.s fa5, fa1
 ; 64-BITS-Q-NEXT:    fmul.d fa0, fa5, fa0
-; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa5, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa5, zero
 ; 64-BITS-Q-NEXT:    ret
 entry:
   %conv = fpext float %b to double
@@ -103,13 +103,13 @@ define half @used_arg_half(half noundef %a, half noundef %b) "zero-call-used-reg
 ; 32-BITS-D-LABEL: used_arg_half:
 ; 32-BITS-D:       # %bb.0: # %entry
 ; 32-BITS-D-NEXT:    fmul.h fa0, fa0, fa1
-; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero
 ; 32-BITS-D-NEXT:    ret
 ;
 ; 32-BITS-Q-LABEL: used_arg_half:
 ; 32-BITS-Q:       # %bb.0: # %entry
 ; 32-BITS-Q-NEXT:    fmul.h fa0, fa0, fa1
-; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero
 ; 32-BITS-Q-NEXT:    ret
 ;
 ; 64-BITS-F-LABEL: used_arg_half:
@@ -127,7 +127,7 @@ define half @used_arg_half(half noundef %a, half noundef %b) "zero-call-used-reg
 ; 64-BITS-Q-LABEL: used_arg_half:
 ; 64-BITS-Q:       # %bb.0: # %entry
 ; 64-BITS-Q-NEXT:    fmul.h fa0, fa0, fa1
-; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero
 ; 64-BITS-Q-NEXT:    ret
 entry:
   %mul = fmul half %a, %b
@@ -144,13 +144,13 @@ define float @used_arg_float(float noundef %a, float noundef %b) "zero-call-used
 ; 32-BITS-D-LABEL: used_arg_float:
 ; 32-BITS-D:       # %bb.0: # %entry
 ; 32-BITS-D-NEXT:    fmul.s fa0, fa0, fa1
-; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero
 ; 32-BITS-D-NEXT:    ret
 ;
 ; 32-BITS-Q-LABEL: used_arg_float:
 ; 32-BITS-Q:       # %bb.0: # %entry
 ; 32-BITS-Q-NEXT:    fmul.s fa0, fa0, fa1
-; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero
 ; 32-BITS-Q-NEXT:    ret
 ;
 ; 64-BITS-F-LABEL: used_arg_float:
@@ -168,7 +168,7 @@ define float @used_arg_float(float noundef %a, float noundef %b) "zero-call-used
 ; 64-BITS-Q-LABEL: used_arg_float:
 ; 64-BITS-Q:       # %bb.0: # %entry
 ; 64-BITS-Q-NEXT:    fmul.s fa0, fa0, fa1
-; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero
 ; 64-BITS-Q-NEXT:    ret
 entry:
   %mul = fmul float %a, %b
@@ -192,13 +192,13 @@ define double @used_arg_double(double noundef %a, double noundef %b) "zero-call-
 ; 32-BITS-D-LABEL: used_arg_double:
 ; 32-BITS-D:       # %bb.0: # %entry
 ; 32-BITS-D-NEXT:    fmul.d fa0, fa0, fa1
-; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero
 ; 32-BITS-D-NEXT:    ret
 ;
 ; 32-BITS-Q-LABEL: used_arg_double:
 ; 32-BITS-Q:       # %bb.0: # %entry
 ; 32-BITS-Q-NEXT:    fmul.d fa0, fa0, fa1
-; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero
 ; 32-BITS-Q-NEXT:    ret
 ;
 ; 64-BITS-F-LABEL: used_arg_double:
@@ -223,7 +223,7 @@ define double @used_arg_double(double noundef %a, double noundef %b) "zero-call-
 ; 64-BITS-Q-LABEL: used_arg_double:
 ; 64-BITS-Q:       # %bb.0: # %entry
 ; 64-BITS-Q-NEXT:    fmul.d fa0, fa0, fa1
-; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero
 ; 64-BITS-Q-NEXT:    ret
 entry:
   %mul = fmul double %a, %b
@@ -287,26 +287,26 @@ define void @all_fp() "zero-call-used-regs"="all" {
 ; 32-BITS-D-NEXT:    li t4, 0
 ; 32-BITS-D-NEXT:    li t5, 0
 ; 32-BITS-D-NEXT:    li t6, 0
-; 32-BITS-D-NEXT:    fcvt.d.w ft0, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft1, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft2, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft3, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft4, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft5, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft6, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft7, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa0, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa2, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa3, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa4, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa5, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa6, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w fa7, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft8, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft9, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft10, zero, rtz
-; 32-BITS-D-NEXT:    fcvt.d.w ft11, zero, rtz
+; 32-BITS-D-NEXT:    fcvt.d.w ft0, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft1, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft2, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft3, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft4, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft5, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft6, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft7, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa0, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa2, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa3, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa4, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa5, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa6, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa7, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft8, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft9, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft10, zero
+; 32-BITS-D-NEXT:    fcvt.d.w ft11, zero
 ; 32-BITS-D-NEXT:    ret
 ;
 ; 32-BITS-Q-LABEL: all_fp:
@@ -326,26 +326,26 @@ define void @all_fp() "zero-call-used-regs"="all" {
 ; 32-BITS-Q-NEXT:    li t4, 0
 ; 32-BITS-Q-NEXT:    li t5, 0
 ; 32-BITS-Q-NEXT:    li t6, 0
-; 32-BITS-Q-NEXT:    fcvt.q.w ft0, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft1, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft2, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft3, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft4, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft5, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft6, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft7, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa0, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa2, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa3, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa4, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa5, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa6, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w fa7, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft8, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft9, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft10, zero, rtz
-; 32-BITS-Q-NEXT:    fcvt.q.w ft11, zero, rtz
+; 32-BITS-Q-NEXT:    fcvt.q.w ft0, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft1, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft2, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft3, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft4, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft5, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft6, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft7, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa0, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa2, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa3, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa4, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa5, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa6, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa7, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft8, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft9, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft10, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w ft11, zero
 ; 32-BITS-Q-NEXT:    ret
 ;
 ; 64-BITS-F-LABEL: all_fp:
@@ -443,26 +443,26 @@ define void @all_fp() "zero-call-used-regs"="all" {
 ; 64-BITS-Q-NEXT:    li t4, 0
 ; 64-BITS-Q-NEXT:    li t5, 0
 ; 64-BITS-Q-NEXT:    li t6, 0
-; 64-BITS-Q-NEXT:    fcvt.q.w ft0, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft1, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft2, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft3, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft4, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft5, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft6, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft7, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa0, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa2, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa3, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa4, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa5, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa6, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w fa7, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft8, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft9, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft10, zero, rtz
-; 64-BITS-Q-NEXT:    fcvt.q.w ft11, zero, rtz
+; 64-BITS-Q-NEXT:    fcvt.q.w ft0, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft1, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft2, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft3, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft4, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft5, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft6, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft7, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa0, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa2, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa3, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa4, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa5, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa6, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa7, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft8, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft9, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft10, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w ft11, zero
 ; 64-BITS-Q-NEXT:    ret
 entry:
   ret void

>From e224b209129ec03baa918dab7a2321ad854bbda5 Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Mon, 22 Jun 2026 21:36:42 +0200
Subject: [PATCH 4/7] Add braces

---
 llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index c35f0cb7d99a2..45ccc8b0055af 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -1479,9 +1479,9 @@ void RISCVFrameLowering::emitZeroCallUsedRegs(BitVector RegsToZero,
   BitVector FinalRegsToZero(TRI.getNumRegs());
 
   for (MCRegister Reg : RegsToZero.set_bits()) {
-    if (TRI.isGeneralPurposeRegister(MF, Reg))
+    if (TRI.isGeneralPurposeRegister(MF, Reg)) {
       FinalRegsToZero.set(Reg.id());
-    else if (TRI.isFPRegister(Reg)) {
+    } else if (TRI.isFPRegister(Reg)) {
       if (MCRegister MaybeReg = getLargestFPRegisterOrZero(STI, TRI, Reg))
         FinalRegsToZero.set(MaybeReg.id());
     }

>From 2d54897dcf0a19480452905a6e478516c7593ccf Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Tue, 23 Jun 2026 10:21:05 +0200
Subject: [PATCH 5/7] [RISCV] Add support for -fzero-call-used-regs=all-arg

Before this patch, `isArgumentRegister` would fail on any non-GPR
register, instead, we now return false in the default case. The function
is thus subject to returning false negative, but the change allows us to
incrementally add support for new register classes.

The patch also adds support for floating point registers to the
function.
---
 llvm/lib/Target/RISCV/RISCVCallingConv.cpp    |  26 ++++
 llvm/lib/Target/RISCV/RISCVCallingConv.h      |   2 +
 llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp   |  10 +-
 .../CodeGen/RISCV/zero-call-used-regs-fp.ll   | 124 ++++++++++++++++++
 4 files changed, 158 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
index 9853644080161..e513dcf560e24 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.cpp
@@ -89,6 +89,10 @@ static const MCPhysReg ArgFPR32s[] = {RISCV::F10_F, RISCV::F11_F, RISCV::F12_F,
 static const MCPhysReg ArgFPR64s[] = {RISCV::F10_D, RISCV::F11_D, RISCV::F12_D,
                                       RISCV::F13_D, RISCV::F14_D, RISCV::F15_D,
                                       RISCV::F16_D, RISCV::F17_D};
+static const MCPhysReg ArgFPR128s[] = {RISCV::F10_Q, RISCV::F11_Q, RISCV::F12_Q,
+                                       RISCV::F13_Q, RISCV::F14_Q, RISCV::F15_Q,
+                                       RISCV::F16_Q, RISCV::F17_Q};
+
 // This is an interim calling convention and it may be changed in the future.
 static const MCPhysReg ArgVRs[] = {
     RISCV::V8,  RISCV::V9,  RISCV::V10, RISCV::V11, RISCV::V12, RISCV::V13,
@@ -177,6 +181,28 @@ ArrayRef<MCPhysReg> RISCV::getArgGPRs(const RISCVABI::ABI ABI) {
   return ArrayRef(ArgIGPRs);
 }
 
+ArrayRef<MCPhysReg> RISCV::getArgFPRs(const RISCVSubtarget &STI) {
+  static const RISCVABI::ABI SoftFPABIs[] = {
+      RISCVABI::ABI_ILP32,
+      RISCVABI::ABI_ILP32E,
+      RISCVABI::ABI_LP64,
+      RISCVABI::ABI_LP64E,
+  };
+
+  RISCVABI::ABI ABI = STI.getTargetABI();
+
+  if (llvm::is_contained(SoftFPABIs, ABI) || !STI.hasStdExtF())
+    return {};
+
+  if (STI.hasStdExtQ())
+    return ArrayRef(ArgFPR128s);
+
+  if (STI.hasStdExtD())
+    return ArrayRef(ArgFPR64s);
+
+  return ArrayRef(ArgFPR32s);
+}
+
 static ArrayRef<MCPhysReg> getArgGPR16s(const RISCVABI::ABI ABI) {
   // The GPRs used for passing arguments in the ILP32* and LP64* ABIs, except
   // the ILP32E ABI.
diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.h b/llvm/lib/Target/RISCV/RISCVCallingConv.h
index dfe41257c17a2..732ffe32c5bfc 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.h
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.h
@@ -11,6 +11,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "MCTargetDesc/RISCVBaseInfo.h"
+#include "RISCV.h"
 #include "llvm/CodeGen/CallingConvLower.h"
 
 namespace llvm {
@@ -24,6 +25,7 @@ CCAssignFn RetCC_RISCV;
 namespace RISCV {
 
 ArrayRef<MCPhysReg> getArgGPRs(const RISCVABI::ABI ABI);
+ArrayRef<MCPhysReg> getArgFPRs(const RISCVSubtarget &STI);
 
 } // end namespace RISCV
 
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index f88f1f31dea8f..6eac8fa75c916 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -827,11 +827,13 @@ Register RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
 bool RISCVRegisterInfo::isArgumentRegister(const MachineFunction &MF,
                                            MCRegister Reg) const {
   auto const &STI = MF.getSubtarget<RISCVSubtarget>();
-  if (!STI.getRegisterInfo()->isGeneralPurposeRegister(MF, Reg))
-    llvm::reportFatalInternalError(
-        "isArgumentRegister is not implemented for non-GPR registers");
+  if (STI.getRegisterInfo()->isGeneralPurposeRegister(MF, Reg))
+    return llvm::is_contained(RISCV::getArgGPRs(STI.getTargetABI()), Reg);
 
-  return llvm::is_contained(RISCV::getArgGPRs(STI.getTargetABI()), Reg);
+  if (STI.getRegisterInfo()->isFPRegister(Reg))
+    return llvm::is_contained(RISCV::getArgFPRs(STI), Reg);
+
+  return false;
 }
 
 StringRef RISCVRegisterInfo::getRegAsmName(MCRegister Reg) const {
diff --git a/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll b/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll
index 282fe6c93011a..47a5ff9035af7 100644
--- a/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll
+++ b/llvm/test/CodeGen/RISCV/zero-call-used-regs-fp.ll
@@ -467,3 +467,127 @@ define void @all_fp() "zero-call-used-regs"="all" {
 entry:
   ret void
 }
+
+define void @all_arg_fp() "zero-call-used-regs"="all-arg" {
+; 32-BITS-F-LABEL: all_arg_fp:
+; 32-BITS-F:       # %bb.0: # %entry
+; 32-BITS-F-NEXT:    li a0, 0
+; 32-BITS-F-NEXT:    li a1, 0
+; 32-BITS-F-NEXT:    li a2, 0
+; 32-BITS-F-NEXT:    li a3, 0
+; 32-BITS-F-NEXT:    li a4, 0
+; 32-BITS-F-NEXT:    li a5, 0
+; 32-BITS-F-NEXT:    li a6, 0
+; 32-BITS-F-NEXT:    li a7, 0
+; 32-BITS-F-NEXT:    fmv.w.x fa0, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa1, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa2, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa3, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa4, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa5, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa6, zero
+; 32-BITS-F-NEXT:    fmv.w.x fa7, zero
+; 32-BITS-F-NEXT:    ret
+;
+; 32-BITS-D-LABEL: all_arg_fp:
+; 32-BITS-D:       # %bb.0: # %entry
+; 32-BITS-D-NEXT:    li a0, 0
+; 32-BITS-D-NEXT:    li a1, 0
+; 32-BITS-D-NEXT:    li a2, 0
+; 32-BITS-D-NEXT:    li a3, 0
+; 32-BITS-D-NEXT:    li a4, 0
+; 32-BITS-D-NEXT:    li a5, 0
+; 32-BITS-D-NEXT:    li a6, 0
+; 32-BITS-D-NEXT:    li a7, 0
+; 32-BITS-D-NEXT:    fcvt.d.w fa0, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa1, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa2, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa3, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa4, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa5, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa6, zero
+; 32-BITS-D-NEXT:    fcvt.d.w fa7, zero
+; 32-BITS-D-NEXT:    ret
+;
+; 32-BITS-Q-LABEL: all_arg_fp:
+; 32-BITS-Q:       # %bb.0: # %entry
+; 32-BITS-Q-NEXT:    li a0, 0
+; 32-BITS-Q-NEXT:    li a1, 0
+; 32-BITS-Q-NEXT:    li a2, 0
+; 32-BITS-Q-NEXT:    li a3, 0
+; 32-BITS-Q-NEXT:    li a4, 0
+; 32-BITS-Q-NEXT:    li a5, 0
+; 32-BITS-Q-NEXT:    li a6, 0
+; 32-BITS-Q-NEXT:    li a7, 0
+; 32-BITS-Q-NEXT:    fcvt.q.w fa0, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa1, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa2, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa3, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa4, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa5, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa6, zero
+; 32-BITS-Q-NEXT:    fcvt.q.w fa7, zero
+; 32-BITS-Q-NEXT:    ret
+;
+; 64-BITS-F-LABEL: all_arg_fp:
+; 64-BITS-F:       # %bb.0: # %entry
+; 64-BITS-F-NEXT:    li a0, 0
+; 64-BITS-F-NEXT:    li a1, 0
+; 64-BITS-F-NEXT:    li a2, 0
+; 64-BITS-F-NEXT:    li a3, 0
+; 64-BITS-F-NEXT:    li a4, 0
+; 64-BITS-F-NEXT:    li a5, 0
+; 64-BITS-F-NEXT:    li a6, 0
+; 64-BITS-F-NEXT:    li a7, 0
+; 64-BITS-F-NEXT:    fmv.w.x fa0, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa1, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa2, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa3, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa4, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa5, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa6, zero
+; 64-BITS-F-NEXT:    fmv.w.x fa7, zero
+; 64-BITS-F-NEXT:    ret
+;
+; 64-BITS-D-LABEL: all_arg_fp:
+; 64-BITS-D:       # %bb.0: # %entry
+; 64-BITS-D-NEXT:    li a0, 0
+; 64-BITS-D-NEXT:    li a1, 0
+; 64-BITS-D-NEXT:    li a2, 0
+; 64-BITS-D-NEXT:    li a3, 0
+; 64-BITS-D-NEXT:    li a4, 0
+; 64-BITS-D-NEXT:    li a5, 0
+; 64-BITS-D-NEXT:    li a6, 0
+; 64-BITS-D-NEXT:    li a7, 0
+; 64-BITS-D-NEXT:    fmv.d.x fa0, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa1, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa2, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa3, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa4, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa5, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa6, zero
+; 64-BITS-D-NEXT:    fmv.d.x fa7, zero
+; 64-BITS-D-NEXT:    ret
+;
+; 64-BITS-Q-LABEL: all_arg_fp:
+; 64-BITS-Q:       # %bb.0: # %entry
+; 64-BITS-Q-NEXT:    li a0, 0
+; 64-BITS-Q-NEXT:    li a1, 0
+; 64-BITS-Q-NEXT:    li a2, 0
+; 64-BITS-Q-NEXT:    li a3, 0
+; 64-BITS-Q-NEXT:    li a4, 0
+; 64-BITS-Q-NEXT:    li a5, 0
+; 64-BITS-Q-NEXT:    li a6, 0
+; 64-BITS-Q-NEXT:    li a7, 0
+; 64-BITS-Q-NEXT:    fcvt.q.w fa0, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa1, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa2, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa3, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa4, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa5, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa6, zero
+; 64-BITS-Q-NEXT:    fcvt.q.w fa7, zero
+; 64-BITS-Q-NEXT:    ret
+entry:
+  ret void
+}

>From ec1f2b2226840e4527a065d88a0e6bfa284321cc Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Wed, 24 Jun 2026 00:01:16 +0200
Subject: [PATCH 6/7] Resolve comments

---
 llvm/lib/Target/RISCV/RISCVCallingConv.h    | 3 ++-
 llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp | 6 ++++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVCallingConv.h b/llvm/lib/Target/RISCV/RISCVCallingConv.h
index 732ffe32c5bfc..fadbc95a2090f 100644
--- a/llvm/lib/Target/RISCV/RISCVCallingConv.h
+++ b/llvm/lib/Target/RISCV/RISCVCallingConv.h
@@ -11,7 +11,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "MCTargetDesc/RISCVBaseInfo.h"
-#include "RISCV.h"
 #include "llvm/CodeGen/CallingConvLower.h"
 
 namespace llvm {
@@ -22,6 +21,8 @@ CCAssignFn CC_RISCV;
 /// This is used for assigning return values to locations when making calls.
 CCAssignFn RetCC_RISCV;
 
+class RISCVSubtarget;
+
 namespace RISCV {
 
 ArrayRef<MCPhysReg> getArgGPRs(const RISCVABI::ABI ABI);
diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
index 6eac8fa75c916..7828f07cdbaa5 100644
--- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.cpp
@@ -827,10 +827,12 @@ Register RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
 bool RISCVRegisterInfo::isArgumentRegister(const MachineFunction &MF,
                                            MCRegister Reg) const {
   auto const &STI = MF.getSubtarget<RISCVSubtarget>();
-  if (STI.getRegisterInfo()->isGeneralPurposeRegister(MF, Reg))
+  const RISCVRegisterInfo *TRI = STI.getRegisterInfo();
+
+  if (TRI->isGeneralPurposeRegister(MF, Reg))
     return llvm::is_contained(RISCV::getArgGPRs(STI.getTargetABI()), Reg);
 
-  if (STI.getRegisterInfo()->isFPRegister(Reg))
+  if (TRI->isFPRegister(Reg))
     return llvm::is_contained(RISCV::getArgFPRs(STI), Reg);
 
   return false;

>From 715d3a0ba16a71666ab78e4232b62fff84ec5df4 Mon Sep 17 00:00:00 2001
From: Lucas Chollet <lucas.chollet at serenityos.org>
Date: Fri, 26 Jun 2026 23:03:18 +0200
Subject: [PATCH 7/7] Use const-ref for DebugLoc

---
 llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
index bc86a60934813..f7652f0bb5df1 100644
--- a/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/RISCV/RISCVExpandPseudoInsts.cpp
@@ -627,7 +627,7 @@ bool RISCVExpandPseudo::expandPseudoReadVLENBViaVSETVLIX0(
 
 bool RISCVExpandPseudo::expandPseudoClearFPR64(
     MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) {
-  DebugLoc DL = MBBI->getDebugLoc();
+  const DebugLoc &DL = MBBI->getDebugLoc();
   Register Dst = MBBI->getOperand(0).getReg();
 
   if (STI->is64Bit()) {



More information about the llvm-commits mailing list