[llvm] [RISCV] Do not generate QC.CM.PUSHFP when rlist has only RA (PR #200541)

via llvm-commits llvm-commits at lists.llvm.org
Fri May 29 19:32:20 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-risc-v

Author: Sudharsan Veeravalli (svs-quic)

<details>
<summary>Changes</summary>

`QC.CM.PUSHFP` is only legal when the push `rlist` includes `{ra, s0}`. The current flow could assume implicit FP updates in cases where `QC.CM.PUSHFP` is not legal and generate the instruction incorrectly.

---
Full diff: https://github.com/llvm/llvm-project/pull/200541.diff


3 Files Affected:

- (modified) llvm/lib/Target/RISCV/RISCVFrameLowering.cpp (+6-2) 
- (modified) llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp (+3-3) 
- (modified) llvm/test/CodeGen/RISCV/zilsd-csr-duplicate.mir (+72-35) 


``````````diff
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 9306fe69ef427..46cd260b70bef 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -2274,8 +2274,12 @@ bool RISCVFrameLowering::spillCalleeSavedRegisters(
     unsigned PushedRegNum = RVFI->getRVPushRegs();
     if (PushedRegNum > 0) {
       // Use encoded number to represent registers to spill.
-      unsigned Opcode = getPushOpcode(
-          RVFI->getPushPopKind(*MF), hasFP(*MF) && !RVFI->useQCIInterrupt(*MF));
+      // `QC.CM.PUSHFP` requires the rlist to include at least `{ra, s0}`.
+      // If only that is not the case, fall back to `QC.CM.PUSH` and let
+      // generic FP setup logic materialize FP explicitly.
+      bool CanUsePushFP =
+          hasFP(*MF) && !RVFI->useQCIInterrupt(*MF) && PushedRegNum >= 2;
+      unsigned Opcode = getPushOpcode(RVFI->getPushPopKind(*MF), CanUsePushFP);
       unsigned RegEnc = RISCVZC::encodeRegListNumRegs(PushedRegNum);
       MachineInstrBuilder PushBuilder =
           BuildMI(MBB, MI, DL, TII.get(Opcode))
diff --git a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp
index 4944df1e731a8..638461396df2a 100644
--- a/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVMachineFunctionInfo.cpp
@@ -125,9 +125,9 @@ bool RISCVMachineFunctionInfo::hasImplicitFPUpdates(
 
   switch (getPushPopKind(MF)) {
   case PushPopKind::VendorXqccmp:
-    // When using Xqccmp, we will use `QC.CM.PUSHFP` when Frame Pointers are
-    // enabled, which will update FP.
-    return true;
+    // `QC.CM.PUSHFP` updates FP implicitly, but it is only encodable when the
+    // push rlist includes `{ra, s0}`.
+    return RVPushRegs >= 2;
   default:
     break;
   }
diff --git a/llvm/test/CodeGen/RISCV/zilsd-csr-duplicate.mir b/llvm/test/CodeGen/RISCV/zilsd-csr-duplicate.mir
index 5fda434cab827..4079d76a70e14 100644
--- a/llvm/test/CodeGen/RISCV/zilsd-csr-duplicate.mir
+++ b/llvm/test/CodeGen/RISCV/zilsd-csr-duplicate.mir
@@ -1,6 +1,8 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
 # RUN: llc -mtriple riscv32 -mattr=+zcmp,+zilsd --run-pass=prologepilog %s -verify-machineinstrs -o - \
-# RUN: | FileCheck %s
+# RUN: | FileCheck --check-prefix=ZCMP-ZILSD %s
+# RUN: llc -mtriple riscv32 -mattr=+xqccmp,+zilsd --run-pass=prologepilog %s -verify-machineinstrs -o - \
+# RUN: | FileCheck --check-prefix=XQCCMP-ZILSD %s
 
 --- |
   declare i32 @foo()
@@ -85,40 +87,75 @@ body:             |
   bb.0.entry:
     liveins: $x10, $x11
 
-    ; CHECK-LABEL: name: zilsd_dup_csr
-    ; CHECK: liveins: $x10, $x11, $x1, $x18_x19
-    ; CHECK-NEXT: {{  $}}
-    ; CHECK-NEXT: frame-setup CM_PUSH 4, 16, implicit-def $x2, implicit $x2, implicit $x1
-    ; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
-    ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x1, -4
-    ; CHECK-NEXT: frame-setup SD_RV32 killed $x8_x9, $x2, 8 :: (store (s64) into %stack.1)
-    ; CHECK-NEXT: frame-setup SD_RV32 killed $x18_x19, $x2, 0 :: (store (s64) into %stack.2)
-    ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x8, -24
-    ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x9, -20
-    ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -32
-    ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -28
-    ; CHECK-NEXT: $x8 = frame-setup ADDI $x2, 32
-    ; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa $x8, 0
-    ; CHECK-NEXT: renamable $x9 = COPY $x11
-    ; CHECK-NEXT: renamable $x18 = COPY $x10
-    ; CHECK-NEXT: PseudoCALL target-flags(riscv-call) @foo, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, implicit-def dead $x10
-    ; CHECK-NEXT: renamable $x9 = nuw ADDI killed renamable $x9, 15
-    ; CHECK-NEXT: renamable $x9 = ANDI killed renamable $x9, -16
-    ; CHECK-NEXT: renamable $x10 = SUB $x2, killed renamable $x9
-    ; CHECK-NEXT: $x2 = COPY renamable $x10
-    ; CHECK-NEXT: SW killed renamable $x10, killed renamable $x18, 0 :: (store (s32) into %ir.p, align 8)
-    ; CHECK-NEXT: $x2 = frame-destroy ADDI $x8, -32
-    ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION def_cfa $x2, 32
-    ; CHECK-NEXT: $x8_x9 = frame-destroy LD_RV32 $x2, 8 :: (load (s64) from %stack.1)
-    ; CHECK-NEXT: $x18_x19 = frame-destroy LD_RV32 $x2, 0 :: (load (s64) from %stack.2)
-    ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $x8
-    ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $x9
-    ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
-    ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
-    ; CHECK-NEXT: frame-destroy CM_POP 4, 16, implicit-def $x2, implicit $x2, implicit-def $x1
-    ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $x1
-    ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
-    ; CHECK-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32_lp64, implicit $x2
+    ; ZCMP-ZILSD-LABEL: name: zilsd_dup_csr
+    ; ZCMP-ZILSD: liveins: $x10, $x11, $x1, $x18_x19
+    ; ZCMP-ZILSD-NEXT: {{  $}}
+    ; ZCMP-ZILSD-NEXT: frame-setup CM_PUSH 4, 16, implicit-def $x2, implicit $x2, implicit $x1
+    ; ZCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
+    ; ZCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x1, -4
+    ; ZCMP-ZILSD-NEXT: frame-setup SD_RV32 killed $x8_x9, $x2, 8 :: (store (s64) into %stack.1)
+    ; ZCMP-ZILSD-NEXT: frame-setup SD_RV32 killed $x18_x19, $x2, 0 :: (store (s64) into %stack.2)
+    ; ZCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x8, -24
+    ; ZCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x9, -20
+    ; ZCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -32
+    ; ZCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -28
+    ; ZCMP-ZILSD-NEXT: $x8 = frame-setup ADDI $x2, 32
+    ; ZCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION def_cfa $x8, 0
+    ; ZCMP-ZILSD-NEXT: renamable $x9 = COPY $x11
+    ; ZCMP-ZILSD-NEXT: renamable $x18 = COPY $x10
+    ; ZCMP-ZILSD-NEXT: PseudoCALL target-flags(riscv-call) @foo, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, implicit-def dead $x10
+    ; ZCMP-ZILSD-NEXT: renamable $x9 = nuw ADDI killed renamable $x9, 15
+    ; ZCMP-ZILSD-NEXT: renamable $x9 = ANDI killed renamable $x9, -16
+    ; ZCMP-ZILSD-NEXT: renamable $x10 = SUB $x2, killed renamable $x9
+    ; ZCMP-ZILSD-NEXT: $x2 = COPY renamable $x10
+    ; ZCMP-ZILSD-NEXT: SW killed renamable $x10, killed renamable $x18, 0 :: (store (s32) into %ir.p, align 8)
+    ; ZCMP-ZILSD-NEXT: $x2 = frame-destroy ADDI $x8, -32
+    ; ZCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION def_cfa $x2, 32
+    ; ZCMP-ZILSD-NEXT: $x8_x9 = frame-destroy LD_RV32 $x2, 8 :: (load (s64) from %stack.1)
+    ; ZCMP-ZILSD-NEXT: $x18_x19 = frame-destroy LD_RV32 $x2, 0 :: (load (s64) from %stack.2)
+    ; ZCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x8
+    ; ZCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x9
+    ; ZCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
+    ; ZCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; ZCMP-ZILSD-NEXT: frame-destroy CM_POP 4, 16, implicit-def $x2, implicit $x2, implicit-def $x1
+    ; ZCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x1
+    ; ZCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; ZCMP-ZILSD-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32_lp64, implicit $x2
+    ;
+    ; XQCCMP-ZILSD-LABEL: name: zilsd_dup_csr
+    ; XQCCMP-ZILSD: liveins: $x10, $x11, $x1, $x18_x19
+    ; XQCCMP-ZILSD-NEXT: {{  $}}
+    ; XQCCMP-ZILSD-NEXT: frame-setup QC_CM_PUSH 4, 16, implicit-def $x2, implicit $x2, implicit $x1
+    ; XQCCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 32
+    ; XQCCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x1, -4
+    ; XQCCMP-ZILSD-NEXT: frame-setup SD_RV32 killed $x8_x9, $x2, 8 :: (store (s64) into %stack.1)
+    ; XQCCMP-ZILSD-NEXT: frame-setup SD_RV32 killed $x18_x19, $x2, 0 :: (store (s64) into %stack.2)
+    ; XQCCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x8, -24
+    ; XQCCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x9, -20
+    ; XQCCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x18, -32
+    ; XQCCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION offset $x19, -28
+    ; XQCCMP-ZILSD-NEXT: $x8 = frame-setup ADDI $x2, 32
+    ; XQCCMP-ZILSD-NEXT: frame-setup CFI_INSTRUCTION def_cfa $x8, 0
+    ; XQCCMP-ZILSD-NEXT: renamable $x9 = COPY $x11
+    ; XQCCMP-ZILSD-NEXT: renamable $x18 = COPY $x10
+    ; XQCCMP-ZILSD-NEXT: PseudoCALL target-flags(riscv-call) @foo, csr_ilp32_lp64, implicit-def dead $x1, implicit-def $x2, implicit-def dead $x10
+    ; XQCCMP-ZILSD-NEXT: renamable $x9 = nuw ADDI killed renamable $x9, 15
+    ; XQCCMP-ZILSD-NEXT: renamable $x9 = ANDI killed renamable $x9, -16
+    ; XQCCMP-ZILSD-NEXT: renamable $x10 = SUB $x2, killed renamable $x9
+    ; XQCCMP-ZILSD-NEXT: $x2 = COPY renamable $x10
+    ; XQCCMP-ZILSD-NEXT: SW killed renamable $x10, killed renamable $x18, 0 :: (store (s32) into %ir.p, align 8)
+    ; XQCCMP-ZILSD-NEXT: $x2 = frame-destroy ADDI $x8, -32
+    ; XQCCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION def_cfa $x2, 32
+    ; XQCCMP-ZILSD-NEXT: $x8_x9 = frame-destroy LD_RV32 $x2, 8 :: (load (s64) from %stack.1)
+    ; XQCCMP-ZILSD-NEXT: $x18_x19 = frame-destroy LD_RV32 $x2, 0 :: (load (s64) from %stack.2)
+    ; XQCCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x8
+    ; XQCCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x9
+    ; XQCCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x18
+    ; XQCCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x19
+    ; XQCCMP-ZILSD-NEXT: frame-destroy QC_CM_POP 4, 16, implicit-def $x2, implicit $x2, implicit-def $x1
+    ; XQCCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION restore $x1
+    ; XQCCMP-ZILSD-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0
+    ; XQCCMP-ZILSD-NEXT: PseudoTAIL target-flags(riscv-call) @bar, csr_ilp32_lp64, implicit $x2
     renamable $x9 = COPY $x11
     renamable $x18 = COPY $x10
     ADJCALLSTACKDOWN 0, 0, implicit-def dead $x2, implicit $x2

``````````

</details>


https://github.com/llvm/llvm-project/pull/200541


More information about the llvm-commits mailing list