[llvm] [RISCV] Preserve call-preserved reg mask for LPAD-aligned calls (PR #210868)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jul 20 20:15:54 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Jerry Zhang Jian (jerryzj)

<details>
<summary>Changes</summary>

RISCVISelDAGToDAG's lowering of RISCVISD::LPAD_CALL / LPAD_CALL_INDIRECT to PseudoCALLLpadAlign / PseudoCALLIndirectLpadAlign (introduced in #<!-- -->177515) only copied the callee, lpad label, chain, and glue operands, dropping the argument-register and register-mask operands in between.

Without the register-mask operand, the register allocator treats these calls as clobbering nothing but ra, so values live across the call are not spilled/reloaded even though the callee is free to clobber caller-saved registers. This caused a miscompile where a pointer held live across a call to getcontext() (a returns_twice function) was corrupted after the call returned, leading to a SIGSEGV in llvm-test-suite's siod test.

Fix the operand copy to include the argument-register and register-mask operands, matching the pseudo-instruction operands of a regular PseudoCALL/PseudoCALLIndirect.

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


2 Files Affected:

- (modified) llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp (+13-3) 
- (added) llvm/test/CodeGen/RISCV/lpad-setjmp-regmask.ll (+31) 


``````````diff
diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 8d9c2d519274c..36576a7279915 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -3236,12 +3236,22 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
       LpadLabel = PreferredLandingPadLabel;
     }
 
-    SmallVector<SDValue, 4> Ops;
+    // Preserve the argument-register and register-mask operands (operands
+    // 2..N-2, between Callee and the optional glue) so the pseudo call
+    // still reports its call-preserved mask to the register allocator.
+    SmallVector<SDValue, 8> Ops;
     Ops.push_back(Node->getOperand(1));
     Ops.push_back(CurDAG->getTargetConstant(LpadLabel, DL, XLenVT));
+
+    unsigned NumOps = Node->getNumOperands();
+    bool HasGlue = Node->getGluedNode() != nullptr;
+    unsigned LastRegOperand = HasGlue ? NumOps - 1 : NumOps;
+    for (unsigned I = 2; I != LastRegOperand; ++I)
+      Ops.push_back(Node->getOperand(I));
+
     Ops.push_back(Node->getOperand(0));
-    if (Node->getGluedNode())
-      Ops.push_back(Node->getOperand(Node->getNumOperands() - 1));
+    if (HasGlue)
+      Ops.push_back(Node->getOperand(NumOps - 1));
 
     ReplaceNode(Node,
                 CurDAG->getMachineNode(PseudoOpc, DL, Node->getVTList(), Ops));
diff --git a/llvm/test/CodeGen/RISCV/lpad-setjmp-regmask.ll b/llvm/test/CodeGen/RISCV/lpad-setjmp-regmask.ll
new file mode 100644
index 0000000000000..62ccf2d9ed6f5
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/lpad-setjmp-regmask.ll
@@ -0,0 +1,31 @@
+; RUN: llc -mtriple=riscv32 -stop-after=finalize-isel < %s | FileCheck %s
+; RUN: llc -mtriple=riscv64 -stop-after=finalize-isel < %s | FileCheck %s
+
+; PseudoCALLLpadAlign/PseudoCALLIndirectLpadAlign must carry the same
+; call-preserved register mask as PseudoCALL/PseudoCALLIndirect, otherwise
+; values live across a returns_twice call are not spilled/reloaded and get
+; corrupted when the callee clobbers them.
+
+declare i32 @setjmp(ptr) returns_twice
+declare void @use(ptr, ptr)
+
+define void @test_direct_call_preserved_mask(ptr %buf) {
+  ; CHECK-LABEL: name: test_direct_call_preserved_mask
+  ; CHECK: PseudoCALLLpadAlign target-flags(riscv-call) @setjmp, 0, csr_ilp32{{d?}}_lp64{{d?}}, implicit-def dead $x1, implicit $x10, implicit-def $x2, implicit-def $x10
+  %call = call i32 @setjmp(ptr %buf)
+  call void @use(ptr %buf, ptr %buf)
+  ret void
+}
+
+define void @test_indirect_call_preserved_mask(ptr %fptr, ptr %buf) {
+  ; CHECK-LABEL: name: test_indirect_call_preserved_mask
+  ; CHECK: PseudoCALLIndirectLpadAlign %{{[0-9]+}}, 0, csr_ilp32{{d?}}_lp64{{d?}}, implicit-def dead $x1, implicit $x10, implicit-def $x2, implicit-def $x10
+  %call = call i32 %fptr(ptr %buf) #0
+  call void @use(ptr %buf, ptr %buf)
+  ret void
+}
+
+attributes #0 = { returns_twice }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 8, !"cf-protection-branch", i32 1}

``````````

</details>


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


More information about the llvm-commits mailing list