[llvm] [AMDGPU][CodeGen] Place `S_NOP` after `S_SETREG_IMM32_B32` in predecessor MBB (PR #209620)

Lucas Ramirez via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 15 02:36:48 PDT 2026


https://github.com/lucas-rami updated https://github.com/llvm/llvm-project/pull/209620

>From cfdc2b3aed9c507b19d26edf2f7fd69f3e26728a Mon Sep 17 00:00:00 2001
From: Lucas Ramirez <lucas.rami at proton.me>
Date: Tue, 14 Jul 2026 15:18:21 +0000
Subject: [PATCH 1/2] [AMDGPU][CodeGen] Place S_NOP after S_SETREG_IMM32_B32 in
 predecessor MBB

When placing a `S_SET_VGPR_MSB` at the beginning of a block, check
whether there exists a `S_SETREG_IMM32_B32(MODE)` in the predecessor
block that fallsthrough into it; if there is one a S_NOP has to be
inserted in between them.
---
 .../Target/AMDGPU/AMDGPULowerVGPREncoding.cpp |  72 +++++++--
 .../CodeGen/AMDGPU/vgpr-setreg-pred-block.mir | 141 ++++++++++++++++++
 2 files changed, 200 insertions(+), 13 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir

diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
index f6c981ad464ab..70a3b427df7d0 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
@@ -46,6 +46,7 @@
 #include "SIDefines.h"
 #include "SIInstrInfo.h"
 #include "llvm/ADT/bit.h"
+#include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/MathExtras.h"
 
@@ -432,23 +433,68 @@ AMDGPULowerVGPREncoding::handleCoissue(MachineBasicBlock::instr_iterator I) {
   return I;
 }
 
+/// Returns whether \p MI is a S_SETREG_IMM32_B32(MODE).
+static bool isSetregMode(const MachineInstr &MI, const SIInstrInfo &TII) {
+  if (MI.getOpcode() != AMDGPU::S_SETREG_IMM32_B32)
+    return false;
+
+  const MachineOperand *SIMM16Op =
+      TII.getNamedOperand(MI, AMDGPU::OpName::simm16);
+  auto [HwRegId, _Offset, _Size] =
+      AMDGPU::Hwreg::HwregEncoding::decode(SIMM16Op->getImm());
+  return HwRegId == AMDGPU::Hwreg::ID_MODE;
+}
+
+/// Backtracks \p I in \p MBB until we hit a non-meta instruction and returns
+/// whether that instruction is a S_SETREG_IMM32_B32(MODE). Returns false when
+/// there are no non-meta instruction in [MBB.instr_begin(), It).
+static bool previousInstrIsSetRegMode(MachineBasicBlock::instr_iterator &It,
+                                      const MachineBasicBlock &MBB,
+                                      const SIInstrInfo &TII) {
+  while (It != MBB.begin()) {
+    It = std::prev(It);
+    if (isSetregMode(*It, TII))
+      return true;
+    if (!It->isMetaInstruction())
+      return false;
+  }
+  return false;
+}
+
 bool AMDGPULowerVGPREncoding::needNopBeforeSetVGPRMSB(
     MachineBasicBlock::instr_iterator I) {
-  while (I != MBB->begin()) {
-    I = std::prev(I);
-    if (I->getOpcode() == AMDGPU::S_SETREG_IMM32_B32) {
-      MachineOperand *SIMM16Op =
-          TII->getNamedOperand(*I, AMDGPU::OpName::simm16);
-      auto [HwRegId, Offset, Size] =
-          AMDGPU::Hwreg::HwregEncoding::decode(SIMM16Op->getImm());
-      if (HwRegId == AMDGPU::Hwreg::ID_MODE)
+  if (previousInstrIsSetRegMode(I, *MBB, *TII))
+    return true;
+  if (I != MBB->begin())
+    return false;
+
+  // Look for a potential fallthrough predecessor block. When it ends with a
+  // S_SETREG_IMM32_B32(MODE) we need to insert a S_NOP too.
+  MachineBasicBlock *CurrentMBB = MBB;
+  bool HasEmptyFallThroughPred;
+  do {
+    HasEmptyFallThroughPred = false;
+    for (MachineBasicBlock *PredMBB : CurrentMBB->predecessors()) {
+      // We assume that an explicit jump to the current block from the block
+      // that would otherwise have naturally fell through to it will remain in
+      // the final assembly.
+      if (PredMBB->getFallThrough(/*JumpToFallThrough=*/false) != CurrentMBB)
+        continue;
+
+      MachineBasicBlock::instr_iterator LastMI = PredMBB->instr_end();
+      if (previousInstrIsSetRegMode(LastMI, *PredMBB, *TII))
         return true;
+      if (LastMI != PredMBB->begin())
+        return false;
+
+      // The predecessor is empty, recursively look for its own potential
+      // fallthrough predecessor.
+      CurrentMBB = PredMBB;
+      HasEmptyFallThroughPred = true;
+      break;
     }
-    if (!I->isMetaInstruction())
-      return false;
-  }
-  // FIXME: Return true if the previous MBB falls through and ends with
-  // S_SETREG_IMM32_B32.
+  } while (HasEmptyFallThroughPred);
+
   return false;
 }
 
diff --git a/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir b/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir
new file mode 100644
index 0000000000000..91f84d97aa787
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir
@@ -0,0 +1,141 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -mtriple=amdgcn -mcpu=gfx1250 -run-pass=amdgpu-lower-vgpr-encoding -o - %s | FileCheck %s
+
+# All S_SETREG_IMM32_B32(MODE) in the file have size=4(<12) and imm32[12:19]=0.
+# This ensures they do not cause the insertion of a S_NOP by default. All
+# V_MOV_B32_e32 use src0/dst VGPRs with MSB=1 which triggers the check for a
+# S_SETREG_IMM32_B32(MODE) in their block predecessors.
+
+---
+name:            implicit_fallthrough_insert_nop
+tracksRegLiveness: true
+body:             |
+  ; CHECK-LABEL: name: implicit_fallthrough_insert_nop
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   S_NOP 0
+  ; CHECK-NEXT:   S_SET_VGPR_MSB 65, implicit-def $mode
+  ; CHECK-NEXT:   $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+  ; CHECK-NEXT:   S_ENDPGM 0
+  bb.0:
+    S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+
+  bb.1:
+    $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            explicit_fallthrough_dont_insert_nop
+tracksRegLiveness: true
+body:             |
+  ; CHECK-LABEL: name: explicit_fallthrough_dont_insert_nop
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+  ; CHECK-NEXT:   S_BRANCH %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   S_SET_VGPR_MSB 65, implicit-def $mode
+  ; CHECK-NEXT:   $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+  ; CHECK-NEXT:   S_ENDPGM 0
+  bb.0:
+    S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+    S_BRANCH %bb.1
+
+  bb.1:
+    $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            empty_pred_block_insert_nop
+tracksRegLiveness: true
+body:             |
+  ; CHECK-LABEL: name: empty_pred_block_insert_nop
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   DBG_VALUE $noreg
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   S_NOP 0
+  ; CHECK-NEXT:   S_SET_VGPR_MSB 65, implicit-def $mode
+  ; CHECK-NEXT:   $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+  ; CHECK-NEXT:   S_ENDPGM 0
+  bb.0:
+    S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+
+  bb.1:
+    DBG_VALUE $noreg
+
+  bb.2:
+    $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            setreg_on_any_pred_insert_nop
+tracksRegLiveness: true
+body:             |
+  ; CHECK-LABEL: name: setreg_on_any_pred_insert_nop
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   S_BRANCH %bb.2
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   successors: %bb.2(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.2:
+  ; CHECK-NEXT:   S_NOP 0
+  ; CHECK-NEXT:   S_SET_VGPR_MSB 65, implicit-def $mode
+  ; CHECK-NEXT:   $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+  ; CHECK-NEXT:   S_ENDPGM 0
+  bb.0:
+    S_BRANCH %bb.2
+
+  bb.1:
+    S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+
+  bb.2:
+    $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    S_ENDPGM 0
+...
+
+---
+name:            implicit_fallthrough_coissue_opt_insert_nop
+tracksRegLiveness: true
+body:             |
+  ; CHECK-LABEL: name: implicit_fallthrough_coissue_opt_insert_nop
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   S_NOP 0
+  ; CHECK-NEXT:   S_SET_VGPR_MSB 65, implicit-def $mode
+  ; CHECK-NEXT:   S_BARRIER_WAIT -1
+  ; CHECK-NEXT:   $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+  ; CHECK-NEXT:   S_ENDPGM 0
+  bb.0:
+    S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+
+  bb.1:
+    S_BARRIER_WAIT -1
+    $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    S_ENDPGM 0
+...

>From a6369ac3b2040fa6d7c34ef63efeeb36cc35cffe Mon Sep 17 00:00:00 2001
From: Lucas Ramirez <lucas.rami at proton.me>
Date: Wed, 15 Jul 2026 09:36:33 +0000
Subject: [PATCH 2/2] Add unit test for second position

---
 .../Target/AMDGPU/AMDGPULowerVGPREncoding.cpp |  2 +-
 .../CodeGen/AMDGPU/vgpr-setreg-pred-block.mir | 26 +++++++++++++++++++
 2 files changed, 27 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
index 70a3b427df7d0..7a7743995fe48 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
@@ -445,7 +445,7 @@ static bool isSetregMode(const MachineInstr &MI, const SIInstrInfo &TII) {
   return HwRegId == AMDGPU::Hwreg::ID_MODE;
 }
 
-/// Backtracks \p I in \p MBB until we hit a non-meta instruction and returns
+/// Backtracks \p It in \p MBB until we hit a non-meta instruction and returns
 /// whether that instruction is a S_SETREG_IMM32_B32(MODE). Returns false when
 /// there are no non-meta instruction in [MBB.instr_begin(), It).
 static bool previousInstrIsSetRegMode(MachineBasicBlock::instr_iterator &It,
diff --git a/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir b/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir
index 91f84d97aa787..604477a9fc05d 100644
--- a/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir
+++ b/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir
@@ -53,6 +53,32 @@ body:             |
     S_ENDPGM 0
 ...
 
+---
+name:            vgpr_instr_in_second_position_dont_insert_nop
+tracksRegLiveness: true
+body:             |
+  ; CHECK-LABEL: name: vgpr_instr_in_second_position_dont_insert_nop
+  ; CHECK: bb.0:
+  ; CHECK-NEXT:   successors: %bb.1(0x80000000)
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT:   S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+  ; CHECK-NEXT:   S_BRANCH %bb.1
+  ; CHECK-NEXT: {{  $}}
+  ; CHECK-NEXT: bb.1:
+  ; CHECK-NEXT:   $sgpr0 = S_MOV_B32 0
+  ; CHECK-NEXT:   S_SET_VGPR_MSB 65, implicit-def $mode
+  ; CHECK-NEXT:   $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+  ; CHECK-NEXT:   S_ENDPGM 0
+  bb.0:
+    S_SETREG_IMM32_B32 5, 6145, implicit-def $mode, implicit $mode
+    S_BRANCH %bb.1
+
+  bb.1:
+    $sgpr0 = S_MOV_B32 0
+    $vgpr256 = V_MOV_B32_e32 $vgpr257, implicit $exec
+    S_ENDPGM 0
+...
+
 ---
 name:            empty_pred_block_insert_nop
 tracksRegLiveness: true



More information about the llvm-commits mailing list