[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 04:09:18 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/3] [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/3] 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
>From 6c4af33958e53712bbdaf113be5624fb1014ebba Mon Sep 17 00:00:00 2001
From: Lucas Ramirez <lucas.rami at proton.me>
Date: Wed, 15 Jul 2026 11:09:02 +0000
Subject: [PATCH 3/3] Address spurious S_NOP issue
---
.../Target/AMDGPU/AMDGPULowerVGPREncoding.cpp | 36 +++++++------------
.../CodeGen/AMDGPU/vgpr-setreg-pred-block.mir | 2 --
2 files changed, 13 insertions(+), 25 deletions(-)
diff --git a/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp b/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
index 7a7743995fe48..ad7195b92d0ba 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPULowerVGPREncoding.cpp
@@ -445,28 +445,15 @@ static bool isSetregMode(const MachineInstr &MI, const SIInstrInfo &TII) {
return HwRegId == AMDGPU::Hwreg::ID_MODE;
}
-/// 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,
- const MachineBasicBlock &MBB,
- const SIInstrInfo &TII) {
- while (It != MBB.begin()) {
- It = std::prev(It);
- if (isSetregMode(*It, TII))
+bool AMDGPULowerVGPREncoding::needNopBeforeSetVGPRMSB(
+ MachineBasicBlock::instr_iterator I) {
+ while (I != MBB->begin()) {
+ I = std::prev(I);
+ if (isSetregMode(*I, *TII))
return true;
- if (!It->isMetaInstruction())
+ if (!I->isMetaInstruction())
return false;
}
- return false;
-}
-
-bool AMDGPULowerVGPREncoding::needNopBeforeSetVGPRMSB(
- MachineBasicBlock::instr_iterator I) {
- 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.
@@ -482,10 +469,13 @@ bool AMDGPULowerVGPREncoding::needNopBeforeSetVGPRMSB(
continue;
MachineBasicBlock::instr_iterator LastMI = PredMBB->instr_end();
- if (previousInstrIsSetRegMode(LastMI, *PredMBB, *TII))
- return true;
- if (LastMI != PredMBB->begin())
- return false;
+ while (LastMI != PredMBB->begin()) {
+ LastMI = std::prev(LastMI);
+ if (isSetregMode(*LastMI, *TII))
+ return true;
+ if (!LastMI->isMetaInstruction())
+ return false;
+ }
// The predecessor is empty, recursively look for its own potential
// fallthrough predecessor.
diff --git a/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir b/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir
index 604477a9fc05d..1ae20fe073798 100644
--- a/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir
+++ b/llvm/test/CodeGen/AMDGPU/vgpr-setreg-pred-block.mir
@@ -62,7 +62,6 @@ body: |
; 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
@@ -71,7 +70,6 @@ body: |
; 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
More information about the llvm-commits
mailing list