[llvm] [RISCV] Preserve call-preserved reg mask for LPAD-aligned calls (PR #210868)
Jerry Zhang Jian via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 20 20:15:21 PDT 2026
https://github.com/jerryzj created https://github.com/llvm/llvm-project/pull/210868
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.
>From c96e6499ab454f4944e3d60d61b509884cbf90f2 Mon Sep 17 00:00:00 2001
From: Jerry Zhang Jian <jerry.zhangjian at sifive.com>
Date: Tue, 21 Jul 2026 11:09:14 +0800
Subject: [PATCH] [RISCV] Preserve call-preserved reg mask for LPAD-aligned
calls
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.
Signed-off-by: Jerry Zhang Jian <jerry.zhangjian at sifive.com>
---
llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp | 16 ++++++++--
.../test/CodeGen/RISCV/lpad-setjmp-regmask.ll | 31 +++++++++++++++++++
2 files changed, 44 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/CodeGen/RISCV/lpad-setjmp-regmask.ll
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}
More information about the llvm-commits
mailing list