[llvm] TwoAddressInstructions: Use the per-operand early clobber flag for tied copies (PR #227581)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 23:49:19 PDT 2026
https://github.com/arsenm created https://github.com/llvm/llvm-project/pull/227581
The live interval update for the copy inserted for a tied operand ended
the new segment at getRegSlot(IsEarlyClobber), using a flag computed
once per instruction for any tied pairs. Use the flag
of the def actually being processed.
With an inline asm mixing "=&r" and "=r" outputs tied to the same input,
the copy for the plain def ended at the early clobber slot, leaving a
hole before its own def. That splits the live range into multiple
connected components and asserts in updatePressureDiffs.
Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
>From f9510b1865cd177cbb51d7f89492bf19279f4673 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <Matthew.Arsenault at amd.com>
Date: Wed, 30 Sep 2026 01:33:29 +0200
Subject: [PATCH] TwoAddressInstructions: Use the per-operand early clobber
flag for tied copies
The live interval update for the copy inserted for a tied operand ended
the new segment at getRegSlot(IsEarlyClobber), using a flag computed
once per instruction for any tied pairs. Use the flag
of the def actually being processed.
With an inline asm mixing "=&r" and "=r" outputs tied to the same input,
the copy for the plain def ended at the early clobber slot, leaving a
hole before its own def. That splits the live range into multiple
connected components and asserts in updatePressureDiffs.
Co-Authored-By: Claude Opus 5 <noreply at anthropic.com>
---
.../lib/CodeGen/TwoAddressInstructionPass.cpp | 2 +-
.../twoaddr-mixed-early-clobber-tied-defs.ll | 24 ++++++++++++++++
.../twoaddr-mixed-early-clobber-tied-defs.mir | 28 +++++++++++++++++++
3 files changed, 53 insertions(+), 1 deletion(-)
create mode 100644 llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.ll
create mode 100644 llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.mir
diff --git a/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp b/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp
index b84391817893d..03ecc2174c475 100644
--- a/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp
+++ b/llvm/lib/CodeGen/TwoAddressInstructionPass.cpp
@@ -1669,7 +1669,7 @@ void TwoAddressInstructionImpl::processTiedPairs(MachineInstr *MI,
LastCopyIdx = LIS->InsertMachineInstrInMaps(*PrevMI).getRegSlot();
SlotIndex endIdx =
- LIS->getInstructionIndex(*MI).getRegSlot(IsEarlyClobber);
+ LIS->getInstructionIndex(*MI).getRegSlot(DstMO.isEarlyClobber());
if (RegA.isVirtual()) {
LiveInterval &LI = LIS->getInterval(RegA);
VNInfo *VNI = LI.getNextValue(LastCopyIdx, LIS->getVNInfoAllocator());
diff --git a/llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.ll b/llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.ll
new file mode 100644
index 0000000000000..8aa2113e8fa6b
--- /dev/null
+++ b/llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.ll
@@ -0,0 +1,24 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -O1 -mtriple=x86_64 < %s | FileCheck %s
+
+define i64 @mixed_early_clobber_tied_defs(ptr %r) {
+; CHECK-LABEL: mixed_early_clobber_tied_defs:
+; CHECK: # %bb.0: # %entry
+; CHECK-NEXT: xorl %ecx, %ecx
+; CHECK-NEXT: xorl %edx, %edx
+; CHECK-NEXT: xorl %eax, %eax
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: movq %rdx, (%rdi)
+; CHECK-NEXT: #APP
+; CHECK-NEXT: #NO_APP
+; CHECK-NEXT: retq
+entry:
+ %0 = tail call { i64, i64 } asm "", "=&r,=r,0,1"(i64 0, i64 0)
+ %asm0 = extractvalue { i64, i64 } %0, 0
+ %asm1 = extractvalue { i64, i64 } %0, 1
+ store i64 %asm0, ptr %r
+ %1 = tail call { i64, i64 } asm "", "=&r,=r,0,1"(i64 %asm1, i64 0)
+ %asm2 = extractvalue { i64, i64 } %1, 0
+ ret i64 %asm2
+}
diff --git a/llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.mir b/llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.mir
new file mode 100644
index 0000000000000..c41e16179c927
--- /dev/null
+++ b/llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.mir
@@ -0,0 +1,28 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=x86_64 -run-pass=liveintervals,twoaddressinstruction -o - %s | FileCheck %s
+
+# Two operands are tied to the same source register, but only one of the
+# defs is early clobber. The copy inserted for the plain def must be live
+# until the register slot, not the earlier early clobber slot.
+
+---
+name: mixed_early_clobber_tied_defs
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: mixed_early_clobber_tied_defs
+ ; CHECK: [[MOV32r0_:%[0-9]+]]:gr32 = MOV32r0 implicit-def dead $eflags
+ ; CHECK-NEXT: [[SUBREG_TO_REG:%[0-9]+]]:gr64_norex2 = SUBREG_TO_REG [[MOV32r0_]], %subreg.sub_32bit
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:gr64_norex2 = COPY [[SUBREG_TO_REG]]
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:gr64_norex2 = COPY [[COPY]]
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:gr64_norex2 = COPY [[COPY]]
+ ; CHECK-NEXT: INLINEASM &"", attdialect, regdef-ec:GR64_NOREX2, def early-clobber [[COPY1]], regdef:GR64_NOREX2, def dead [[COPY2]], reguse tiedto:$0, [[COPY1]](tied-def 3), reguse tiedto:$1, [[COPY2]](tied-def 5)
+ ; CHECK-NEXT: $rax = COPY [[COPY1]]
+ ; CHECK-NEXT: RET 0, killed $rax
+ %0:gr32 = MOV32r0 implicit-def dead $eflags
+ %1:gr64_norex2 = SUBREG_TO_REG %0, %subreg.sub_32bit
+ %2:gr64_norex2 = COPY %1
+ INLINEASM &"", attdialect, regdef-ec:GR64_NOREX2, def early-clobber %3:gr64_norex2, regdef:GR64_NOREX2, def %4:gr64_norex2, reguse tiedto:$0, %2(tied-def 3), reguse tiedto:$1, %2(tied-def 5)
+ $rax = COPY %3
+ RET 0, killed $rax
+...
More information about the llvm-commits
mailing list