[llvm] TwoAddressInstructions: Use the per-operand early clobber flag for tied copies (PR #227581)

via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 29 23:50:34 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-regalloc

Author: Matt Arsenault (arsenm)

<details>
<summary>Changes</summary>

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@<!-- -->anthropic.com>

---
Full diff: https://github.com/llvm/llvm-project/pull/227581.diff


3 Files Affected:

- (modified) llvm/lib/CodeGen/TwoAddressInstructionPass.cpp (+1-1) 
- (added) llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.ll (+24) 
- (added) llvm/test/CodeGen/X86/twoaddr-mixed-early-clobber-tied-defs.mir (+28) 


``````````diff
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
+...

``````````

</details>


https://github.com/llvm/llvm-project/pull/227581


More information about the llvm-commits mailing list