[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