Skip to content

Conversation

MacDue
Copy link
Member

@MacDue MacDue commented Aug 19, 2025

These liveins are not defined by predecessors, so attempting to preserve them results in generating invalid MIR (as the registers may not be defined).

This resolves:

@llvmbot
Copy link
Member

llvmbot commented Aug 19, 2025

@llvm/pr-subscribers-llvm-regalloc

Author: Benjamin Maxwell (MacDue)

Changes

These liveins are not defined by predecessors, so attempting to preserve them results in generating invalid MIR (as the registers may not be defined). This takes a similar approach to: c1dc267

This resolves:


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

5 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/LiveRegUnits.h (+6-2)
  • (modified) llvm/lib/CodeGen/LiveRegUnits.cpp (+22-4)
  • (modified) llvm/lib/Target/AArch64/MachineSMEABIPass.cpp (+1-1)
  • (added) llvm/test/CodeGen/AArch64/sme-abi-eh-liveins.mir (+82)
  • (modified) llvm/test/CodeGen/AArch64/sme-za-exceptions.ll (+3-5)
diff --git a/llvm/include/llvm/CodeGen/LiveRegUnits.h b/llvm/include/llvm/CodeGen/LiveRegUnits.h
index 37c31cc6f4ac5..f58188377b520 100644
--- a/llvm/include/llvm/CodeGen/LiveRegUnits.h
+++ b/llvm/include/llvm/CodeGen/LiveRegUnits.h
@@ -26,6 +26,7 @@ namespace llvm {
 
 class MachineInstr;
 class MachineBasicBlock;
+class TargetLowering;
 
 /// A set of register units used to track register liveness.
 class LiveRegUnits {
@@ -135,8 +136,11 @@ class LiveRegUnits {
   /// Adds registers living out of block \p MBB.
   /// Live out registers are the union of the live-in registers of the successor
   /// blocks and pristine registers. Live out registers of the end block are the
-  /// callee saved registers.
-  LLVM_ABI void addLiveOuts(const MachineBasicBlock &MBB);
+  /// callee saved registers. If the target lowering information \p TLI is
+  /// provided, runtime-defined live ins of successors will be excluded from the
+  /// live outs.
+  LLVM_ABI void addLiveOuts(const MachineBasicBlock &MBB,
+                            const TargetLowering *TLI = nullptr);
 
   /// Adds registers living into block \p MBB.
   LLVM_ABI void addLiveIns(const MachineBasicBlock &MBB);
diff --git a/llvm/lib/CodeGen/LiveRegUnits.cpp b/llvm/lib/CodeGen/LiveRegUnits.cpp
index 34de09dd2944b..c034d5f3925fc 100644
--- a/llvm/lib/CodeGen/LiveRegUnits.cpp
+++ b/llvm/lib/CodeGen/LiveRegUnits.cpp
@@ -16,6 +16,7 @@
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetLowering.h"
 
 using namespace llvm;
 
@@ -86,9 +87,15 @@ void LiveRegUnits::accumulate(const MachineInstr &MI) {
 
 /// Add live-in registers of basic block \p MBB to \p LiveUnits.
 static void addBlockLiveIns(LiveRegUnits &LiveUnits,
-                            const MachineBasicBlock &MBB) {
-  for (const auto &LI : MBB.liveins())
+                            const MachineBasicBlock &MBB,
+                            MCPhysReg ExceptionPointer = {},
+                            MCPhysReg ExceptionSelector = {}) {
+  for (const auto &LI : MBB.liveins()) {
+    if (MBB.isEHPad() &&
+        (LI.PhysReg == ExceptionPointer || LI.PhysReg == ExceptionSelector))
+      continue;
     LiveUnits.addRegMasked(LI.PhysReg, LI.LaneMask);
+  }
 }
 
 /// Adds all callee saved registers to \p LiveUnits.
@@ -135,14 +142,25 @@ void LiveRegUnits::addPristines(const MachineFunction &MF) {
   addUnits(Pristine.getBitVector());
 }
 
-void LiveRegUnits::addLiveOuts(const MachineBasicBlock &MBB) {
+void LiveRegUnits::addLiveOuts(const MachineBasicBlock &MBB,
+                               const TargetLowering *TLI) {
   const MachineFunction &MF = *MBB.getParent();
 
   addPristines(MF);
 
+  MCPhysReg ExceptionPointer;
+  MCPhysReg ExceptionSelector;
+
+  // Remove live-ins from successors that are defined by the runtime.
+  if (TLI && MF.getFunction().hasPersonalityFn()) {
+    auto PersonalityFn = MF.getFunction().getPersonalityFn();
+    ExceptionPointer = TLI->getExceptionPointerRegister(PersonalityFn);
+    ExceptionSelector = TLI->getExceptionSelectorRegister(PersonalityFn);
+  }
+
   // To get the live-outs we simply merge the live-ins of all successors.
   for (const MachineBasicBlock *Succ : MBB.successors())
-    addBlockLiveIns(*this, *Succ);
+    addBlockLiveIns(*this, *Succ, ExceptionPointer, ExceptionSelector);
 
   // For the return block: Add all callee saved registers.
   if (MBB.isReturnBlock()) {
diff --git a/llvm/lib/Target/AArch64/MachineSMEABIPass.cpp b/llvm/lib/Target/AArch64/MachineSMEABIPass.cpp
index b58dfdf32e4ab..2f00977e9a1cc 100644
--- a/llvm/lib/Target/AArch64/MachineSMEABIPass.cpp
+++ b/llvm/lib/Target/AArch64/MachineSMEABIPass.cpp
@@ -277,7 +277,7 @@ void MachineSMEABI::collectNeededZAStates(SMEAttrs SMEFnAttrs) {
     }
 
     LiveRegUnits LiveUnits(*TRI);
-    LiveUnits.addLiveOuts(MBB);
+    LiveUnits.addLiveOuts(MBB, Subtarget->getTargetLowering());
 
     auto GetPhysLiveRegs = [&] {
       LiveRegs PhysLiveRegs = LiveRegs::None;
diff --git a/llvm/test/CodeGen/AArch64/sme-abi-eh-liveins.mir b/llvm/test/CodeGen/AArch64/sme-abi-eh-liveins.mir
new file mode 100644
index 0000000000000..711745e528bbf
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sme-abi-eh-liveins.mir
@@ -0,0 +1,82 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -mtriple=aarch64 -mattr=+sve -mattr=+sme -run-pass=aarch64-machine-sme-abi -verify-machineinstrs %s -o - | FileCheck %s
+
+# This test verifies that runtime defined live-ins are not included in the live
+# outs of predecessors in the MachineSMEABIPass, as including them would result
+# in copies of undefined registers.
+
+--- |
+  define void @sme_abi_eh_liveins() "aarch64_inout_za" personality ptr @__gxx_personality_v0 { entry: unreachable }
+
+  declare i32 @__gxx_personality_v0(...)
+...
+---
+name:            sme_abi_eh_liveins
+tracksRegLiveness: true
+isSSA:           true
+noVRegs:         false
+
+body:             |
+  ; CHECK-LABEL: name: sme_abi_eh_liveins
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x00000000), %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[RDSVLI_XI:%[0-9]+]]:gpr64 = RDSVLI_XI 1, implicit $vg
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gpr64 = COPY $sp
+  ; CHECK-NEXT:   [[MSUBXrrr:%[0-9]+]]:gpr64 = MSUBXrrr [[RDSVLI_XI]], [[RDSVLI_XI]], [[COPY]]
+  ; CHECK-NEXT:   $sp = COPY [[MSUBXrrr]]
+  ; CHECK-NEXT:   STPXi [[MSUBXrrr]], [[RDSVLI_XI]], %stack.0, 0
+  ; CHECK-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   InOutZAUsePseudo
+  ; CHECK-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   [[ADDXri:%[0-9]+]]:gpr64sp = ADDXri %stack.0, 0, 0
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gpr64 = COPY [[ADDXri]]
+  ; CHECK-NEXT:   MSR 56965, [[COPY1]]
+  ; CHECK-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   RequiresZASavePseudo
+  ; CHECK-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   MSRpstatesvcrImm1 2, 1, implicit-def $nzcv
+  ; CHECK-NEXT:   [[MRS:%[0-9]+]]:gpr64 = MRS 56965, implicit-def $nzcv
+  ; CHECK-NEXT:   $x0 = ADDXri %stack.0, 0, 0
+  ; CHECK-NEXT:   RestoreZAPseudo [[MRS]], $x0, &__arm_tpidr2_restore, csr_aarch64_sme_abi_support_routines_preservemost_from_x0
+  ; CHECK-NEXT:   MSR 56965, $xzr
+  ; CHECK-NEXT:   B %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1 (landing-pad):
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT:   liveins: $x0, $x1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   MSRpstatesvcrImm1 2, 1, implicit-def $nzcv
+  ; CHECK-NEXT:   [[MRS1:%[0-9]+]]:gpr64 = MRS 56965, implicit-def $nzcv
+  ; CHECK-NEXT:   $x0 = ADDXri %stack.0, 0, 0
+  ; CHECK-NEXT:   RestoreZAPseudo [[MRS1]], $x0, &__arm_tpidr2_restore, csr_aarch64_sme_abi_support_routines_preservemost_from_x0
+  ; CHECK-NEXT:   MSR 56965, $xzr
+  ; CHECK-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   InOutZAUsePseudo
+  ; CHECK-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  bb.0:
+    successors: %bb.2(0x00000000), %bb.1(0x80000000)
+
+    ; Simulate shared ZA call
+    ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+    InOutZAUsePseudo
+    ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+
+    ; Simulate private ZA call at the end of the block
+    ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+    RequiresZASavePseudo
+    ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+
+    B %bb.2
+
+  bb.1 (landing-pad):
+    liveins: $x0, $x1
+
+    ; Simulate shared ZA call
+    ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+    InOutZAUsePseudo
+    ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+
+  bb.2:
diff --git a/llvm/test/CodeGen/AArch64/sme-za-exceptions.ll b/llvm/test/CodeGen/AArch64/sme-za-exceptions.ll
index c497a95a58c8a..bb88142efa592 100644
--- a/llvm/test/CodeGen/AArch64/sme-za-exceptions.ll
+++ b/llvm/test/CodeGen/AArch64/sme-za-exceptions.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sme -aarch64-new-sme-abi < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sme -aarch64-new-sme-abi -verify-machineinstrs < %s | FileCheck %s
 
 ; A simple EH test case that corresponds to the following C++ source:
 ;
@@ -62,16 +62,14 @@ define void @za_with_raii(i1 %fail) "aarch64_inout_za" personality ptr @__gxx_pe
 ; CHECK-NEXT:    ldr x1, [x1, :got_lo12:typeinfo_for_char_const_ptr]
 ; CHECK-NEXT:    bl __cxa_throw
 ; CHECK-NEXT:  .Ltmp1:
-; CHECK-NEXT:    mov x8, x0
 ; CHECK-NEXT:    smstart za
-; CHECK-NEXT:    mrs x9, TPIDR2_EL0
+; CHECK-NEXT:    mrs x8, TPIDR2_EL0
 ; CHECK-NEXT:    sub x0, x29, #16
-; CHECK-NEXT:    cbnz x9, .LBB0_4
+; CHECK-NEXT:    cbnz x8, .LBB0_4
 ; CHECK-NEXT:  // %bb.3: // %throw_exception
 ; CHECK-NEXT:    bl __arm_tpidr2_restore
 ; CHECK-NEXT:  .LBB0_4: // %throw_exception
 ; CHECK-NEXT:    msr TPIDR2_EL0, xzr
-; CHECK-NEXT:    // kill: def $x0 killed $x8
 ; CHECK-NEXT:  // %bb.5: // %throw_fail
 ; CHECK-NEXT:  .LBB0_6: // %unwind_dtors
 ; CHECK-NEXT:  .Ltmp2:

@llvmbot
Copy link
Member

llvmbot commented Aug 19, 2025

@llvm/pr-subscribers-backend-aarch64

Author: Benjamin Maxwell (MacDue)

Changes

These liveins are not defined by predecessors, so attempting to preserve them results in generating invalid MIR (as the registers may not be defined). This takes a similar approach to: c1dc267

This resolves:


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

5 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/LiveRegUnits.h (+6-2)
  • (modified) llvm/lib/CodeGen/LiveRegUnits.cpp (+22-4)
  • (modified) llvm/lib/Target/AArch64/MachineSMEABIPass.cpp (+1-1)
  • (added) llvm/test/CodeGen/AArch64/sme-abi-eh-liveins.mir (+82)
  • (modified) llvm/test/CodeGen/AArch64/sme-za-exceptions.ll (+3-5)
diff --git a/llvm/include/llvm/CodeGen/LiveRegUnits.h b/llvm/include/llvm/CodeGen/LiveRegUnits.h
index 37c31cc6f4ac5..f58188377b520 100644
--- a/llvm/include/llvm/CodeGen/LiveRegUnits.h
+++ b/llvm/include/llvm/CodeGen/LiveRegUnits.h
@@ -26,6 +26,7 @@ namespace llvm {
 
 class MachineInstr;
 class MachineBasicBlock;
+class TargetLowering;
 
 /// A set of register units used to track register liveness.
 class LiveRegUnits {
@@ -135,8 +136,11 @@ class LiveRegUnits {
   /// Adds registers living out of block \p MBB.
   /// Live out registers are the union of the live-in registers of the successor
   /// blocks and pristine registers. Live out registers of the end block are the
-  /// callee saved registers.
-  LLVM_ABI void addLiveOuts(const MachineBasicBlock &MBB);
+  /// callee saved registers. If the target lowering information \p TLI is
+  /// provided, runtime-defined live ins of successors will be excluded from the
+  /// live outs.
+  LLVM_ABI void addLiveOuts(const MachineBasicBlock &MBB,
+                            const TargetLowering *TLI = nullptr);
 
   /// Adds registers living into block \p MBB.
   LLVM_ABI void addLiveIns(const MachineBasicBlock &MBB);
diff --git a/llvm/lib/CodeGen/LiveRegUnits.cpp b/llvm/lib/CodeGen/LiveRegUnits.cpp
index 34de09dd2944b..c034d5f3925fc 100644
--- a/llvm/lib/CodeGen/LiveRegUnits.cpp
+++ b/llvm/lib/CodeGen/LiveRegUnits.cpp
@@ -16,6 +16,7 @@
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetLowering.h"
 
 using namespace llvm;
 
@@ -86,9 +87,15 @@ void LiveRegUnits::accumulate(const MachineInstr &MI) {
 
 /// Add live-in registers of basic block \p MBB to \p LiveUnits.
 static void addBlockLiveIns(LiveRegUnits &LiveUnits,
-                            const MachineBasicBlock &MBB) {
-  for (const auto &LI : MBB.liveins())
+                            const MachineBasicBlock &MBB,
+                            MCPhysReg ExceptionPointer = {},
+                            MCPhysReg ExceptionSelector = {}) {
+  for (const auto &LI : MBB.liveins()) {
+    if (MBB.isEHPad() &&
+        (LI.PhysReg == ExceptionPointer || LI.PhysReg == ExceptionSelector))
+      continue;
     LiveUnits.addRegMasked(LI.PhysReg, LI.LaneMask);
+  }
 }
 
 /// Adds all callee saved registers to \p LiveUnits.
@@ -135,14 +142,25 @@ void LiveRegUnits::addPristines(const MachineFunction &MF) {
   addUnits(Pristine.getBitVector());
 }
 
-void LiveRegUnits::addLiveOuts(const MachineBasicBlock &MBB) {
+void LiveRegUnits::addLiveOuts(const MachineBasicBlock &MBB,
+                               const TargetLowering *TLI) {
   const MachineFunction &MF = *MBB.getParent();
 
   addPristines(MF);
 
+  MCPhysReg ExceptionPointer;
+  MCPhysReg ExceptionSelector;
+
+  // Remove live-ins from successors that are defined by the runtime.
+  if (TLI && MF.getFunction().hasPersonalityFn()) {
+    auto PersonalityFn = MF.getFunction().getPersonalityFn();
+    ExceptionPointer = TLI->getExceptionPointerRegister(PersonalityFn);
+    ExceptionSelector = TLI->getExceptionSelectorRegister(PersonalityFn);
+  }
+
   // To get the live-outs we simply merge the live-ins of all successors.
   for (const MachineBasicBlock *Succ : MBB.successors())
-    addBlockLiveIns(*this, *Succ);
+    addBlockLiveIns(*this, *Succ, ExceptionPointer, ExceptionSelector);
 
   // For the return block: Add all callee saved registers.
   if (MBB.isReturnBlock()) {
diff --git a/llvm/lib/Target/AArch64/MachineSMEABIPass.cpp b/llvm/lib/Target/AArch64/MachineSMEABIPass.cpp
index b58dfdf32e4ab..2f00977e9a1cc 100644
--- a/llvm/lib/Target/AArch64/MachineSMEABIPass.cpp
+++ b/llvm/lib/Target/AArch64/MachineSMEABIPass.cpp
@@ -277,7 +277,7 @@ void MachineSMEABI::collectNeededZAStates(SMEAttrs SMEFnAttrs) {
     }
 
     LiveRegUnits LiveUnits(*TRI);
-    LiveUnits.addLiveOuts(MBB);
+    LiveUnits.addLiveOuts(MBB, Subtarget->getTargetLowering());
 
     auto GetPhysLiveRegs = [&] {
       LiveRegs PhysLiveRegs = LiveRegs::None;
diff --git a/llvm/test/CodeGen/AArch64/sme-abi-eh-liveins.mir b/llvm/test/CodeGen/AArch64/sme-abi-eh-liveins.mir
new file mode 100644
index 0000000000000..711745e528bbf
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sme-abi-eh-liveins.mir
@@ -0,0 +1,82 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -mtriple=aarch64 -mattr=+sve -mattr=+sme -run-pass=aarch64-machine-sme-abi -verify-machineinstrs %s -o - | FileCheck %s
+
+# This test verifies that runtime defined live-ins are not included in the live
+# outs of predecessors in the MachineSMEABIPass, as including them would result
+# in copies of undefined registers.
+
+--- |
+  define void @sme_abi_eh_liveins() "aarch64_inout_za" personality ptr @__gxx_personality_v0 { entry: unreachable }
+
+  declare i32 @__gxx_personality_v0(...)
+...
+---
+name:            sme_abi_eh_liveins
+tracksRegLiveness: true
+isSSA:           true
+noVRegs:         false
+
+body:             |
+  ; CHECK-LABEL: name: sme_abi_eh_liveins
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x00000000), %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   [[RDSVLI_XI:%[0-9]+]]:gpr64 = RDSVLI_XI 1, implicit $vg
+  ; CHECK-NEXT:   [[COPY:%[0-9]+]]:gpr64 = COPY $sp
+  ; CHECK-NEXT:   [[MSUBXrrr:%[0-9]+]]:gpr64 = MSUBXrrr [[RDSVLI_XI]], [[RDSVLI_XI]], [[COPY]]
+  ; CHECK-NEXT:   $sp = COPY [[MSUBXrrr]]
+  ; CHECK-NEXT:   STPXi [[MSUBXrrr]], [[RDSVLI_XI]], %stack.0, 0
+  ; CHECK-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   InOutZAUsePseudo
+  ; CHECK-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   [[ADDXri:%[0-9]+]]:gpr64sp = ADDXri %stack.0, 0, 0
+  ; CHECK-NEXT:   [[COPY1:%[0-9]+]]:gpr64 = COPY [[ADDXri]]
+  ; CHECK-NEXT:   MSR 56965, [[COPY1]]
+  ; CHECK-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   RequiresZASavePseudo
+  ; CHECK-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   MSRpstatesvcrImm1 2, 1, implicit-def $nzcv
+  ; CHECK-NEXT:   [[MRS:%[0-9]+]]:gpr64 = MRS 56965, implicit-def $nzcv
+  ; CHECK-NEXT:   $x0 = ADDXri %stack.0, 0, 0
+  ; CHECK-NEXT:   RestoreZAPseudo [[MRS]], $x0, &__arm_tpidr2_restore, csr_aarch64_sme_abi_support_routines_preservemost_from_x0
+  ; CHECK-NEXT:   MSR 56965, $xzr
+  ; CHECK-NEXT:   B %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1 (landing-pad):
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT:   liveins: $x0, $x1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   MSRpstatesvcrImm1 2, 1, implicit-def $nzcv
+  ; CHECK-NEXT:   [[MRS1:%[0-9]+]]:gpr64 = MRS 56965, implicit-def $nzcv
+  ; CHECK-NEXT:   $x0 = ADDXri %stack.0, 0, 0
+  ; CHECK-NEXT:   RestoreZAPseudo [[MRS1]], $x0, &__arm_tpidr2_restore, csr_aarch64_sme_abi_support_routines_preservemost_from_x0
+  ; CHECK-NEXT:   MSR 56965, $xzr
+  ; CHECK-NEXT:   ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT:   InOutZAUsePseudo
+  ; CHECK-NEXT:   ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  bb.0:
+    successors: %bb.2(0x00000000), %bb.1(0x80000000)
+
+    ; Simulate shared ZA call
+    ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+    InOutZAUsePseudo
+    ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+
+    ; Simulate private ZA call at the end of the block
+    ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+    RequiresZASavePseudo
+    ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+
+    B %bb.2
+
+  bb.1 (landing-pad):
+    liveins: $x0, $x1
+
+    ; Simulate shared ZA call
+    ADJCALLSTACKDOWN 0, 0, implicit-def dead $sp, implicit $sp
+    InOutZAUsePseudo
+    ADJCALLSTACKUP 0, 0, implicit-def dead $sp, implicit $sp
+
+  bb.2:
diff --git a/llvm/test/CodeGen/AArch64/sme-za-exceptions.ll b/llvm/test/CodeGen/AArch64/sme-za-exceptions.ll
index c497a95a58c8a..bb88142efa592 100644
--- a/llvm/test/CodeGen/AArch64/sme-za-exceptions.ll
+++ b/llvm/test/CodeGen/AArch64/sme-za-exceptions.ll
@@ -1,5 +1,5 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sme -aarch64-new-sme-abi < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sme -aarch64-new-sme-abi -verify-machineinstrs < %s | FileCheck %s
 
 ; A simple EH test case that corresponds to the following C++ source:
 ;
@@ -62,16 +62,14 @@ define void @za_with_raii(i1 %fail) "aarch64_inout_za" personality ptr @__gxx_pe
 ; CHECK-NEXT:    ldr x1, [x1, :got_lo12:typeinfo_for_char_const_ptr]
 ; CHECK-NEXT:    bl __cxa_throw
 ; CHECK-NEXT:  .Ltmp1:
-; CHECK-NEXT:    mov x8, x0
 ; CHECK-NEXT:    smstart za
-; CHECK-NEXT:    mrs x9, TPIDR2_EL0
+; CHECK-NEXT:    mrs x8, TPIDR2_EL0
 ; CHECK-NEXT:    sub x0, x29, #16
-; CHECK-NEXT:    cbnz x9, .LBB0_4
+; CHECK-NEXT:    cbnz x8, .LBB0_4
 ; CHECK-NEXT:  // %bb.3: // %throw_exception
 ; CHECK-NEXT:    bl __arm_tpidr2_restore
 ; CHECK-NEXT:  .LBB0_4: // %throw_exception
 ; CHECK-NEXT:    msr TPIDR2_EL0, xzr
-; CHECK-NEXT:    // kill: def $x0 killed $x8
 ; CHECK-NEXT:  // %bb.5: // %throw_fail
 ; CHECK-NEXT:  .LBB0_6: // %unwind_dtors
 ; CHECK-NEXT:  .Ltmp2:

MacDue added a commit to MacDue/llvm-project that referenced this pull request Aug 19, 2025
MacDue added a commit that referenced this pull request Aug 19, 2025
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Aug 19, 2025
MacDue added 2 commits August 20, 2025 09:23
These liveins are not defined by predecessors, so attempting to preserve
them results in generating invalid MIR (as the registers may not be
defined). This takes a similar approach to: llvm@c1dc267
@MacDue MacDue changed the title [AArch64][SME] Exclude runtime defined liveins when computing liveouts [LiveRegUnits] Exclude runtime defined liveins when computing liveouts Aug 20, 2025
Comment on lines -1784 to -1786
assert(MF.getProperties().hasTracksLiveness() &&
"Liveness information is accurate");

Copy link
Member Author

Choose a reason for hiding this comment

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

Note: This assert occurs too early (so asserts for blocks with no successors), removing this is safe as the liveout_iterator internally calls livein_begin(), which contains the same assert.

Comment on lines +556 to +557
if ((*BlockI)->isEHPad() && (LiveRegI->PhysReg == ExceptionPointer ||
LiveRegI->PhysReg == ExceptionSelector))
Copy link
Member Author

Choose a reason for hiding this comment

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

This was a latent bug, these registers should not be skipped outside of landing-pads (this matches the logic in operator++).

@MacDue MacDue merged commit 810ea69 into llvm:main Aug 21, 2025
9 checks passed
@MacDue MacDue deleted the fix_mir branch August 21, 2025 08:06
MacDue added a commit to MacDue/llvm-project that referenced this pull request Aug 21, 2025
This is done for consistency with LiveRegUnits (see llvm#154325). This is
technically not an NFC, as `MBB.liveouts()` excludes runtime-defined
liveins, but no users currently depend on this.
MacDue added a commit that referenced this pull request Aug 23, 2025
This is done for consistency with LiveRegUnits (see #154325). This is
technically not an NFC, as `MBB.liveouts()` excludes runtime-defined
liveins, but no users currently depend on this.
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.

3 participants