LLVM 24.0.0git
SPIRVAuxDataHandler.cpp
Go to the documentation of this file.
1//===-- SPIRVAuxDataHandler.cpp - NonSemantic.AuxData emitter -*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "SPIRVSubtarget.h"
12#include "SPIRVUtils.h"
14#include "llvm/IR/Attributes.h"
15#include "llvm/IR/Constants.h"
16#include "llvm/IR/Function.h"
19#include "llvm/IR/LLVMContext.h"
20#include "llvm/IR/Metadata.h"
21#include "llvm/IR/Module.h"
22#include "llvm/MC/MCInst.h"
23#include "llvm/MC/MCStreamer.h"
27
28using namespace llvm;
29
31 "spirv-preserve-auxdata",
32 cl::desc("Preserve LLVM attributes and metadata as "
33 "NonSemantic.AuxData ExtInst annotations (requires "
34 "SPV_KHR_non_semantic_info)"),
35 cl::Hidden, cl::init(false));
36
37namespace {
38enum AuxDataLinkageType : uint32_t {
39 AvailableExternally = 0,
40};
41
42constexpr unsigned NonSemanticAuxDataSet =
43 static_cast<unsigned>(SPIRV::InstructionSet::NonSemantic_AuxData);
44
45AttributeSet getGOAttrs(const GlobalObject *GO) {
46 if (const auto *F = dyn_cast<Function>(GO))
47 return F->getAttributes().getFnAttrs();
48 return cast<GlobalVariable>(GO)->getAttributes();
49}
50} // namespace
51
52static bool wasAvailableExternally(const GlobalObject *GO) {
53 if (const auto *F = dyn_cast<Function>(GO))
54 return F->hasFnAttribute(SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR);
55 return cast<GlobalVariable>(GO)->getAttributes().hasAttribute(
57}
58
60 : Asm(AP), Mod(M) {
61 for (const GlobalObject &GO : M.global_objects())
63 LinkagePreservedGOs.push_back(&GO);
64}
65
67 return SPVPreserveAuxData || TT.getVendor() == Triple::VendorType::AMD;
68}
69
72 if (!spirvPreserveAuxData(Mod.getTargetTriple()))
73 return;
74 if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_non_semantic_info)) {
76 report_fatal_error("-spirv-preserve-auxdata requires the "
77 "SPV_KHR_non_semantic_info extension to be enabled.");
78 return;
79 }
80 MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info);
81 if (!MAI.ExtInstSetMap.contains(NonSemanticAuxDataSet))
82 MAI.ExtInstSetMap[NonSemanticAuxDataSet] = MAI.getNextIDRegister();
83
84 // Instruction metadata forward-references its target, which requires
85 // SPV_KHR_relaxed_extended_instruction; drop it if unavailable.
86 if (MAI.InstrAuxDataRecords.empty())
87 return;
88 if (ST.canUseExtension(
89 SPIRV::Extension::SPV_KHR_relaxed_extended_instruction))
91 SPIRV::Extension::SPV_KHR_relaxed_extended_instruction);
92 else
93 MAI.InstrAuxDataRecords.clear();
94}
95
97SPIRVAuxDataHandler::getOrEmitString(StringRef S,
99 auto [It, Inserted] = StringRegs.try_emplace(S);
100 if (!Inserted)
101 return It->second;
102 MCRegister Reg = MAI.getNextIDRegister();
103 It->second = Reg;
104 MCInst Inst;
105 Inst.setOpcode(SPIRV::OpString);
107 addStringImm(S, Inst);
108 emitMCInst(Inst);
109 return Reg;
110}
111
112void SPIRVAuxDataHandler::collectAttributesFor(const GlobalObject *GO,
116 for (const Attribute &A : getGOAttrs(GO)) {
117 if (A.isStringAttribute() &&
118 A.getKindAsString() == SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR)
119 continue;
120 ExtInstRecord Rec;
121 Rec.Opcode = Opcode;
122 Rec.Target = GO;
123 if (A.isStringAttribute()) {
124 Rec.Operands.push_back({getOrEmitString(A.getKindAsString(), MAI)});
125 StringRef Val = A.getValueAsString();
126 if (!Val.empty())
127 Rec.Operands.push_back({getOrEmitString(Val, MAI)});
128 } else {
129 Rec.Operands.push_back(
130 {getOrEmitString(StringPool.save(A.getAsString()), MAI)});
131 }
132 PendingRecords.push_back(std::move(Rec));
133 }
134}
135
136void SPIRVAuxDataHandler::collectMetadataFor(const GlobalObject *GO,
137 ArrayRef<StringRef> MDNames,
140 GO->getAllMetadata(AllMD);
141 if (AllMD.empty())
142 return;
143 AuxDataOpcode Opcode =
145 // MDString operands become OpStrings; ValueAsMetadata constants (e.g.
146 // !{i32 5}) become OpConstants emitted at section 10. Any other operand
147 // kind would need full value translation, so skip the whole node.
148 auto CollectOperands =
149 [&](MDNode *MD) -> std::optional<SmallVector<Operand, 4>> {
150 SmallVector<Operand, 4> Out;
151 for (const MDOperand &MdOp : MD->operands()) {
152 Metadata *Md = MdOp.get();
153 if (auto *MDStr = dyn_cast_or_null<MDString>(Md)) {
154 Out.push_back({getOrEmitString(MDStr->getString(), MAI)});
155 } else if (auto *VAM = dyn_cast_or_null<ValueAsMetadata>(Md)) {
156 auto *C = dyn_cast<Constant>(VAM->getValue());
157 if (!C || !(isa<ConstantInt>(C) || isa<ConstantFP>(C)))
158 return std::nullopt;
159 Out.push_back({MCRegister(), C});
160 } else {
161 return std::nullopt;
162 }
163 }
164 return Out;
165 };
166 for (const auto &MD : AllMD) {
167 if (MD.first == LLVMContext::MD_dbg)
168 continue;
169 StringRef MDName = MDNames[MD.first];
170 if (MDName == "spirv.Decorations" || MDName == "spirv.ParameterDecorations")
171 continue;
172 auto Operands = CollectOperands(MD.second);
173 if (!Operands)
174 continue;
175 ExtInstRecord Rec;
176 Rec.Opcode = Opcode;
177 Rec.Target = GO;
178 Rec.Operands.push_back({getOrEmitString(MDName, MAI)});
179 Rec.Operands.append(Operands->begin(), Operands->end());
180 PendingRecords.push_back(std::move(Rec));
181 }
182}
183
185 if (!MAI.getExtInstSetReg(NonSemanticAuxDataSet).isValid())
186 return;
187 // One OpString per metadata kind; stop scanning once every kind is seen.
188 constexpr unsigned AllMDKindsSeen =
189 (1u << (static_cast<unsigned>(
191 1)) -
192 1;
193 unsigned SeenMask = 0;
194 for (const auto &Rec : MAI.InstrAuxDataRecords) {
195 unsigned Bit = 1u << static_cast<unsigned>(Rec.Kind);
196 if (SeenMask & Bit)
197 continue;
198 SeenMask |= Bit;
199 getOrEmitString(MAI.getAMDGPUAtomicMDName(Rec.Kind), MAI);
200 if (SeenMask == AllMDKindsSeen)
201 break;
202 }
203 // Global object attributes/metadata need -spirv-preserve-auxdata; the AMD
204 // triple alone doesn't enable them.
206 return;
208 Mod.getContext().getMDKindNames(MDNames);
209 for (const GlobalObject &GO : Mod.global_objects()) {
210 if (GO.isDeclaration())
211 continue;
212 collectAttributesFor(&GO, MAI);
213 collectMetadataFor(&GO, MDNames, MAI);
214 }
215}
216
218 MCRegister ExtSetReg = MAI.getExtInstSetReg(NonSemanticAuxDataSet);
219 if (!ExtSetReg.isValid())
220 return;
221
222 MCRegister VoidTypeReg = findOrEmitOpTypeVoid(MAI);
223
224 for (const ExtInstRecord &Rec : PendingRecords) {
225 MCRegister TargetReg = MAI.getGlobalObjReg(Rec.Target);
226 if (!TargetReg.isValid())
227 continue;
229 Operands.push_back(TargetReg);
230 for (const Operand &Op : Rec.Operands)
231 Operands.push_back(Op.Const ? emitConstant(Op.Const, MAI) : Op.Reg);
232 emitAuxDataExtInst(Rec.Opcode, VoidTypeReg, ExtSetReg, Operands, MAI);
233 }
234
235 for (const auto &Rec : MAI.InstrAuxDataRecords) {
236 MCRegister TargetReg = MAI.getRegisterAlias(Rec.MF, Rec.TargetReg);
237 if (!TargetReg.isValid())
238 continue;
239 MCRegister MDNameReg =
240 getOrEmitString(MAI.getAMDGPUAtomicMDName(Rec.Kind), MAI);
241 // TargetReg is defined in a function body, emitted after this section, so
242 // it is always a forward reference.
243 emitAuxDataExtInst(InstructionMetadataOpcode, VoidTypeReg, ExtSetReg,
244 {TargetReg, MDNameReg}, MAI, /*UseForwardRefs=*/true);
245 }
246
247 if (LinkagePreservedGOs.empty())
248 return;
249
250 MCRegister UInt32TypeReg = findOrEmitOpTypeUInt32(MAI);
251 MCRegister AEConstReg;
252 for (const GlobalObject *GO : LinkagePreservedGOs) {
253 MCRegister TargetReg = MAI.getGlobalObjReg(GO);
254 if (!TargetReg.isValid())
255 continue;
256 if (!AEConstReg.isValid())
257 AEConstReg =
258 emitOpConstantUInt32(AvailableExternally, UInt32TypeReg, MAI);
259 emitAuxDataExtInst(LinkageOpcode, VoidTypeReg, ExtSetReg,
260 {TargetReg, AEConstReg}, MAI);
261 }
262}
263
264void SPIRVAuxDataHandler::emitAuxDataExtInst(AuxDataOpcode Opcode,
265 MCRegister VoidTypeReg,
266 MCRegister ExtSetReg,
269 bool UseForwardRefs) {
270 MCInst Inst;
271 Inst.setOpcode(UseForwardRefs ? SPIRV::OpExtInstWithForwardRefsKHR
272 : SPIRV::OpExtInst);
274 Inst.addOperand(MCOperand::createReg(VoidTypeReg));
275 Inst.addOperand(MCOperand::createReg(ExtSetReg));
276 Inst.addOperand(MCOperand::createImm(Opcode));
277 for (MCRegister R : Operands)
279 emitMCInst(Inst);
280}
281
282void SPIRVAuxDataHandler::emitMCInst(MCInst &Inst) {
283 Asm.OutStreamer->emitInstruction(Inst, Asm.getSubtargetInfo());
284}
285
287SPIRVAuxDataHandler::findOrEmitOpTypeVoid(SPIRV::ModuleAnalysisInfo &MAI) {
288 for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
289 if (MI->getOpcode() == SPIRV::OpTypeVoid)
290 return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
291 MCRegister Reg = MAI.getNextIDRegister();
292 MCInst Inst;
293 Inst.setOpcode(SPIRV::OpTypeVoid);
295 emitMCInst(Inst);
296 return Reg;
297}
298
300SPIRVAuxDataHandler::findOrEmitOpTypeInt(unsigned BitWidth,
302 // SPIR-V OpTypeInt: <width>, <signedness>. Signedness 0 = unsigned, 1 =
303 // signed; we always emit unsigned.
304 constexpr int64_t UnsignedSignedness = 0;
305 for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
306 if (MI->getOpcode() == SPIRV::OpTypeInt &&
307 MI->getOperand(1).getImm() == static_cast<int64_t>(BitWidth) &&
308 MI->getOperand(2).getImm() == UnsignedSignedness)
309 return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
310 MCRegister Reg = MAI.getNextIDRegister();
311 MCInst Inst;
312 Inst.setOpcode(SPIRV::OpTypeInt);
315 Inst.addOperand(MCOperand::createImm(UnsignedSignedness));
316 emitMCInst(Inst);
317 return Reg;
318}
319
321SPIRVAuxDataHandler::findOrEmitOpTypeUInt32(SPIRV::ModuleAnalysisInfo &MAI) {
322 return findOrEmitOpTypeInt(32, MAI);
323}
324
326SPIRVAuxDataHandler::findOrEmitOpTypeFloat(unsigned BitWidth,
328 for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars))
329 if (MI->getOpcode() == SPIRV::OpTypeFloat &&
330 MI->getOperand(1).getImm() == static_cast<int64_t>(BitWidth))
331 return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg());
332 MCRegister Reg = MAI.getNextIDRegister();
333 MCInst Inst;
334 Inst.setOpcode(SPIRV::OpTypeFloat);
337 emitMCInst(Inst);
338 return Reg;
339}
340
341MCRegister SPIRVAuxDataHandler::emitConstant(const Constant *C,
343 auto [It, Inserted] = ConstantRegs.try_emplace(C);
344 if (!Inserted)
345 return It->second;
346
347 APInt Bits;
348 unsigned Opcode;
349 MCRegister TypeReg;
350 if (const auto *CI = dyn_cast<ConstantInt>(C)) {
351 Bits = CI->getValue();
352 Opcode = SPIRV::OpConstantI;
353 TypeReg = findOrEmitOpTypeInt(Bits.getBitWidth(), MAI);
354 } else {
355 const auto *CF = cast<ConstantFP>(C);
356 Bits = CF->getValueAPF().bitcastToAPInt();
357 Opcode = SPIRV::OpConstantF;
358 TypeReg = findOrEmitOpTypeFloat(Bits.getBitWidth(), MAI);
359 }
360
361 MCRegister Reg = MAI.getNextIDRegister();
362 It->second = Reg;
363 MCInst Inst;
364 Inst.setOpcode(Opcode);
366 Inst.addOperand(MCOperand::createReg(TypeReg));
367 // SPIR-V encodes the literal as ceil(width/32) little-endian 32-bit words.
368 unsigned NumWords = divideCeil(Bits.getBitWidth(), 32);
369 for (unsigned I = 0; I < NumWords; ++I)
370 Inst.addOperand(MCOperand::createImm(Bits.extractBitsAsZExtValue(
371 std::min(32u, Bits.getBitWidth() - I * 32), I * 32)));
372 // The asm printer needs this hint to render an f16 literal correctly.
373 if (Opcode == SPIRV::OpConstantF && Bits.getBitWidth() == 16)
375 emitMCInst(Inst);
376 return Reg;
377}
378
379MCRegister SPIRVAuxDataHandler::emitOpConstantUInt32(
380 uint32_t Value, MCRegister UInt32TypeReg, SPIRV::ModuleAnalysisInfo &MAI) {
381 MCRegister Reg = MAI.getNextIDRegister();
382 MCInst Inst;
383 Inst.setOpcode(SPIRV::OpConstantI);
385 Inst.addOperand(MCOperand::createReg(UInt32TypeReg));
386 Inst.addOperand(MCOperand::createImm(static_cast<int64_t>(Value)));
387 emitMCInst(Inst);
388 return Reg;
389}
This file contains the simple types necessary to represent the attributes associated with functions a...
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
dxil translate DXIL Translate Metadata
IRTranslator LLVM IR MI
Module.h This file contains the declarations for the Module class.
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
Register Reg
This file contains the declarations for metadata subclasses.
if(PassOpts->AAPipeline)
SI Fold Operands
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::Hidden, cl::init(false))
static bool wasAvailableExternally(const GlobalObject *GO)
#define SPIRV_WAS_AVAILABLE_EXTERNALLY_ATTR
Definition SPIRVUtils.h:566
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
This class is intended to be used as a driving class for all asm writers.
Definition AsmPrinter.h:95
std::unique_ptr< MCStreamer > OutStreamer
This is the MCStreamer object for the file we are generating.
Definition AsmPrinter.h:110
const MCSubtargetInfo & getSubtargetInfo() const
Return information about subtarget.
This class holds the attributes for a particular argument, parameter, function, or return value.
Definition Attributes.h:410
Functions, function parameters, and return types can have attributes to indicate how they should be t...
Definition Attributes.h:106
This is an important base class in LLVM.
Definition Constant.h:43
LLVM_ABI void getAllMetadata(SmallVectorImpl< std::pair< unsigned, MDNode * > > &MDs) const
Appends all metadata attached to this value to MDs, sorting by KindID.
LLVM_ABI bool isDeclaration() const
Return true if the primary definition of this global value is outside of the current translation unit...
Definition Globals.cpp:408
Instances of this class represent a single low-level machine instruction.
Definition MCInst.h:188
void setFlags(unsigned F)
Definition MCInst.h:204
void addOperand(const MCOperand Op)
Definition MCInst.h:215
void setOpcode(unsigned Op)
Definition MCInst.h:201
static MCOperand createReg(MCRegister Reg)
Definition MCInst.h:138
static MCOperand createImm(int64_t Val)
Definition MCInst.h:145
Wrapper class representing physical registers. Should be passed by value.
Definition MCRegister.h:41
constexpr bool isValid() const
Definition MCRegister.h:84
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
SPIRVAuxDataHandler(AsmPrinter &AP, const Module &M)
void emitAuxDataStrings(SPIRV::ModuleAnalysisInfo &MAI)
Emit OpStrings and stage ExtInst records; call in module section 7.
void emitAuxData(SPIRV::ModuleAnalysisInfo &MAI)
Emit the staged ExtInst records; call in module section 10.
void prepareModuleOutput(const SPIRVSubtarget &ST, SPIRV::ModuleAnalysisInfo &MAI)
Register extension + ext-inst-set; call before output of section 1.
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
constexpr bool empty() const
Check if the string is empty.
Definition StringRef.h:141
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
LLVM Value Representation.
Definition Value.h:75
initializer< Ty > init(const Ty &Val)
This is an optimization pass for GlobalISel generic memory operations.
void addStringImm(StringRef Str, MCInst &Inst)
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
@ GlobalVariableMetadataOpcode
@ FunctionAttributeOpcode
@ GlobalVariableAttributeOpcode
@ InstructionMetadataOpcode
@ FunctionMetadataOpcode
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
bool spirvPreserveAuxData(const Triple &TT)
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
constexpr T divideCeil(U Numerator, V Denominator)
Returns the integer ceil(Numerator / Denominator).
Definition MathExtras.h:389
DWARFExpression::Operation Op
constexpr unsigned BitWidth
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
MCRegister getExtInstSetReg(unsigned SetNum)
DenseMap< unsigned, MCRegister > ExtInstSetMap
InstrList & getMSInstrs(unsigned MSType)
MCRegister getRegisterAlias(const MachineFunction *MF, Register Reg)
SmallVector< InstrAuxDataRecord > InstrAuxDataRecords
static StringRef getAMDGPUAtomicMDName(AMDGPUAtomicMDKind Kind)
MCRegister getGlobalObjReg(const GlobalObject *GO)
void addExtension(Extension::Extension ToAdd)