[llvm] [CodeGen] Transfer implicit-defs to first instruction when lowering multi-instruction COPY (PR #194892)

Cristián Carrión via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 29 08:46:14 PDT 2026


https://github.com/ccarrions created https://github.com/llvm/llvm-project/pull/194892

When lowering a `COPY` with an implict-def, all implicit operands are transfered to the last instruction. If the `implicit-def` defines a super-register of the copy destination, a following backward liveness scan will remove all previous sub-register defs of the replacement instructions. 
Now, in transferImplicitOperands, if and `implicit-def` register overlaps the copy destination, it is transfered to the first replacement instruction rather than the last.

Related to: [#194889](https://github.com/llvm/llvm-project/issues/194889)

>From 792eb3d86a434da3f4117ab6e6effbdc430b6330 Mon Sep 17 00:00:00 2001
From: "Carrion Saldivia, Cristian" <cristian.carrion.saldivia at intel.com>
Date: Wed, 29 Apr 2026 08:36:01 -0700
Subject: [PATCH] [CodeGen] Transfer implicit-defs to first instruction when
 lowering multi-instruction COPY

---
 llvm/lib/CodeGen/TargetInstrInfo.cpp          | 32 ++++++++++++++-----
 .../lower-copy-implicit-def-placement.mir     | 22 +++++++++++++
 2 files changed, 46 insertions(+), 8 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/lower-copy-implicit-def-placement.mir

diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp
index bc930df5a9bc1..341565bd24f09 100644
--- a/llvm/lib/CodeGen/TargetInstrInfo.cpp
+++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp
@@ -871,20 +871,26 @@ MachineInstr *TargetInstrInfo::foldMemoryOperand(MachineInstr &MI,
 /// replacement instructions immediately precede it.  Copy any implicit
 /// operands from MI to the replacement instruction.
 static void transferImplicitOperands(MachineInstr *MI,
+                                     MachineBasicBlock::iterator FirstMI,
                                      const TargetRegisterInfo *TRI) {
-  MachineBasicBlock::iterator CopyMI = MI;
-  --CopyMI;
+  MachineBasicBlock::iterator LastMI = MI;
+  --LastMI;
 
   Register DstReg = MI->getOperand(0).getReg();
   for (const MachineOperand &MO : MI->implicit_operands()) {
-    CopyMI->addOperand(MO);
+    // Must transfer implicit-def of super-registers to first replacement
+    // instruction to keep subregister defs alive in future backward liveness
+    // scans.
+    bool ToFirst = MO.isDef() && TRI->regsOverlap(DstReg, MO.getReg());
+    MachineBasicBlock::iterator Target = ToFirst ? FirstMI : LastMI;
+    Target->addOperand(MO);
 
     // Be conservative about preserving kills when subregister defs are
     // involved. If there was implicit kill of a super-register overlapping the
     // copy result, we would kill the subregisters previous copies defined.
 
     if (MO.isKill() && TRI->regsOverlap(DstReg, MO.getReg()))
-      CopyMI->getOperand(CopyMI->getNumOperands() - 1).setIsKill(false);
+      Target->getOperand(Target->getNumOperands() - 1).setIsKill(false);
   }
 }
 
@@ -913,13 +919,23 @@ void TargetInstrInfo::lowerCopy(
     return;
   }
 
-  copyPhysReg(*MI->getParent(), MI, MI->getDebugLoc(), DstMO.getReg(),
-              SrcMO.getReg(), SrcMO.isKill(),
+  MachineBasicBlock &MBB = *MI->getParent();
+  auto It = MI->getIterator();
+  // Record the position just before the expansion so we can find the first
+  // instruction inserted by copyPhysReg.
+  MachineBasicBlock::iterator Anchor =
+      (It == MBB.begin()) ? MBB.end() : std::prev(It);
+
+  copyPhysReg(MBB, MI, MI->getDebugLoc(), DstMO.getReg(), SrcMO.getReg(),
+              SrcMO.isKill(),
               DstMO.getReg().isPhysical() ? DstMO.isRenamable() : false,
               SrcMO.getReg().isPhysical() ? SrcMO.isRenamable() : false);
 
-  if (MI->getNumOperands() > 2)
-    transferImplicitOperands(MI, &TRI);
+  if (MI->getNumOperands() > 2) {
+    MachineBasicBlock::iterator FirstMI =
+        (Anchor == MBB.end()) ? MBB.begin() : std::next(Anchor);
+    transferImplicitOperands(MI, FirstMI, &TRI);
+  }
   MI->eraseFromParent();
 }
 
diff --git a/llvm/test/CodeGen/AArch64/lower-copy-implicit-def-placement.mir b/llvm/test/CodeGen/AArch64/lower-copy-implicit-def-placement.mir
new file mode 100644
index 0000000000000..99cea63324eb0
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/lower-copy-implicit-def-placement.mir
@@ -0,0 +1,22 @@
+# RUN: llc -mtriple=aarch64 -run-pass=postrapseudos,dead-mi-elimination %s -o - | FileCheck %s
+
+# Checks that after lowering copies with implicit-def they are transfered to the first
+# generated instruction
+---
+name:            lower-copy-implicit-def
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    liveins: $q4_q5, $q6_q7, $x0
+
+    ; CHECK-LABEL: name: lower-copy-implicit-def
+    ; CHECK:      $q0 = ORRv16i8 $q4, $q4, implicit-def $q0_q1_q2_q3
+    ; CHECK-NEXT: $q1 = ORRv16i8 $q5, $q5
+    ; CHECK-NEXT: $q2 = ORRv16i8 $q6, $q6
+    ; CHECK-NEXT: $q3 = ORRv16i8 $q7, $q7
+    ; CHECK-NEXT: ST4Fourv4s $q0_q1_q2_q3, $x0
+    $q0_q1 = COPY $q4_q5, implicit-def $q0_q1_q2_q3
+    $q2_q3 = COPY $q6_q7
+    ST4Fourv4s $q0_q1_q2_q3, $x0
+    RET_ReallyLR
+...



More information about the llvm-commits mailing list