[llvm] [CodeGen] DetectDeadLanes: do not rewrite operand flags on bundles (PR #222124)

Kevin McAfee via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 11:46:01 PDT 2026


https://github.com/kalxr updated https://github.com/llvm/llvm-project/pull/222124

>From 462918058c300bdb81234a64404dd26eafd7100e Mon Sep 17 00:00:00 2001
From: Kevin McAfee <kmcafee at nvidia.com>
Date: Fri, 4 Sep 2026 21:03:37 +0000
Subject: [PATCH 1/2] precommit

---
 .../AMDGPU/detect-dead-lanes-bundle.mir       | 102 ++++++++++++++++++
 .../test/CodeGen/AMDGPU/detect-dead-lanes.mir |  71 ------------
 2 files changed, 102 insertions(+), 71 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/detect-dead-lanes-bundle.mir

diff --git a/llvm/test/CodeGen/AMDGPU/detect-dead-lanes-bundle.mir b/llvm/test/CodeGen/AMDGPU/detect-dead-lanes-bundle.mir
new file mode 100644
index 0000000000000..300929011d6d0
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/detect-dead-lanes-bundle.mir
@@ -0,0 +1,102 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=amdgcn -mcpu=gfx600 -run-pass detect-dead-lanes -verify-machineinstrs -o - %s | FileCheck %s
+# RUN: llc -mtriple=amdgcn -mcpu=gfx600 -passes detect-dead-lanes -verify-machineinstrs -o - %s | FileCheck %s
+---
+name: bundle_header_use_not_marked_undef
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: name: bundle_header_use_not_marked_undef
+    ; CHECK: [[DEF:%[0-9]+]]:sreg_64 = IMPLICIT_DEF
+    ; CHECK-NEXT: BUNDLE implicit undef [[DEF]] {
+    ; CHECK-NEXT:   S_NOP 0, implicit undef [[DEF]]
+    ; CHECK-NEXT: }
+    ; CHECK-NEXT: S_NOP 0, implicit undef [[DEF]]
+    %0:sreg_64 = IMPLICIT_DEF
+    BUNDLE implicit %0 {
+      S_NOP 0, implicit %0
+    }
+    S_NOP 0, implicit %0
+...
+---
+name: bundle_header_use_not_marked_undef_via_reg_sequence
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: name: bundle_header_use_not_marked_undef_via_reg_sequence
+    ; CHECK: [[DEF:%[0-9]+]]:sgpr_128 = IMPLICIT_DEF
+    ; CHECK-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE undef [[DEF]].sub0, %subreg.sub0, undef [[DEF]].sub1, %subreg.sub1
+    ; CHECK-NEXT: BUNDLE implicit undef [[REG_SEQUENCE]] {
+    ; CHECK-NEXT:   S_NOP 0, implicit undef [[REG_SEQUENCE]]
+    ; CHECK-NEXT: }
+    %0:sgpr_128 = IMPLICIT_DEF
+    %1:sreg_64 = REG_SEQUENCE %0.sub0, %subreg.sub0, %0.sub1, %subreg.sub1
+    BUNDLE implicit %1 {
+      S_NOP 0, implicit %1
+    }
+...
+---
+name: headerless_bundle_def_not_marked_dead
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: name: headerless_bundle_def_not_marked_dead
+    ; CHECK: dead [[S_MOV_B64_:%[0-9]+]]:sreg_64 = S_MOV_B64 0 {
+    ; CHECK-NEXT:   S_NOP 0, implicit internal [[S_MOV_B64_]]
+    ; CHECK-NEXT: }
+    %0:sreg_64 = S_MOV_B64 0 {
+      S_NOP 0, implicit internal %0
+    }
+...
+---
+name: bundle_header_subreg_use_not_marked_undef
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $sgpr0
+    ; CHECK-LABEL: name: bundle_header_subreg_use_not_marked_undef
+    ; CHECK: liveins: $sgpr0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE $sgpr0, %subreg.sub0
+    ; CHECK-NEXT: BUNDLE implicit killed [[REG_SEQUENCE]].sub0, implicit killed undef [[REG_SEQUENCE]].sub1 {
+    ; CHECK-NEXT:   S_NOP 0, implicit killed [[REG_SEQUENCE]].sub0
+    ; CHECK-NEXT:   S_NOP 0, implicit killed undef [[REG_SEQUENCE]].sub1
+    ; CHECK-NEXT: }
+    %0:sreg_64 = REG_SEQUENCE $sgpr0, %subreg.sub0
+    BUNDLE implicit killed %0.sub0, implicit killed %0.sub1 {
+      S_NOP 0, implicit killed %0.sub0
+      S_NOP 0, implicit killed %0.sub1
+    }
+...
+---
+name: bundle_header_def_not_marked_dead
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: name: bundle_header_def_not_marked_dead
+    ; CHECK: BUNDLE implicit-def %0, implicit-def dead %1 {
+    ; CHECK-NEXT:   S_NOP 0, implicit-def %0
+    ; CHECK-NEXT:   S_NOP 0, implicit-def dead %1
+    ; CHECK-NEXT: }
+    ; CHECK-NEXT: S_NOP 0, implicit %0.sub0
+    BUNDLE implicit-def %0:sreg_64, implicit-def %1:sreg_64 {
+      S_NOP 0, implicit-def %0
+      S_NOP 0, implicit-def %1
+    }
+    S_NOP 0, implicit %0.sub0
+...
+---
+name: bundle_kill_operand_not_marked_undef
+tracksRegLiveness: true
+body: |
+  bb.0:
+    ; CHECK-LABEL: name: bundle_kill_operand_not_marked_undef
+    ; CHECK: S_NOP 0, implicit-def dead %0
+    ; CHECK-NEXT: BUNDLE implicit undef %0 {
+    ; CHECK-NEXT:   KILL undef %0
+    ; CHECK-NEXT: }
+    S_NOP 0, implicit-def %0:sreg_64
+    BUNDLE implicit undef %0 {
+      KILL %0
+    }
+...
diff --git a/llvm/test/CodeGen/AMDGPU/detect-dead-lanes.mir b/llvm/test/CodeGen/AMDGPU/detect-dead-lanes.mir
index e3c59807882ca..0d467bb3cad43 100644
--- a/llvm/test/CodeGen/AMDGPU/detect-dead-lanes.mir
+++ b/llvm/test/CodeGen/AMDGPU/detect-dead-lanes.mir
@@ -402,74 +402,3 @@ body: |
     S_NOP 0, implicit %2.sub2
     S_NOP 0, implicit %2.sub3
 ...
----
-# Check that operands inside a BUNDLE are visited. A register referenced both
-# by the BUNDLE header and by a bundled instruction must have the undef flag
-# set on both, otherwise the two disagree about whether the bundle reads it.
-# CHECK-LABEL: name: bundle_undef
-# CHECK: BUNDLE implicit undef %0 {
-# CHECK-NEXT: S_NOP 0, implicit undef %0
-name: bundle_undef
-tracksRegLiveness: true
-body: |
-  bb.0:
-    %0:sreg_32_xm0 = IMPLICIT_DEF
-    BUNDLE implicit %0 {
-      S_NOP 0, implicit %0
-    }
-...
----
-# Check that an undefined subregister use inside a BUNDLE is marked undef while
-# a defined subregister use is unchanged, and that existing kill flags are
-# preserved.
-# CHECK-LABEL: name: bundle_subregs
-# CHECK: %0:sreg_64 = REG_SEQUENCE $sgpr0, %subreg.sub0
-# CHECK: BUNDLE implicit killed %0.sub0, implicit killed undef %0.sub1 {
-# CHECK-NEXT: S_NOP 0, implicit killed %0.sub0
-# CHECK-NEXT: S_NOP 0, implicit killed undef %0.sub1
-name: bundle_subregs
-tracksRegLiveness: true
-body: |
-  bb.0:
-    liveins: $sgpr0
-    %0:sreg_64 = REG_SEQUENCE $sgpr0, %subreg.sub0
-    BUNDLE implicit killed %0.sub0, implicit killed %0.sub1 {
-      S_NOP 0, implicit killed %0.sub0
-      S_NOP 0, implicit killed %0.sub1
-    }
-...
----
-# Check that unused definitions inside a BUNDLE are marked dead on both the
-# BUNDLE header and the bundled instruction, while used definitions are left
-# unchanged.
-# CHECK-LABEL: name: bundle_defs
-# CHECK: BUNDLE implicit-def %0, implicit-def dead %1 {
-# CHECK-NEXT: S_NOP 0, implicit-def %0
-# CHECK-NEXT: S_NOP 0, implicit-def dead %1
-# CHECK: S_NOP 0, implicit %0.sub0
-name: bundle_defs
-tracksRegLiveness: true
-body: |
-  bb.0:
-    BUNDLE implicit-def %0:sreg_64, implicit-def %1:sreg_64 {
-      S_NOP 0, implicit-def %0
-      S_NOP 0, implicit-def %1
-    }
-    S_NOP 0, implicit %0.sub0
-...
----
-# Check that a KILL inside a BUNDLE is visited. KILL operands do not count as
-# uses, so the definition becomes dead and the KILL operand becomes undef.
-# CHECK-LABEL: name: bundle_kill
-# CHECK: S_NOP 0, implicit-def dead %0
-# CHECK: BUNDLE implicit undef %0 {
-# CHECK-NEXT: KILL undef %0
-name: bundle_kill
-tracksRegLiveness: true
-body: |
-  bb.0:
-    S_NOP 0, implicit-def %0:sreg_64
-    BUNDLE implicit undef %0 {
-      KILL %0
-    }
-...

>From e53c6ff35d1a3a562c5ac8d14220b23ded396838 Mon Sep 17 00:00:00 2001
From: Kevin McAfee <kmcafee at nvidia.com>
Date: Fri, 4 Sep 2026 22:24:01 +0000
Subject: [PATCH 2/2] [CodeGen] DetectDeadLanes: do not rewrite operand flags
 on bundles

modifySubRegisterOperandStatus() only visits top level instructions, so
for a bundle it only sees the header. Marking bundle header operands
dead or undef could make the header disagree with the bundled
instructions, which are what LiveIntervals looks at. Rewriting the
bundled instructions would not be correct either: bundle internal reads
are ignored by MachineOperand::readsReg(), so an internal def only used
inside the bundle would be marked dead.
---
 llvm/lib/CodeGen/DetectDeadLanes.cpp          |  7 ++++++
 .../AMDGPU/detect-dead-lanes-bundle.mir       | 24 +++++++++----------
 2 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/CodeGen/DetectDeadLanes.cpp b/llvm/lib/CodeGen/DetectDeadLanes.cpp
index b5088050e1396..8bfd4e1103ccd 100644
--- a/llvm/lib/CodeGen/DetectDeadLanes.cpp
+++ b/llvm/lib/CodeGen/DetectDeadLanes.cpp
@@ -498,6 +498,13 @@ DetectDeadLanes::modifySubRegisterOperandStatus(const DeadLaneDetector &DLD,
   // Mark operands as dead/unused.
   for (MachineBasicBlock &MBB : MF) {
     for (MachineInstr &MI : MBB) {
+      // Marking bundle header operands dead or undef could make the header
+      // disagree with the bundled instructions. Rewriting the bundled
+      // instructions would not be correct either: bundle internal reads are
+      // ignored by MachineOperand::readsReg(), so an internal def only used
+      // inside the bundle would be marked dead.
+      if (MI.isBundled())
+        continue;
       for (MachineOperand &MO : mi_bundle_ops(MI)) {
         if (!MO.isReg())
           continue;
diff --git a/llvm/test/CodeGen/AMDGPU/detect-dead-lanes-bundle.mir b/llvm/test/CodeGen/AMDGPU/detect-dead-lanes-bundle.mir
index 300929011d6d0..b571c12f60deb 100644
--- a/llvm/test/CodeGen/AMDGPU/detect-dead-lanes-bundle.mir
+++ b/llvm/test/CodeGen/AMDGPU/detect-dead-lanes-bundle.mir
@@ -1,6 +1,6 @@
 # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -mtriple=amdgcn -mcpu=gfx600 -run-pass detect-dead-lanes -verify-machineinstrs -o - %s | FileCheck %s
-# RUN: llc -mtriple=amdgcn -mcpu=gfx600 -passes detect-dead-lanes -verify-machineinstrs -o - %s | FileCheck %s
+# RUN: llc -mtriple=amdgcn -mcpu=amdgpu9.00 -run-pass=detect-dead-lanes -o - %s | FileCheck %s
+# RUN: llc -mtriple=amdgcn -mcpu=amdgpu6.00 -passes=detect-dead-lanes -o - %s | FileCheck %s
 ---
 name: bundle_header_use_not_marked_undef
 tracksRegLiveness: true
@@ -8,8 +8,8 @@ body: |
   bb.0:
     ; CHECK-LABEL: name: bundle_header_use_not_marked_undef
     ; CHECK: [[DEF:%[0-9]+]]:sreg_64 = IMPLICIT_DEF
-    ; CHECK-NEXT: BUNDLE implicit undef [[DEF]] {
-    ; CHECK-NEXT:   S_NOP 0, implicit undef [[DEF]]
+    ; CHECK-NEXT: BUNDLE implicit [[DEF]] {
+    ; CHECK-NEXT:   S_NOP 0, implicit [[DEF]]
     ; CHECK-NEXT: }
     ; CHECK-NEXT: S_NOP 0, implicit undef [[DEF]]
     %0:sreg_64 = IMPLICIT_DEF
@@ -26,8 +26,8 @@ body: |
     ; CHECK-LABEL: name: bundle_header_use_not_marked_undef_via_reg_sequence
     ; CHECK: [[DEF:%[0-9]+]]:sgpr_128 = IMPLICIT_DEF
     ; CHECK-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE undef [[DEF]].sub0, %subreg.sub0, undef [[DEF]].sub1, %subreg.sub1
-    ; CHECK-NEXT: BUNDLE implicit undef [[REG_SEQUENCE]] {
-    ; CHECK-NEXT:   S_NOP 0, implicit undef [[REG_SEQUENCE]]
+    ; CHECK-NEXT: BUNDLE implicit [[REG_SEQUENCE]] {
+    ; CHECK-NEXT:   S_NOP 0, implicit [[REG_SEQUENCE]]
     ; CHECK-NEXT: }
     %0:sgpr_128 = IMPLICIT_DEF
     %1:sreg_64 = REG_SEQUENCE %0.sub0, %subreg.sub0, %0.sub1, %subreg.sub1
@@ -41,7 +41,7 @@ tracksRegLiveness: true
 body: |
   bb.0:
     ; CHECK-LABEL: name: headerless_bundle_def_not_marked_dead
-    ; CHECK: dead [[S_MOV_B64_:%[0-9]+]]:sreg_64 = S_MOV_B64 0 {
+    ; CHECK: [[S_MOV_B64_:%[0-9]+]]:sreg_64 = S_MOV_B64 0 {
     ; CHECK-NEXT:   S_NOP 0, implicit internal [[S_MOV_B64_]]
     ; CHECK-NEXT: }
     %0:sreg_64 = S_MOV_B64 0 {
@@ -58,9 +58,9 @@ body: |
     ; CHECK: liveins: $sgpr0
     ; CHECK-NEXT: {{  $}}
     ; CHECK-NEXT: [[REG_SEQUENCE:%[0-9]+]]:sreg_64 = REG_SEQUENCE $sgpr0, %subreg.sub0
-    ; CHECK-NEXT: BUNDLE implicit killed [[REG_SEQUENCE]].sub0, implicit killed undef [[REG_SEQUENCE]].sub1 {
+    ; CHECK-NEXT: BUNDLE implicit killed [[REG_SEQUENCE]].sub0, implicit killed [[REG_SEQUENCE]].sub1 {
     ; CHECK-NEXT:   S_NOP 0, implicit killed [[REG_SEQUENCE]].sub0
-    ; CHECK-NEXT:   S_NOP 0, implicit killed undef [[REG_SEQUENCE]].sub1
+    ; CHECK-NEXT:   S_NOP 0, implicit killed [[REG_SEQUENCE]].sub1
     ; CHECK-NEXT: }
     %0:sreg_64 = REG_SEQUENCE $sgpr0, %subreg.sub0
     BUNDLE implicit killed %0.sub0, implicit killed %0.sub1 {
@@ -74,9 +74,9 @@ tracksRegLiveness: true
 body: |
   bb.0:
     ; CHECK-LABEL: name: bundle_header_def_not_marked_dead
-    ; CHECK: BUNDLE implicit-def %0, implicit-def dead %1 {
+    ; CHECK: BUNDLE implicit-def %0, implicit-def %1 {
     ; CHECK-NEXT:   S_NOP 0, implicit-def %0
-    ; CHECK-NEXT:   S_NOP 0, implicit-def dead %1
+    ; CHECK-NEXT:   S_NOP 0, implicit-def %1
     ; CHECK-NEXT: }
     ; CHECK-NEXT: S_NOP 0, implicit %0.sub0
     BUNDLE implicit-def %0:sreg_64, implicit-def %1:sreg_64 {
@@ -93,7 +93,7 @@ body: |
     ; CHECK-LABEL: name: bundle_kill_operand_not_marked_undef
     ; CHECK: S_NOP 0, implicit-def dead %0
     ; CHECK-NEXT: BUNDLE implicit undef %0 {
-    ; CHECK-NEXT:   KILL undef %0
+    ; CHECK-NEXT:   KILL %0
     ; CHECK-NEXT: }
     S_NOP 0, implicit-def %0:sreg_64
     BUNDLE implicit undef %0 {



More information about the llvm-commits mailing list