[llvm-branch-commits] [llvm] [AMDGPU] Add amdhsa.globals code object metadata (PR #217261)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Aug 19 03:14:17 PDT 2026


https://github.com/skc7 created https://github.com/llvm/llvm-project/pull/217261

Pre-requisite PR: https://github.com/llvm/llvm-project/pull/217257

This PR adds changes to amdgpu backend, to consume the `!sanitize.unpadded.size` IR attachment and report the declared size in the code object metadata note under a new optional root key taking the value from the !sanitize.unpadded.size attachment ASan leaves on the padded global:

  amdhsa.globals:
    - .name: g
      .size:  4

The key is omitted entirely when no global carries the attachment, so the note is byte-identical for builds without sanitizer instrumentation.
Emission lives in MetadataStreamerMsgPackV4, so V4, V5 and V6 all carry the key, and the verifier accepts it as an optional sequence of maps requiring .name and .size.

>From 1e44dfdde4b05e2c8bb0734376479d777a663e00 Mon Sep 17 00:00:00 2001
From: skc7 <Krishna.Sankisa at amd.com>
Date: Wed, 19 Aug 2026 11:34:28 +0530
Subject: [PATCH] [AMDGPU] Add amdhsa.globals code object metadata

---
 llvm/docs/AMDGPUUsage.rst                     | 29 ++++++++++++
 .../BinaryFormat/AMDGPUMetadataVerifier.h     |  1 +
 .../BinaryFormat/AMDGPUMetadataVerifier.cpp   | 20 +++++++++
 .../AMDGPU/AMDGPUHSAMetadataStreamer.cpp      | 26 +++++++++++
 .../Target/AMDGPU/AMDGPUHSAMetadataStreamer.h |  2 +
 .../AMDGPU/hsa-metadata-globals-absent.ll     | 17 +++++++
 .../AMDGPU/hsa-metadata-globals-asan.ll       | 23 ++++++++++
 .../CodeGen/AMDGPU/hsa-metadata-globals.ll    | 44 +++++++++++++++++++
 8 files changed, 162 insertions(+)
 create mode 100644 llvm/test/CodeGen/AMDGPU/hsa-metadata-globals-absent.ll
 create mode 100644 llvm/test/CodeGen/AMDGPU/hsa-metadata-globals-asan.ll
 create mode 100644 llvm/test/CodeGen/AMDGPU/hsa-metadata-globals.ll

diff --git a/llvm/docs/AMDGPUUsage.rst b/llvm/docs/AMDGPUUsage.rst
index 21cf1a3081baf..a89d47a64010a 100644
--- a/llvm/docs/AMDGPUUsage.rst
+++ b/llvm/docs/AMDGPUUsage.rst
@@ -5670,6 +5670,35 @@ defined in table :ref:`amdgpu-amdhsa-code-object-metadata-map-table-v4`.
                                                 A canonical target ID must be
                                                 used. See :ref:`amdgpu-target-triples`
                                                 and :ref:`amdgpu-target-id`.
+     "amdhsa.globals"  sequence of              Sequence of the maps for each global
+                       map                      variable defined in the code object with
+                                                external linkage whose allocated size
+                                                differs from its declared size. Absent
+                                                when no global variable is affected.
+                                                Local symbols are excluded because their
+                                                names are not unique across code objects.
+                                                See
+                                                :ref:`amdgpu-amdhsa-code-object-global-metadata-map-table-v4`
+                                                for the definition of the keys included
+                                                in that map.
+     ================= ============== ========= =======================================
+
+..
+
+  .. table:: AMDHSA Code Object V4 Global Metadata Map
+     :name: amdgpu-amdhsa-code-object-global-metadata-map-table-v4
+
+     ================= ============== ========= =======================================
+     String Key        Value Type     Required? Description
+     ================= ============== ========= =======================================
+     ".name"           string         Required  Name of the global variable ELF symbol.
+     ".size"           integer        Required  Size in bytes of the global variable as
+                                                declared in the source. This is less
+                                                than the size of the ELF symbol when a
+                                                sanitizer has appended a redzone to the
+                                                variable, in which case ``st_size``
+                                                describes the padded object rather than
+                                                the declared one.
      ================= ============== ========= =======================================
 
 .. _amdgpu-amdhsa-code-object-metadata-v5:
diff --git a/llvm/include/llvm/BinaryFormat/AMDGPUMetadataVerifier.h b/llvm/include/llvm/BinaryFormat/AMDGPUMetadataVerifier.h
index 0102fe655ae2e..0bb745064fc20 100644
--- a/llvm/include/llvm/BinaryFormat/AMDGPUMetadataVerifier.h
+++ b/llvm/include/llvm/BinaryFormat/AMDGPUMetadataVerifier.h
@@ -61,6 +61,7 @@ class MetadataVerifier {
                           bool Required);
   bool verifyKernelArgs(msgpack::DocNode &Node);
   bool verifyKernel(msgpack::DocNode &Node);
+  bool verifyGlobal(msgpack::DocNode &Node);
 
 public:
   /// Construct a MetadataVerifier, specifying whether it will operate in \p
diff --git a/llvm/lib/BinaryFormat/AMDGPUMetadataVerifier.cpp b/llvm/lib/BinaryFormat/AMDGPUMetadataVerifier.cpp
index a3cd157e6aa61..5af4866538664 100644
--- a/llvm/lib/BinaryFormat/AMDGPUMetadataVerifier.cpp
+++ b/llvm/lib/BinaryFormat/AMDGPUMetadataVerifier.cpp
@@ -291,6 +291,19 @@ bool MetadataVerifier::verifyKernel(msgpack::DocNode &Node) {
   return true;
 }
 
+bool MetadataVerifier::verifyGlobal(msgpack::DocNode &Node) {
+  if (!Node.isMap())
+    return false;
+  auto &GlobalMap = Node.getMap();
+
+  if (!verifyScalarEntry(GlobalMap, ".name", true, msgpack::Type::String))
+    return false;
+  if (!verifyIntegerEntry(GlobalMap, ".size", true))
+    return false;
+
+  return true;
+}
+
 bool MetadataVerifier::verify(msgpack::DocNode &HSAMetadataRoot) {
   if (!HSAMetadataRoot.isMap())
     return false;
@@ -310,6 +323,13 @@ bool MetadataVerifier::verify(msgpack::DocNode &HSAMetadataRoot) {
             });
           }))
     return false;
+  if (!verifyEntry(RootMap, "amdhsa.globals", false,
+                   [this](msgpack::DocNode &Node) {
+                     return verifyArray(Node, [this](msgpack::DocNode &Node) {
+                       return verifyGlobal(Node);
+                     });
+                   }))
+    return false;
   if (!verifyEntry(RootMap, "amdhsa.kernels", true,
                    [this](msgpack::DocNode &Node) {
                      return verifyArray(Node, [this](msgpack::DocNode &Node) {
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUHSAMetadataStreamer.cpp b/llvm/lib/Target/AMDGPU/AMDGPUHSAMetadataStreamer.cpp
index 900b5d6717619..5ce58ff2b09cc 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUHSAMetadataStreamer.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUHSAMetadataStreamer.cpp
@@ -233,6 +233,31 @@ void MetadataStreamerMsgPackV4::emitPrintf(const Module &Mod) {
   getRootMetadata("amdhsa.printf") = Printf;
 }
 
+void MetadataStreamerMsgPackV4::emitGlobals(const Module &Mod) {
+  auto Globals = HSAMetadataDoc->getArrayNode();
+  for (const GlobalVariable &GV : Mod.globals()) {
+    const MDNode *UnpaddedSize =
+        GV.getMetadata(LLVMContext::MD_sanitize_unpadded_size);
+    if (!UnpaddedSize)
+      continue;
+    // A local name is not unique across the code objects a consumer may merge,
+    // and a global with no storage here was padded where it is defined.
+    if (GV.hasLocalLinkage() || GV.isDeclarationForLinker())
+      continue;
+
+    auto Global = HSAMetadataDoc->getMapNode();
+    Global[".name"] = HSAMetadataDoc->getNode(GV.getName());
+    Global[".size"] = HSAMetadataDoc->getNode(
+        mdconst::extract<ConstantInt>(UnpaddedSize->getOperand(0))
+            ->getZExtValue());
+    Globals.push_back(Global);
+  }
+
+  // Absence of the key is what tells a consumer st_size is the declared size.
+  if (!Globals.empty())
+    getRootMetadata("amdhsa.globals") = Globals;
+}
+
 void MetadataStreamerMsgPackV4::emitKernelLanguage(const Function &Func,
                                                    msgpack::MapDocNode Kern) {
   // TODO: What about other languages?
@@ -562,6 +587,7 @@ void MetadataStreamerMsgPackV4::begin(const Module &Mod,
   emitVersion();
   emitTargetID(TargetID);
   emitPrintf(Mod);
+  emitGlobals(Mod);
   getRootMetadata("amdhsa.kernels") = HSAMetadataDoc->getArrayNode();
   DelayedExprs->clear();
 }
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUHSAMetadataStreamer.h b/llvm/lib/Target/AMDGPU/AMDGPUHSAMetadataStreamer.h
index f4b4eb97ec750..3881243471559 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUHSAMetadataStreamer.h
+++ b/llvm/lib/Target/AMDGPU/AMDGPUHSAMetadataStreamer.h
@@ -97,6 +97,8 @@ class LLVM_EXTERNAL_VISIBILITY MetadataStreamerMsgPackV4
 
   void emitPrintf(const Module &Mod);
 
+  void emitGlobals(const Module &Mod);
+
   void emitKernelLanguage(const Function &Func, msgpack::MapDocNode Kern);
 
   void emitKernelAttrs(const AMDGPUTargetMachine &TM, const MachineFunction &MF,
diff --git a/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals-absent.ll b/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals-absent.ll
new file mode 100644
index 0000000000000..c0a9be579c6b1
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals-absent.ll
@@ -0,0 +1,17 @@
+; Code objects with nothing to report carry no amdhsa.globals key at all, so
+; the note is unchanged for builds without sanitizer instrumentation.
+
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -amdgpu-dump-hsa-metadata -amdgpu-verify-hsa-metadata -filetype=obj -o /dev/null < %s 2>&1 | FileCheck %s
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -filetype=obj -o - < %s | llvm-readelf --notes - | FileCheck %s
+
+ at scalar = addrspace(1) global i32 0, align 4
+ at array = addrspace(1) global [64 x float] zeroinitializer, align 4
+ at ro = addrspace(4) global i64 0, align 8
+
+define amdgpu_kernel void @kern() {
+  ret void
+}
+
+; CHECK-NOT: amdhsa.globals
+; CHECK:     amdhsa.kernels:
+; CHECK-NOT: amdhsa.globals
diff --git a/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals-asan.ll b/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals-asan.ll
new file mode 100644
index 0000000000000..d549690ca7e97
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals-asan.ll
@@ -0,0 +1,23 @@
+; Check that the instrumentation and the note agree end to end: asan attaches
+; the declared size and the streamer reports it.
+
+; RUN: opt -passes=asan -S < %s | llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -amdgpu-dump-hsa-metadata -amdgpu-verify-hsa-metadata -filetype=obj -o /dev/null 2>&1 | FileCheck %s
+
+target triple = "amdgcn-amd-amdhsa"
+
+ at scalar = addrspace(1) global i32 7, align 4
+ at array = addrspace(1) global [64 x float] zeroinitializer, align 4
+
+define amdgpu_kernel void @kern() sanitize_address {
+  ret void
+}
+
+; The descriptor globals asan adds for its runtime carry no attachment, so the
+; sequence ends here.
+
+; CHECK:      amdhsa.globals:
+; CHECK-NEXT:   - .name: scalar
+; CHECK-NEXT:     .size: 4
+; CHECK-NEXT:   - .name: array
+; CHECK-NEXT:     .size: 256
+; CHECK-NEXT: amdhsa.kernels:
diff --git a/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals.ll b/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals.ll
new file mode 100644
index 0000000000000..4f6d2d36d8f0a
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/hsa-metadata-globals.ll
@@ -0,0 +1,44 @@
+; The note reports the declared size of globals whose allocated size differs,
+; so a host runtime resolving them through ELF symbols does not have to treat
+; the redzone as part of the object.
+
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -amdgpu-dump-hsa-metadata -amdgpu-verify-hsa-metadata -filetype=obj -o /dev/null < %s 2>&1 | FileCheck %s
+; RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -filetype=obj -o - < %s | llvm-readelf --notes - | FileCheck %s
+
+ at padded = addrspace(1) global { i32, [28 x i8] } zeroinitializer, align 32, !sanitize.unpadded.size !0
+ at padded_arr = addrspace(1) global { [16 x i8], [48 x i8] } zeroinitializer, align 32, !sanitize.unpadded.size !1
+ at ro = addrspace(4) global { i64, [24 x i8] } zeroinitializer, align 32, !sanitize.unpadded.size !2
+
+; No attachment means the allocated size is already the declared size.
+ at plain = addrspace(1) global i32 0, align 4
+
+; A local name says nothing on its own, because another code object merged with
+; this one can define a different object under it, so these are not reported.
+ at internal_padded = internal addrspace(1) global { [12 x i8], [52 x i8] } zeroinitializer, align 32, !sanitize.unpadded.size !3
+ at private_padded = private addrspace(1) global { i32, [28 x i8] } zeroinitializer, align 32, !sanitize.unpadded.size !0
+
+; Neither a declaration nor an available_externally definition is emitted here,
+; so the padding they describe belongs to the code object that defines them.
+ at extern_padded = external addrspace(1) global { i32, [28 x i8] }, !sanitize.unpadded.size !0
+ at ae_padded = available_externally addrspace(1) global { i32, [28 x i8] } zeroinitializer, align 32, !sanitize.unpadded.size !0
+
+define amdgpu_kernel void @kern() {
+  ret void
+}
+
+; The trailing amdhsa.kernels match closes the sequence, so any extra entry
+; would break the chain.
+
+; CHECK:      amdhsa.globals:
+; CHECK-NEXT:   - .name: padded
+; CHECK-NEXT:     .size: 4
+; CHECK-NEXT:   - .name: padded_arr
+; CHECK-NEXT:     .size: 16
+; CHECK-NEXT:   - .name: ro
+; CHECK-NEXT:     .size: 8
+; CHECK-NEXT: amdhsa.kernels:
+
+!0 = !{i64 4}
+!1 = !{i64 16}
+!2 = !{i64 8}
+!3 = !{i64 12}



More information about the llvm-branch-commits mailing list