[llvm] [CodeGen] Preserve IMPLICIT_DEF for subregister joins (PR #218348)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 24 01:29:12 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-regalloc

@llvm/pr-subscribers-backend-x86

Author: Karl Hylen (Hylen)

<details>
<summary>Changes</summary>

This is a followup to https://github.com/llvm/llvm-project/pull/204867.

In our downstream target using subregister liveness we observed LiveIntervals after register coalescing that made sense looking at the input before the coalescer but would have been recomputed differently, with a value reaching a use that it shouldn't. This was because of IMPLICIT_DEF removal. As a result of this we saw assertions in the later pass `RenameIndependentSubregs`.

I managed to write an X86 MIR test that shows the problem. Running it before the fix the IMPLICIT_DEF in bb.3 is removed and the `MOV16ri` value from bb.1 looks like it reaches the `sub_8bit_hi` use of `TEST8rr` in bb.5. But the intervals indicate the reachability represented by the input to the coalescer. I get `L0000000000000002 [48r,176B:0)`  with:
``` 
32B	bb.1:
...
48B	  %1:gr16_abcd = MOV16ri 1
...
176B	bb.3:
...
288B	bb.5:
...
320B	  TEST8rr %1.sub_8bit_hi:gr16_abcd, %1.sub_8bit_hi:gr16_abcd, implicit-def dead $eflags
```

Preserving the IMPLICIT_DEF makes the output of the coalescer agree with the live intervals and the input MIR.

Assisted-by: OpenAI Codex

I reviewed, understand, and validated all tool-generated changes and take responsibility for this contribution.

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


2 Files Affected:

- (modified) llvm/lib/CodeGen/RegisterCoalescer.cpp (+7-4) 
- (added) llvm/test/CodeGen/X86/coalescer-preserve-subreg-implicit-def.mir (+60) 


``````````diff
diff --git a/llvm/lib/CodeGen/RegisterCoalescer.cpp b/llvm/lib/CodeGen/RegisterCoalescer.cpp
index fae504c7c9cba..e3c5dc2bf635a 100644
--- a/llvm/lib/CodeGen/RegisterCoalescer.cpp
+++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp
@@ -2855,7 +2855,8 @@ JoinVals::ConflictResolution JoinVals::analyzeValue(unsigned ValNo,
     if (SubRangeJoin) {
       // We don't care about the lanes when joining subregister ranges.
       V.WriteLanes = V.ValidLanes = LaneBitmask::getLane(0);
-      if (DefMI->isImplicitDef()) {
+      // Preserve lane definitions in MIR when tracking subregister liveness.
+      if (DefMI->isImplicitDef() && !TrackSubRegLiveness) {
         V.ValidLanes = LaneBitmask::getNone();
         V.ErasableImplicitDef = true;
       }
@@ -2888,8 +2889,10 @@ JoinVals::ConflictResolution JoinVals::analyzeValue(unsigned ValNo,
         }
       }
 
-      // An IMPLICIT_DEF writes undef values.
-      if (DefMI->isImplicitDef()) {
+      // An IMPLICIT_DEF writes undef values. When tracking subregister
+      // liveness, treat it like an ordinary definition so the lane conflict
+      // checks below decide whether the join can preserve it.
+      if (DefMI->isImplicitDef() && !TrackSubRegLiveness) {
         // We normally expect IMPLICIT_DEF values to be live only until the end
         // of their block. If the value is really live longer and gets pruned in
         // another block, this flag is cleared again.
@@ -2995,7 +2998,7 @@ JoinVals::ConflictResolution JoinVals::analyzeValue(unsigned ValNo,
     return CR_Replace;
 
   // Check for simple erasable conflicts.
-  if (DefMI->isImplicitDef())
+  if (DefMI->isImplicitDef() && V.ErasableImplicitDef)
     return CR_Erase;
 
   // Include the non-conflict where DefMI is a coalescable copy that kills
diff --git a/llvm/test/CodeGen/X86/coalescer-preserve-subreg-implicit-def.mir b/llvm/test/CodeGen/X86/coalescer-preserve-subreg-implicit-def.mir
new file mode 100644
index 0000000000000..01f4a6ca61553
--- /dev/null
+++ b/llvm/test/CodeGen/X86/coalescer-preserve-subreg-implicit-def.mir
@@ -0,0 +1,60 @@
+# RUN: llc -mtriple=x86_64-unknown-linux-gnu -run-pass=register-coalescer \
+# RUN:   -enable-subreg-liveness -verify-coalescing -o - %s | FileCheck %s
+
+# An IMPLICIT_DEF must be preserved when a coalesced register tracks
+# subregister liveness. Otherwise the value defining the high byte in bb.1
+# also reaches the use in bb.5 along the bb.3 edge, where the high byte was
+# explicitly made undef. The low-byte use in bb.3 keeps another lane live.
+
+# CHECK-LABEL: name: preserve_subreg_implicit_def
+# CHECK: bb.3:
+# CHECK: [[REG:%[0-9]+]].sub_8bit_hi:gr16_abcd = IMPLICIT_DEF
+# CHECK-NEXT: TEST8rr [[REG]].sub_8bit, [[REG]].sub_8bit, implicit-def $eflags
+# CHECK: bb.5:
+# CHECK: TEST8rr [[REG]].sub_8bit_hi, [[REG]].sub_8bit_hi, implicit-def dead $eflags
+
+---
+name: preserve_subreg_implicit_def
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    successors: %bb.1(0x80000000)
+
+    JMP_1 %bb.1
+
+  bb.1:
+    successors: %bb.2(0x40000000), %bb.3(0x40000000)
+
+    %0:gr16_abcd = MOV16ri 1
+    TEST8rr %0.sub_8bit, %0.sub_8bit, implicit-def $eflags
+    JCC_1 %bb.3, 5, implicit killed $eflags
+
+  bb.2:
+    successors: %bb.5(0x80000000)
+
+    undef %1.sub_8bit_hi:gr16_abcd = COPY %0.sub_8bit_hi
+    %2:gr8_abcd_h = COPY %1.sub_8bit_hi
+    %3:gr8_abcd_h = COPY %2
+    JMP_1 %bb.5
+
+  bb.3:
+    successors: %bb.5(0x30000000), %bb.4(0x50000000)
+
+    %3:gr8_abcd_h = IMPLICIT_DEF
+    TEST8rr %0.sub_8bit, %0.sub_8bit, implicit-def $eflags
+    JCC_1 %bb.5, 5, implicit killed $eflags
+
+  bb.4:
+    successors: %bb.5(0x80000000)
+
+    %4:gr8_abcd_h = MOV8ri 2
+    %3:gr8_abcd_h = COPY %4
+
+  bb.5:
+    successors: %bb.1(0x80000000)
+
+    %5:gr8_abcd_h = COPY %3
+    TEST8rr %5, %5, implicit-def dead $eflags
+    JMP_1 %bb.1
+
+...

``````````

</details>


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


More information about the llvm-commits mailing list