[llvm] [SPIR-V] Add NonSemantic.AuxData emission (-spirv-preserve-auxdata) (PR #200002)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 01:30:41 PDT 2026
https://github.com/aobolensk updated https://github.com/llvm/llvm-project/pull/200002
>From 80e3a2be07648c5bf7d9a58acedb95a7459b3c34 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 27 May 2026 17:14:14 +0200
Subject: [PATCH 1/9] [SPIR-V] Skip available_externally functions in module
analysis
These definitions have no MachineFunction so the module analysis and AsmPrinter would assert on a null MF or an invalid global object register. Filter them out using isDeclarationForLinker
---
llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp | 2 +-
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 4 +--
.../linkage/available-externally-function.ll | 28 +++++++++++++++++++
3 files changed, 31 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 3f10d52a61b0e..803f2a012f8f4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -569,7 +569,7 @@ void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
const Function &F = *FI;
// Only operands of OpEntryPoint instructions are allowed to be
// <Entry Point> operands of OpExecutionMode
- if (F.isDeclaration() || !isEntryPoint(F))
+ if (F.isDeclarationForLinker() || !isEntryPoint(F))
continue;
MCRegister FReg = MAI->getGlobalObjReg(&F);
assert(FReg.isValid());
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 769945db154bc..4a26745725451 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -666,7 +666,7 @@ static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI,
void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
InstrTraces IS;
for (const Function &F : M) {
- if (F.isDeclaration())
+ if (F.isDeclarationForLinker())
continue;
MachineFunction *MF = MMI->getMachineFunction(F);
assert(MF);
@@ -725,7 +725,7 @@ void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
// numbered.
void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) {
for (const Function &F : M) {
- if (F.isDeclaration())
+ if (F.isDeclarationForLinker())
continue;
MachineFunction *MF = MMI->getMachineFunction(F);
assert(MF);
diff --git a/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll b/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
new file mode 100644
index 0000000000000..335118ef3e46a
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
@@ -0,0 +1,28 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --implicit-check-not=ae_kernel --implicit-check-not=ae_func
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --implicit-check-not=ae_kernel --implicit-check-not=ae_func
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; available_externally definitions must not be emitted: they have no
+; MachineFunction, so the module analysis and AsmPrinter would otherwise
+; assert.
+
+; CHECK: OpName %[[#used:]] "used_kernel"
+; CHECK: %[[#used]] = OpFunction
+
+define available_externally spir_kernel void @ae_kernel(ptr addrspace(1) %out) {
+entry:
+ store i32 1, ptr addrspace(1) %out, align 4
+ ret void
+}
+
+define available_externally spir_func i32 @ae_func(i32 %x) {
+entry:
+ %r = add i32 %x, 1
+ ret i32 %r
+}
+
+define spir_kernel void @used_kernel(ptr addrspace(1) %out, i32 %n) {
+entry:
+ store i32 %n, ptr addrspace(1) %out, align 4
+ ret void
+}
>From 5cc7250a28a722679a8e751199311dee3d8d458c Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 28 May 2026 17:20:43 +0200
Subject: [PATCH 2/9] Rewrok the patch
---
llvm/lib/Target/SPIRV/CMakeLists.txt | 1 +
.../SPIRV/MCTargetDesc/SPIRVBaseInfo.cpp | 5 +-
llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp | 15 ++-
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp | 123 ++++++++++++++++++
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h | 56 ++++++++
llvm/lib/Target/SPIRV/SPIRVBuiltins.td | 1 +
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp | 4 +-
.../Target/SPIRV/SPIRVPrepareFunctions.cpp | 7 +
llvm/lib/Target/SPIRV/SPIRVUtils.cpp | 5 +
llvm/lib/Target/SPIRV/SPIRVUtils.h | 2 +
.../linkage/available-externally-function.ll | 51 ++++++--
11 files changed, 252 insertions(+), 18 deletions(-)
create mode 100644 llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
create mode 100644 llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index 36ad7f976d6e9..cf77264a95d63 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -52,6 +52,7 @@ add_llvm_target(SPIRVCodeGen
SPIRVTypeInst.cpp
SPIRVUtils.cpp
SPIRVNonSemanticDebugHandler.cpp
+ SPIRVAuxDataHandler.cpp
SPIRVCBufferAccess.cpp
SPIRVPushConstantAccess.cpp
diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVBaseInfo.cpp b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVBaseInfo.cpp
index d6b6079810471..4daac32d84215 100644
--- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVBaseInfo.cpp
+++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVBaseInfo.cpp
@@ -235,6 +235,8 @@ std::string getExtInstSetName(SPIRV::InstructionSet::InstructionSet Set) {
return "GLSL.std.450";
case SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100:
return "NonSemantic.Shader.DebugInfo.100";
+ case SPIRV::InstructionSet::NonSemantic_AuxData:
+ return "NonSemantic.AuxData";
case SPIRV::InstructionSet::SPV_AMD_shader_trinary_minmax:
return "SPV_AMD_shader_trinary_minmax";
}
@@ -245,7 +247,8 @@ SPIRV::InstructionSet::InstructionSet
getExtInstSetFromString(std::string SetName) {
for (auto Set :
{SPIRV::InstructionSet::GLSL_std_450, SPIRV::InstructionSet::OpenCL_std,
- SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100}) {
+ SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100,
+ SPIRV::InstructionSet::NonSemantic_AuxData}) {
if (SetName == getExtInstSetName(Set))
return Set;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 803f2a012f8f4..9f2c3edcad47e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -13,6 +13,7 @@
#include "MCTargetDesc/SPIRVInstPrinter.h"
#include "SPIRV.h"
+#include "SPIRVAuxDataHandler.h"
#include "SPIRVInstrInfo.h"
#include "SPIRVMCInstLower.h"
#include "SPIRVModuleAnalysis.h"
@@ -111,6 +112,8 @@ class SPIRVAsmPrinter : public AsmPrinter {
// object), so this pointer cannot dangle.
SPIRVNonSemanticDebugHandler *NSDebugHandler = nullptr;
+ std::unique_ptr<SPIRVAuxDataHandler> AuxDataHandler;
+
protected:
void cleanUp(Module &M);
};
@@ -569,7 +572,7 @@ void SPIRVAsmPrinter::outputExecutionMode(const Module &M) {
const Function &F = *FI;
// Only operands of OpEntryPoint instructions are allowed to be
// <Entry Point> operands of OpExecutionMode
- if (F.isDeclarationForLinker() || !isEntryPoint(F))
+ if (F.isDeclaration() || !isEntryPoint(F))
continue;
MCRegister FReg = MAI->getGlobalObjReg(&F);
assert(FReg.isValid());
@@ -850,10 +853,18 @@ void SPIRVAsmPrinter::outputModuleSections() {
MAI = &getAnalysis<SPIRVModuleAnalysis>().MAI;
assert(ST && TII && MAI && M && "Module analysis is required");
+ if (!AuxDataHandler) {
+ auto Handler = std::make_unique<SPIRVAuxDataHandler>(*this, *M);
+ if (Handler->hasWork())
+ AuxDataHandler = std::move(Handler);
+ }
+
// Let the NSDI handler add its extension and ext inst import entry to MAI
// before the module header sections are emitted.
if (NSDebugHandler)
NSDebugHandler->prepareModuleOutput(*ST, *MAI);
+ if (AuxDataHandler)
+ AuxDataHandler->prepareModuleOutput(*ST, *MAI);
// Output instructions according to the Logical Layout of a Module:
// 1,2. All OpCapability instructions, then optional OpExtension
@@ -890,6 +901,8 @@ void SPIRVAsmPrinter::outputModuleSections() {
// MB_NonSemanticGlobalDI section in MAI is intentionally left empty.
if (NSDebugHandler)
NSDebugHandler->emitNonSemanticGlobalDebugInfo(*MAI);
+ if (AuxDataHandler)
+ AuxDataHandler->emitAuxData(*MAI);
// 11. All function declarations (functions without a body).
outputExtFuncDecls();
// 12. All function definitions (functions with a body).
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
new file mode 100644
index 0000000000000..4d810c55bc5a0
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -0,0 +1,123 @@
+//===-- SPIRVAuxDataHandler.cpp - NonSemantic.AuxData emitter -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRVAuxDataHandler.h"
+#include "MCTargetDesc/SPIRVMCTargetDesc.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVUtils.h"
+#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCStreamer.h"
+
+using namespace llvm;
+
+namespace {
+constexpr int64_t AuxDataLinkage = 4;
+constexpr uint32_t LinkageAvailableExternally = 0;
+} // namespace
+
+SPIRVAuxDataHandler::SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M)
+ : Asm(AP) {
+ for (const Function &F : M)
+ if (F.hasFnAttribute(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR))
+ LinkagePreservedFns.push_back(&F);
+}
+
+void SPIRVAuxDataHandler::prepareModuleOutput(const SPIRVSubtarget &ST,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ if (LinkagePreservedFns.empty())
+ return;
+ if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_non_semantic_info))
+ return;
+ MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info);
+ constexpr unsigned AuxSet =
+ static_cast<unsigned>(SPIRV::InstructionSet::NonSemantic_AuxData);
+ if (!MAI.ExtInstSetMap.count(AuxSet))
+ MAI.ExtInstSetMap[AuxSet] = MAI.getNextIDRegister();
+}
+
+void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
+ if (LinkagePreservedFns.empty())
+ return;
+ constexpr unsigned AuxSet =
+ static_cast<unsigned>(SPIRV::InstructionSet::NonSemantic_AuxData);
+ MCRegister ExtSetReg = MAI.getExtInstSetReg(AuxSet);
+ if (!ExtSetReg.isValid())
+ return;
+
+ MCRegister VoidTypeReg = findOrEmitOpTypeVoid(MAI);
+ MCRegister I32TypeReg = findOrEmitOpTypeInt32(MAI);
+ // Share one OpConstant across all AE functions.
+ MCRegister ZeroReg;
+
+ for (const Function *F : LinkagePreservedFns) {
+ MCRegister FnReg = MAI.getGlobalObjReg(F);
+ if (!FnReg.isValid())
+ continue;
+ if (!ZeroReg.isValid())
+ ZeroReg = emitOpConstantI32(LinkageAvailableExternally, I32TypeReg, MAI);
+ MCRegister ValReg = ZeroReg;
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpExtInst);
+ Inst.addOperand(MCOperand::createReg(MAI.getNextIDRegister()));
+ Inst.addOperand(MCOperand::createReg(VoidTypeReg));
+ Inst.addOperand(MCOperand::createReg(ExtSetReg));
+ Inst.addOperand(MCOperand::createImm(AuxDataLinkage));
+ Inst.addOperand(MCOperand::createReg(FnReg));
+ Inst.addOperand(MCOperand::createReg(ValReg));
+ emitMCInst(Inst);
+ }
+}
+
+void SPIRVAuxDataHandler::emitMCInst(MCInst &Inst) {
+ Asm.OutStreamer->emitInstruction(Inst, Asm.getSubtargetInfo());
+}
+
+MCRegister
+SPIRVAuxDataHandler::findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI) {
+ for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
+ if (MI->getOpcode() == SPIRV::OpTypeVoid)
+ return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
+ MCRegister Reg = MAI.getNextIDRegister();
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpTypeVoid);
+ Inst.addOperand(MCOperand::createReg(Reg));
+ emitMCInst(Inst);
+ return Reg;
+}
+
+MCRegister
+SPIRVAuxDataHandler::findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI) {
+ for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
+ if (MI->getOpcode() == SPIRV::OpTypeInt &&
+ MI->getOperand(1).getImm() == 32 && MI->getOperand(2).getImm() == 0)
+ return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
+ MCRegister Reg = MAI.getNextIDRegister();
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpTypeInt);
+ Inst.addOperand(MCOperand::createReg(Reg));
+ Inst.addOperand(MCOperand::createImm(32));
+ Inst.addOperand(MCOperand::createImm(0));
+ emitMCInst(Inst);
+ return Reg;
+}
+
+MCRegister
+SPIRVAuxDataHandler::emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ MCRegister Reg = MAI.getNextIDRegister();
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpConstantI);
+ Inst.addOperand(MCOperand::createReg(Reg));
+ Inst.addOperand(MCOperand::createReg(I32TypeReg));
+ Inst.addOperand(MCOperand::createImm(static_cast<int64_t>(Value)));
+ emitMCInst(Inst);
+ return Reg;
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
new file mode 100644
index 0000000000000..fde451f7c9670
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
@@ -0,0 +1,56 @@
+//===-- SPIRVAuxDataHandler.h - NonSemantic.AuxData emitter -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Emits NonSemantic.AuxData ExtInst annotations preserving LLVM-level info
+// with no native SPIR-V representation (currently: available_externally
+// linkage). Matches SPIRV-LLVM-Translator's --spirv-preserve-auxdata.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVAUXDATAHANDLER_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVAUXDATAHANDLER_H
+
+#include "SPIRVModuleAnalysis.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCRegister.h"
+
+namespace llvm {
+
+class AsmPrinter;
+class Function;
+class Module;
+class SPIRVSubtarget;
+
+class SPIRVAuxDataHandler {
+public:
+ SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M);
+
+ bool hasWork() const { return !LinkagePreservedFns.empty(); }
+
+ /// Register the extension and ext-inst-set in MAI. Must run before
+ /// outputGlobalRequirements() / outputOpExtInstImports().
+ void prepareModuleOutput(const SPIRVSubtarget &ST,
+ SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// Emit AuxData annotations in module section 10.
+ void emitAuxData(SPIRV::ModuleAnalysisInfo &MAI);
+
+private:
+ AsmPrinter &Asm;
+ SmallVector<const Function *> LinkagePreservedFns;
+
+ void emitMCInst(MCInst &Inst);
+ MCRegister findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI);
+ MCRegister findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI);
+ MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
+ SPIRV::ModuleAnalysisInfo &MAI);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVAUXDATAHANDLER_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
index 806d283ff715f..64d5695ef2167 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
@@ -27,6 +27,7 @@ def OpenCL_std : InstructionSet<0>;
def GLSL_std_450 : InstructionSet<1>;
def SPV_AMD_shader_trinary_minmax : InstructionSet<2>;
def NonSemantic_Shader_DebugInfo_100 : InstructionSet<3>;
+def NonSemantic_AuxData : InstructionSet<4>;
// Define various builtin groups
def BuiltinGroup : GenericEnum {
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index 4a26745725451..769945db154bc 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -666,7 +666,7 @@ static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI,
void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
InstrTraces IS;
for (const Function &F : M) {
- if (F.isDeclarationForLinker())
+ if (F.isDeclaration())
continue;
MachineFunction *MF = MMI->getMachineFunction(F);
assert(MF);
@@ -725,7 +725,7 @@ void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
// numbered.
void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) {
for (const Function &F : M) {
- if (F.isDeclarationForLinker())
+ if (F.isDeclaration())
continue;
MachineFunction *MF = MMI->getMachineFunction(F);
assert(MF);
diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 916353ffdf381..4211197fc97d2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -830,6 +830,13 @@ bool SPIRVPrepareFunctionsImpl::runOnModule(Module &M) {
Changed |= terminateBlocksAfterTrap(M, Intrinsic::ubsantrap);
for (Function &F : M) {
+ // MachineFunctionPass skips available_externally; strip + tag so AuxData
+ // can re-emit the original linkage as NonSemantic.AuxData::Linkage.
+ if (F.hasAvailableExternallyLinkage() && !F.isDeclaration()) {
+ F.addFnAttr(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR);
+ F.setLinkage(GlobalValue::ExternalLinkage);
+ Changed = true;
+ }
Changed |= substituteAbortKHRCalls(&F);
Changed |= substituteIntrinsicCalls(&F);
Changed |= sortBlocks(F);
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index 0ef31f4182b4e..e56d2e47e9045 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -1217,6 +1217,11 @@ getSpirvLinkageTypeFor(const SPIRVSubtarget &ST, const GlobalValue &GV) {
if (GV.hasLocalLinkage())
return std::nullopt;
+ // Preserved via NonSemantic.AuxData; skip LinkageAttributes.
+ if (const auto *F = dyn_cast<Function>(&GV))
+ if (F->hasFnAttribute(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR))
+ return std::nullopt;
+
if (GV.isDeclarationForLinker()) {
// Interface variables must not get Import linkage.
if (const auto *GVar = dyn_cast<GlobalVariable>(&GV)) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index 27b196cb8dad3..c53e4154d35c1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -526,6 +526,8 @@ CallInst *buildIntrWithMD(Intrinsic::ID IntrID, ArrayRef<Type *> Types,
MachineInstr *getVRegDef(MachineRegisterInfo &MRI, Register Reg);
#define SPIRV_BACKEND_SERVICE_FUN_NAME "__spirv_backend_service_fun"
+#define SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR "spv.was-available-externally"
+
bool getVacantFunctionName(Module &M, std::string &Name);
void setRegClassType(Register Reg, const Type *Ty, SPIRVGlobalRegistry *GR,
diff --git a/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll b/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
index 335118ef3e46a..35f800d1ea1cd 100644
--- a/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
+++ b/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
@@ -1,19 +1,41 @@
-; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --implicit-check-not=ae_kernel --implicit-check-not=ae_func
-; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --implicit-check-not=ae_kernel --implicit-check-not=ae_func
+; Without SPV_KHR_non_semantic_info: emitted as plain external definitions
+; (original linkage has no native SPIR-V representation).
+
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=NOAUX
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=NOAUX
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
-; available_externally definitions must not be emitted: they have no
-; MachineFunction, so the module analysis and AsmPrinter would otherwise
-; assert.
+; NOAUX-NOT: OpExtension "SPV_KHR_non_semantic_info"
+; NOAUX-NOT: NonSemantic.AuxData
+; NOAUX-DAG: OpName %[[#ae_func:]] "ae_func"
+; NOAUX-DAG: OpName %[[#caller:]] "caller"
+; NOAUX-NOT: OpDecorate %[[#ae_func]] LinkageAttributes
+; NOAUX: %[[#ae_func]] = OpFunction
+; NOAUX: OpFunctionEnd
+; NOAUX: %[[#caller]] = OpFunction
+; NOAUX: OpFunctionCall %[[#]] %[[#ae_func]]
-; CHECK: OpName %[[#used:]] "used_kernel"
-; CHECK: %[[#used]] = OpFunction
+; With SPV_KHR_non_semantic_info: linkage preserved via a
+; NonSemantic.AuxData::Linkage annotation for round-trip recovery.
-define available_externally spir_kernel void @ae_kernel(ptr addrspace(1) %out) {
-entry:
- store i32 1, ptr addrspace(1) %out, align 4
- ret void
-}
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=AUX
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=AUX
+;; spirv-val rejects the AuxData forward-ref without
+;; SPV_KHR_relaxed_extended_instruction; matches Translator behavior.
+
+; AUX-DAG: OpExtension "SPV_KHR_non_semantic_info"
+; AUX-DAG: %[[#auxset:]] = OpExtInstImport "NonSemantic.AuxData"
+; AUX-DAG: OpName %[[#ae_func:]] "ae_func"
+; AUX-DAG: OpName %[[#caller:]] "caller"
+; AUX-NOT: OpDecorate %[[#ae_func]] LinkageAttributes
+; AUX-DAG: %[[#void:]] = OpTypeVoid
+; AUX-DAG: %[[#i32:]] = OpTypeInt 32 0
+; AUX-DAG: %[[#zero:]] = OpConstant %[[#i32]] 0
+; AUX: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#ae_func]] %[[#zero]]
+; AUX: %[[#ae_func]] = OpFunction
+; AUX: OpFunctionEnd
+; AUX: %[[#caller]] = OpFunction
+; AUX: OpFunctionCall %[[#]] %[[#ae_func]]
define available_externally spir_func i32 @ae_func(i32 %x) {
entry:
@@ -21,8 +43,9 @@ entry:
ret i32 %r
}
-define spir_kernel void @used_kernel(ptr addrspace(1) %out, i32 %n) {
+define spir_kernel void @caller(ptr addrspace(1) %out, i32 %n) {
entry:
- store i32 %n, ptr addrspace(1) %out, align 4
+ %v = call spir_func i32 @ae_func(i32 %n)
+ store i32 %v, ptr addrspace(1) %out, align 4
ret void
}
>From c85885b249b7c018623706bc96475e61cfbb1af9 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 29 May 2026 10:15:10 +0200
Subject: [PATCH 3/9] Address comments
---
llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp | 2 +
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp | 210 +++++++++++++++---
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h | 50 ++++-
.../preserve-auxdata.ll | 50 +++++
4 files changed, 274 insertions(+), 38 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 9f2c3edcad47e..8e9913a326ba2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -347,6 +347,8 @@ void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
// emitNonSemanticGlobalDebugInfo().
if (NSDebugHandler)
NSDebugHandler->emitNonSemanticDebugStrings(*MAI);
+ if (AuxDataHandler)
+ AuxDataHandler->emitAuxDataStrings(*MAI);
}
void SPIRVAsmPrinter::outputOpExtInstImports(const Module &M) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
index 4d810c55bc5a0..08d3675868693 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -11,71 +11,216 @@
#include "SPIRVSubtarget.h"
#include "SPIRVUtils.h"
#include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/IR/Attributes.h"
#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalObject.h"
+#include "llvm/IR/GlobalVariable.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
+#include "llvm/Support/CommandLine.h"
using namespace llvm;
+static cl::opt<bool> SPVPreserveAuxData(
+ "spirv-preserve-auxdata",
+ cl::desc("Preserve LLVM attributes and metadata as "
+ "NonSemantic.AuxData ExtInst annotations (requires "
+ "SPV_KHR_non_semantic_info)"),
+ cl::Optional, cl::Hidden, cl::init(false));
+
namespace {
-constexpr int64_t AuxDataLinkage = 4;
-constexpr uint32_t LinkageAvailableExternally = 0;
+// Khronos NonSemantic.AuxData opcodes.
+enum AuxDataOpcode : uint32_t {
+ FunctionMetadata = 0,
+ FunctionAttribute = 1,
+ GlobalVariableMetadata = 2,
+ GlobalVariableAttribute = 3,
+ Linkage = 4,
+};
+
+enum AuxDataLinkageType : uint32_t {
+ AvailableExternally = 0,
+};
+
+constexpr unsigned NonSemanticAuxDataSet =
+ static_cast<unsigned>(SPIRV::InstructionSet::NonSemantic_AuxData);
+
+AttributeSet getGOAttrs(const GlobalObject *GO) {
+ if (const auto *F = dyn_cast<Function>(GO))
+ return F->getAttributes().getFnAttrs();
+ return cast<GlobalVariable>(GO)->getAttributes();
+}
} // namespace
SPIRVAuxDataHandler::SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M)
- : Asm(AP) {
+ : Asm(AP), Mod(M) {
for (const Function &F : M)
if (F.hasFnAttribute(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR))
LinkagePreservedFns.push_back(&F);
}
+bool SPIRVAuxDataHandler::hasWork() const {
+ return !LinkagePreservedFns.empty() || SPVPreserveAuxData;
+}
+
void SPIRVAuxDataHandler::prepareModuleOutput(const SPIRVSubtarget &ST,
SPIRV::ModuleAnalysisInfo &MAI) {
- if (LinkagePreservedFns.empty())
+ if (!hasWork())
return;
if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_non_semantic_info))
return;
MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info);
- constexpr unsigned AuxSet =
- static_cast<unsigned>(SPIRV::InstructionSet::NonSemantic_AuxData);
- if (!MAI.ExtInstSetMap.count(AuxSet))
- MAI.ExtInstSetMap[AuxSet] = MAI.getNextIDRegister();
+ if (!MAI.ExtInstSetMap.count(NonSemanticAuxDataSet))
+ MAI.ExtInstSetMap[NonSemanticAuxDataSet] = MAI.getNextIDRegister();
}
-void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
- if (LinkagePreservedFns.empty())
+MCRegister
+SPIRVAuxDataHandler::getOrEmitString(StringRef S,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ auto [It, Inserted] = StringRegs.try_emplace(S);
+ if (!Inserted)
+ return It->second;
+ MCRegister Reg = MAI.getNextIDRegister();
+ It->second = Reg;
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpString);
+ Inst.addOperand(MCOperand::createReg(Reg));
+ addStringImm(S, Inst);
+ emitMCInst(Inst);
+ return Reg;
+}
+
+void SPIRVAuxDataHandler::collectAttributesFor(
+ const GlobalObject *GO, function_ref<MCRegister()> GetNameReg,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ uint32_t Opcode =
+ isa<Function>(GO) ? FunctionAttribute : GlobalVariableAttribute;
+ for (const Attribute &A : getGOAttrs(GO)) {
+ if (A.isStringAttribute() &&
+ A.getKindAsString() == SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR)
+ continue;
+ ExtInstRecord Rec;
+ Rec.Opcode = Opcode;
+ Rec.Operands.push_back(GetNameReg());
+ if (A.isStringAttribute()) {
+ Rec.Operands.push_back(getOrEmitString(A.getKindAsString(), MAI));
+ StringRef Val = A.getValueAsString();
+ if (!Val.empty())
+ Rec.Operands.push_back(getOrEmitString(Val, MAI));
+ } else {
+ Rec.Operands.push_back(
+ getOrEmitString(StringPool.save(A.getAsString()), MAI));
+ }
+ PendingRecords.push_back(std::move(Rec));
+ }
+}
+
+void SPIRVAuxDataHandler::collectMetadataFor(
+ const GlobalObject *GO, function_ref<MCRegister()> GetNameReg,
+ ArrayRef<StringRef> MDNames, SPIRV::ModuleAnalysisInfo &MAI) {
+ SmallVector<std::pair<unsigned, MDNode *>> AllMD;
+ GO->getAllMetadata(AllMD);
+ if (AllMD.empty())
return;
- constexpr unsigned AuxSet =
- static_cast<unsigned>(SPIRV::InstructionSet::NonSemantic_AuxData);
- MCRegister ExtSetReg = MAI.getExtInstSetReg(AuxSet);
+ uint32_t Opcode =
+ isa<Function>(GO) ? FunctionMetadata : GlobalVariableMetadata;
+ // Skip non-MDString operands: emitting them would require a full value
+ // translation we can't safely drive from here.
+ auto CollectStrings = [&](MDNode *MD) -> std::optional<SmallVector<MCRegister, 4>> {
+ SmallVector<MCRegister, 4> Out;
+ for (const MDOperand &MdOp : MD->operands()) {
+ auto *MDStr = dyn_cast_or_null<MDString>(MdOp.get());
+ if (!MDStr)
+ return std::nullopt;
+ Out.push_back(getOrEmitString(MDStr->getString(), MAI));
+ }
+ return Out;
+ };
+ for (const auto &MD : AllMD) {
+ if (MD.first == LLVMContext::MD_dbg)
+ continue;
+ StringRef MDName = MDNames[MD.first];
+ if (MDName == "spirv.Decorations" ||
+ MDName == "spirv.ParameterDecorations")
+ continue;
+ auto Operands = CollectStrings(MD.second);
+ if (!Operands)
+ continue;
+ ExtInstRecord Rec;
+ Rec.Opcode = Opcode;
+ Rec.Operands.push_back(GetNameReg());
+ Rec.Operands.push_back(getOrEmitString(MDName, MAI));
+ Rec.Operands.append(Operands->begin(), Operands->end());
+ PendingRecords.push_back(std::move(Rec));
+ }
+}
+
+void SPIRVAuxDataHandler::emitAuxDataStrings(SPIRV::ModuleAnalysisInfo &MAI) {
+ if (!SPVPreserveAuxData)
+ return;
+ if (!MAI.getExtInstSetReg(NonSemanticAuxDataSet).isValid())
+ return;
+ SmallVector<StringRef> MDNames;
+ Mod.getContext().getMDKindNames(MDNames);
+ for (const GlobalObject &GO : Mod.global_objects()) {
+ if (GO.isDeclaration())
+ continue;
+ // Defer the name OpString until the first record actually fires.
+ MCRegister NameReg;
+ auto GetNameReg = [&]() {
+ if (!NameReg.isValid())
+ NameReg = getOrEmitString(GO.getName(), MAI);
+ return NameReg;
+ };
+ collectAttributesFor(&GO, GetNameReg, MAI);
+ collectMetadataFor(&GO, GetNameReg, MDNames, MAI);
+ }
+}
+
+void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
+ MCRegister ExtSetReg = MAI.getExtInstSetReg(NonSemanticAuxDataSet);
if (!ExtSetReg.isValid())
return;
MCRegister VoidTypeReg = findOrEmitOpTypeVoid(MAI);
- MCRegister I32TypeReg = findOrEmitOpTypeInt32(MAI);
- // Share one OpConstant across all AE functions.
- MCRegister ZeroReg;
+ for (const ExtInstRecord &Rec : PendingRecords)
+ emitAuxDataExtInst(Rec.Opcode, VoidTypeReg, ExtSetReg, Rec.Operands, MAI);
+
+ if (LinkagePreservedFns.empty())
+ return;
+
+ MCRegister I32TypeReg = findOrEmitOpTypeInt32(MAI);
+ MCRegister AEConstReg;
for (const Function *F : LinkagePreservedFns) {
MCRegister FnReg = MAI.getGlobalObjReg(F);
if (!FnReg.isValid())
continue;
- if (!ZeroReg.isValid())
- ZeroReg = emitOpConstantI32(LinkageAvailableExternally, I32TypeReg, MAI);
- MCRegister ValReg = ZeroReg;
- MCInst Inst;
- Inst.setOpcode(SPIRV::OpExtInst);
- Inst.addOperand(MCOperand::createReg(MAI.getNextIDRegister()));
- Inst.addOperand(MCOperand::createReg(VoidTypeReg));
- Inst.addOperand(MCOperand::createReg(ExtSetReg));
- Inst.addOperand(MCOperand::createImm(AuxDataLinkage));
- Inst.addOperand(MCOperand::createReg(FnReg));
- Inst.addOperand(MCOperand::createReg(ValReg));
- emitMCInst(Inst);
+ if (!AEConstReg.isValid())
+ AEConstReg =
+ emitOpConstantI32(AvailableExternally, I32TypeReg, MAI);
+ emitAuxDataExtInst(Linkage, VoidTypeReg, ExtSetReg, {FnReg, AEConstReg},
+ MAI);
}
}
+void SPIRVAuxDataHandler::emitAuxDataExtInst(
+ uint32_t Opcode, MCRegister VoidTypeReg, MCRegister ExtSetReg,
+ ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI) {
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpExtInst);
+ Inst.addOperand(MCOperand::createReg(MAI.getNextIDRegister()));
+ Inst.addOperand(MCOperand::createReg(VoidTypeReg));
+ Inst.addOperand(MCOperand::createReg(ExtSetReg));
+ Inst.addOperand(MCOperand::createImm(static_cast<int64_t>(Opcode)));
+ for (MCRegister R : Operands)
+ Inst.addOperand(MCOperand::createReg(R));
+ emitMCInst(Inst);
+}
+
void SPIRVAuxDataHandler::emitMCInst(MCInst &Inst) {
Asm.OutStreamer->emitInstruction(Inst, Asm.getSubtargetInfo());
}
@@ -95,16 +240,19 @@ SPIRVAuxDataHandler::findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI) {
MCRegister
SPIRVAuxDataHandler::findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI) {
+ constexpr int64_t Int32BitWidth = 32;
+ constexpr int64_t UnsignedSignedness = 0;
for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
if (MI->getOpcode() == SPIRV::OpTypeInt &&
- MI->getOperand(1).getImm() == 32 && MI->getOperand(2).getImm() == 0)
+ MI->getOperand(1).getImm() == Int32BitWidth &&
+ MI->getOperand(2).getImm() == UnsignedSignedness)
return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
MCRegister Reg = MAI.getNextIDRegister();
MCInst Inst;
Inst.setOpcode(SPIRV::OpTypeInt);
Inst.addOperand(MCOperand::createReg(Reg));
- Inst.addOperand(MCOperand::createImm(32));
- Inst.addOperand(MCOperand::createImm(0));
+ Inst.addOperand(MCOperand::createImm(Int32BitWidth));
+ Inst.addOperand(MCOperand::createImm(UnsignedSignedness));
emitMCInst(Inst);
return Reg;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
index fde451f7c9670..b60b77e8de1e4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
@@ -6,9 +6,9 @@
//
//===----------------------------------------------------------------------===//
//
-// Emits NonSemantic.AuxData ExtInst annotations preserving LLVM-level info
-// with no native SPIR-V representation (currently: available_externally
-// linkage). Matches SPIRV-LLVM-Translator's --spirv-preserve-auxdata.
+// Emits NonSemantic.AuxData ExtInst annotations (mirrors SPIRV-LLVM-Translator
+// --spirv-preserve-auxdata). Linkage records emit unconditionally for AE-tagged
+// functions; attribute/metadata records are gated by -spirv-preserve-auxdata.
//
//===----------------------------------------------------------------------===//
@@ -16,13 +16,20 @@
#define LLVM_LIB_TARGET_SPIRV_SPIRVAUXDATAHANDLER_H
#include "SPIRVModuleAnalysis.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCRegister.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/StringSaver.h"
namespace llvm {
class AsmPrinter;
class Function;
+class GlobalObject;
class Module;
class SPIRVSubtarget;
@@ -30,25 +37,54 @@ class SPIRVAuxDataHandler {
public:
SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M);
- bool hasWork() const { return !LinkagePreservedFns.empty(); }
+ bool hasWork() const;
- /// Register the extension and ext-inst-set in MAI. Must run before
- /// outputGlobalRequirements() / outputOpExtInstImports().
+ /// Register extension + ext-inst-set; call before output of section 1.
void prepareModuleOutput(const SPIRVSubtarget &ST,
SPIRV::ModuleAnalysisInfo &MAI);
- /// Emit AuxData annotations in module section 10.
+ /// Emit OpStrings and stage ExtInst records; call in module section 7.
+ void emitAuxDataStrings(SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// Emit the staged ExtInst records; call in module section 10.
void emitAuxData(SPIRV::ModuleAnalysisInfo &MAI);
private:
+ struct ExtInstRecord {
+ uint32_t Opcode;
+ SmallVector<MCRegister, 4> Operands;
+ };
+
AsmPrinter &Asm;
+ const Module &Mod;
+
SmallVector<const Function *> LinkagePreservedFns;
+ // Backing storage for non-string-attribute strings; StringRegs keys are
+ // StringRefs into it.
+ BumpPtrAllocator StringAlloc;
+ UniqueStringSaver StringPool{StringAlloc};
+
+ DenseMap<StringRef, MCRegister> StringRegs;
+ SmallVector<ExtInstRecord> PendingRecords;
+
+ MCRegister getOrEmitString(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
+ void collectAttributesFor(const GlobalObject *GO,
+ function_ref<MCRegister()> GetNameReg,
+ SPIRV::ModuleAnalysisInfo &MAI);
+ void collectMetadataFor(const GlobalObject *GO,
+ function_ref<MCRegister()> GetNameReg,
+ ArrayRef<StringRef> MDNames,
+ SPIRV::ModuleAnalysisInfo &MAI);
+
void emitMCInst(MCInst &Inst);
MCRegister findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI);
MCRegister findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI);
MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
SPIRV::ModuleAnalysisInfo &MAI);
+ void emitAuxDataExtInst(uint32_t Opcode, MCRegister VoidTypeReg,
+ MCRegister ExtSetReg, ArrayRef<MCRegister> Operands,
+ SPIRV::ModuleAnalysisInfo &MAI);
};
} // namespace llvm
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
new file mode 100644
index 0000000000000..cc6c1f8637937
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
@@ -0,0 +1,50 @@
+; -spirv-preserve-auxdata: emit NonSemantic.AuxData for attrs/metadata.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
+; RUN: --spirv-ext=+SPV_KHR_non_semantic_info -spirv-preserve-auxdata \
+; RUN: %s -o - | FileCheck %s
+
+; Off by default: only the Linkage record fires (for the AE function).
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
+; RUN: --spirv-ext=+SPV_KHR_non_semantic_info %s -o - \
+; RUN: | FileCheck %s --check-prefix=OFF
+
+; OFF-NOT: OpString "my-attr"
+; OFF-NOT: OpString "nounwind"
+; OFF-NOT: OpString "some.md"
+; OFF-NOT: OpString "spv.was-available-externally"
+
+; CHECK-DAG: %[[#auxset:]] = OpExtInstImport "NonSemantic.AuxData"
+; CHECK-DAG: %[[#fname:]] = OpString "fn"
+; CHECK-DAG: %[[#akind:]] = OpString "my-attr"
+; CHECK-DAG: %[[#aval:]] = OpString "val"
+; CHECK-DAG: %[[#nounwind:]] = OpString "nounwind"
+; CHECK-DAG: %[[#gname:]] = OpString "gv"
+; CHECK-DAG: %[[#mdname:]] = OpString "some.md"
+; CHECK-DAG: %[[#mdval:]] = OpString "hello"
+; CHECK-DAG: %[[#void:]] = OpTypeVoid
+; CHECK-DAG: %[[#i32:]] = OpTypeInt 32 0
+
+; Internal marker must not leak as a string.
+; CHECK-NOT: OpString "spv.was-available-externally"
+; @ae has no payload, so no name OpString.
+; CHECK-NOT: OpString "ae"
+
+; Records emit in module order; Linkage's 2nd operand is the shared AE const.
+; CHECK-DAG: %{{[0-9]+}} = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fname]] %[[#akind]] %[[#aval]]
+; CHECK-DAG: %{{[0-9]+}} = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fname]] %[[#nounwind]]
+; CHECK-DAG: %{{[0-9]+}} = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#gname]] %[[#mdname]] %[[#mdval]]
+; CHECK-DAG: %[[#aelink:]] = OpConstant %[[#i32]] 0
+; CHECK-DAG: %{{[0-9]+}} = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %{{[0-9]+}} %[[#aelink]]
+
+ at gv = global i32 0, align 4, !some.md !0
+
+define spir_func i32 @fn(i32 %x) "my-attr"="val" nounwind {
+ ret i32 %x
+}
+
+define available_externally spir_func i32 @ae(i32 %x) {
+ ret i32 %x
+}
+
+!0 = !{!"hello"}
>From b734fc25197262098a5ad331135d5a6177117d44 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 29 May 2026 10:41:42 +0200
Subject: [PATCH 4/9] fmt
---
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
index 08d3675868693..516bad6f83b0f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -129,7 +129,8 @@ void SPIRVAuxDataHandler::collectMetadataFor(
isa<Function>(GO) ? FunctionMetadata : GlobalVariableMetadata;
// Skip non-MDString operands: emitting them would require a full value
// translation we can't safely drive from here.
- auto CollectStrings = [&](MDNode *MD) -> std::optional<SmallVector<MCRegister, 4>> {
+ auto CollectStrings =
+ [&](MDNode *MD) -> std::optional<SmallVector<MCRegister, 4>> {
SmallVector<MCRegister, 4> Out;
for (const MDOperand &MdOp : MD->operands()) {
auto *MDStr = dyn_cast_or_null<MDString>(MdOp.get());
@@ -143,8 +144,7 @@ void SPIRVAuxDataHandler::collectMetadataFor(
if (MD.first == LLVMContext::MD_dbg)
continue;
StringRef MDName = MDNames[MD.first];
- if (MDName == "spirv.Decorations" ||
- MDName == "spirv.ParameterDecorations")
+ if (MDName == "spirv.Decorations" || MDName == "spirv.ParameterDecorations")
continue;
auto Operands = CollectStrings(MD.second);
if (!Operands)
@@ -200,16 +200,17 @@ void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
if (!FnReg.isValid())
continue;
if (!AEConstReg.isValid())
- AEConstReg =
- emitOpConstantI32(AvailableExternally, I32TypeReg, MAI);
+ AEConstReg = emitOpConstantI32(AvailableExternally, I32TypeReg, MAI);
emitAuxDataExtInst(Linkage, VoidTypeReg, ExtSetReg, {FnReg, AEConstReg},
MAI);
}
}
-void SPIRVAuxDataHandler::emitAuxDataExtInst(
- uint32_t Opcode, MCRegister VoidTypeReg, MCRegister ExtSetReg,
- ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI) {
+void SPIRVAuxDataHandler::emitAuxDataExtInst(uint32_t Opcode,
+ MCRegister VoidTypeReg,
+ MCRegister ExtSetReg,
+ ArrayRef<MCRegister> Operands,
+ SPIRV::ModuleAnalysisInfo &MAI) {
MCInst Inst;
Inst.setOpcode(SPIRV::OpExtInst);
Inst.addOperand(MCOperand::createReg(MAI.getNextIDRegister()));
>From 7da17b426ca4eabe81e78046050c0e6aa52c6e98 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 29 May 2026 18:45:40 +0200
Subject: [PATCH 5/9] Address comments
---
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp | 34 +++++++++----------
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h | 14 ++++++--
2 files changed, 28 insertions(+), 20 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
index 516bad6f83b0f..8eaf15e4caa8d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -21,6 +21,7 @@
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
@@ -32,15 +33,6 @@ static cl::opt<bool> SPVPreserveAuxData(
cl::Optional, cl::Hidden, cl::init(false));
namespace {
-// Khronos NonSemantic.AuxData opcodes.
-enum AuxDataOpcode : uint32_t {
- FunctionMetadata = 0,
- FunctionAttribute = 1,
- GlobalVariableMetadata = 2,
- GlobalVariableAttribute = 3,
- Linkage = 4,
-};
-
enum AuxDataLinkageType : uint32_t {
AvailableExternally = 0,
};
@@ -70,8 +62,12 @@ void SPIRVAuxDataHandler::prepareModuleOutput(const SPIRVSubtarget &ST,
SPIRV::ModuleAnalysisInfo &MAI) {
if (!hasWork())
return;
- if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_non_semantic_info))
+ if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_non_semantic_info)) {
+ if (SPVPreserveAuxData)
+ report_fatal_error("-spirv-preserve-auxdata requires the "
+ "SPV_KHR_non_semantic_info extension to be enabled.");
return;
+ }
MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info);
if (!MAI.ExtInstSetMap.count(NonSemanticAuxDataSet))
MAI.ExtInstSetMap[NonSemanticAuxDataSet] = MAI.getNextIDRegister();
@@ -96,8 +92,8 @@ SPIRVAuxDataHandler::getOrEmitString(StringRef S,
void SPIRVAuxDataHandler::collectAttributesFor(
const GlobalObject *GO, function_ref<MCRegister()> GetNameReg,
SPIRV::ModuleAnalysisInfo &MAI) {
- uint32_t Opcode =
- isa<Function>(GO) ? FunctionAttribute : GlobalVariableAttribute;
+ AuxDataOpcode Opcode = isa<Function>(GO) ? FunctionAttributeOpcode
+ : GlobalVariableAttributeOpcode;
for (const Attribute &A : getGOAttrs(GO)) {
if (A.isStringAttribute() &&
A.getKindAsString() == SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR)
@@ -125,8 +121,8 @@ void SPIRVAuxDataHandler::collectMetadataFor(
GO->getAllMetadata(AllMD);
if (AllMD.empty())
return;
- uint32_t Opcode =
- isa<Function>(GO) ? FunctionMetadata : GlobalVariableMetadata;
+ AuxDataOpcode Opcode =
+ isa<Function>(GO) ? FunctionMetadataOpcode : GlobalVariableMetadataOpcode;
// Skip non-MDString operands: emitting them would require a full value
// translation we can't safely drive from here.
auto CollectStrings =
@@ -201,12 +197,12 @@ void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
continue;
if (!AEConstReg.isValid())
AEConstReg = emitOpConstantI32(AvailableExternally, I32TypeReg, MAI);
- emitAuxDataExtInst(Linkage, VoidTypeReg, ExtSetReg, {FnReg, AEConstReg},
- MAI);
+ emitAuxDataExtInst(LinkageOpcode, VoidTypeReg, ExtSetReg,
+ {FnReg, AEConstReg}, MAI);
}
}
-void SPIRVAuxDataHandler::emitAuxDataExtInst(uint32_t Opcode,
+void SPIRVAuxDataHandler::emitAuxDataExtInst(AuxDataOpcode Opcode,
MCRegister VoidTypeReg,
MCRegister ExtSetReg,
ArrayRef<MCRegister> Operands,
@@ -216,7 +212,7 @@ void SPIRVAuxDataHandler::emitAuxDataExtInst(uint32_t Opcode,
Inst.addOperand(MCOperand::createReg(MAI.getNextIDRegister()));
Inst.addOperand(MCOperand::createReg(VoidTypeReg));
Inst.addOperand(MCOperand::createReg(ExtSetReg));
- Inst.addOperand(MCOperand::createImm(static_cast<int64_t>(Opcode)));
+ Inst.addOperand(MCOperand::createImm(Opcode));
for (MCRegister R : Operands)
Inst.addOperand(MCOperand::createReg(R));
emitMCInst(Inst);
@@ -241,6 +237,8 @@ SPIRVAuxDataHandler::findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI) {
MCRegister
SPIRVAuxDataHandler::findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI) {
+ // SPIR-V OpTypeInt: <width>, <signedness>. Signedness 0 = unsigned, 1 =
+ // signed (we want unsigned i32).
constexpr int64_t Int32BitWidth = 32;
constexpr int64_t UnsignedSignedness = 0;
for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
index b60b77e8de1e4..cfd12785b64da 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
@@ -33,6 +33,16 @@ class GlobalObject;
class Module;
class SPIRVSubtarget;
+// Khronos NonSemantic.AuxData opcodes (int64_t to drop casts at MCOperand
+// boundaries).
+enum AuxDataOpcode : int64_t {
+ FunctionMetadataOpcode = 0,
+ FunctionAttributeOpcode = 1,
+ GlobalVariableMetadataOpcode = 2,
+ GlobalVariableAttributeOpcode = 3,
+ LinkageOpcode = 4,
+};
+
class SPIRVAuxDataHandler {
public:
SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M);
@@ -51,7 +61,7 @@ class SPIRVAuxDataHandler {
private:
struct ExtInstRecord {
- uint32_t Opcode;
+ AuxDataOpcode Opcode;
SmallVector<MCRegister, 4> Operands;
};
@@ -82,7 +92,7 @@ class SPIRVAuxDataHandler {
MCRegister findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI);
MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
SPIRV::ModuleAnalysisInfo &MAI);
- void emitAuxDataExtInst(uint32_t Opcode, MCRegister VoidTypeReg,
+ void emitAuxDataExtInst(AuxDataOpcode Opcode, MCRegister VoidTypeReg,
MCRegister ExtSetReg, ArrayRef<MCRegister> Operands,
SPIRV::ModuleAnalysisInfo &MAI);
};
>From e12eedb3af106e24fdead5f10be1f529c7dd1975 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Fri, 29 May 2026 19:25:20 +0200
Subject: [PATCH 6/9] Rename funcs
---
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp | 14 +++++++-------
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h | 6 +++---
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
index 8eaf15e4caa8d..31c8e55d3da58 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -189,14 +189,15 @@ void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
if (LinkagePreservedFns.empty())
return;
- MCRegister I32TypeReg = findOrEmitOpTypeInt32(MAI);
+ MCRegister UInt32TypeReg = findOrEmitOpTypeUInt32(MAI);
MCRegister AEConstReg;
for (const Function *F : LinkagePreservedFns) {
MCRegister FnReg = MAI.getGlobalObjReg(F);
if (!FnReg.isValid())
continue;
if (!AEConstReg.isValid())
- AEConstReg = emitOpConstantI32(AvailableExternally, I32TypeReg, MAI);
+ AEConstReg =
+ emitOpConstantUInt32(AvailableExternally, UInt32TypeReg, MAI);
emitAuxDataExtInst(LinkageOpcode, VoidTypeReg, ExtSetReg,
{FnReg, AEConstReg}, MAI);
}
@@ -236,7 +237,7 @@ SPIRVAuxDataHandler::findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI) {
}
MCRegister
-SPIRVAuxDataHandler::findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI) {
+SPIRVAuxDataHandler::findOrEmitOpTypeUInt32(SPIRV::ModuleAnalysisInfo &MAI) {
// SPIR-V OpTypeInt: <width>, <signedness>. Signedness 0 = unsigned, 1 =
// signed (we want unsigned i32).
constexpr int64_t Int32BitWidth = 32;
@@ -256,14 +257,13 @@ SPIRVAuxDataHandler::findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI) {
return Reg;
}
-MCRegister
-SPIRVAuxDataHandler::emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
- SPIRV::ModuleAnalysisInfo &MAI) {
+MCRegister SPIRVAuxDataHandler::emitOpConstantUInt32(
+ uint32_t Value, MCRegister UInt32TypeReg, SPIRV::ModuleAnalysisInfo &MAI) {
MCRegister Reg = MAI.getNextIDRegister();
MCInst Inst;
Inst.setOpcode(SPIRV::OpConstantI);
Inst.addOperand(MCOperand::createReg(Reg));
- Inst.addOperand(MCOperand::createReg(I32TypeReg));
+ Inst.addOperand(MCOperand::createReg(UInt32TypeReg));
Inst.addOperand(MCOperand::createImm(static_cast<int64_t>(Value)));
emitMCInst(Inst);
return Reg;
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
index cfd12785b64da..5a672b1abcde2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
@@ -89,9 +89,9 @@ class SPIRVAuxDataHandler {
void emitMCInst(MCInst &Inst);
MCRegister findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI);
- MCRegister findOrEmitOpTypeInt32(SPIRV::ModuleAnalysisInfo &MAI);
- MCRegister emitOpConstantI32(uint32_t Value, MCRegister I32TypeReg,
- SPIRV::ModuleAnalysisInfo &MAI);
+ MCRegister findOrEmitOpTypeUInt32(SPIRV::ModuleAnalysisInfo &MAI);
+ MCRegister emitOpConstantUInt32(uint32_t Value, MCRegister UInt32TypeReg,
+ SPIRV::ModuleAnalysisInfo &MAI);
void emitAuxDataExtInst(AuxDataOpcode Opcode, MCRegister VoidTypeReg,
MCRegister ExtSetReg, ArrayRef<MCRegister> Operands,
SPIRV::ModuleAnalysisInfo &MAI);
>From 4f9b2a4a60d2cf0cf40015cbd4845869217b56c1 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 1 Jun 2026 14:23:31 +0200
Subject: [PATCH 7/9] Add NonSemantic.AuxData forward-path tests
---
.../preserve-all-function-attributes.ll | 32 +++++++++++++++++
.../preserve-all-function-metadata-debug.ll | 35 +++++++++++++++++++
.../preserve-all-function-metadata.ll | 34 ++++++++++++++++++
.../preserve-auxdata-requires-extension.ll | 12 +++++++
.../preserve-auxdata.ll | 8 ++---
.../preserve-gv-attributes.ll | 28 +++++++++++++++
.../preserve-gv-metadata.ll | 28 +++++++++++++++
7 files changed, 173 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata-requires-extension.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll
create mode 100644 llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll
new file mode 100644
index 0000000000000..e034a51a9ae10
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll
@@ -0,0 +1,32 @@
+; Adapted from the SPIRV-LLVM-Translator --spirv-preserve-auxdata forward-path
+; tests. The backend only emits, so the reverse-translation checks are dropped.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
+; RUN: --spirv-ext=+SPV_KHR_non_semantic_info -spirv-preserve-auxdata \
+; RUN: %s -o - | FileCheck %s
+
+; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
+
+; CHECK-DAG: %[[#Fcn0:]] = OpString "mul_add"
+; CHECK-DAG: %[[#Attr0:]] = OpString "foo"
+; CHECK-DAG: %[[#Fcn1:]] = OpString "test"
+; CHECK-DAG: %[[#Attr1LHS:]] = OpString "bar"
+; CHECK-DAG: %[[#Attr1RHS:]] = OpString "baz"
+
+; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
+
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#Fcn0]] %[[#Attr0]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#Fcn1]] %[[#Attr1LHS]] %[[#Attr1RHS]]
+
+target triple = "spir64-unknown-unknown"
+
+define spir_func void @mul_add() #0 {
+ ret void
+}
+
+define spir_func void @test() #1 {
+ ret void
+}
+
+attributes #0 = { "foo" }
+attributes #1 = { "bar"="baz" }
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll
new file mode 100644
index 0000000000000..069068b754366
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll
@@ -0,0 +1,35 @@
+; Adapted from the SPIRV-LLVM-Translator --spirv-preserve-auxdata forward-path
+; tests. Debug info (!dbg) must never be emitted as NonSemantic.AuxData. The
+; "keep-me" attribute is a positive control: it confirms the feature is active,
+; so the absence of a debug record is meaningful rather than the feature being
+; off.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
+; RUN: --spirv-ext=+SPV_KHR_non_semantic_info -spirv-preserve-auxdata \
+; RUN: %s -o - | FileCheck %s
+
+; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
+; CHECK-DAG: %[[#Fcn:]] = OpString "foo"
+; CHECK-DAG: %[[#Attr:]] = OpString "keep-me"
+; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
+
+; CHECK: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#Fcn]] %[[#Attr]]
+; CHECK-NOT: OpExtInst %[[#VoidT]] %[[#Import]]
+
+target triple = "spir64-unknown-unknown"
+
+define spir_func void @foo() #0 !dbg !4 {
+ ret void
+}
+
+attributes #0 = { "keep-me" }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, emissionKind: FullDebug)
+!1 = !DIFile(filename: "foo.c", directory: "./")
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !6, unit: !0)
+!6 = !DISubroutineType(types: !7)
+!7 = !{null}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll
new file mode 100644
index 0000000000000..77b323a838440
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll
@@ -0,0 +1,34 @@
+; Adapted from the SPIRV-LLVM-Translator --spirv-preserve-auxdata forward-path
+; tests. The backend only records metadata whose operands are all MDStrings, so
+; the numeric !{i32 5} metadata on @test_val is dropped; only @test_string is
+; emitted.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
+; RUN: --spirv-ext=+SPV_KHR_non_semantic_info -spirv-preserve-auxdata \
+; RUN: %s -o - | FileCheck %s
+
+; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
+
+; CHECK-DAG: %[[#MDName:]] = OpString "bar"
+; CHECK-DAG: %[[#MDValue:]] = OpString "baz"
+; CHECK-DAG: %[[#Fcn1:]] = OpString "test_string"
+
+; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
+
+; Numeric function metadata is not representable as OpStrings and is skipped.
+; CHECK-NOT: OpString "test_val"
+
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#Fcn1]] %[[#MDName]] %[[#MDValue]]
+
+target triple = "spir64-unknown-unknown"
+
+define spir_func void @test_val() !foo !1 {
+ ret void
+}
+
+define spir_func void @test_string() !bar !2 {
+ ret void
+}
+
+!1 = !{i32 5}
+!2 = !{!"baz"}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata-requires-extension.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata-requires-extension.ll
new file mode 100644
index 0000000000000..3927d55754bd1
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata-requires-extension.ll
@@ -0,0 +1,12 @@
+; -spirv-preserve-auxdata is an error unless SPV_KHR_non_semantic_info is on.
+
+; RUN: not --crash llc -O0 -mtriple=spirv64-unknown-unknown \
+; RUN: -spirv-preserve-auxdata %s -o - 2>&1 | FileCheck %s
+
+; CHECK: -spirv-preserve-auxdata requires the SPV_KHR_non_semantic_info extension
+
+target triple = "spir64-unknown-unknown"
+
+define spir_func void @fn() "my-attr"="val" {
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
index cc6c1f8637937..9c6bd3b7558d0 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
@@ -31,11 +31,11 @@
; CHECK-NOT: OpString "ae"
; Records emit in module order; Linkage's 2nd operand is the shared AE const.
-; CHECK-DAG: %{{[0-9]+}} = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fname]] %[[#akind]] %[[#aval]]
-; CHECK-DAG: %{{[0-9]+}} = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fname]] %[[#nounwind]]
-; CHECK-DAG: %{{[0-9]+}} = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#gname]] %[[#mdname]] %[[#mdval]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fname]] %[[#akind]] %[[#aval]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fname]] %[[#nounwind]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#gname]] %[[#mdname]] %[[#mdval]]
; CHECK-DAG: %[[#aelink:]] = OpConstant %[[#i32]] 0
-; CHECK-DAG: %{{[0-9]+}} = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %{{[0-9]+}} %[[#aelink]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#]] %[[#aelink]]
@gv = global i32 0, align 4, !some.md !0
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll
new file mode 100644
index 0000000000000..20a700b2a773c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll
@@ -0,0 +1,28 @@
+; Adapted from the SPIRV-LLVM-Translator --spirv-preserve-auxdata forward-path
+; tests. Global-variable attributes are emitted as NonSemantic.AuxData records.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
+; RUN: --spirv-ext=+SPV_KHR_non_semantic_info -spirv-preserve-auxdata \
+; RUN: %s -o - | FileCheck %s
+
+; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
+
+; CHECK-DAG: %[[#GVName:]] = OpString "g"
+; CHECK-DAG: %[[#Attr0:]] = OpString "flag"
+; CHECK-DAG: %[[#Attr1LHS:]] = OpString "my-gv-attr"
+; CHECK-DAG: %[[#Attr1RHS:]] = OpString "7"
+
+; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
+
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GVName]] %[[#Attr0]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GVName]] %[[#Attr1LHS]] %[[#Attr1RHS]]
+
+target triple = "spir64-unknown-unknown"
+
+ at g = addrspace(1) global i32 0 #0
+
+define spir_func void @use() {
+ ret void
+}
+
+attributes #0 = { "my-gv-attr"="7" "flag" }
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll
new file mode 100644
index 0000000000000..191f510a04c06
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll
@@ -0,0 +1,28 @@
+; Adapted from the SPIRV-LLVM-Translator --spirv-preserve-auxdata forward-path
+; tests. Global-variable metadata with all-MDString operands is emitted as a
+; NonSemantic.AuxData record.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
+; RUN: --spirv-ext=+SPV_KHR_non_semantic_info -spirv-preserve-auxdata \
+; RUN: %s -o - | FileCheck %s
+
+; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
+
+; CHECK-DAG: %[[#MD0:]] = OpString "hi"
+; CHECK-DAG: %[[#MD1:]] = OpString "there"
+; CHECK-DAG: %[[#GVName:]] = OpString "a"
+; CHECK-DAG: %[[#MDName:]] = OpString "some.gv.md"
+
+; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
+
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GVName]] %[[#MDName]] %[[#MD0]] %[[#MD1]]
+
+target triple = "spir64-unknown-unknown"
+
+ at a = addrspace(1) global i8 0, !some.gv.md !0
+
+define spir_func void @use() {
+ ret void
+}
+
+!0 = !{!"hi", !"there"}
>From 825a730b8fd0678290c77b5ac8d31500a3111d43 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Tue, 2 Jun 2026 13:38:49 +0200
Subject: [PATCH 8/9] Address comment
---
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp | 4 +---
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h | 5 ++--
llvm/lib/Target/SPIRV/SPIRVUtils.cpp | 5 ----
.../preserve-auxdata.ll | 3 ++-
.../linkage/available-externally-function.ll | 23 ++++++++++++-------
5 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
index 31c8e55d3da58..63e46a5776fc7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -54,9 +54,7 @@ SPIRVAuxDataHandler::SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M)
LinkagePreservedFns.push_back(&F);
}
-bool SPIRVAuxDataHandler::hasWork() const {
- return !LinkagePreservedFns.empty() || SPVPreserveAuxData;
-}
+bool SPIRVAuxDataHandler::hasWork() const { return SPVPreserveAuxData; }
void SPIRVAuxDataHandler::prepareModuleOutput(const SPIRVSubtarget &ST,
SPIRV::ModuleAnalysisInfo &MAI) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
index 5a672b1abcde2..68ef62c170f5f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
@@ -7,8 +7,9 @@
//===----------------------------------------------------------------------===//
//
// Emits NonSemantic.AuxData ExtInst annotations (mirrors SPIRV-LLVM-Translator
-// --spirv-preserve-auxdata). Linkage records emit unconditionally for AE-tagged
-// functions; attribute/metadata records are gated by -spirv-preserve-auxdata.
+// --spirv-preserve-auxdata).
+// Otherwise, AE-tagged functions emit plain Export linkage via
+// LinkageAttributes.
//
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index e56d2e47e9045..0ef31f4182b4e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -1217,11 +1217,6 @@ getSpirvLinkageTypeFor(const SPIRVSubtarget &ST, const GlobalValue &GV) {
if (GV.hasLocalLinkage())
return std::nullopt;
- // Preserved via NonSemantic.AuxData; skip LinkageAttributes.
- if (const auto *F = dyn_cast<Function>(&GV))
- if (F->hasFnAttribute(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR))
- return std::nullopt;
-
if (GV.isDeclarationForLinker()) {
// Interface variables must not get Import linkage.
if (const auto *GVar = dyn_cast<GlobalVariable>(&GV)) {
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
index 9c6bd3b7558d0..89b8526282e47 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
@@ -4,11 +4,12 @@
; RUN: --spirv-ext=+SPV_KHR_non_semantic_info -spirv-preserve-auxdata \
; RUN: %s -o - | FileCheck %s
-; Off by default: only the Linkage record fires (for the AE function).
+; Off by default: no AuxData records at all, even with the extension enabled.
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
; RUN: --spirv-ext=+SPV_KHR_non_semantic_info %s -o - \
; RUN: | FileCheck %s --check-prefix=OFF
+; OFF-NOT: OpExtInstImport "NonSemantic.AuxData"
; OFF-NOT: OpString "my-attr"
; OFF-NOT: OpString "nounwind"
; OFF-NOT: OpString "some.md"
diff --git a/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll b/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
index 35f800d1ea1cd..ed7e7e146cf42 100644
--- a/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
+++ b/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
@@ -1,25 +1,32 @@
-; Without SPV_KHR_non_semantic_info: emitted as plain external definitions
-; (original linkage has no native SPIR-V representation).
+; available_externally functions are emitted as definitions with Export
+; linkage. The original linkage is only preserved as a NonSemantic.AuxData
+; annotation when -spirv-preserve-auxdata is passed.
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=NOAUX
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=NOAUX
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=NOAUX
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+; Passing the flag without the extension is a fatal error.
+
+; RUN: not --crash llc -O0 -mtriple=spirv64-unknown-unknown --spirv-preserve-auxdata %s -o - 2>&1 | FileCheck %s --check-prefix=ERR
+; ERR: -spirv-preserve-auxdata requires the SPV_KHR_non_semantic_info extension to be enabled.
+
; NOAUX-NOT: OpExtension "SPV_KHR_non_semantic_info"
; NOAUX-NOT: NonSemantic.AuxData
; NOAUX-DAG: OpName %[[#ae_func:]] "ae_func"
; NOAUX-DAG: OpName %[[#caller:]] "caller"
-; NOAUX-NOT: OpDecorate %[[#ae_func]] LinkageAttributes
+; NOAUX-DAG: OpDecorate %[[#ae_func]] LinkageAttributes "ae_func" Export
; NOAUX: %[[#ae_func]] = OpFunction
; NOAUX: OpFunctionEnd
; NOAUX: %[[#caller]] = OpFunction
; NOAUX: OpFunctionCall %[[#]] %[[#ae_func]]
-; With SPV_KHR_non_semantic_info: linkage preserved via a
-; NonSemantic.AuxData::Linkage annotation for round-trip recovery.
+; With -spirv-preserve-auxdata: Export linkage plus a NonSemantic.AuxData
+; Linkage annotation recording the original available_externally linkage.
-; RUN: llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=AUX
-; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s --check-prefix=AUX
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info --spirv-preserve-auxdata %s -o - | FileCheck %s --check-prefix=AUX
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info --spirv-preserve-auxdata %s -o - | FileCheck %s --check-prefix=AUX
;; spirv-val rejects the AuxData forward-ref without
;; SPV_KHR_relaxed_extended_instruction; matches Translator behavior.
@@ -27,7 +34,7 @@
; AUX-DAG: %[[#auxset:]] = OpExtInstImport "NonSemantic.AuxData"
; AUX-DAG: OpName %[[#ae_func:]] "ae_func"
; AUX-DAG: OpName %[[#caller:]] "caller"
-; AUX-NOT: OpDecorate %[[#ae_func]] LinkageAttributes
+; AUX-DAG: OpDecorate %[[#ae_func]] LinkageAttributes "ae_func" Export
; AUX-DAG: %[[#void:]] = OpTypeVoid
; AUX-DAG: %[[#i32:]] = OpTypeInt 32 0
; AUX-DAG: %[[#zero:]] = OpConstant %[[#i32]] 0
>From b6d51cf2d121b72a0e9ab5abc7c27505c6a558f6 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Mon, 8 Jun 2026 10:30:26 +0200
Subject: [PATCH 9/9] Address review comments
---
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp | 165 +++++++++++++-----
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h | 27 ++-
.../Target/SPIRV/SPIRVPrepareFunctions.cpp | 10 ++
.../preserve-all-function-attributes.ll | 6 +-
.../preserve-all-function-metadata-debug.ll | 2 +-
.../preserve-all-function-metadata.ll | 18 +-
.../preserve-auxdata.ll | 28 +--
.../preserve-gv-attributes.ll | 8 +-
.../preserve-gv-metadata.ll | 6 +-
.../linkage/available-externally-global.ll | 33 ++++
.../CodeGen/SPIRV/linkage/linkage-types.ll | 3 +-
11 files changed, 227 insertions(+), 79 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/linkage/available-externally-global.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
index 63e46a5776fc7..d60b204620720 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -12,6 +12,7 @@
#include "SPIRVUtils.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalObject.h"
#include "llvm/IR/GlobalVariable.h"
@@ -47,11 +48,18 @@ AttributeSet getGOAttrs(const GlobalObject *GO) {
}
} // namespace
+static bool wasAvailableExternally(const GlobalObject *GO) {
+ if (const auto *F = dyn_cast<Function>(GO))
+ return F->hasFnAttribute(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR);
+ return cast<GlobalVariable>(GO)->getAttributes().hasAttribute(
+ SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR);
+}
+
SPIRVAuxDataHandler::SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M)
: Asm(AP), Mod(M) {
- for (const Function &F : M)
- if (F.hasFnAttribute(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR))
- LinkagePreservedFns.push_back(&F);
+ for (const GlobalObject &GO : M.global_objects())
+ if (wasAvailableExternally(&GO))
+ LinkagePreservedGOs.push_back(&GO);
}
bool SPIRVAuxDataHandler::hasWork() const { return SPVPreserveAuxData; }
@@ -87,9 +95,8 @@ SPIRVAuxDataHandler::getOrEmitString(StringRef S,
return Reg;
}
-void SPIRVAuxDataHandler::collectAttributesFor(
- const GlobalObject *GO, function_ref<MCRegister()> GetNameReg,
- SPIRV::ModuleAnalysisInfo &MAI) {
+void SPIRVAuxDataHandler::collectAttributesFor(const GlobalObject *GO,
+ SPIRV::ModuleAnalysisInfo &MAI) {
AuxDataOpcode Opcode = isa<Function>(GO) ? FunctionAttributeOpcode
: GlobalVariableAttributeOpcode;
for (const Attribute &A : getGOAttrs(GO)) {
@@ -98,39 +105,47 @@ void SPIRVAuxDataHandler::collectAttributesFor(
continue;
ExtInstRecord Rec;
Rec.Opcode = Opcode;
- Rec.Operands.push_back(GetNameReg());
+ Rec.Target = GO;
if (A.isStringAttribute()) {
- Rec.Operands.push_back(getOrEmitString(A.getKindAsString(), MAI));
+ Rec.Operands.push_back({getOrEmitString(A.getKindAsString(), MAI)});
StringRef Val = A.getValueAsString();
if (!Val.empty())
- Rec.Operands.push_back(getOrEmitString(Val, MAI));
+ Rec.Operands.push_back({getOrEmitString(Val, MAI)});
} else {
Rec.Operands.push_back(
- getOrEmitString(StringPool.save(A.getAsString()), MAI));
+ {getOrEmitString(StringPool.save(A.getAsString()), MAI)});
}
PendingRecords.push_back(std::move(Rec));
}
}
-void SPIRVAuxDataHandler::collectMetadataFor(
- const GlobalObject *GO, function_ref<MCRegister()> GetNameReg,
- ArrayRef<StringRef> MDNames, SPIRV::ModuleAnalysisInfo &MAI) {
+void SPIRVAuxDataHandler::collectMetadataFor(const GlobalObject *GO,
+ ArrayRef<StringRef> MDNames,
+ SPIRV::ModuleAnalysisInfo &MAI) {
SmallVector<std::pair<unsigned, MDNode *>> AllMD;
GO->getAllMetadata(AllMD);
if (AllMD.empty())
return;
AuxDataOpcode Opcode =
isa<Function>(GO) ? FunctionMetadataOpcode : GlobalVariableMetadataOpcode;
- // Skip non-MDString operands: emitting them would require a full value
- // translation we can't safely drive from here.
- auto CollectStrings =
- [&](MDNode *MD) -> std::optional<SmallVector<MCRegister, 4>> {
- SmallVector<MCRegister, 4> Out;
+ // MDString operands become OpStrings; ValueAsMetadata constants (e.g.
+ // !{i32 5}) become OpConstants emitted at section 10. Any other operand
+ // kind would need full value translation, so skip the whole node.
+ auto CollectOperands =
+ [&](MDNode *MD) -> std::optional<SmallVector<Operand, 4>> {
+ SmallVector<Operand, 4> Out;
for (const MDOperand &MdOp : MD->operands()) {
- auto *MDStr = dyn_cast_or_null<MDString>(MdOp.get());
- if (!MDStr)
+ Metadata *Md = MdOp.get();
+ if (auto *MDStr = dyn_cast_or_null<MDString>(Md)) {
+ Out.push_back({getOrEmitString(MDStr->getString(), MAI)});
+ } else if (auto *VAM = dyn_cast_or_null<ValueAsMetadata>(Md)) {
+ auto *C = dyn_cast<Constant>(VAM->getValue());
+ if (!C || !(isa<ConstantInt>(C) || isa<ConstantFP>(C)))
+ return std::nullopt;
+ Out.push_back({MCRegister(), C});
+ } else {
return std::nullopt;
- Out.push_back(getOrEmitString(MDStr->getString(), MAI));
+ }
}
return Out;
};
@@ -140,13 +155,13 @@ void SPIRVAuxDataHandler::collectMetadataFor(
StringRef MDName = MDNames[MD.first];
if (MDName == "spirv.Decorations" || MDName == "spirv.ParameterDecorations")
continue;
- auto Operands = CollectStrings(MD.second);
+ auto Operands = CollectOperands(MD.second);
if (!Operands)
continue;
ExtInstRecord Rec;
Rec.Opcode = Opcode;
- Rec.Operands.push_back(GetNameReg());
- Rec.Operands.push_back(getOrEmitString(MDName, MAI));
+ Rec.Target = GO;
+ Rec.Operands.push_back({getOrEmitString(MDName, MAI)});
Rec.Operands.append(Operands->begin(), Operands->end());
PendingRecords.push_back(std::move(Rec));
}
@@ -162,15 +177,8 @@ void SPIRVAuxDataHandler::emitAuxDataStrings(SPIRV::ModuleAnalysisInfo &MAI) {
for (const GlobalObject &GO : Mod.global_objects()) {
if (GO.isDeclaration())
continue;
- // Defer the name OpString until the first record actually fires.
- MCRegister NameReg;
- auto GetNameReg = [&]() {
- if (!NameReg.isValid())
- NameReg = getOrEmitString(GO.getName(), MAI);
- return NameReg;
- };
- collectAttributesFor(&GO, GetNameReg, MAI);
- collectMetadataFor(&GO, GetNameReg, MDNames, MAI);
+ collectAttributesFor(&GO, MAI);
+ collectMetadataFor(&GO, MDNames, MAI);
}
}
@@ -181,23 +189,31 @@ void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
MCRegister VoidTypeReg = findOrEmitOpTypeVoid(MAI);
- for (const ExtInstRecord &Rec : PendingRecords)
- emitAuxDataExtInst(Rec.Opcode, VoidTypeReg, ExtSetReg, Rec.Operands, MAI);
+ for (const ExtInstRecord &Rec : PendingRecords) {
+ MCRegister TargetReg = MAI.getGlobalObjReg(Rec.Target);
+ if (!TargetReg.isValid())
+ continue;
+ SmallVector<MCRegister, 5> Operands;
+ Operands.push_back(TargetReg);
+ for (const Operand &Op : Rec.Operands)
+ Operands.push_back(Op.Const ? emitConstant(Op.Const, MAI) : Op.Reg);
+ emitAuxDataExtInst(Rec.Opcode, VoidTypeReg, ExtSetReg, Operands, MAI);
+ }
- if (LinkagePreservedFns.empty())
+ if (LinkagePreservedGOs.empty())
return;
MCRegister UInt32TypeReg = findOrEmitOpTypeUInt32(MAI);
MCRegister AEConstReg;
- for (const Function *F : LinkagePreservedFns) {
- MCRegister FnReg = MAI.getGlobalObjReg(F);
- if (!FnReg.isValid())
+ for (const GlobalObject *GO : LinkagePreservedGOs) {
+ MCRegister TargetReg = MAI.getGlobalObjReg(GO);
+ if (!TargetReg.isValid())
continue;
if (!AEConstReg.isValid())
AEConstReg =
emitOpConstantUInt32(AvailableExternally, UInt32TypeReg, MAI);
emitAuxDataExtInst(LinkageOpcode, VoidTypeReg, ExtSetReg,
- {FnReg, AEConstReg}, MAI);
+ {TargetReg, AEConstReg}, MAI);
}
}
@@ -235,26 +251,85 @@ SPIRVAuxDataHandler::findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI) {
}
MCRegister
-SPIRVAuxDataHandler::findOrEmitOpTypeUInt32(SPIRV::ModuleAnalysisInfo &MAI) {
+SPIRVAuxDataHandler::findOrEmitOpTypeInt(unsigned BitWidth,
+ SPIRV::ModuleAnalysisInfo &MAI) {
// SPIR-V OpTypeInt: <width>, <signedness>. Signedness 0 = unsigned, 1 =
- // signed (we want unsigned i32).
- constexpr int64_t Int32BitWidth = 32;
+ // signed; we always emit unsigned.
constexpr int64_t UnsignedSignedness = 0;
for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
if (MI->getOpcode() == SPIRV::OpTypeInt &&
- MI->getOperand(1).getImm() == Int32BitWidth &&
+ MI->getOperand(1).getImm() == static_cast<int64_t>(BitWidth) &&
MI->getOperand(2).getImm() == UnsignedSignedness)
return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
MCRegister Reg = MAI.getNextIDRegister();
MCInst Inst;
Inst.setOpcode(SPIRV::OpTypeInt);
Inst.addOperand(MCOperand::createReg(Reg));
- Inst.addOperand(MCOperand::createImm(Int32BitWidth));
+ Inst.addOperand(MCOperand::createImm(BitWidth));
Inst.addOperand(MCOperand::createImm(UnsignedSignedness));
emitMCInst(Inst);
return Reg;
}
+MCRegister
+SPIRVAuxDataHandler::findOrEmitOpTypeUInt32(SPIRV::ModuleAnalysisInfo &MAI) {
+ return findOrEmitOpTypeInt(32, MAI);
+}
+
+MCRegister
+SPIRVAuxDataHandler::findOrEmitOpTypeFloat(unsigned BitWidth,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
+ if (MI->getOpcode() == SPIRV::OpTypeFloat &&
+ MI->getOperand(1).getImm() == static_cast<int64_t>(BitWidth))
+ return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
+ MCRegister Reg = MAI.getNextIDRegister();
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpTypeFloat);
+ Inst.addOperand(MCOperand::createReg(Reg));
+ Inst.addOperand(MCOperand::createImm(BitWidth));
+ emitMCInst(Inst);
+ return Reg;
+}
+
+MCRegister SPIRVAuxDataHandler::emitConstant(const Constant *C,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ auto [It, Inserted] = ConstantRegs.try_emplace(C);
+ if (!Inserted)
+ return It->second;
+
+ APInt Bits;
+ unsigned Opcode;
+ MCRegister TypeReg;
+ if (const auto *CI = dyn_cast<ConstantInt>(C)) {
+ Bits = CI->getValue();
+ Opcode = SPIRV::OpConstantI;
+ TypeReg = findOrEmitOpTypeInt(Bits.getBitWidth(), MAI);
+ } else {
+ const auto *CF = cast<ConstantFP>(C);
+ Bits = CF->getValueAPF().bitcastToAPInt();
+ Opcode = SPIRV::OpConstantF;
+ TypeReg = findOrEmitOpTypeFloat(Bits.getBitWidth(), MAI);
+ }
+
+ MCRegister Reg = MAI.getNextIDRegister();
+ It->second = Reg;
+ MCInst Inst;
+ Inst.setOpcode(Opcode);
+ Inst.addOperand(MCOperand::createReg(Reg));
+ Inst.addOperand(MCOperand::createReg(TypeReg));
+ // SPIR-V encodes the literal as ceil(width/32) little-endian 32-bit words.
+ unsigned NumWords = std::max(1u, (Bits.getBitWidth() + 31) / 32);
+ for (unsigned I = 0; I < NumWords; ++I)
+ Inst.addOperand(MCOperand::createImm(Bits.extractBitsAsZExtValue(
+ std::min(32u, Bits.getBitWidth() - I * 32), I * 32)));
+ // The asm printer needs this hint to render an f16 literal correctly.
+ if (Opcode == SPIRV::OpConstantF && Bits.getBitWidth() == 16)
+ Inst.setFlags(SPIRV::INST_PRINTER_WIDTH16);
+ emitMCInst(Inst);
+ return Reg;
+}
+
MCRegister SPIRVAuxDataHandler::emitOpConstantUInt32(
uint32_t Value, MCRegister UInt32TypeReg, SPIRV::ModuleAnalysisInfo &MAI) {
MCRegister Reg = MAI.getNextIDRegister();
diff --git a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
index 68ef62c170f5f..1d6ba998e6c2e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
@@ -19,7 +19,6 @@
#include "SPIRVModuleAnalysis.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/MC/MCRegister.h"
@@ -29,6 +28,7 @@
namespace llvm {
class AsmPrinter;
+class Constant;
class Function;
class GlobalObject;
class Module;
@@ -61,15 +61,25 @@ class SPIRVAuxDataHandler {
void emitAuxData(SPIRV::ModuleAnalysisInfo &MAI);
private:
+ // An ExtInst operand is either an already-emitted OpString (Reg valid) or a
+ // ValueAsMetadata constant whose OpConstant is emitted lazily at section 10
+ // (Const set). Exactly one is populated.
+ struct Operand {
+ MCRegister Reg;
+ const Constant *Const = nullptr;
+ };
+ // The Target operand (the OpFunction / OpVariable <id>) is resolved from
+ // GlobalObjMap at emit time, so records keep the GlobalObject, not a reg.
struct ExtInstRecord {
AuxDataOpcode Opcode;
- SmallVector<MCRegister, 4> Operands;
+ const GlobalObject *Target;
+ SmallVector<Operand, 4> Operands;
};
AsmPrinter &Asm;
const Module &Mod;
- SmallVector<const Function *> LinkagePreservedFns;
+ SmallVector<const GlobalObject *> LinkagePreservedGOs;
// Backing storage for non-string-attribute strings; StringRegs keys are
// StringRefs into it.
@@ -77,22 +87,25 @@ class SPIRVAuxDataHandler {
UniqueStringSaver StringPool{StringAlloc};
DenseMap<StringRef, MCRegister> StringRegs;
+ DenseMap<const Constant *, MCRegister> ConstantRegs;
SmallVector<ExtInstRecord> PendingRecords;
MCRegister getOrEmitString(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
void collectAttributesFor(const GlobalObject *GO,
- function_ref<MCRegister()> GetNameReg,
SPIRV::ModuleAnalysisInfo &MAI);
- void collectMetadataFor(const GlobalObject *GO,
- function_ref<MCRegister()> GetNameReg,
- ArrayRef<StringRef> MDNames,
+ void collectMetadataFor(const GlobalObject *GO, ArrayRef<StringRef> MDNames,
SPIRV::ModuleAnalysisInfo &MAI);
void emitMCInst(MCInst &Inst);
MCRegister findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI);
+ MCRegister findOrEmitOpTypeInt(unsigned BitWidth,
+ SPIRV::ModuleAnalysisInfo &MAI);
MCRegister findOrEmitOpTypeUInt32(SPIRV::ModuleAnalysisInfo &MAI);
+ MCRegister findOrEmitOpTypeFloat(unsigned BitWidth,
+ SPIRV::ModuleAnalysisInfo &MAI);
MCRegister emitOpConstantUInt32(uint32_t Value, MCRegister UInt32TypeReg,
SPIRV::ModuleAnalysisInfo &MAI);
+ MCRegister emitConstant(const Constant *C, SPIRV::ModuleAnalysisInfo &MAI);
void emitAuxDataExtInst(AuxDataOpcode Opcode, MCRegister VoidTypeReg,
MCRegister ExtSetReg, ArrayRef<MCRegister> Operands,
SPIRV::ModuleAnalysisInfo &MAI);
diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 4211197fc97d2..10772409f96a5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -829,6 +829,16 @@ bool SPIRVPrepareFunctionsImpl::runOnModule(Module &M) {
Changed |= terminateBlocksAfterTrap(M, Intrinsic::trap);
Changed |= terminateBlocksAfterTrap(M, Intrinsic::ubsantrap);
+ for (GlobalVariable &GV : M.globals()) {
+ // Strip + tag available_externally globals so AuxData can re-emit the
+ // original linkage as NonSemantic.AuxData::Linkage.
+ if (GV.hasAvailableExternallyLinkage() && !GV.isDeclaration()) {
+ GV.addAttribute(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR);
+ GV.setLinkage(GlobalValue::ExternalLinkage);
+ Changed = true;
+ }
+ }
+
for (Function &F : M) {
// MachineFunctionPass skips available_externally; strip + tag so AuxData
// can re-emit the original linkage as NonSemantic.AuxData::Linkage.
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll
index e034a51a9ae10..ec15eb6c1e080 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll
@@ -7,12 +7,14 @@
; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
-; CHECK-DAG: %[[#Fcn0:]] = OpString "mul_add"
; CHECK-DAG: %[[#Attr0:]] = OpString "foo"
-; CHECK-DAG: %[[#Fcn1:]] = OpString "test"
; CHECK-DAG: %[[#Attr1LHS:]] = OpString "bar"
; CHECK-DAG: %[[#Attr1RHS:]] = OpString "baz"
+; The Target operand is the real OpFunction <id>, not an OpString name.
+; CHECK-DAG: OpName %[[#Fcn0:]] "mul_add"
+; CHECK-DAG: OpName %[[#Fcn1:]] "test"
+
; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#Fcn0]] %[[#Attr0]]
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll
index 069068b754366..13465a374c652 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll
@@ -9,8 +9,8 @@
; RUN: %s -o - | FileCheck %s
; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
-; CHECK-DAG: %[[#Fcn:]] = OpString "foo"
; CHECK-DAG: %[[#Attr:]] = OpString "keep-me"
+; CHECK-DAG: OpName %[[#Fcn:]] "foo"
; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
; CHECK: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#Fcn]] %[[#Attr]]
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll
index 77b323a838440..43ed97b48a20d 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll
@@ -1,7 +1,6 @@
; Adapted from the SPIRV-LLVM-Translator --spirv-preserve-auxdata forward-path
-; tests. The backend only records metadata whose operands are all MDStrings, so
-; the numeric !{i32 5} metadata on @test_val is dropped; only @test_string is
-; emitted.
+; tests. Numeric metadata operands become OpConstants; string metadata operands
+; become OpStrings.
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown \
; RUN: --spirv-ext=+SPV_KHR_non_semantic_info -spirv-preserve-auxdata \
@@ -9,15 +8,20 @@
; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
+; CHECK-DAG: %[[#MDNameVal:]] = OpString "foo"
; CHECK-DAG: %[[#MDName:]] = OpString "bar"
; CHECK-DAG: %[[#MDValue:]] = OpString "baz"
-; CHECK-DAG: %[[#Fcn1:]] = OpString "test_string"
-; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
+; The Target operand is the real OpFunction <id>, not an OpString name.
+; CHECK-DAG: OpName %[[#Fcn0:]] "test_val"
+; CHECK-DAG: OpName %[[#Fcn1:]] "test_string"
-; Numeric function metadata is not representable as OpStrings and is skipped.
-; CHECK-NOT: OpString "test_val"
+; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
+; CHECK-DAG: %[[#I32:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#Const:]] = OpConstant %[[#I32]] 5
+; Numeric metadata operand is emitted as an OpConstant id.
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#Fcn0]] %[[#MDNameVal]] %[[#Const]]
; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#Fcn1]] %[[#MDName]] %[[#MDValue]]
target triple = "spir64-unknown-unknown"
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
index 89b8526282e47..d84f95bf7c70c 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
@@ -16,36 +16,42 @@
; OFF-NOT: OpString "spv.was-available-externally"
; CHECK-DAG: %[[#auxset:]] = OpExtInstImport "NonSemantic.AuxData"
-; CHECK-DAG: %[[#fname:]] = OpString "fn"
; CHECK-DAG: %[[#akind:]] = OpString "my-attr"
; CHECK-DAG: %[[#aval:]] = OpString "val"
; CHECK-DAG: %[[#nounwind:]] = OpString "nounwind"
-; CHECK-DAG: %[[#gname:]] = OpString "gv"
; CHECK-DAG: %[[#mdname:]] = OpString "some.md"
; CHECK-DAG: %[[#mdval:]] = OpString "hello"
+; CHECK-DAG: %[[#numname:]] = OpString "num.md"
; CHECK-DAG: %[[#void:]] = OpTypeVoid
; CHECK-DAG: %[[#i32:]] = OpTypeInt 32 0
+; The Target operand of every record is the real OpFunction / OpVariable <id>,
+; not an OpString of the name. Names are only emitted via OpName.
+; CHECK-DAG: OpName %[[#fn:]] "fn"
+; CHECK-DAG: OpName %[[#gv:]] "gv"
+
; Internal marker must not leak as a string.
; CHECK-NOT: OpString "spv.was-available-externally"
-; @ae has no payload, so no name OpString.
-; CHECK-NOT: OpString "ae"
-; Records emit in module order; Linkage's 2nd operand is the shared AE const.
-; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fname]] %[[#akind]] %[[#aval]]
-; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fname]] %[[#nounwind]]
-; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#gname]] %[[#mdname]] %[[#mdval]]
-; CHECK-DAG: %[[#aelink:]] = OpConstant %[[#i32]] 0
-; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#]] %[[#aelink]]
+; Records emit in module order; numeric metadata becomes OpConstant operands.
+; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fn]] %[[#akind]] %[[#aval]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#fn]] %[[#nounwind]]
+; CHECK-DAG: %[[#mdconst:]] = OpConstant %[[#i32]] 5
+; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#gv]] %[[#mdname]] %[[#mdval]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#gv]] %[[#numname]] %[[#mdconst]]
- at gv = global i32 0, align 4, !some.md !0
+ at gv = addrspace(1) global i32 0, align 4, !some.md !0, !num.md !1
define spir_func i32 @fn(i32 %x) "my-attr"="val" nounwind {
ret i32 %x
}
+; @ae has no payload, so no attribute/metadata records are emitted for it.
+; CHECK-NOT: OpString "ae"
+
define available_externally spir_func i32 @ae(i32 %x) {
ret i32 %x
}
!0 = !{!"hello"}
+!1 = !{i32 5}
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll
index 20a700b2a773c..ab7e6f7eaa0e8 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll
@@ -7,15 +7,17 @@
; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
-; CHECK-DAG: %[[#GVName:]] = OpString "g"
; CHECK-DAG: %[[#Attr0:]] = OpString "flag"
; CHECK-DAG: %[[#Attr1LHS:]] = OpString "my-gv-attr"
; CHECK-DAG: %[[#Attr1RHS:]] = OpString "7"
+; The Target operand is the real OpVariable <id>, not an OpString name.
+; CHECK-DAG: OpName %[[#GV:]] "g"
+
; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
-; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GVName]] %[[#Attr0]]
-; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GVName]] %[[#Attr1LHS]] %[[#Attr1RHS]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GV]] %[[#Attr0]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GV]] %[[#Attr1LHS]] %[[#Attr1RHS]]
target triple = "spir64-unknown-unknown"
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll
index 191f510a04c06..3759ca362af67 100644
--- a/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll
@@ -10,12 +10,14 @@
; CHECK-DAG: %[[#MD0:]] = OpString "hi"
; CHECK-DAG: %[[#MD1:]] = OpString "there"
-; CHECK-DAG: %[[#GVName:]] = OpString "a"
; CHECK-DAG: %[[#MDName:]] = OpString "some.gv.md"
+; The Target operand is the real OpVariable <id>, not an OpString name.
+; CHECK-DAG: OpName %[[#GV:]] "a"
+
; CHECK-DAG: %[[#VoidT:]] = OpTypeVoid
-; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GVName]] %[[#MDName]] %[[#MD0]] %[[#MD1]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GV]] %[[#MDName]] %[[#MD0]] %[[#MD1]]
target triple = "spir64-unknown-unknown"
diff --git a/llvm/test/CodeGen/SPIRV/linkage/available-externally-global.ll b/llvm/test/CodeGen/SPIRV/linkage/available-externally-global.ll
new file mode 100644
index 0000000000000..93672265cbdd2
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/linkage/available-externally-global.ll
@@ -0,0 +1,33 @@
+; available_externally globals are emitted as definitions with Export linkage.
+; The original linkage is only preserved as a NonSemantic.AuxData annotation
+; when -spirv-preserve-auxdata is passed.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=NOAUX
+
+; NOAUX-NOT: NonSemantic.AuxData
+; NOAUX-DAG: OpName %[[#ae_gv:]] "ae_gv"
+; NOAUX-DAG: OpDecorate %[[#ae_gv]] LinkageAttributes "ae_gv" Export
+; NOAUX: %[[#ae_gv]] = OpVariable
+
+; With -spirv-preserve-auxdata: Export linkage plus a NonSemantic.AuxData
+; Linkage annotation recording the original available_externally linkage.
+
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info --spirv-preserve-auxdata %s -o - | FileCheck %s --check-prefix=AUX
+
+; AUX-DAG: %[[#auxset:]] = OpExtInstImport "NonSemantic.AuxData"
+; AUX-DAG: OpName %[[#ae_gv:]] "ae_gv"
+; AUX-DAG: OpDecorate %[[#ae_gv]] LinkageAttributes "ae_gv" Export
+; AUX-DAG: %[[#void:]] = OpTypeVoid
+; AUX-DAG: %[[#i32:]] = OpTypeInt 32 0
+; AUX-DAG: %[[#zero:]] = OpConstant %[[#i32]] 0
+; AUX-DAG: %[[#ae_gv]] = OpVariable
+; AUX: %[[#]] = OpExtInst %[[#void]] %[[#auxset]] {{.+}} %[[#ae_gv]] %[[#zero]]
+
+ at ae_gv = available_externally addrspace(1) global i32 42, align 4
+
+define spir_kernel void @caller(ptr addrspace(1) %out) {
+entry:
+ %v = load i32, ptr addrspace(1) @ae_gv, align 4
+ store i32 %v, ptr addrspace(1) %out, align 4
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/linkage/linkage-types.ll b/llvm/test/CodeGen/SPIRV/linkage/linkage-types.ll
index dbe86ecac9c4b..1fd432cc3b37c 100644
--- a/llvm/test/CodeGen/SPIRV/linkage/linkage-types.ll
+++ b/llvm/test/CodeGen/SPIRV/linkage/linkage-types.ll
@@ -54,7 +54,8 @@
; SPIRV-DAG: OpName %[[#g:]] "g"
; SPIRV-DAG: OpName %[[#inline_fun:]] "inline_fun"
-; SPIRV-DAG: OpDecorate %[[#ae]] LinkageAttributes "ae" Import
+; available_externally is emitted as a definition with Export linkage.
+; SPIRV-DAG: OpDecorate %[[#ae]] LinkageAttributes "ae" Export
; SPIRV-DAG: OpDecorate %[[#e]] LinkageAttributes "e" Import
; SPIRV-DAG: OpDecorate %[[#f]] LinkageAttributes "f" Export
; SPIRV-DAG: OpDecorate %[[#w]] LinkageAttributes "w" Export
More information about the llvm-commits
mailing list