[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