[llvm] [AMDGPU] Add assembler check for GFX1250 unclaused vmem workaround (PR #209517)

Jay Foad via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 08:39:59 PDT 2026


https://github.com/jayfoad updated https://github.com/llvm/llvm-project/pull/209517

>From 100f88b8e868f51c89ff817dbd3909b5c576ea8b Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Fri, 12 Jun 2026 12:31:16 +0100
Subject: [PATCH 1/2] [AMDGPU] Add assembler check for GFX1250 unclaused vmem
 workaround

Warn if an entrypoint does not start with the standard workaround
sequence.
---
 .../AMDGPU/AsmParser/AMDGPUAsmParser.cpp      | 49 +++++++++++++++++++
 llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s  | 28 +++++++++++
 llvm/test/MC/AMDGPU/hsa-gfx1250-v4.s          | 12 +++++
 llvm/test/MC/AMDGPU/hsa-gfx1251-v4.s          | 12 +++++
 4 files changed, 101 insertions(+)
 create mode 100644 llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s

diff --git a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
index f019c280997d6..a2b6a68a78f88 100644
--- a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
+++ b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
@@ -1412,6 +1412,21 @@ class AMDGPUAsmParser : public MCTargetAsmParser {
   /// update the target ID first.
   bool TargetDirectiveEmitted = false;
 
+  /// State for checking that every kernel named in a .amdhsa_kernel directive
+  /// begins with the required prologue instruction sequence. Because the
+  /// directive may appear either before or after the kernel's label (it is
+  /// normally emitted after the function body, in .rodata), validation is
+  /// deferred to onEndOfFile(). We record an order-independent timeline of
+  /// parsed labels and emitted instruction opcodes, plus the set of symbols
+  /// named by .amdhsa_kernel directives, and match them up at end of file.
+  SmallVector<unsigned> OpcodeStream;
+  SmallVector<std::tuple<const MCSymbol *, SMLoc, unsigned>>
+      OpcodeStreamSymbols;
+  SmallPtrSet<const MCSymbol *, 8> AMDHSAKernelSymbols;
+
+  /// Verify recorded kernel prologues.
+  void checkKernelPrologues();
+
 private:
   void createConstantSymbol(StringRef Id, int64_t Val);
 
@@ -1708,6 +1723,7 @@ class AMDGPUAsmParser : public MCTargetAsmParser {
                                uint64_t &ErrorInfo,
                                bool MatchingInlineAsm) override;
   bool ParseDirective(AsmToken DirectiveID) override;
+  void doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) override;
   void onEndOfFile() override;
   ParseStatus parseOperand(OperandVector &Operands, StringRef Mnemonic,
                            OperandMode Mode = OperandMode_Default);
@@ -5933,6 +5949,8 @@ bool AMDGPUAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
     }
     emitTargetDirective();
     Out.emitInstruction(Inst, getSTI());
+    // Record for kernel prologue checking.
+    OpcodeStream.push_back(Inst.getOpcode());
     return false;
   }
 
@@ -6139,6 +6157,10 @@ bool AMDGPUAsmParser::ParseDirectiveAMDHSAKernel() {
   if (getParser().parseIdentifier(KernelName))
     return true;
 
+  // Remember the kernel name so its prologue can be checked at end of file.
+  // The matching label may have been parsed already or may follow later.
+  AMDHSAKernelSymbols.insert(getContext().getOrCreateSymbol(KernelName));
+
   AMDGPU::MCKernelDescriptor KD =
       AMDGPU::MCKernelDescriptor::getDefaultAmdhsaKernelDescriptor(
           &getSTI(), getContext());
@@ -7067,8 +7089,35 @@ bool AMDGPUAsmParser::ParseDirectiveAMDGPUInfo() {
   return false;
 }
 
+void AMDGPUAsmParser::doBeforeLabelEmit(MCSymbol *Symbol, SMLoc IDLoc) {
+  // Record every parsed label in the timeline so that, at end of file, the
+  // instructions following a kernel's label can be located regardless of
+  // whether the .amdhsa_kernel directive came before or after the label.
+  OpcodeStreamSymbols.emplace_back(Symbol, IDLoc, OpcodeStream.size());
+}
+
+void AMDGPUAsmParser::checkKernelPrologues() {
+  if (getFeatureBits()[AMDGPU::FeatureRequiresInitialUnclausedVmem]) {
+    static const unsigned Required[] = {GLOBAL_WB_gfx12, V_NOP_e32_gfx12};
+    for (auto [Sym, Loc, Offset] : OpcodeStreamSymbols) {
+      if (!AMDHSAKernelSymbols.contains(Sym))
+        continue;
+      ArrayRef<unsigned> Prologue = ArrayRef(OpcodeStream).drop_front(Offset);
+      if (Prologue.take_front(std::size(Required)) != ArrayRef(Required)) {
+        Warning(Loc, "kernel '" + Sym->getName() +
+                         "' does not begin with the required prologue "
+                         "sequence");
+      }
+    }
+  }
+  OpcodeStream.clear();
+  OpcodeStreamSymbols.clear();
+  AMDHSAKernelSymbols.clear();
+}
+
 void AMDGPUAsmParser::onEndOfFile() {
   emitTargetDirective();
+  checkKernelPrologues();
   if (InfoData)
     getTargetStreamer().emitAMDGPUInfo(*InfoData);
 }
diff --git a/llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s b/llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s
new file mode 100644
index 0000000000000..604a16e48ed7a
--- /dev/null
+++ b/llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s
@@ -0,0 +1,28 @@
+// RUN: llvm-mc -triple=amdgcn-amd-amdhsa -mcpu=gfx1250 %s -filetype=null 2>&1 | FileCheck %s -implicit-check-not=warning: -check-prefix=GFX1250
+
+// GFX1250: :[[@LINE+1]]:1: warning: kernel 'test_wrong_before' does not begin with the required prologue sequence
+test_wrong_before:
+  s_nop 1
+
+.amdhsa_kernel test_wrong_before
+  .amdhsa_next_free_sgpr 0
+  .amdhsa_next_free_vgpr 0
+.end_amdhsa_kernel
+
+.amdhsa_kernel test_wrong_after
+  .amdhsa_next_free_sgpr 0
+  .amdhsa_next_free_vgpr 0
+.end_amdhsa_kernel
+
+// GFX1250: :[[@LINE+1]]:1: warning: kernel 'test_wrong_after' does not begin with the required prologue sequence
+test_wrong_after:
+  s_nop 2
+
+.amdhsa_kernel test_correct
+  .amdhsa_next_free_sgpr 0
+  .amdhsa_next_free_vgpr 0
+.end_amdhsa_kernel
+
+test_correct:
+  global_wb
+  v_nop
diff --git a/llvm/test/MC/AMDGPU/hsa-gfx1250-v4.s b/llvm/test/MC/AMDGPU/hsa-gfx1250-v4.s
index 294f46cbde48f..bb877ede1f4c1 100644
--- a/llvm/test/MC/AMDGPU/hsa-gfx1250-v4.s
+++ b/llvm/test/MC/AMDGPU/hsa-gfx1250-v4.s
@@ -70,31 +70,43 @@
 .p2align 8
 .type minimal, at function
 minimal:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type complete, at function
 complete:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type special_sgpr, at function
 special_sgpr:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type disabled_user_sgpr, at function
 disabled_user_sgpr:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type max_lds_size, at function
 max_lds_size:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type max_vgprs, at function
 max_vgprs:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .rodata
diff --git a/llvm/test/MC/AMDGPU/hsa-gfx1251-v4.s b/llvm/test/MC/AMDGPU/hsa-gfx1251-v4.s
index 8e1e34651be6c..201d374f0ec28 100644
--- a/llvm/test/MC/AMDGPU/hsa-gfx1251-v4.s
+++ b/llvm/test/MC/AMDGPU/hsa-gfx1251-v4.s
@@ -70,31 +70,43 @@
 .p2align 8
 .type minimal, at function
 minimal:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type complete, at function
 complete:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type special_sgpr, at function
 special_sgpr:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type disabled_user_sgpr, at function
 disabled_user_sgpr:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type max_lds_size, at function
 max_lds_size:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .p2align 8
 .type max_vgprs, at function
 max_vgprs:
+  global_wb scope:SCOPE_CU
+  v_nop
   s_endpgm
 
 .rodata

>From e89b467f1374dd2111696d88fb4d1870411c5e88 Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Thu, 16 Jul 2026 16:37:48 +0100
Subject: [PATCH 2/2] More explicit message

---
 llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp | 2 +-
 llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s         | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
index a2b6a68a78f88..efc98536096f6 100644
--- a/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
+++ b/llvm/lib/Target/AMDGPU/AsmParser/AMDGPUAsmParser.cpp
@@ -7106,7 +7106,7 @@ void AMDGPUAsmParser::checkKernelPrologues() {
       if (Prologue.take_front(std::size(Required)) != ArrayRef(Required)) {
         Warning(Loc, "kernel '" + Sym->getName() +
                          "' does not begin with the required prologue "
-                         "sequence");
+                         "sequence: GLOBAL_WB followed by V_NOP");
       }
     }
   }
diff --git a/llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s b/llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s
index 604a16e48ed7a..824eef3611c1a 100644
--- a/llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s
+++ b/llvm/test/MC/AMDGPU/amdhsa-kernel-prologue.s
@@ -1,6 +1,6 @@
 // RUN: llvm-mc -triple=amdgcn-amd-amdhsa -mcpu=gfx1250 %s -filetype=null 2>&1 | FileCheck %s -implicit-check-not=warning: -check-prefix=GFX1250
 
-// GFX1250: :[[@LINE+1]]:1: warning: kernel 'test_wrong_before' does not begin with the required prologue sequence
+// GFX1250: :[[@LINE+1]]:1: warning: kernel 'test_wrong_before' does not begin with the required prologue sequence: GLOBAL_WB followed by V_NOP
 test_wrong_before:
   s_nop 1
 
@@ -14,7 +14,7 @@ test_wrong_before:
   .amdhsa_next_free_vgpr 0
 .end_amdhsa_kernel
 
-// GFX1250: :[[@LINE+1]]:1: warning: kernel 'test_wrong_after' does not begin with the required prologue sequence
+// GFX1250: :[[@LINE+1]]:1: warning: kernel 'test_wrong_after' does not begin with the required prologue sequence: GLOBAL_WB followed by V_NOP
 test_wrong_after:
   s_nop 2
 



More information about the llvm-commits mailing list