[llvm] ARM: Don't duplicate the LR def when expanding call pseudos (PR #225062)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 04:17:20 PDT 2026
https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/225062
Several pseudo expansions in ARMExpandPseudoInsts built a real call and
then copied the pseudo's operands onto the new instruction. Each of these
call opcodes declares LR as an implicit-def, but the original instruction
already had that. The original def may have been marked as dead, so the result
would be a redundant operand with an inconsistent dead flag. This avoids
a failure in a future verifier check.
Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>
>From 4c7e9070dd25fe2fad89a55e412e354f3ed29aec Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Fri, 18 Sep 2026 22:06:27 +0200
Subject: [PATCH] ARM: Don't duplicate the LR def when expanding call pseudos
Several pseudo expansions in ARMExpandPseudoInsts built a real call and
then copied the pseudo's operands onto the new instruction. Each of these
call opcodes declares LR as an implicit-def, but the original instruction
already had that. The original def may have been marked as dead, so the result
would be a redundant operand with an inconsistent dead flag. This avoids
a failure in a future verifier check.
Co-authored-by: Claude (Claude-Opus-4.8) <noreply at anthropic.com>
---
llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp | 54 ++++++++++++-------
.../CodeGen/ARM/cmse-vlldm-no-reorder.mir | 2 +-
2 files changed, 36 insertions(+), 20 deletions(-)
diff --git a/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp b/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
index c55c87ec79ef3..22b32aa3da63b 100644
--- a/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/ARM/ARMExpandPseudoInsts.cpp
@@ -2437,15 +2437,20 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
ClearRegs); // save+clear FP regs with ClearRegs
CMSEClearGPRegs(MBB, MBBI, DL, ClearRegs, JumpReg);
- const MachineInstrBuilder NewCall =
- BuildMI(MBB, MBBI, DL, TII->get(ARM::tBLXNSr))
- .add(predOps(ARMCC::AL))
- .addReg(JumpReg, RegState::Kill);
+ // Be careful not to duplicate the LR def that already exists on the
+ // pseudoinstruction.
+ MachineFunction &MF = *MBB.getParent();
+ MachineInstr *NewCall = MF.CreateMachineInstr(TII->get(ARM::tBLXNSr), DL,
+ /*NoImplicit=*/true);
+ MBB.insert(MBBI, NewCall);
+ MachineInstrBuilder(MF, NewCall)
+ .add(predOps(ARMCC::AL))
+ .addReg(JumpReg, RegState::Kill);
for (const MachineOperand &MO : llvm::drop_begin(MI.operands()))
NewCall->addOperand(MO);
if (MI.isCandidateForAdditionalCallInfo())
- MI.getMF()->moveAdditionalCallInfo(&MI, NewCall.getInstr());
+ MI.getMF()->moveAdditionalCallInfo(&MI, NewCall);
CMSERestoreFPRegs(MBB, MBBI, DL, OriginalClearRegs); // restore FP registers
@@ -2664,22 +2669,30 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
MIB.addImm(0);
MIB.add(predOps(ARMCC::AL));
- MIB =
- BuildMI(MBB, MBBI, MI.getDebugLoc(),
- TII->get(Thumb ? gettBLXrOpcode(*MF) : getBLXOpcode(*MF)));
+ // The pesudo already has an LR def, avoid introducing a duplicated copy
+ // from the original operand list.
+ unsigned CallOpc = Thumb ? gettBLXrOpcode(*MF) : getBLXOpcode(*MF);
+ MachineInstr *Call = MF->CreateMachineInstr(
+ TII->get(CallOpc), MI.getDebugLoc(), /*NoImplicit=*/true);
+ MBB.insert(MBBI, Call);
+ MIB = MachineInstrBuilder(*MF, Call);
if (Thumb)
MIB.add(predOps(ARMCC::AL));
MIB.addReg(Reg, RegState::Kill);
} else {
- MIB = BuildMI(MBB, MBBI, MI.getDebugLoc(),
- TII->get(Thumb ? ARM::tBL : ARM::BL));
+ unsigned CallOpc = Thumb ? ARM::tBL : ARM::BL;
+ MachineInstr *Call = MF->CreateMachineInstr(
+ TII->get(CallOpc), MI.getDebugLoc(), /*NoImplicit=*/true);
+ MBB.insert(MBBI, Call);
+ MIB = MachineInstrBuilder(*MF, Call);
if (Thumb)
MIB.add(predOps(ARMCC::AL));
MIB.addExternalSymbol("__aeabi_read_tp", 0);
}
MIB.cloneMemRefs(MI);
- MIB.copyImplicitOps(MI);
+ for (const MachineOperand &MO : MI.operands())
+ MIB.add(MO);
// Update the call info.
if (MI.isCandidateForAdditionalCallInfo())
MF->moveAdditionalCallInfo(&MI, &*MIB);
@@ -3249,15 +3262,13 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
const bool Thumb = Opcode == ARM::tBL_PUSHLR;
Register Reg = MI.getOperand(0).getReg();
assert(Reg == ARM::LR && "expect LR register!");
- MachineInstrBuilder MIB;
+ MachineFunction &MF = *MBB.getParent();
+ unsigned CallOpc = Thumb ? ARM::tBL : ARM::BL;
if (Thumb) {
// push {lr}
BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(ARM::tPUSH))
.add(predOps(ARMCC::AL))
.addReg(Reg);
-
- // bl __gnu_mcount_nc
- MIB = BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(ARM::tBL));
} else {
// stmdb sp!, {lr}
BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(ARM::STMDB_UPD))
@@ -3265,13 +3276,18 @@ bool ARMExpandPseudo::ExpandMI(MachineBasicBlock &MBB,
.addReg(ARM::SP)
.add(predOps(ARMCC::AL))
.addReg(Reg);
-
- // bl __gnu_mcount_nc
- MIB = BuildMI(MBB, MBBI, MI.getDebugLoc(), TII->get(ARM::BL));
}
+
+ // bl __gnu_mcount_nc. Be careful not to duplicate the LR-def the original
+ // instruction already has.
+ MachineInstr *Call =
+ MF.CreateMachineInstr(TII->get(CallOpc), MI.getDebugLoc(),
+ /*NoImplicit=*/true);
+ MBB.insert(MBBI, Call);
+ MachineInstrBuilder MIB(MF, Call);
MIB.cloneMemRefs(MI);
for (const MachineOperand &MO : llvm::drop_begin(MI.operands()))
- MIB.add(MO);
+ Call->addOperand(MO);
MI.eraseFromParent();
return true;
}
diff --git a/llvm/test/CodeGen/ARM/cmse-vlldm-no-reorder.mir b/llvm/test/CodeGen/ARM/cmse-vlldm-no-reorder.mir
index 9bc335c2ab09a..ef32fe9b1b91d 100644
--- a/llvm/test/CodeGen/ARM/cmse-vlldm-no-reorder.mir
+++ b/llvm/test/CodeGen/ARM/cmse-vlldm-no-reorder.mir
@@ -104,7 +104,7 @@ body: |
# CHECK-NEXT: $r11 = tMOVr $r0, 14 /* CC::al */, $noreg
# CHECK-NEXT: $r12 = tMOVr $r0, 14 /* CC::al */, $noreg
# CHECK-NEXT: t2MSR_M 3072, $r0, 14 /* CC::al */, $noreg, implicit-def $cpsr
-# CHECK-NEXT: tBLXNSr 14 /* CC::al */, $noreg, killed $r0, csr_aapcs, implicit-def $lr, implicit $sp, implicit-def dead $lr, implicit $sp, implicit-def $sp, implicit-def $s0
+# CHECK-NEXT: tBLXNSr 14 /* CC::al */, $noreg, killed $r0, csr_aapcs, implicit-def dead $lr, implicit $sp, implicit-def $sp, implicit-def $s0
# CHECK-NEXT: $r12 = VMOVRS $s0, 14 /* CC::al */, $noreg
# CHECK-NEXT: VLLDM $sp, 14 /* CC::al */, $noreg, 0, implicit-def $vpr, implicit-def $fpscr, implicit-def $fpscr_nzcv, implicit-def $d0, implicit-def $d1, implicit-def $d2, implicit-def $d3, implicit-def $d4, implicit-def $d5, implicit-def $d6, implicit-def $d7, implicit-def $d8, implicit-def $d9, implicit-def $d10, implicit-def $d11, implicit-def $d12, implicit-def $d13, implicit-def $d14, implicit-def $d15
# CHECK-NEXT: $s0 = VMOVSR $r12, 14 /* CC::al */, $noreg
More information about the llvm-commits
mailing list