[llvm] [RegAllocFast] Reload virtual registers parked on a landing pad's EH live-ins (PR #216478)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 15 02:55:22 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-aarch64
Author: yhirose
<details>
<summary>Changes</summary>
RegAllocFast drops the reload for a virtual register whose assigned physical
register is a live-in of the block. That's sound for the entry block, where
live-ins are incoming ABI argument registers, but not for a landing pad: the
exception pointer and selector live-ins are written by the unwinder at run
time, not by a predecessor, so a virtual register parked on one of them never
receives its value. The MachineVerifier accepts the result, so this is
silent wrong code.
`allocateBasicBlock` already handles this via the EH-aware `MBB.liveouts()`
iterator, added by c1dc267258e0 for this pass and refined by #<!-- -->154325;
`reloadAtBegin` still walks the raw `MBB.liveins()`. This patch skips the
exception pointer/selector there too, computed the same way MachineLICM does
in #<!-- -->122446. If the pad reads those registers itself, the reverse walk
already evicts and reloads the virtual register before that read, so the
skip is safe.
`clang -O0` itself doesn't hit this, since it keeps values in `alloca`s
across EH edges and pads carry no PHIs. The practical trigger is a tiered
JIT: `setCodeGenOptLevel(CodeGenOptLevel::None)` on IR that already went
through mem2reg/SROA. Reproduces on LLVM 12 through main.
Adds `llvm/test/CodeGen/AArch64/fast-regalloc-eh-pad-livein.mir`.
Fixes: #<!-- -->216477
*Investigated and drafted with AI-assistant help; reviewed and verified
myself.*
---
Full diff: https://github.com/llvm/llvm-project/pull/216478.diff
2 Files Affected:
- (modified) llvm/lib/CodeGen/RegAllocFast.cpp (+21)
- (added) llvm/test/CodeGen/AArch64/fast-regalloc-eh-pad-livein.mir (+87)
``````````diff
diff --git a/llvm/lib/CodeGen/RegAllocFast.cpp b/llvm/lib/CodeGen/RegAllocFast.cpp
index f10283272f1d9..8bf87a1c3512c 100644
--- a/llvm/lib/CodeGen/RegAllocFast.cpp
+++ b/llvm/lib/CodeGen/RegAllocFast.cpp
@@ -33,6 +33,7 @@
#include "llvm/CodeGen/RegAllocRegistry.h"
#include "llvm/CodeGen/RegisterClassInfo.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
+#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
@@ -42,6 +43,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Target/TargetMachine.h"
#include <cassert>
#include <tuple>
#include <vector>
@@ -704,8 +706,27 @@ void RegAllocFastImpl::reloadAtBegin(MachineBasicBlock &MBB) {
if (LiveVirtRegs.empty())
return;
+ // The exception pointer and selector live-ins of a landing pad are written by
+ // the unwinder at run time rather than by a predecessor, so the value a
+ // virtual register assigned to one of them is expected to hold does not
+ // actually arrive in that register. Such a register still needs its reload.
+ MCRegister ExceptionPointer, ExceptionSelector;
+ if (MBB.isEHPad()) {
+ const MachineFunction &MF = *MBB.getParent();
+ if (MF.getFunction().hasPersonalityFn()) {
+ auto PersonalityFn = MF.getFunction().getPersonalityFn();
+ const TargetLowering &TLI = *MF.getSubtarget().getTargetLowering();
+ ExceptionPointer = TLI.getExceptionPointerRegister(
+ TLI.getTargetMachine().getExceptionModel(), PersonalityFn);
+ ExceptionSelector = TLI.getExceptionSelectorRegister(
+ TLI.getTargetMachine().getExceptionModel(), PersonalityFn);
+ }
+ }
+
for (MachineBasicBlock::RegisterMaskPair P : MBB.liveins()) {
MCRegister Reg = P.PhysReg;
+ if (Reg == ExceptionPointer || Reg == ExceptionSelector)
+ continue;
// Set state to live-in. This possibly overrides mappings to virtual
// registers but we don't care anymore at this point.
setPhysRegState(Reg, regLiveIn);
diff --git a/llvm/test/CodeGen/AArch64/fast-regalloc-eh-pad-livein.mir b/llvm/test/CodeGen/AArch64/fast-regalloc-eh-pad-livein.mir
new file mode 100644
index 0000000000000..e7869c5cf6363
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/fast-regalloc-eh-pad-livein.mir
@@ -0,0 +1,87 @@
+# RUN: llc -mtriple=aarch64-unknown-linux-gnu -run-pass=regallocfast -o - %s | FileCheck %s
+# RUN: llc -mtriple=aarch64-unknown-linux-gnu -passes=regallocfast -o - %s | FileCheck %s
+
+# The exception pointer and selector live-ins of a landing pad are written by
+# the unwinder at run time, not by a predecessor, so a virtual register that the
+# fast allocator parked on one of them does not actually arrive there and still
+# needs to be reloaded at the start of the pad.
+#
+# Here 13 values are live into the pad, which is one more than the 11 registers
+# the allocator reaches before it gets to $x0 and $x1. Without the reloads, the
+# two values allocated to $x0/$x1 silently read the unwinder's exception pointer
+# and selector instead.
+
+--- |
+ declare void @may_throw()
+ declare i32 @__gxx_personality_v0(...)
+
+ define void @eh_pad_livein_needs_reload(i64 %x) personality ptr @__gxx_personality_v0 {
+ entry:
+ invoke void @may_throw()
+ to label %done unwind label %pad
+
+ done:
+ ret void
+
+ pad:
+ %lp = landingpad { ptr, i32 }
+ cleanup
+ ret void
+ }
+...
+---
+name: eh_pad_livein_needs_reload
+alignment: 4
+tracksRegLiveness: true
+stack:
+ - { id: 0, size: 8, alignment: 8 }
+body: |
+ ; CHECK-LABEL: bb.2.pad
+ ; CHECK: EH_LABEL
+ ; CHECK-DAG: $x0 = LDRXui
+ ; CHECK-DAG: $x1 = LDRXui
+ bb.0.entry:
+ successors: %bb.1(0x7ffff800), %bb.2(0x00000800)
+ liveins: $x0
+
+ %0:gpr64 = COPY $x0
+ %1:gpr64 = COPY $x0
+ %2:gpr64 = COPY $x0
+ %3:gpr64 = COPY $x0
+ %4:gpr64 = COPY $x0
+ %5:gpr64 = COPY $x0
+ %6:gpr64 = COPY $x0
+ %7:gpr64 = COPY $x0
+ %8:gpr64 = COPY $x0
+ %9:gpr64 = COPY $x0
+ %10:gpr64 = COPY $x0
+ %11:gpr64 = COPY $x0
+ %12:gpr64 = COPY $x0
+ EH_LABEL <mcsymbol .Ltmp0>
+ BL @may_throw, csr_aarch64_aapcs, implicit-def dead $lr, implicit $sp
+ EH_LABEL <mcsymbol .Ltmp1>
+ B %bb.1
+
+ bb.1.done:
+ RET_ReallyLR
+
+ bb.2.pad (landing-pad):
+ liveins: $x0, $x1
+
+ EH_LABEL <mcsymbol .Ltmp2>
+ STRXui %0, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %1, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %2, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %3, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %4, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %5, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %6, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %7, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %8, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %9, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %10, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %11, %stack.0, 0 :: (store (s64) into %stack.0)
+ STRXui %12, %stack.0, 0 :: (store (s64) into %stack.0)
+ RET_ReallyLR
+
+...
``````````
</details>
https://github.com/llvm/llvm-project/pull/216478
More information about the llvm-commits
mailing list