[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:54 PDT 2026


================
@@ -0,0 +1,268 @@
+//===-- 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/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
+
+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);
+}
+
+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, function_ref<MCRegister()> GetNameReg,
+    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.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;
+  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;
+    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);
+
+  for (const ExtInstRecord &Rec : PendingRecords)
+    emitAuxDataExtInst(Rec.Opcode, VoidTypeReg, ExtSetReg, Rec.Operands, MAI);
+
+  if (LinkagePreservedFns.empty())
+    return;
+
+  MCRegister UInt32TypeReg = findOrEmitOpTypeUInt32(MAI);
+  MCRegister AEConstReg;
+  for (const Function *F : LinkagePreservedFns) {
+    MCRegister FnReg = MAI.getGlobalObjReg(F);
----------------
aobolensk wrote:

Fixed `<id>`s 

https://github.com/llvm/llvm-project/pull/200002


More information about the llvm-commits mailing list