[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