[llvm] [CodeGen] Treat Reg uses which are partially defined within bundle as internal read (PR #201004)

Vikram Hegde via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 11 23:05:17 PDT 2026


https://github.com/vikramRH updated https://github.com/llvm/llvm-project/pull/201004

>From 44ab57057f802c28604c41a3d359315b7dbec8b7 Mon Sep 17 00:00:00 2001
From: vikhegde <vikram.hegde at amd.com>
Date: Mon, 1 Jun 2026 14:55:42 +0530
Subject: [PATCH 1/2] [CodeGen] Treat Reg uses which are partailly defined
 within bundle as internal read

---
 llvm/lib/CodeGen/MachineInstrBundle.cpp       | 52 ++++++++++---
 llvm/test/CodeGen/AMDGPU/finalizebundle.mir   | 76 +++++++++++++++++++
 .../CodeGen/AMDGPU/hard-clauses-gfx1250.mir   | 42 ++++++++++
 3 files changed, 161 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/CodeGen/MachineInstrBundle.cpp b/llvm/lib/CodeGen/MachineInstrBundle.cpp
index 663b755193695..c84c562905ad2 100644
--- a/llvm/lib/CodeGen/MachineInstrBundle.cpp
+++ b/llvm/lib/CodeGen/MachineInstrBundle.cpp
@@ -10,10 +10,12 @@
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/CodeGen/LiveRegUnits.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/MachineFunctionAnalysisManager.h"
 #include "llvm/CodeGen/MachineFunctionPass.h"
 #include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/Passes.h"
 #include "llvm/CodeGen/TargetInstrInfo.h"
 #include "llvm/CodeGen/TargetRegisterInfo.h"
@@ -115,16 +117,23 @@ static DebugLoc getDebugLoc(MachineBasicBlock::instr_iterator FirstMI,
 }
 
 /// Check if target reg is contained in given lists, which are:
-/// LocalDefsV as given list for virtual regs
-/// LocalDefsP as given list for physical regs, in BitVector[RegUnit] form
+/// LocalDefsV as given list for virtual regs.
+/// LocalDefsP as given list for physical regs, in BitVector[RegUnit] form.
+/// This uses the ExternDefs to see if the reg is "partially defined"
+/// within the bundle. i.e a subreg of Reg is defined within the bundle
+/// while the remianing part is undef.
 static bool containsReg(SmallSetVector<Register, 32> LocalDefsV,
-                        const BitVector &LocalDefsP, Register Reg,
-                        const TargetRegisterInfo *TRI) {
+                        const BitVector &LocalDefsP,
+                        const BitVector &ExternDefs,
+                        const TargetRegisterInfo *TRI, Register Reg,
+                        bool TracksLiveness, bool IsUndefUse) {
   if (Reg.isPhysical()) {
-    for (MCRegUnit Unit : TRI->regunits(Reg.asMCReg()))
-      if (!LocalDefsP[static_cast<unsigned>(Unit)])
-        return false;
-
+    for (MCRegUnit Unit : TRI->regunits(Reg.asMCReg())) {
+      unsigned U = static_cast<unsigned>(Unit);
+      if (LocalDefsP[U] || (TracksLiveness && !IsUndefUse && !ExternDefs[U]))
+        continue;
+      return false;
+    }
     return true;
   }
   return LocalDefsV.contains(Reg);
@@ -150,6 +159,30 @@ void llvm::finalizeBundle(MachineBasicBlock &MBB,
       BuildMI(MF, getDebugLoc(FirstMI, LastMI), TII->get(TargetOpcode::BUNDLE));
   Bundle.prepend(MIB);
 
+  // Compute the regunits whose definitions reach the bundle from outside,
+  // we use this to mark a partial-def + full-reg-uses as IsInternalRead.
+  bool TracksLiveness = MF.getRegInfo().tracksLiveness();
+  LiveRegUnits ExternDefs(*TRI);
+  if (TracksLiveness) {
+    if (MF.getRegInfo().reservedRegsFrozen()) {
+      const BitVector &Reserved = MF.getRegInfo().getReservedRegs();
+      for (int RegIdx = Reserved.find_first(); RegIdx >= 0;
+           RegIdx = Reserved.find_next(RegIdx))
+        ExternDefs.addReg(MCRegister(RegIdx));
+    }
+    ExternDefs.addLiveIns(MBB);
+    for (auto It = MBB.instr_begin(); It != FirstMI; ++It) {
+      if (It->isDebugInstr())
+        continue;
+      for (const MachineOperand &MO : It->operands()) {
+        if (!MO.isReg() || !MO.isDef() || MO.isDead() ||
+            !MO.getReg().isPhysical())
+          continue;
+        ExternDefs.addReg(MO.getReg().asMCReg());
+      }
+    }
+  }
+
   SmallSetVector<Register, 32> LocalDefs;
   BitVector LocalDefsP(TRI->getNumRegUnits());
   SmallSet<Register, 8> DeadDefSet;
@@ -168,7 +201,8 @@ void llvm::finalizeBundle(MachineBasicBlock &MBB,
       if (!Reg)
         continue;
 
-      if (containsReg(LocalDefs, LocalDefsP, Reg, TRI)) {
+      if (containsReg(LocalDefs, LocalDefsP, ExternDefs.getBitVector(), TRI,
+                      Reg, TracksLiveness, MO.isUndef())) {
         MO.setIsInternalRead();
         if (MO.isKill()) {
           // Internal def is now killed.
diff --git a/llvm/test/CodeGen/AMDGPU/finalizebundle.mir b/llvm/test/CodeGen/AMDGPU/finalizebundle.mir
index 590d69b8eb869..528826da656cb 100644
--- a/llvm/test/CodeGen/AMDGPU/finalizebundle.mir
+++ b/llvm/test/CodeGen/AMDGPU/finalizebundle.mir
@@ -11,6 +11,10 @@
   define void @test_mmo_merge1() { unreachable }
   define void @test_mmo_merge2() { unreachable }
   define void @test_mmo_drop() { unreachable }
+  define void @test_partial_def_1() { unreachable }
+  define void @test_partial_def_2() { unreachable }
+  define void @test_partial_def_3() { unreachable }
+  define void @test_partial_def_4() { unreachable }
 
 ...
 
@@ -99,3 +103,75 @@ body: |
     DS_WRITE_B32_gfx9 %0:vgpr_32, %1:vgpr_32, 0, 0, implicit $exec :: (store (s32) into @foo, addrspace 3)
     DS_WRITE_B32_gfx9 %0:vgpr_32, %1:vgpr_32, 4, 0, implicit $exec
 ...
+
+---
+name: test_partial_def_1
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $sgpr0_sgpr1, $vgpr1
+    ; CHECK-LABEL: name: test_partial_def_1
+    ; CHECK: liveins: $sgpr0_sgpr1, $vgpr1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: BUNDLE implicit-def $vgpr0_lo16, implicit-def dead $vgpr0, implicit $sgpr0_sgpr1, implicit $vgpr1, implicit $exec {
+    ; CHECK-NEXT:   $vgpr0_lo16 = GLOBAL_LOAD_SHORT_D16_SADDR_t16 $sgpr0_sgpr1, $vgpr1, 0, 0, implicit $exec
+    ; CHECK-NEXT:   $vgpr0 = GLOBAL_LOAD_SHORT_D16_HI_SADDR $sgpr0_sgpr1, $vgpr1, 512, 0, internal killed $vgpr0, implicit $exec
+    ; CHECK-NEXT: }
+    $vgpr0_lo16 = GLOBAL_LOAD_SHORT_D16_SADDR_t16 $sgpr0_sgpr1, $vgpr1, 0, 0, implicit $exec
+    $vgpr0 = GLOBAL_LOAD_SHORT_D16_HI_SADDR $sgpr0_sgpr1, $vgpr1, 512, 0, killed $vgpr0, implicit $exec
+...
+
+---
+name: test_partial_def_2
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0
+    ; CHECK-LABEL: name: test_partial_def_2
+    ; CHECK: liveins: $vgpr0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: BUNDLE implicit-def $vgpr2_vgpr3, implicit-def $vgpr5_vgpr6, implicit $vgpr0_vgpr1 {
+    ; CHECK-NEXT:   $vgpr2_vgpr3 = COPY $vgpr0_vgpr1
+    ; CHECK-NEXT:   $vgpr5_vgpr6 = COPY internal $vgpr3_vgpr4
+    ; CHECK-NEXT: }
+    $vgpr2_vgpr3 = COPY $vgpr0_vgpr1
+    $vgpr5_vgpr6 = COPY $vgpr3_vgpr4
+...
+
+---
+name: test_partial_def_3
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0_vgpr1
+    ; CHECK-LABEL: name: test_partial_def_3
+    ; CHECK: liveins: $vgpr0_vgpr1
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: BUNDLE implicit-def $vgpr2_vgpr3, implicit-def $vgpr3_vgpr4, implicit-def $vgpr5_vgpr6_vgpr7, implicit-def $vgpr8_vgpr9, implicit $vgpr0_vgpr1 {
+    ; CHECK-NEXT:   $vgpr2_vgpr3 = COPY $vgpr0_vgpr1
+    ; CHECK-NEXT:   $vgpr3_vgpr4 = COPY $vgpr0_vgpr1
+    ; CHECK-NEXT:   $vgpr5_vgpr6_vgpr7 = COPY internal $vgpr3_vgpr4_vgpr5
+    ; CHECK-NEXT:   $vgpr8_vgpr9 = COPY internal $vgpr3_vgpr4
+    ; CHECK-NEXT: }
+    $vgpr2_vgpr3 = COPY $vgpr0_vgpr1
+    $vgpr3_vgpr4 = COPY $vgpr0_vgpr1
+    $vgpr5_vgpr6_vgpr7 = COPY $vgpr3_vgpr4_vgpr5
+    $vgpr8_vgpr9 = COPY $vgpr3_vgpr4
+...
+
+---
+name: test_partial_def_4
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $vgpr0
+    ; CHECK-LABEL: name: test_partial_def_4
+    ; CHECK: liveins: $vgpr0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: BUNDLE implicit-def $vgpr2_vgpr3, implicit-def $vgpr5_vgpr6, implicit $vgpr0_vgpr1, implicit undef $vgpr3_vgpr4 {
+    ; CHECK-NEXT:   $vgpr2_vgpr3 = COPY $vgpr0_vgpr1
+    ; CHECK-NEXT:   $vgpr5_vgpr6 = COPY undef $vgpr3_vgpr4
+    ; CHECK-NEXT: }
+    $vgpr2_vgpr3 = COPY $vgpr0_vgpr1
+    $vgpr5_vgpr6 = COPY undef $vgpr3_vgpr4
+...
diff --git a/llvm/test/CodeGen/AMDGPU/hard-clauses-gfx1250.mir b/llvm/test/CodeGen/AMDGPU/hard-clauses-gfx1250.mir
index 79480bc8017f7..f186e98946c25 100644
--- a/llvm/test/CodeGen/AMDGPU/hard-clauses-gfx1250.mir
+++ b/llvm/test/CodeGen/AMDGPU/hard-clauses-gfx1250.mir
@@ -635,3 +635,45 @@ body: |
     $vgpr2 = GLOBAL_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr
     $vgpr3 = GLOBAL_LOAD_DWORD $vgpr0_vgpr1, 4, 24, implicit $exec, implicit $flat_scr
 ...
+
+---
+name: global_load_dword_short_partial_def_1
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $sgpr0_sgpr1, $vgpr3
+    ; GFX12-LABEL: name: global_load_dword_short_partial_def_1
+    ; GFX12: liveins: $sgpr0_sgpr1, $vgpr3
+    ; GFX12-NEXT: {{  $}}
+    ; GFX12-NEXT: $vgpr1 = COPY $vgpr3
+    ; GFX12-NEXT: BUNDLE implicit-def $vgpr0_lo16, implicit-def dead $vgpr0, implicit $sgpr0_sgpr1, implicit $vgpr1, implicit $exec {
+    ; GFX12-NEXT:   S_CLAUSE 1
+    ; GFX12-NEXT:   $vgpr0_lo16 = GLOBAL_LOAD_SHORT_D16_SADDR_t16 $sgpr0_sgpr1, $vgpr1, 0, 0, implicit $exec
+    ; GFX12-NEXT:   $vgpr0 = GLOBAL_LOAD_SHORT_D16_HI_SADDR $sgpr0_sgpr1, $vgpr1, 512, 0, internal killed $vgpr0, implicit $exec
+    ; GFX12-NEXT: }
+    $vgpr1 = COPY $vgpr3
+    $vgpr0_lo16 = GLOBAL_LOAD_SHORT_D16_SADDR_t16 $sgpr0_sgpr1, $vgpr1, 0, 0, implicit $exec
+    $vgpr0 = GLOBAL_LOAD_SHORT_D16_HI_SADDR $sgpr0_sgpr1, $vgpr1, 512, 0, killed $vgpr0, implicit $exec
+...
+
+---
+name: global_load_dword_short_partial_def_2
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $sgpr0_sgpr1, $vgpr3
+    ; GFX12-LABEL: name: global_load_dword_short_partial_def_2
+    ; GFX12: liveins: $sgpr0_sgpr1, $vgpr3
+    ; GFX12-NEXT: {{  $}}
+    ; GFX12-NEXT: $vgpr1 = COPY $vgpr3
+    ; GFX12-NEXT: $vgpr0_hi16 = COPY $vgpr1_hi16
+    ; GFX12-NEXT: BUNDLE implicit-def $vgpr0_lo16, implicit-def $vgpr0, implicit $sgpr0_sgpr1, implicit $vgpr1, implicit $exec, implicit killed $vgpr0 {
+    ; GFX12-NEXT:   S_CLAUSE 1
+    ; GFX12-NEXT:   $vgpr0_lo16 = GLOBAL_LOAD_SHORT_D16_SADDR_t16 $sgpr0_sgpr1, $vgpr1, 0, 0, implicit $exec
+    ; GFX12-NEXT:   $vgpr0 = GLOBAL_LOAD_SHORT_D16_HI_SADDR $sgpr0_sgpr1, $vgpr1, 512, 0, killed $vgpr0, implicit $exec
+    ; GFX12-NEXT: }
+    $vgpr1 = COPY $vgpr3
+    $vgpr0_hi16 = COPY $vgpr1_hi16
+    $vgpr0_lo16 = GLOBAL_LOAD_SHORT_D16_SADDR_t16 $sgpr0_sgpr1, $vgpr1, 0, 0, implicit $exec
+    $vgpr0 = GLOBAL_LOAD_SHORT_D16_HI_SADDR $sgpr0_sgpr1, $vgpr1, 512, 0, killed $vgpr0, implicit $exec
+...

>From 636740f73ade33cc8b1d6569f40a9a77bd29cb79 Mon Sep 17 00:00:00 2001
From: vikhegde <vikram.hegde at amd.com>
Date: Tue, 9 Jun 2026 17:15:35 +0530
Subject: [PATCH 2/2] add test

---
 .../si-insert-hard-clause-bundle-fail.ll      | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 llvm/test/CodeGen/AMDGPU/si-insert-hard-clause-bundle-fail.ll

diff --git a/llvm/test/CodeGen/AMDGPU/si-insert-hard-clause-bundle-fail.ll b/llvm/test/CodeGen/AMDGPU/si-insert-hard-clause-bundle-fail.ll
new file mode 100644
index 0000000000000..d45a527f91123
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/si-insert-hard-clause-bundle-fail.ll
@@ -0,0 +1,59 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; test that the test does not fail machine verification due to instruction bundling.
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1150 -verify-machineinstrs -o - < %s | FileCheck %s
+
+define <8 x i16> @test(i32 %block_offset.023.i.i.i.i.i, ptr addrspace(1) %invariant.gep.i.i.i.i.i) {
+; CHECK-LABEL: test:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
+; CHECK-NEXT:    v_dual_mov_b32 v3, v0 :: v_dual_mov_b32 v4, 0
+; CHECK-NEXT:    s_delay_alu instid0(VALU_DEP_1) | instskip(NEXT) | instid1(VALU_DEP_1)
+; CHECK-NEXT:    v_lshlrev_b64 v[3:4], 1, v[3:4]
+; CHECK-NEXT:    v_add_co_u32 v6, vcc_lo, v1, v3
+; CHECK-NEXT:    s_delay_alu instid0(VALU_DEP_1)
+; CHECK-NEXT:    v_add_co_ci_u32_e64 v7, null, v2, v4, vcc_lo
+; CHECK-NEXT:    s_clause 0x6
+; CHECK-NEXT:    global_load_d16_b16 v0, v[6:7], off
+; CHECK-NEXT:    global_load_d16_b16 v5, v[6:7], off offset:1024
+; CHECK-NEXT:    global_load_d16_b16 v4, v[6:7], off offset:2048
+; CHECK-NEXT:    global_load_d16_b16 v3, v[6:7], off offset:3072
+; CHECK-NEXT:    global_load_d16_hi_b16 v0, v[1:2], off offset:20
+; CHECK-NEXT:    global_load_d16_hi_b16 v5, v[6:7], off offset:1536
+; CHECK-NEXT:    global_load_d16_hi_b16 v4, v[6:7], off offset:2560
+; CHECK-NEXT:    s_waitcnt vmcnt(0)
+; CHECK-NEXT:    v_dual_mov_b32 v1, v5 :: v_dual_mov_b32 v2, v4
+; CHECK-NEXT:    s_setpc_b64 s[30:31]
+  %idx.ext.i.i.i.i.i = zext i32 %block_offset.023.i.i.i.i.i to i64
+  %gep.i.i.i.i.i = getelementptr inbounds nuw [2 x i8], ptr addrspace(1) %invariant.gep.i.i.i.i.i, i64 %idx.ext.i.i.i.i.i
+  %48 = load i16, ptr addrspace(1) %gep.i.i.i.i.i, align 2, !tbaa !23
+  %49 = insertelement <8 x i16> poison, i16 %48, i64 0
+  %arrayidx.1.i.i.i.i.i.i = getelementptr inbounds nuw i8, ptr addrspace(1) %gep.i.i.i.i.i, i64 512
+  %50 = load i16, ptr addrspace(1) %arrayidx.1.i.i.i.i.i.i, align 2, !tbaa !23
+  %51 = insertelement <8 x i16> %49, i16 %50, i64 1
+  %arrayidx.2.i.i.i.i.i.i = getelementptr inbounds nuw i8, ptr addrspace(1) %gep.i.i.i.i.i, i64 1024
+  %52 = load i16, ptr addrspace(1) %arrayidx.2.i.i.i.i.i.i, align 2, !tbaa !23
+  %53 = insertelement <8 x i16> %51, i16 %52, i64 2
+  %arrayidx.3.i.i.i.i.i.i = getelementptr inbounds nuw i8, ptr addrspace(1) %gep.i.i.i.i.i, i64 1536
+  %54 = load i16, ptr addrspace(1) %arrayidx.3.i.i.i.i.i.i, align 2, !tbaa !23
+  %55 = insertelement <8 x i16> %53, i16 %54, i64 3
+  %arrayidx.4.i.i.i.i.i.i = getelementptr inbounds nuw i8, ptr addrspace(1) %gep.i.i.i.i.i, i64 2048
+  %56 = load i16, ptr addrspace(1) %arrayidx.4.i.i.i.i.i.i, align 2, !tbaa !23
+  %57 = insertelement <8 x i16> %55, i16 %56, i64 4
+  %arrayidx.5.i.i.i.i.i.i = getelementptr inbounds nuw i8, ptr addrspace(1) %gep.i.i.i.i.i, i64 2560
+  %58 = load i16, ptr addrspace(1) %arrayidx.5.i.i.i.i.i.i, align 2, !tbaa !23
+  %59 = insertelement <8 x i16> %57, i16 %58, i64 5
+  %arrayidx.6.i.i.i.i.i.i = getelementptr inbounds nuw i8, ptr addrspace(1) %gep.i.i.i.i.i, i64 3072
+  %60 = load i16, ptr addrspace(1) %arrayidx.6.i.i.i.i.i.i, align 2, !tbaa !23
+  %61 = insertelement <8 x i16> %59, i16 %60, i64 6
+  %arrayidx.7.i.i.i.i.i.i = getelementptr inbounds nuw i8, ptr addrspace(1) %gep.i.i.i.i.i, i64 3584
+  %62 = load i16, ptr addrspace(1) %arrayidx.7.i.i.i.i.i.i, align 2, !tbaa !23
+  %63 = insertelement <8 x i16> %61, i16 %62, i64 7
+  %tmp3 = getelementptr inbounds nuw [2 x i8], ptr addrspace(1) %invariant.gep.i.i.i.i.i, i64 10
+  %tmp4 = load i16, ptr addrspace(1) %tmp3, align 2, !tbaa !23
+  %tmp5 = insertelement <8 x i16> %61, i16 %tmp4, i64 1
+  ret <8 x i16> %tmp5
+}
+
+!7 = !{!"omnipotent char", !8, i64 0}
+!8 = !{!"Simple C++ TBAA"}
+!23 = !{!7, !7, i64 0}



More information about the llvm-commits mailing list