[llvm] [AMDGPU] Fix setreg handling in the VGPR MSB lowering (PR #186491)

Stanislav Mekhanoshin via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 27 01:24:32 PDT 2026


https://github.com/rampitec updated https://github.com/llvm/llvm-project/pull/186491

>From b3fed589eb80b813395a63c2d5f4bcea72af8301 Mon Sep 17 00:00:00 2001
From: Stanislav Mekhanoshin <Stanislav.Mekhanoshin at amd.com>
Date: Thu, 12 Mar 2026 11:52:48 -0700
Subject: [PATCH] [AMDGPU] Fix setreg handling in the VGPR MSB lowering

There are multiple issues with it:

1. It can skip inserting S_SET_VGPR_MSB if we set the mode via
   piggybacking. We are now relying on the HW bug for correct
   behavior. If/when the bug is fixed lowering will be incorrect.
2. We should just unconditionally update MSBs if immediate allows it.
   We shall set correct bits and keep the rest of the immediate
   (that is done). There is no reasonable way for an user to change
   MSBs nor does it do anything good to set it with SETREG and then
   immediately overwrite with S_SET_VGPR_MSB.
3. We can always update immediate if Offset is zero.
4. Redundant mode changes created as seen in the
   hazard-setreg-vgpr-msb-gfx1250.mir.

With unconditional immediate update most of time and not relying on
the SETREG for setting MSBs there is no good reason to complicate
handling by supporting SETREG as a piggybacking target. Moreover,
piggybacking on SETREG becomes dangerous if offset handling is fixed.

Arguably we shall handle SETREG even w/o SWAR if it targets mask
covering MSB. An user cannot reasonably modify MSBs inside compiler's
workflow.

That said if someone uses non-imm form of setreg and targeting MSB
they are free to go shooting themselves into their foot because that
is non analyzable. But we do actually use imm form for bit 25 in
the compiler itself with non-zero offset.
---
 .../Target/AMDGPU/AMDGPULowerVGPREncoding.cpp |  62 ++++-----
 .../AMDGPU/hazard-setreg-vgpr-msb-gfx1250.mir |  30 ++---
 .../CodeGen/AMDGPU/vgpr-setreg-mode-swar.mir  | 119 +++++++++++++-----
 3 files changed, 123 insertions(+), 88 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
index bcac1dc35ae53..7a8d4872d1eba 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
@@ -159,6 +159,9 @@ class AMDGPULowerVGPREncoding {
   // instruction that we inserted, which implicitly waits for XCNT==0.
   bool XCntIsZero;
 
+  /// List of S_SETREG_IMM32_B32 instructions seen since last mode change.
+  SmallVector<MachineInstr *> SetRegImmList;
+
   /// Insert mode change before \p I. \returns true if mode was changed.
   bool setMode(ModeTy NewMode, MachineBasicBlock::instr_iterator I);
 
@@ -238,22 +241,15 @@ bool AMDGPULowerVGPREncoding::setMode(ModeTy NewMode,
   LLVM_DEBUG(dbgs() << "    Rewritten=" << Rewritten << " after update\n");
 
   if (MostRecentModeSet && !Rewritten) {
-    // Update MostRecentModeSet with the new mode. It can be either
-    // S_SET_VGPR_MSB or S_SETREG_IMM32_B32 (with Size <= 12).
-    if (MostRecentModeSet->getOpcode() == AMDGPU::S_SET_VGPR_MSB) {
-      MachineOperand &Op = MostRecentModeSet->getOperand(0);
-      // Carry old mode bits from the existing instruction.
-      int64_t OldModeBits = Op.getImm() & (ModeMask << ModeWidth);
-      Op.setImm(CurrentMode.encode() | OldModeBits);
-      LLVM_DEBUG(dbgs() << "    -> piggybacked onto S_SET_VGPR_MSB: "
-                        << *MostRecentModeSet);
-    } else {
-      assert(MostRecentModeSet->getOpcode() == AMDGPU::S_SETREG_IMM32_B32 &&
-             "unexpected MostRecentModeSet opcode");
-      updateSetregModeImm(*MostRecentModeSet, CurrentMode.encode());
-      LLVM_DEBUG(dbgs() << "    -> piggybacked onto S_SETREG_IMM32_B32: "
-                        << *MostRecentModeSet);
-    }
+    // Update MostRecentModeSet with the new mode.
+    MachineOperand &Op = MostRecentModeSet->getOperand(0);
+    // Carry old mode bits from the existing instruction.
+    int64_t OldModeBits = Op.getImm() & (ModeMask << ModeWidth);
+    Op.setImm(CurrentMode.encode() | OldModeBits);
+    LLVM_DEBUG(dbgs() << "    -> piggybacked onto S_SET_VGPR_MSB: "
+                      << *MostRecentModeSet);
+    for (MachineInstr *I : SetRegImmList)
+      handleSetregMode(*I);
 
     return true;
   }
@@ -283,6 +279,7 @@ bool AMDGPULowerVGPREncoding::setMode(ModeTy NewMode,
   XCntIsZero = true;
 
   CurrentMode = NewMode;
+  SetRegImmList.clear();
   return true;
 }
 
@@ -480,6 +477,7 @@ bool AMDGPULowerVGPREncoding::updateSetregModeImm(MachineInstr &MI,
 
   MachineOperand *ImmOp = TII->getNamedOperand(MI, AMDGPU::OpName::imm);
   int64_t OldImm = ImmOp->getImm();
+  // Note that Offset is ignored for mode bits here.
   int64_t NewImm =
       (OldImm & ~AMDGPU::Hwreg::VGPR_MSB_MASK) | (SetregMode << VGPRMSBShift);
   ImmOp->setImm(NewImm);
@@ -498,7 +496,6 @@ bool AMDGPULowerVGPREncoding::handleSetregMode(MachineInstr &MI) {
   assert(SIMM16Op && "SIMM16Op must be present");
 
   auto [HwRegId, Offset, Size] = HwregEncoding::decode(SIMM16Op->getImm());
-  (void)Offset;
   LLVM_DEBUG(dbgs() << "    HwRegId=" << HwRegId << " Offset=" << Offset
                     << " Size=" << Size << '\n');
   if (HwRegId != ID_MODE) {
@@ -515,20 +512,14 @@ bool AMDGPULowerVGPREncoding::handleSetregMode(MachineInstr &MI) {
   });
 
   // Case 1: Size <= 12 - the original instruction uses imm32[0:Size-1], so
-  // imm32[12:19] is unused. Safe to set imm32[12:19] to the correct VGPR
-  // MSBs.
-  if (Size <= VGPRMSBShift) {
+  // imm32[12:19] is unused, or Offset is zero and it is safe to set
+  // imm32[12:19] to the correct VGPR MSBs.
+  if (!Offset || Size <= VGPRMSBShift) {
+    // Set imm32[12:19] to the correct VGPR MSBs.
     LLVM_DEBUG(dbgs() << "    Case 1: Size(" << Size << ") <= VGPRMSBShift("
-                      << VGPRMSBShift
-                      << "), treating as mode scope boundary\n");
-    // This instruction is at the boundary of the old mode's control range.
-    // Reset CurrentMode so that the next setMode call can freely piggyback
-    // the required mode into bits[12:19] without triggering Rewritten.
-    MostRecentModeSet = &MI;
-    CurrentMode = {};
-    bool Changed = updateSetregModeImm(MI, 0);
-    LLVM_DEBUG(dbgs() << "    -> reset CurrentMode, cleared bits[12:19]: "
-                      << MI);
+                      << VGPRMSBShift << "), update mode bits[12:19]\n");
+    bool Changed = updateSetregModeImm(MI, ModeValue);
+    LLVM_DEBUG(dbgs() << "    -> " << MI);
     return Changed;
   }
 
@@ -545,13 +536,7 @@ bool AMDGPULowerVGPREncoding::handleSetregMode(MachineInstr &MI) {
                     << " SetregModeValue=0x"
                     << Twine::utohexstr(SetregModeValue) << '\n');
   if (ImmBits12To19 == SetregModeValue) {
-    // Already correct, but we must invalidate MostRecentModeSet because this
-    // instruction will overwrite mode[12:19]. We can't update this instruction
-    // via piggybacking (bits[12:19] are meaningful), so if CurrentMode changes,
-    // a new s_set_vgpr_msb will be inserted after this instruction.
-    MostRecentModeSet = nullptr;
-    LLVM_DEBUG(dbgs() << "    -> bits[12:19] already correct, "
-                         "invalidated MostRecentModeSet\n");
+    LLVM_DEBUG(dbgs() << "    -> bits[12:19] already correct\n");
     return false;
   }
 
@@ -564,6 +549,7 @@ bool AMDGPULowerVGPREncoding::handleSetregMode(MachineInstr &MI) {
   MostRecentModeSet = BuildMI(*MBB, InsertPt, MI.getDebugLoc(),
                               TII->get(AMDGPU::S_SET_VGPR_MSB))
                           .addImm(ModeValue);
+  SetRegImmList.clear();
   LLVM_DEBUG(dbgs() << "    -> inserted S_SET_VGPR_MSB after setreg: "
                     << *MostRecentModeSet);
   return true;
@@ -586,6 +572,7 @@ bool AMDGPULowerVGPREncoding::run(MachineFunction &MF) {
   for (auto &MBB : MF) {
     MostRecentModeSet = nullptr;
     XCntIsZero = false;
+    SetRegImmList.clear();
     this->MBB = &MBB;
 
     LLVM_DEBUG(dbgs() << "BB#" << MBB.getNumber() << ' ' << MBB.getName()
@@ -625,6 +612,7 @@ bool AMDGPULowerVGPREncoding::run(MachineFunction &MF) {
 
       if (MI.getOpcode() == AMDGPU::S_SETREG_IMM32_B32 &&
           ST.hasSetregVGPRMSBFixup()) {
+        SetRegImmList.push_back(&MI);
         Changed |= handleSetregMode(MI);
         continue;
       }
diff --git a/llvm/test/CodeGen/AMDGPU/hazard-setreg-vgpr-msb-gfx1250.mir b/llvm/test/CodeGen/AMDGPU/hazard-setreg-vgpr-msb-gfx1250.mir
index 95c8c67566f51..4f48796228037 100644
--- a/llvm/test/CodeGen/AMDGPU/hazard-setreg-vgpr-msb-gfx1250.mir
+++ b/llvm/test/CodeGen/AMDGPU/hazard-setreg-vgpr-msb-gfx1250.mir
@@ -19,22 +19,21 @@ body: |
     ; CHECK-LABEL: name: setreg_mode_size_gt_12_mismatch
     ; CHECK: S_SET_VGPR_MSB 64, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 undef $sgpr0, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 146108, 30721, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 6844, 30721, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_NOP 0
-    ; CHECK-NEXT: S_SET_VGPR_MSB 64, implicit-def $mode
-    ; CHECK-NEXT: S_ENDPGM 0
+    ; CHECK-NEXT: S_SET_VGPR_MSB 16384, implicit-def $mode
+    ; CHECK-NEXT: SI_RETURN
     $vgpr256 = V_MOV_B32_e32 undef $sgpr0, implicit $exec
     ; hwreg(MODE, 0, 16): simm16 = 0x7801 = 30721
     ; imm32 = 0x23ABC = 146108 (bits 12:19 = 0x23, doesn't match VGPR MSB mode)
     S_SETREG_IMM32_B32 146108, 30721, implicit-def $mode, implicit $mode
-    S_ENDPGM 0
+    SI_RETURN
 ...
 
 ---
 # Case 2 with different next MSB: setreg (size=16) with imm32[12:19] that
-# doesn't match current VGPR MSB. S_NOP + S_SET_VGPR_MSB is inserted to
-# restore current mode, then another S_SET_VGPR_MSB for the next VALU
-# (v512/v513).
+# doesn't match current VGPR MSB. S_NOP + S_SET_VGPR_MSB are inserted for the
+# next VALU (v512/v513).
 name: setreg_mode_size_gt_12_matches_next
 tracksRegLiveness: true
 body: |
@@ -42,9 +41,8 @@ body: |
     ; CHECK-LABEL: name: setreg_mode_size_gt_12_matches_next
     ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 undef $vgpr257, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 43708, 30721, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 23228, 30721, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_NOP 0
-    ; CHECK-NEXT: S_SET_VGPR_MSB 65, implicit-def $mode
     ; CHECK-NEXT: S_SET_VGPR_MSB 16770, implicit-def $mode
     ; CHECK-NEXT: $vgpr512 = V_MOV_B32_e32 undef $vgpr513, implicit $exec
     ; CHECK-NEXT: S_ENDPGM 0
@@ -77,22 +75,19 @@ body: |
 ...
 
 ---
-# Case 2 but no high VGPRs before setreg. The lowering pass still inserts
-# S_NOP + S_SET_VGPR_MSB 0 (redundant but safe).
+# Case 2 but no high VGPRs before setreg.
 name: setreg_mode_size_gt_12_no_high_vgpr
 tracksRegLiveness: true
 body: |
   bb.0:
     ; CHECK-LABEL: name: setreg_mode_size_gt_12_no_high_vgpr
     ; CHECK: $vgpr0 = V_MOV_B32_e32 undef $sgpr0, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 146108, 30721, implicit-def $mode, implicit $mode
-    ; CHECK-NEXT: S_NOP 0
-    ; CHECK-NEXT: S_SET_VGPR_MSB 0, implicit-def $mode
-    ; CHECK-NEXT: S_ENDPGM 0
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 2748, 30721, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: SI_RETURN
     $vgpr0 = V_MOV_B32_e32 undef $sgpr0, implicit $exec
     ; hwreg(MODE, 0, 16): simm16 = 0x7801 = 30721
     S_SETREG_IMM32_B32 146108, 30721, implicit-def $mode, implicit $mode
-    S_ENDPGM 0
+    SI_RETURN
 ...
 
 ---
@@ -105,9 +100,8 @@ body: |
   bb.0:
     ; CHECK-LABEL: name: setreg_mode_size_gt_12_high_vgpr_after
     ; CHECK: $vgpr0 = V_MOV_B32_e32 undef $sgpr0, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 146108, 30721, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 2748, 30721, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_NOP 0
-    ; CHECK-NEXT: S_SET_VGPR_MSB 0, implicit-def $mode
     ; CHECK-NEXT: S_SET_VGPR_MSB 64, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 undef $sgpr0, implicit $exec
     ; CHECK-NEXT: S_ENDPGM 0
diff --git a/llvm/test/CodeGen/AMDGPU/vgpr-setreg-mode-swar.mir b/llvm/test/CodeGen/AMDGPU/vgpr-setreg-mode-swar.mir
index 6f08fa8df94fb..6c34490f9a7f1 100644
--- a/llvm/test/CodeGen/AMDGPU/vgpr-setreg-mode-swar.mir
+++ b/llvm/test/CodeGen/AMDGPU/vgpr-setreg-mode-swar.mir
@@ -1,4 +1,3 @@
-# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
 # RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 -run-pass=amdgpu-lower-vgpr-encoding -o - %s | FileCheck %s
 # RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 -start-before=amdgpu-lower-vgpr-encoding -o - %s | FileCheck --check-prefix=ASM %s
 # RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx1250 -start-before=amdgpu-lower-vgpr-encoding -o - %s | llvm-mc -triple=amdgcn-amd-amdhsa -mcpu=gfx1250 -filetype=obj -o - | llvm-objdump -d --mcpu=gfx1250 - | FileCheck --check-prefix=DIS %s
@@ -8,11 +7,11 @@
 # S_SET_VGPR_MSB format: (src0_msb[0-1], src1_msb[2-3], src2_msb[4-5], dst_msb[6-7])
 # MODE register format:  (dst_msb[0-1], src0_msb[2-3], src1_msb[4-5], src2_msb[6-7])
 # vgpr256/257 (both MSB=1): S_SET_VGPR_MSB mode = (1 << 0) | (1 << 6) = 65
-# Setreg (Size=4 <= 12) resets the mode scope and clears bits[12:19] to 0.
-# No VGPR instruction follows, so bits[12:19] remain 0. Setreg imm = 5.
+#                           MODE register mode = (1 << 0) | (1 << 2) = 5
+# New setreg imm = 0x5 | (5 << 12) = 0x5005 = 20485
 
 # ASM-LABEL: {{^}}setreg_size_lt_12:
-# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 5 ;  msbs: dst=0 src0=0 src1=0 src2=0
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 0x5005 ;  msbs: dst=1 src0=1 src1=0 src2=0
 
 # DIS-LABEL: <setreg_size_lt_12>:
 
@@ -23,7 +22,7 @@ body:             |
     ; CHECK-LABEL: name: setreg_size_lt_12
     ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 20485, 6145, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_ENDPGM 0
     $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
     ; size=4, offset=0, hwreg=MODE: simm16 = 0x1801 = 6145
@@ -34,10 +33,11 @@ body:             |
 ---
 # Case 1b: Size == 12 (boundary), imm32[12:19]=0
 # Setreg (Size=12 <= 12) resets the mode scope and clears bits[12:19] to 0.
-# No VGPR instruction follows, so bits[12:19] remain 0. Setreg imm = 0xABC = 2748.
+# vgpr256/257: S_SET_VGPR_MSB mode = 65, MODE register mode = 5
+# New setreg imm = 0xABC | (5 << 12) = 0x5ABC = 23228
 
 # ASM-LABEL: {{^}}setreg_size_eq_12:
-# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 12), 0xabc ;  msbs: dst=0 src0=0 src1=0 src2=0
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 12), 0x5abc ;  msbs: dst=1 src0=1 src1=0 src2=0
 
 # DIS-LABEL: <setreg_size_eq_12>:
 
@@ -48,7 +48,7 @@ body:             |
     ; CHECK-LABEL: name: setreg_size_eq_12
     ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 2748, 22529, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 23228, 22529, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_ENDPGM 0
     $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
     ; size=12, offset=0, hwreg=MODE: simm16 = 0x5801 = 22529
@@ -58,11 +58,11 @@ body:             |
 
 ---
 # Case 1c: Size <= 12 with existing non-zero bits in imm32[12:19]
-# imm32 = 0x23005 (bits 12:19 = 0x23). Setreg resets mode scope and clears
-# bits[12:19] to 0. No VGPR instruction follows, so result = 0x00005 = 5.
+# vgpr256/257: S_SET_VGPR_MSB mode = 65, MODE register mode = 5
+# imm32 = 0x23005 (bits 12:19 = 0x23), result = 0x5005 = 20485 (bits 12:19 replaced with 5)
 
 # ASM-LABEL: {{^}}setreg_size_lt_12_nonzero_upper:
-# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 5 ;  msbs: dst=0 src0=0 src1=0 src2=0
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 0x5005 ;  msbs: dst=1 src0=1 src1=0 src2=0
 
 # DIS-LABEL: <setreg_size_lt_12_nonzero_upper>:
 
@@ -73,7 +73,7 @@ body:             |
     ; CHECK-LABEL: name: setreg_size_lt_12_nonzero_upper
     ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 20485, 6145, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_ENDPGM 0
     $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
     ; size=4, offset=0, hwreg=MODE: simm16 = 0x1801 = 6145
@@ -111,10 +111,10 @@ body:             |
 ---
 # Case 3: Size > 12 (size=16), imm32[12:19] doesn't match VGPR MSBs
 # vgpr256/257: S_SET_VGPR_MSB mode = 65, MODE register mode = 5
-# imm32 = 0x23ABC = 146108 (bits 12:19 = 0x23 != 5), must insert s_set_vgpr_msb after
+# imm32 = 0x23ABC = 146108 (bits 12:19 = 0x23 != 5), update mode bits
 
 # ASM-LABEL: {{^}}setreg_size_gt_12_mismatch:
-# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 16), 0x23abc ;  msbs: dst=3 src0=0 src1=2 src2=0
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 16), 0x5abc ;  msbs: dst=1 src0=1 src1=0 src2=0
 
 # DIS-LABEL: <setreg_size_gt_12_mismatch>:
 
@@ -125,9 +125,7 @@ body:             |
     ; CHECK-LABEL: name: setreg_size_gt_12_mismatch
     ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 146108, 30721, implicit-def $mode, implicit $mode
-    ; CHECK-NEXT: S_NOP 0
-    ; CHECK-NEXT: S_SET_VGPR_MSB 65, implicit-def $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 23228, 30721, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_ENDPGM 0
     $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
     ; size=16, offset=0, hwreg=MODE: simm16 = 0x7801 = 30721
@@ -162,12 +160,12 @@ body:             |
 ...
 
 ---
-# Case 5: Size <= 12 with VGPR MSBs already present in imm32[12:19]
-# imm32 = 0x5005 = 20485 (bits 12:19 = 5). Setreg resets mode scope and clears
-# bits[12:19] to 0, regardless of prior content. Result = 5.
+# Case 5: Size <= 12 but VGPR MSBs already present (no change needed)
+# vgpr256/257: S_SET_VGPR_MSB mode = 65, MODE register mode = 5
+# imm32 = 0x5005 = 20485 (bits 12:19 = 5 = MODE register mode)
 
 # ASM-LABEL: {{^}}setreg_size_lt_12_already_correct:
-# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 5 ;  msbs: dst=0 src0=0 src1=0 src2=0
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 0x5005 ;  msbs: dst=1 src0=1 src1=0 src2=0
 
 # DIS-LABEL: <setreg_size_lt_12_already_correct>:
 
@@ -178,7 +176,7 @@ body:             |
     ; CHECK-LABEL: name: setreg_size_lt_12_already_correct
     ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 20485, 6145, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_ENDPGM 0
     $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
     ; size=4, offset=0, hwreg=MODE: simm16 = 0x1801 = 6145
@@ -190,10 +188,11 @@ body:             |
 ---
 # Case 6: Different VGPR MSB value (using different high VGPRs)
 # vgpr512/513 (both MSB=2): S_SET_VGPR_MSB mode = (2 << 0) | (2 << 6) = 130
-# Setreg resets mode scope and clears bits[12:19] to 0. No VGPR follows. Result = 5.
+#                           MODE register mode = (2 << 0) | (2 << 2) = 10
+# New setreg imm = 0x5 | (10 << 12) = 0xA005 = 40965
 
 # ASM-LABEL: {{^}}setreg_different_vgpr_msb:
-# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 5 ;  msbs: dst=0 src0=0 src1=0 src2=0
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 0xa005 ;  msbs: dst=2 src0=2 src1=0 src2=0
 
 # DIS-LABEL: <setreg_different_vgpr_msb>:
 
@@ -204,7 +203,7 @@ body:             |
     ; CHECK-LABEL: name: setreg_different_vgpr_msb
     ; CHECK: S_SET_VGPR_MSB 130, implicit-def $mode
     ; CHECK-NEXT: $vgpr512 = V_MOV_B32_e32 $vgpr513, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 40965, 6145, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: S_ENDPGM 0
     $vgpr512 = V_MOV_B32_e32 $vgpr513, implicit $exec
     ; size=4, offset=0, hwreg=MODE: simm16 = 0x1801 = 6145
@@ -233,7 +232,7 @@ tracksRegLiveness: true
 body:             |
   bb.0:
     ; CHECK-LABEL: name: setreg_size_le_12_piggyback_superset
-    ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
+    ; CHECK: S_SET_VGPR_MSB 73, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
     ; CHECK-NEXT: S_SETREG_IMM32_B32 151557, 6145, implicit-def $mode, implicit $mode
     ; CHECK-NEXT: $vgpr256 = V_ADD_U32_e32 $vgpr257, $vgpr512, implicit $exec
@@ -249,16 +248,15 @@ body:             |
 ---
 # Case 8: s_setreg_imm32_b32 (Size <= 12) followed by VGPR with different mode bits
 # First VGPR (V_MOV vgpr256, vgpr0): S_SET_VGPR_MSB mode = 64, MODE register mode = 1
-# Second VGPR (V_MOV vgpr256, vgpr256): needs mode = 65, MODE register mode = 5
-# The setreg (Size=4 <= 12) resets the mode scope. Its bits[12:19] are cleared to 0.
-# The second VGPR's setMode piggybacks mode = 65 into the setreg's bits[12:19],
-# giving imm32 = 5 | (5 << 12) = 20485 = 0x5005. No separate S_SET_VGPR_MSB needed.
+# Second VGPR (V_MOV vgpr256, vgpr256): S_SET_VGPR_MSB mode = 65, MODE register mode = 5
+# Setreg gets MODE mode = 1 from first VGPR. Second VGPR needs different src0 bits,
+# so a new S_SET_VGPR_MSB is inserted. The new S_SET_VGPR_MSB has mode = 65 | (64 << 8) = 16449.
 
 # ASM-LABEL: {{^}}setreg_size_le_12_then_different_vgpr:
-# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 0x5005 ;  msbs: dst=1 src0=1 src1=0 src2=0
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 0x1005 ;  msbs: dst=1 src0=0 src1=0 src2=0
 
 # DIS-LABEL: <setreg_size_le_12_then_different_vgpr>:
-# DIS: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 0x5005
+# DIS: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 0, 4), 0x1005
 # DIS: v_mov_b32_e32 v0 /*v256*/, v0 /*v256*/
 
 name:            setreg_size_le_12_then_different_vgpr
@@ -272,7 +270,9 @@ body:             |
     ; CHECK-NEXT: {{  $}}
     ; CHECK-NEXT: S_SET_VGPR_MSB 64, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr0, implicit $exec
-    ; CHECK-NEXT: S_SETREG_IMM32_B32 20485, 6145, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 4101, 6145, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_NOP 0
+    ; CHECK-NEXT: S_SET_VGPR_MSB 16449, implicit-def $mode
     ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr256, implicit $exec
     ; CHECK-NEXT: S_ENDPGM 0
     $vgpr256 = V_MOV_B32_e32 $vgpr0, implicit $exec
@@ -316,6 +316,59 @@ body:             |
     S_ENDPGM 0
 ...
 
+---
+# S_SET_VGPR_MSB format: (src0_msb[0-1], src1_msb[2-3], src2_msb[4-5], dst_msb[6-7])
+# MODE register format:  (dst_msb[0-1], src0_msb[2-3], src1_msb[4-5], src2_msb[6-7])
+# vgpr256/257 (both MSB=1): S_SET_VGPR_MSB mode = (1 << 0) | (1 << 6) = 65
+#                           MODE register mode = (1 << 0) | (1 << 2) = 5
+# New setreg imm = 0x5 | (5 << 12) = 0x5005 = 20485
+
+# ASM-LABEL: {{^}}setreg_offset_8:
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 8, 4), 0x5005 ;  msbs: dst=1 src0=1 src1=0 src2=0
+
+# DIS-LABEL: <setreg_offset_8>:
+
+name:            setreg_offset_8
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    ; CHECK-LABEL: name: setreg_offset_8
+    ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
+    ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 20485, 6657, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_ENDPGM 0
+    $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    ; size=4, offset=8, hwreg=MODE: simm16 = 0x1801 = 6145
+    S_SETREG_IMM32_B32 5, 6657, implicit-def $mode, implicit $mode
+    S_ENDPGM 0
+...
+
+---
+
+# ASM-LABEL: {{^}}setreg_offset_25:
+# New imm = 1 | (5 << 12) = 0x5001 = 20481
+
+# ASM: s_setreg_imm32_b32 hwreg(HW_REG_WAVE_MODE, 25, 1), 0x5001 ;  msbs: dst=1 src0=1 src1=0 src2=0
+
+# DIS-LABEL: <setreg_offset_25>:
+
+name:            setreg_offset_25
+tracksRegLiveness: true
+body:             |
+  bb.0:
+    ; CHECK-LABEL: name: setreg_offset_25
+    ; CHECK: S_SET_VGPR_MSB 65, implicit-def $mode
+    ; CHECK-NEXT: $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    ; CHECK-NEXT: S_SETREG_IMM32_B32 20481, 1601, implicit-def $mode, implicit $mode
+    ; CHECK-NEXT: S_ENDPGM 0
+    $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    ; size=1, offset=25, hwreg=MODE: simm16 = 0x641 = 1601
+    S_SETREG_IMM32_B32 1, 1601, implicit-def $mode, implicit $mode
+    S_ENDPGM 0
+...
+
+# Update multiple SETREG instructions back to the first S_SET_VGPR_MSB
+
 # ASM-LABEL: {{^}}multiple_setreg:
 # ASM: s_set_vgpr_msb 0x41                     ;  msbs: dst=1 src0=1 src1=0 src2=0
 # ASM: v_mov_b32_e32 v0 /*v256*/, v1 /*v257*/



More information about the llvm-commits mailing list