Skip to content

[MachineLICM] Recognize registers clobbered at EH landing pad entry #122446

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 25, 2025

Conversation

uweigand
Copy link
Member

EH landing pad entry implicitly clobbers target-specific exception pointer and exception selector registers. The post-RA MachineLICM pass needs to take these into account when deciding whether to hoist an instruction out of the loop that initializes one of these registers.

Fixes: #122315

@llvmbot
Copy link
Member

llvmbot commented Jan 10, 2025

@llvm/pr-subscribers-backend-systemz

Author: Ulrich Weigand (uweigand)

Changes

EH landing pad entry implicitly clobbers target-specific exception pointer and exception selector registers. The post-RA MachineLICM pass needs to take these into account when deciding whether to hoist an instruction out of the loop that initializes one of these registers.

Fixes: #122315


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

2 Files Affected:

  • (modified) llvm/lib/CodeGen/MachineLICM.cpp (+15)
  • (added) llvm/test/CodeGen/SystemZ/pr122315.ll (+59)
diff --git a/llvm/lib/CodeGen/MachineLICM.cpp b/llvm/lib/CodeGen/MachineLICM.cpp
index d1d5509dc482a2..9718e24caee0af 100644
--- a/llvm/lib/CodeGen/MachineLICM.cpp
+++ b/llvm/lib/CodeGen/MachineLICM.cpp
@@ -639,6 +639,21 @@ void MachineLICMImpl::HoistRegionPostRA(MachineLoop *CurLoop,
     if (const uint32_t *Mask = BB->getBeginClobberMask(TRI))
       applyBitsNotInRegMaskToRegUnitsMask(*TRI, RUClobbers, Mask);
 
+    // EH landing pads clobber exception pointer/selector registers
+    if (BB->isEHPad()) {
+      const MachineFunction &MF = *BB->getParent();
+      if (MF.getFunction().hasPersonalityFn()) {
+        auto PersonalityFn = MF.getFunction().getPersonalityFn();
+        const TargetLowering &TLI = *MF.getSubtarget().getTargetLowering();
+        if (unsigned Reg = TLI.getExceptionPointerRegister(PersonalityFn))
+          for (MCRegUnitIterator RUI(Reg, TRI); RUI.isValid(); ++RUI)
+            RUClobbers.set(*RUI);
+        if (unsigned Reg = TLI.getExceptionSelectorRegister(PersonalityFn))
+          for (MCRegUnitIterator RUI(Reg, TRI); RUI.isValid(); ++RUI)
+            RUClobbers.set(*RUI);
+      }
+    }
+
     SpeculationState = SpeculateUnknown;
     for (MachineInstr &MI : *BB)
       ProcessMI(&MI, RUDefs, RUClobbers, StoredFIs, Candidates, CurLoop);
diff --git a/llvm/test/CodeGen/SystemZ/pr122315.ll b/llvm/test/CodeGen/SystemZ/pr122315.ll
new file mode 100644
index 00000000000000..b094d11dbc9a56
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/pr122315.ll
@@ -0,0 +1,59 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; Verify that MachineLICM recognizes that EH landing pad entry clobbers %r6/%r7
+;
+; RUN: llc < %s -verify-machineinstrs -mtriple=s390x-linux-gnu -mcpu=z10 | FileCheck %s
+
+declare i64 @personality(i64, i64, i64, ptr, ptr)
+declare void @callee(i64, i64, i64, i64, i64)
+declare void @panic()
+
+define void @test() uwtable personality ptr @personality {
+; CHECK-LABEL: test:
+; CHECK:       .Lfunc_begin0:
+; CHECK-NEXT:    .cfi_startproc
+; CHECK-NEXT:    .cfi_personality 0, personality
+; CHECK-NEXT:    .cfi_lsda 0, .Lexception0
+; CHECK-NEXT:  # %bb.0: # %start
+; CHECK-NEXT:    stmg %r6, %r15, 48(%r15)
+; CHECK-NEXT:    .cfi_offset %r6, -112
+; CHECK-NEXT:    .cfi_offset %r7, -104
+; CHECK-NEXT:    .cfi_offset %r14, -48
+; CHECK-NEXT:    .cfi_offset %r15, -40
+; CHECK-NEXT:    aghi %r15, -160
+; CHECK-NEXT:    .cfi_def_cfa_offset 320
+; CHECK-NEXT:  .LBB0_1: # %bb1
+; CHECK-NEXT:    # =>This Inner Loop Header: Depth=1
+; CHECK-NEXT:    lghi %r2, 0
+; CHECK-NEXT:    lghi %r3, 0
+; CHECK-NEXT:    lghi %r4, 0
+; CHECK-NEXT:    lghi %r5, 0
+; CHECK-NEXT:    lghi %r6, 0
+; CHECK-NEXT:    brasl %r14, callee@PLT
+; CHECK-NEXT:  .Ltmp0:
+; CHECK-NEXT:    brasl %r14, panic@PLT
+; CHECK-NEXT:  .Ltmp1:
+; CHECK-NEXT:    j .LBB0_3
+; CHECK-NEXT:  .LBB0_2: # %bb3
+; CHECK-NEXT:    # in Loop: Header=BB0_1 Depth=1
+; CHECK-NEXT:  .Ltmp2:
+; CHECK-NEXT:    j .LBB0_1
+; CHECK-NEXT:  .LBB0_3: # %bb2
+; CHECK-NEXT:    lmg %r6, %r15, 208(%r15)
+; CHECK-NEXT:    br %r14
+start:
+  br label %bb1
+
+bb1:
+  call void @callee(i64 0, i64 0, i64 0, i64 0, i64 0)
+  invoke void @panic()
+          to label %bb2 unwind label %bb3
+
+bb2:
+  ret void
+
+bb3:
+  %lp = landingpad { ptr, i32 }
+          catch ptr null
+  br label %bb1
+}
+

Comment on lines 644 to 653
const MachineFunction &MF = *BB->getParent();
if (MF.getFunction().hasPersonalityFn()) {
auto PersonalityFn = MF.getFunction().getPersonalityFn();
const TargetLowering &TLI = *MF.getSubtarget().getTargetLowering();
if (unsigned Reg = TLI.getExceptionPointerRegister(PersonalityFn))
for (MCRegUnitIterator RUI(Reg, TRI); RUI.isValid(); ++RUI)
RUClobbers.set(*RUI);
if (unsigned Reg = TLI.getExceptionSelectorRegister(PersonalityFn))
for (MCRegUnitIterator RUI(Reg, TRI); RUI.isValid(); ++RUI)
RUClobbers.set(*RUI);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the wrong place for this. This pass is already inventing its own liveness tracking instead of LiveRegUnits, which it should be using.

Why is this case special? Are these not reserved and/or callee saved?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are not reserved, but they are callee saved. That's why they're considered candidates to be hoisted even though there's a call in the loop. And normally that would be fine except for the special EH landing pad case.

I'm not sure where else these should be handled, I simply noticed that several other places also handle these registers specially.

Copy link
Contributor

@s-barannikov s-barannikov Apr 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be just

    if (BB->isEHPad()) {
      if (const uint32_t *Mask =
              TRI->getCustomEHPadPreservedMask(*BB->getParent()))
        applyBitsNotInRegMaskToRegUnitsMask(*TRI, RUClobbers, Mask);
    }

Note however that this only partly addresses the issue. The continue at the beginning of the loop is wrong because it results in un-analyzed instructions. If an inner loop (with header-landingpad) had a register def/clobber, it won't be taken into account by the loop over Candidates below.

I've been debugging a similar issue for quite some time now, but I'm struggling to reproduce it on an upstream target =\

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be just

Please ignore this. It appears this method only needs to be implemented if the unwinder clobbers registers other than exception pointer/selector. DWARF exception handling would usually preserve those other registers.

That said, it should be queried by LICM, but it is a different issue.

Copy link
Contributor

@s-barannikov s-barannikov Apr 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether livein_iterator (used just above) should take care of these registers implicitly like liveout_iterator does

liveout_iterator &operator++() {
do {
++LiveRegI;
if (!advanceToValidPosition())
return *this;
} while ((*BlockI)->isEHPad() &&
(LiveRegI->PhysReg == ExceptionPointer ||
LiveRegI->PhysReg == ExceptionSelector));
return *this;
}

ADD
liveout_iterator seems to skip those registers when it discovers them on the live-in list of a successor block.
Doesn't that mean that they should be on that list already? It looks like instruction selector does add them to the list, but then for (const auto &LI : BB->liveins()) { should have already dealt with them making this change unnecessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they're already in the live-ins list of the EH landing pad. But that for loop only adds them to RUDefs, which is not sufficient - they need to be added to RUClobbers; their values are not inherited from the predecessor block as for a "normal" live-in, but their values are actually clobbered by the EH runtime library, which is not visible to the analysis at this point.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that makes sense to me now.

if (BB->isEHPad()) {
const MachineFunction &MF = *BB->getParent();
if (MF.getFunction().hasPersonalityFn()) {
auto PersonalityFn = MF.getFunction().getPersonalityFn();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no auto

if (MF.getFunction().hasPersonalityFn()) {
auto PersonalityFn = MF.getFunction().getPersonalityFn();
const TargetLowering &TLI = *MF.getSubtarget().getTargetLowering();
if (unsigned Reg = TLI.getExceptionPointerRegister(PersonalityFn))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use MCRegister

// EH landing pads clobber exception pointer/selector registers
if (BB->isEHPad()) {
const MachineFunction &MF = *BB->getParent();
if (MF.getFunction().hasPersonalityFn()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the verifier not enforce there must be a personality function if there is an EH pad?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed it does, thanks! Patch updated.

@uweigand uweigand force-pushed the machinelicm-ehpad-clobber branch from 001ecec to f736ef7 Compare April 25, 2025 15:36
Copy link
Contributor

@s-barannikov s-barannikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a suggestion

EH landing pad entry implicitly clobbers target-specific
exception pointer and exception selector registers.  The
post-RA MachineLICM pass needs to take these into account
when deciding whether to hoist an instruction out of the
loop that initializes one of these registers.

Fixes: llvm#122315
@uweigand uweigand force-pushed the machinelicm-ehpad-clobber branch from f736ef7 to ed4eb1f Compare April 25, 2025 17:46
@uweigand uweigand merged commit be7ef6c into llvm:main Apr 25, 2025
11 checks passed
@uweigand uweigand deleted the machinelicm-ehpad-clobber branch April 25, 2025 20:27
jyli0116 pushed a commit to jyli0116/llvm-project that referenced this pull request Apr 28, 2025
…lvm#122446)

EH landing pad entry implicitly clobbers target-specific exception
pointer and exception selector registers. The post-RA MachineLICM pass
needs to take these into account when deciding whether to hoist an
instruction out of the loop that initializes one of these registers.

Fixes: llvm#122315
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…lvm#122446)

EH landing pad entry implicitly clobbers target-specific exception
pointer and exception selector registers. The post-RA MachineLICM pass
needs to take these into account when deciding whether to hoist an
instruction out of the loop that initializes one of these registers.

Fixes: llvm#122315
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…lvm#122446)

EH landing pad entry implicitly clobbers target-specific exception
pointer and exception selector registers. The post-RA MachineLICM pass
needs to take these into account when deciding whether to hoist an
instruction out of the loop that initializes one of these registers.

Fixes: llvm#122315
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
…lvm#122446)

EH landing pad entry implicitly clobbers target-specific exception
pointer and exception selector registers. The post-RA MachineLICM pass
needs to take these into account when deciding whether to hoist an
instruction out of the loop that initializes one of these registers.

Fixes: llvm#122315
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[MachineLICM] Wrong codegen post-RA due to ignored EH landing pad clobbers
4 participants