[llvm] b1880a0 - [SPIR-V] Add NonSemantic.AuxData emission (-spirv-preserve-auxdata) (#200002)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 9 05:58:55 PDT 2026
Author: Arseniy Obolenskiy
Date: 2026-06-09T14:58:50+02:00
New Revision: b1880a09469217aab192ded75643f860fb3d55a8
URL: https://github.com/llvm/llvm-project/commit/b1880a09469217aab192ded75643f860fb3d55a8
DIFF: https://github.com/llvm/llvm-project/commit/b1880a09469217aab192ded75643f860fb3d55a8.diff
LOG: [SPIR-V] Add NonSemantic.AuxData emission (-spirv-preserve-auxdata) (#200002)
Add a NonSemantic.AuxData extended instruction set to the SPIR-V
backend, matching SPIRV-LLVM-Translator's --spirv-preserve-auxdata wire
format
- `-spirv-preserve-auxdata` emits LLVM attributes and metadata as
NonSemantic.AuxData records (requires SPV_KHR_non_semantic_info).
- `available_externally` functions keep their linkage via an AuxData
linkage record, emitted unconditionally.
Added:
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll
llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata-debug.ll
llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll
llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata-requires-extension.ll
llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll
llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll
llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
llvm/test/CodeGen/SPIRV/linkage/available-externally-global.ll
Modified:
llvm/lib/Target/SPIRV/CMakeLists.txt
llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVBaseInfo.cpp
llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
llvm/lib/Target/SPIRV/SPIRVBuiltins.td
llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
llvm/lib/Target/SPIRV/SPIRVUtils.h
llvm/test/CodeGen/SPIRV/linkage/linkage-types.ll
Removed:
################################################################################
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 1b825698be2b3..befe8e3fda4f2 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);
};
@@ -344,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) {
@@ -868,10 +873,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
@@ -908,6 +921,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..d60b204620720
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.cpp
@@ -0,0 +1,343 @@
+//===-- 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/Attributes.h"
+#include "llvm/IR/Constants.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"
+#include "llvm/Support/ErrorHandling.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 {
+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
+
+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 GlobalObject &GO : M.global_objects())
+ if (wasAvailableExternally(&GO))
+ LinkagePreservedGOs.push_back(&GO);
+}
+
+bool SPIRVAuxDataHandler::hasWork() const { return SPVPreserveAuxData; }
+
+void SPIRVAuxDataHandler::prepareModuleOutput(const SPIRVSubtarget &ST,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ if (!hasWork())
+ return;
+ 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();
+}
+
+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,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ AuxDataOpcode Opcode = isa<Function>(GO) ? FunctionAttributeOpcode
+ : GlobalVariableAttributeOpcode;
+ for (const Attribute &A : getGOAttrs(GO)) {
+ if (A.isStringAttribute() &&
+ A.getKindAsString() == SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR)
+ continue;
+ ExtInstRecord Rec;
+ Rec.Opcode = Opcode;
+ Rec.Target = GO;
+ 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,
+ 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;
+ // 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()) {
+ 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;
+ }
+ }
+ 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 = CollectOperands(MD.second);
+ if (!Operands)
+ continue;
+ ExtInstRecord Rec;
+ Rec.Opcode = Opcode;
+ Rec.Target = GO;
+ 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;
+ collectAttributesFor(&GO, MAI);
+ collectMetadataFor(&GO, MDNames, MAI);
+ }
+}
+
+void SPIRVAuxDataHandler::emitAuxData(SPIRV::ModuleAnalysisInfo &MAI) {
+ MCRegister ExtSetReg = MAI.getExtInstSetReg(NonSemanticAuxDataSet);
+ if (!ExtSetReg.isValid())
+ return;
+
+ MCRegister VoidTypeReg = findOrEmitOpTypeVoid(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 (LinkagePreservedGOs.empty())
+ return;
+
+ MCRegister UInt32TypeReg = findOrEmitOpTypeUInt32(MAI);
+ MCRegister AEConstReg;
+ 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,
+ {TargetReg, AEConstReg}, MAI);
+ }
+}
+
+void SPIRVAuxDataHandler::emitAuxDataExtInst(AuxDataOpcode 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(Opcode));
+ for (MCRegister R : Operands)
+ Inst.addOperand(MCOperand::createReg(R));
+ 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::findOrEmitOpTypeInt(unsigned BitWidth,
+ SPIRV::ModuleAnalysisInfo &MAI) {
+ // SPIR-V OpTypeInt: <width>, <signedness>. Signedness 0 = unsigned, 1 =
+ // 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() == 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(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();
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpConstantI);
+ Inst.addOperand(MCOperand::createReg(Reg));
+ 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
new file mode 100644
index 0000000000000..1d6ba998e6c2e
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVAuxDataHandler.h
@@ -0,0 +1,116 @@
+//===-- 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 (mirrors SPIRV-LLVM-Translator
+// --spirv-preserve-auxdata).
+// Otherwise, AE-tagged functions emit plain Export linkage via
+// LinkageAttributes.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVAUXDATAHANDLER_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVAUXDATAHANDLER_H
+
+#include "SPIRVModuleAnalysis.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.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 Constant;
+class Function;
+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);
+
+ bool hasWork() const;
+
+ /// Register extension + ext-inst-set; call before output of section 1.
+ void prepareModuleOutput(const SPIRVSubtarget &ST,
+ SPIRV::ModuleAnalysisInfo &MAI);
+
+ /// 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:
+ // 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;
+ const GlobalObject *Target;
+ SmallVector<Operand, 4> Operands;
+ };
+
+ AsmPrinter &Asm;
+ const Module &Mod;
+
+ SmallVector<const GlobalObject *> LinkagePreservedGOs;
+
+ // Backing storage for non-string-attribute strings; StringRegs keys are
+ // StringRefs into it.
+ BumpPtrAllocator StringAlloc;
+ 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,
+ SPIRV::ModuleAnalysisInfo &MAI);
+ 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);
+};
+
+} // 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/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index ae3dca706cb08..29fb4d1d9ec04 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -829,7 +829,24 @@ 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.
+ 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.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index dd74d4c7476e5..44abdd1271bb3 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -541,6 +541,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/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..ec15eb6c1e080
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-attributes.ll
@@ -0,0 +1,34 @@
+; 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: %[[#Attr0:]] = OpString "foo"
+; 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]]
+; 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..13465a374c652
--- /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: %[[#Attr:]] = OpString "keep-me"
+; CHECK-DAG: OpName %[[#Fcn:]] "foo"
+; 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..43ed97b48a20d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-all-function-metadata.ll
@@ -0,0 +1,38 @@
+; Adapted from the SPIRV-LLVM-Translator --spirv-preserve-auxdata forward-path
+; 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 \
+; RUN: %s -o - | FileCheck %s
+
+; CHECK: %[[#Import:]] = OpExtInstImport "NonSemantic.AuxData"
+
+; CHECK-DAG: %[[#MDNameVal:]] = OpString "foo"
+; CHECK-DAG: %[[#MDName:]] = OpString "bar"
+; CHECK-DAG: %[[#MDValue:]] = OpString "baz"
+
+; The Target operand is the real OpFunction <id>, not an OpString name.
+; CHECK-DAG: OpName %[[#Fcn0:]] "test_val"
+; CHECK-DAG: OpName %[[#Fcn1:]] "test_string"
+
+; 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"
+
+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
new file mode 100644
index 0000000000000..d84f95bf7c70c
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-auxdata.ll
@@ -0,0 +1,57 @@
+; -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: 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"
+; OFF-NOT: OpString "spv.was-available-externally"
+
+; CHECK-DAG: %[[#auxset:]] = OpExtInstImport "NonSemantic.AuxData"
+; CHECK-DAG: %[[#akind:]] = OpString "my-attr"
+; CHECK-DAG: %[[#aval:]] = OpString "val"
+; CHECK-DAG: %[[#nounwind:]] = OpString "nounwind"
+; 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"
+
+; 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 = 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
new file mode 100644
index 0000000000000..ab7e6f7eaa0e8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-attributes.ll
@@ -0,0 +1,30 @@
+; 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: %[[#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]] {{.+}} %[[#GV]] %[[#Attr0]]
+; CHECK-DAG: %[[#]] = OpExtInst %[[#VoidT]] %[[#Import]] {{.+}} %[[#GV]] %[[#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..3759ca362af67
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_KHR_non_semantic_info/preserve-gv-metadata.ll
@@ -0,0 +1,30 @@
+; 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: %[[#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]] {{.+}} %[[#GV]] %[[#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"}
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..ed7e7e146cf42
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/linkage/available-externally-function.ll
@@ -0,0 +1,58 @@
+; 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-DAG: OpDecorate %[[#ae_func]] LinkageAttributes "ae_func" Export
+; NOAUX: %[[#ae_func]] = OpFunction
+; NOAUX: OpFunctionEnd
+; NOAUX: %[[#caller]] = OpFunction
+; NOAUX: OpFunctionCall %[[#]] %[[#ae_func]]
+
+; 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 --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.
+
+; 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-DAG: OpDecorate %[[#ae_func]] LinkageAttributes "ae_func" Export
+; 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:
+ %r = add i32 %x, 1
+ ret i32 %r
+}
+
+define spir_kernel void @caller(ptr addrspace(1) %out, i32 %n) {
+entry:
+ %v = call spir_func i32 @ae_func(i32 %n)
+ store i32 %v, ptr addrspace(1) %out, align 4
+ ret void
+}
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