1//===-- SPIRVNonSemanticDebugHandler.cpp - NSDI AsmPrinter handler -*- C++
2//-*-===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#include "SPIRVNonSemanticDebugHandler.h"
11#include "MCTargetDesc/SPIRVMCTargetDesc.h"
12#include "SPIRVSubtarget.h"
13#include "SPIRVUtils.h"
14#include "llvm/ADT/SmallVectorExtras.h"
15#include "llvm/BinaryFormat/Dwarf.h"
16#include "llvm/CodeGen/AsmPrinter.h"
17#include "llvm/IR/DebugInfo.h"
18#include "llvm/IR/DebugInfoMetadata.h"
19#include "llvm/IR/GlobalVariable.h"
20#include "llvm/IR/Module.h"
21#include "llvm/MC/MCInst.h"
22#include "llvm/MC/MCStreamer.h"
23#include "llvm/Support/ErrorHandling.h"
24#include "llvm/Support/Path.h"
25#include <cassert>
26
27using namespace llvm;
28
29namespace {
30
31/// Look up \p Key in a register map and return its value, or std::nullopt when
32/// the key is absent.
33template <typename MapT>
34static std::optional<MCRegister> lookupOptReg(const MapT &Map,
35 typename MapT::key_type Key) {
36 auto It = Map.find(Key);
37 if (It == Map.end())
38 return std::nullopt;
39 assert(It->second.isValid() && "invalid register stored in map");
40 return It->second;
41}
42
43/// Partition \p Ty into \p BasicTypes, \p PointerTypes, \p SubroutineTypes,
44/// and \p VectorTypes for NSDI emission. Used when iterating
45/// DebugInfoFinder.types(); each DI node is seen once, so no recursion into
46/// pointer bases. Other composites and non-pointer derived kinds are ignored
47/// because they are not yet supported. Only types that are supported (later
48/// used) are partitioned.
49static void
50partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes,
51 SmallVector<const DIDerivedType *> &PointerTypes,
52 SmallVector<const DISubroutineType *> &SubroutineTypes,
53 SmallVector<const DICompositeType *> &VectorTypes) {
54 if (const auto *BT = dyn_cast<DIBasicType>(Val: Ty)) {
55 BasicTypes.push_back(Elt: BT);
56 return;
57 }
58 if (const auto *ST = dyn_cast<DISubroutineType>(Val: Ty)) {
59 SubroutineTypes.push_back(Elt: ST);
60 return;
61 }
62 if (const auto *CT = dyn_cast<DICompositeType>(Val: Ty)) {
63 if (CT->getTag() == dwarf::DW_TAG_array_type && CT->isVector())
64 VectorTypes.push_back(Elt: CT);
65 return;
66 }
67 const auto *DT = dyn_cast<DIDerivedType>(Val: Ty);
68 if (DT && DT->getTag() == dwarf::DW_TAG_pointer_type)
69 PointerTypes.push_back(Elt: DT);
70}
71
72enum : uint32_t {
73 NSDIFlagIsProtected = 1u << 0,
74 NSDIFlagIsPrivate = 1u << 1,
75 NSDIFlagIsPublic = NSDIFlagIsPrivate | NSDIFlagIsProtected,
76 NSDIFlagIsLocal = 1u << 2,
77 NSDIFlagIsDefinition = 1u << 3,
78 NSDIFlagFwdDecl = 1u << 4,
79 NSDIFlagArtificial = 1u << 5,
80 NSDIFlagExplicit = 1u << 6,
81 NSDIFlagPrototyped = 1u << 7,
82 NSDIFlagObjectPointer = 1u << 8,
83 NSDIFlagStaticMember = 1u << 9,
84 NSDIFlagIndirectVariable = 1u << 10,
85 NSDIFlagLValueReference = 1u << 11,
86 NSDIFlagRValueReference = 1u << 12,
87 NSDIFlagIsOptimized = 1u << 13,
88 NSDIFlagIsEnumClass = 1u << 14,
89 NSDIFlagTypePassByValue = 1u << 15,
90 NSDIFlagTypePassByReference = 1u << 16,
91 NSDIFlagUnknownPhysicalLayout = 1u << 17,
92};
93
94static uint32_t mapDIFlagsToNonSemantic(DINode::DIFlags DFlags) {
95 uint32_t Flags = 0;
96 if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPublic)
97 Flags |= NSDIFlagIsPublic;
98 if ((DFlags & DINode::FlagAccessibility) == DINode::FlagProtected)
99 Flags |= NSDIFlagIsProtected;
100 if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPrivate)
101 Flags |= NSDIFlagIsPrivate;
102 if (DFlags & DINode::FlagFwdDecl)
103 Flags |= NSDIFlagFwdDecl;
104 if (DFlags & DINode::FlagArtificial)
105 Flags |= NSDIFlagArtificial;
106 if (DFlags & DINode::FlagExplicit)
107 Flags |= NSDIFlagExplicit;
108 if (DFlags & DINode::FlagPrototyped)
109 Flags |= NSDIFlagPrototyped;
110 if (DFlags & DINode::FlagObjectPointer)
111 Flags |= NSDIFlagObjectPointer;
112 if (DFlags & DINode::FlagStaticMember)
113 Flags |= NSDIFlagStaticMember;
114 if (DFlags & DINode::FlagLValueReference)
115 Flags |= NSDIFlagLValueReference;
116 if (DFlags & DINode::FlagRValueReference)
117 Flags |= NSDIFlagRValueReference;
118 if (DFlags & DINode::FlagTypePassByValue)
119 Flags |= NSDIFlagTypePassByValue;
120 if (DFlags & DINode::FlagTypePassByReference)
121 Flags |= NSDIFlagTypePassByReference;
122 if (DFlags & DINode::FlagEnumClass)
123 Flags |= NSDIFlagIsEnumClass;
124 return Flags;
125}
126
127static uint32_t transDebugFlags(const DINode *DN) {
128 uint32_t Flags = 0;
129 if (const auto *GV = dyn_cast<DIGlobalVariable>(Val: DN)) {
130 if (GV->isLocalToUnit())
131 Flags |= NSDIFlagIsLocal;
132 if (GV->isDefinition())
133 Flags |= NSDIFlagIsDefinition;
134 }
135 if (const auto *SP = dyn_cast<DISubprogram>(Val: DN)) {
136 if (SP->isLocalToUnit())
137 Flags |= NSDIFlagIsLocal;
138 if (SP->isOptimized())
139 Flags |= NSDIFlagIsOptimized;
140 if (SP->isDefinition())
141 Flags |= NSDIFlagIsDefinition;
142 Flags |= mapDIFlagsToNonSemantic(DFlags: SP->getFlags());
143 }
144 if (DN->getTag() == dwarf::DW_TAG_reference_type)
145 Flags |= NSDIFlagLValueReference;
146 if (DN->getTag() == dwarf::DW_TAG_rvalue_reference_type)
147 Flags |= NSDIFlagRValueReference;
148 if (const auto *Ty = dyn_cast<DIType>(Val: DN))
149 Flags |= mapDIFlagsToNonSemantic(DFlags: Ty->getFlags());
150 if (const auto *LV = dyn_cast<DILocalVariable>(Val: DN))
151 Flags |= mapDIFlagsToNonSemantic(DFlags: LV->getFlags());
152 return Flags;
153}
154
155} // namespace
156
157SPIRVNonSemanticDebugHandler::SPIRVNonSemanticDebugHandler(AsmPrinter &AP)
158 : DebugHandlerBase(&AP) {}
159
160// Map DWARF source language codes to NonSemantic.Shader.DebugInfo.100 source
161// language codes. Values are from the SourceLanguage enum in the
162// NonSemantic.Shader.DebugInfo.100 specification, section 4.3.
163unsigned SPIRVNonSemanticDebugHandler::toNSDISrcLang(unsigned DwarfSrcLang) {
164 switch (DwarfSrcLang) {
165 case dwarf::DW_LANG_OpenCL:
166 return 3; // OpenCL_C
167 case dwarf::DW_LANG_OpenCL_CPP:
168 return 4; // OpenCL_CPP
169 case dwarf::DW_LANG_CPP_for_OpenCL:
170 return 6; // CPP_for_OpenCL
171 case dwarf::DW_LANG_GLSL:
172 return 2; // GLSL
173 case dwarf::DW_LANG_HLSL:
174 return 5; // HLSL
175 case dwarf::DW_LANG_SYCL:
176 return 7; // SYCL
177 case dwarf::DW_LANG_Zig:
178 return 12; // Zig
179 default:
180 return 0; // Unknown
181 }
182}
183
184void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
185 // The base class sets Asm = nullptr when the module has no compile units,
186 // and initializes lexical scope tracking otherwise.
187 DebugHandlerBase::beginModule(M);
188
189 if (!Asm)
190 return;
191
192 CompileUnits.clear();
193 BasicTypes.clear();
194 PointerTypes.clear();
195 SubroutineTypes.clear();
196 VectorTypes.clear();
197 SubprogramDeclarations.clear();
198 GlobalVariableDebugInfoMap.clear();
199 DebugFunctionDeclarationRegs.clear();
200 ScopeToPathOpStringReg.clear();
201 CUToCompilationUnitDbgReg.clear();
202 DebugSourceRegByFileStr.clear();
203 DebugTypeRegs.clear();
204 OpStringContentCache.clear();
205 I32ConstantCache.clear();
206 DebugTypeFunctionCache.clear();
207 GlobalDIEmitted = false;
208#ifndef NDEBUG
209 NonSemanticOpStringsSectionEmitted = false;
210#endif
211 CachedDebugInfoNoneReg = MCRegister();
212 CachedEmptyStringReg = MCRegister();
213 CachedOpTypeVoidReg = MCRegister();
214 CachedOpTypeInt32Reg = MCRegister();
215
216 // Collect compile-unit info: file paths and source languages.
217 for (const DICompileUnit *CU : M->debug_compile_units()) {
218 const DIFile *File = CU->getFile();
219 CompileUnitInfo Info;
220 Info.TheCU = CU;
221 if (sys::path::is_absolute(path: File->getFilename()))
222 Info.FilePath = File->getFilename();
223 else
224 sys::path::append(path&: Info.FilePath, a: File->getDirectory(),
225 b: File->getFilename());
226 // getName() returns the language code regardless of whether the name is
227 // versioned. getUnversionedName() would assert on versioned names.
228 Info.SpirvSourceLanguage = toNSDISrcLang(DwarfSrcLang: CU->getSourceLanguage().getName());
229 CompileUnits.push_back(Elt: std::move(Info));
230 }
231
232 // Collect DWARF version from module flags. For CodeView modules there is no
233 // "Dwarf Version" flag; DwarfVersion remains 0, which is the correct value
234 // for the DebugCompilationUnit DWARF Version operand in that case.
235 if (const NamedMDNode *Flags = M->getNamedMetadata(Name: "llvm.module.flags")) {
236 for (const auto *Op : Flags->operands()) {
237 const MDOperand &NameOp = Op->getOperand(I: 1);
238 if (NameOp.equalsStr(Str: "Dwarf Version"))
239 DwarfVersion =
240 cast<ConstantInt>(
241 Val: cast<ConstantAsMetadata>(Val: Op->getOperand(I: 2))->getValue())
242 ->getSExtValue();
243 }
244 }
245
246 // Find all debug info types that may be referenced by NSDI instructions.
247 DebugInfoFinder Finder;
248 Finder.processModule(M: *M);
249 llvm::for_each(Range: Finder.types(), F: [&](DIType *Ty) {
250 partitionTypes(Ty, BasicTypes, PointerTypes, SubroutineTypes, VectorTypes);
251 });
252
253 for (const DISubprogram *SP : Finder.subprograms()) {
254 if (!SP->isDefinition())
255 SubprogramDeclarations.push_back(Elt: SP);
256 }
257
258 // Walk LLVM globals to map each DIGlobalVariable to its llvm::GlobalVariable.
259 DenseMap<const DIGlobalVariable *, const GlobalVariable *> DIGVToLLVMGV;
260 for (const GlobalVariable &G : M->globals()) {
261 SmallVector<DIGlobalVariableExpression *> GVEs;
262 G.getDebugInfo(GVs&: GVEs);
263 for (DIGlobalVariableExpression *GVE : GVEs) {
264 if (const DIGlobalVariable *GV = GVE->getVariable()) {
265 DIGVToLLVMGV.try_emplace(Key: GV, Args: &G);
266 }
267 }
268 }
269
270 for (const DIGlobalVariableExpression *GVE : Finder.global_variables()) {
271 const DIGlobalVariable *GV = GVE->getVariable();
272 const DIExpression *Expr = GVE->getExpression();
273 GlobalVariableDebugInfoMap.try_emplace(
274 Key: GV, Args: GlobalVariableDebugInfo{.Expr: Expr, .LLVMGV: DIGVToLLVMGV.lookup(Val: GV)});
275 }
276}
277
278void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
279 const SPIRVSubtarget &ST, SPIRV::ModuleAnalysisInfo &MAI) {
280 if (CompileUnits.empty())
281 return;
282 if (!ST.canUseExtension(E: SPIRV::Extension::SPV_KHR_non_semantic_info))
283 return;
284
285 // Add the extension to requirements so OpExtension is output.
286 MAI.Reqs.addExtension(ToAdd: SPIRV::Extension::SPV_KHR_non_semantic_info);
287
288 // Add the NonSemantic.Shader.DebugInfo.100 entry to ExtInstSetMap so that
289 // outputOpExtInstImports() emits the OpExtInstImport instruction. Allocate a
290 // fresh result ID for it now; the same ID is used in emitExtInst() operands.
291 constexpr unsigned NSSet = static_cast<unsigned>(
292 SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100);
293 if (!MAI.ExtInstSetMap.count(Val: NSSet))
294 MAI.ExtInstSetMap[NSSet] = MAI.getNextIDRegister();
295}
296
297void SPIRVNonSemanticDebugHandler::emitMCInst(MCInst &Inst) {
298 Asm->OutStreamer->emitInstruction(Inst, STI: Asm->getSubtargetInfo());
299}
300
301MCRegister
302SPIRVNonSemanticDebugHandler::emitOpString(StringRef S,
303 SPIRV::ModuleAnalysisInfo &MAI) {
304 MCRegister Reg = MAI.getNextIDRegister();
305 MCInst Inst;
306 Inst.setOpcode(SPIRV::OpString);
307 Inst.addOperand(Op: MCOperand::createReg(Reg));
308 addStringImm(Str: S, Inst);
309 emitMCInst(Inst);
310 return Reg;
311}
312
313MCRegister SPIRVNonSemanticDebugHandler::emitOpStringIfNew(
314 StringRef S, SPIRV::ModuleAnalysisInfo &MAI) {
315#ifndef NDEBUG
316 assert(!NonSemanticOpStringsSectionEmitted &&
317 "emitOpStringIfNew is only valid while emitting SPIR-V section 7");
318#endif
319 auto [It, Inserted] = OpStringContentCache.try_emplace(Key: S, Args: MCRegister());
320 if (Inserted)
321 It->second = emitOpString(S, MAI);
322
323 return It->second;
324}
325
326MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) {
327#ifndef NDEBUG
328 assert(NonSemanticOpStringsSectionEmitted &&
329 "getCachedOpStringReg requires emitNonSemanticDebugStrings() first");
330#endif
331 auto It = OpStringContentCache.find(Key: S);
332 assert(It != OpStringContentCache.end() &&
333 "NSDI OpString missing from cache; emitNonSemanticDebugStrings must "
334 "cache every string used in section 10");
335 return It->second;
336}
337
338MCRegister SPIRVNonSemanticDebugHandler::getCachedScopePathOpStringReg(
339 const DIScope *Scope, bool UseEmptyPathIfNullScope) {
340 if (!Scope) {
341 assert(UseEmptyPathIfNullScope &&
342 "null scope path lookup requires UseEmptyPathIfNullScope");
343 assert(CachedEmptyStringReg.isValid() &&
344 "empty path OpString must be cached in emitNonSemanticDebugStrings");
345 return CachedEmptyStringReg;
346 }
347 auto It = ScopeToPathOpStringReg.find(Val: Scope);
348 assert(It != ScopeToPathOpStringReg.end() &&
349 "path OpString must be cached in emitNonSemanticDebugStrings");
350 MCRegister FileStrReg = It->second;
351 assert(FileStrReg.isValid() && "path OpString id must be valid once cached");
352 return FileStrReg;
353}
354
355MCRegister SPIRVNonSemanticDebugHandler::emitOpConstantI32(
356 uint32_t Value, MCRegister I32TypeReg, SPIRV::ModuleAnalysisInfo &MAI) {
357 auto [It, Inserted] = I32ConstantCache.try_emplace(Key: Value);
358 if (!Inserted)
359 return It->second;
360
361 MCRegister Reg = MAI.getNextIDRegister();
362 It->second = Reg;
363 MCInst Inst;
364 Inst.setOpcode(SPIRV::OpConstantI);
365 Inst.addOperand(Op: MCOperand::createReg(Reg));
366 Inst.addOperand(Op: MCOperand::createReg(Reg: I32TypeReg));
367 Inst.addOperand(Op: MCOperand::createImm(Val: static_cast<int64_t>(Value)));
368 emitMCInst(Inst);
369 return Reg;
370}
371
372MCRegister SPIRVNonSemanticDebugHandler::emitExtInst(
373 SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode,
374 MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
375 ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI) {
376 MCRegister Reg = MAI.getNextIDRegister();
377 MCInst Inst;
378 Inst.setOpcode(SPIRV::OpExtInst);
379 Inst.addOperand(Op: MCOperand::createReg(Reg));
380 Inst.addOperand(Op: MCOperand::createReg(Reg: VoidTypeReg));
381 Inst.addOperand(Op: MCOperand::createReg(Reg: ExtInstSetReg));
382 Inst.addOperand(Op: MCOperand::createImm(Val: static_cast<int64_t>(Opcode)));
383 for (MCRegister R : Operands)
384 Inst.addOperand(Op: MCOperand::createReg(Reg: R));
385 emitMCInst(Inst);
386 return Reg;
387}
388
389MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugTypeFunction(
390 ArrayRef<MCRegister> Ops, MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
391 SPIRV::ModuleAnalysisInfo &MAI) {
392 auto [It, Inserted] =
393 DebugTypeFunctionCache.try_emplace(Key: SmallVector<MCRegister, 8>(Ops));
394 if (!Inserted)
395 return It->second;
396
397 MCRegister Reg = emitExtInst(Opcode: SPIRV::NonSemanticExtInst::DebugTypeFunction,
398 VoidTypeReg, ExtInstSetReg, Operands: Ops, MAI);
399 It->second = Reg;
400 return Reg;
401}
402
403MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeVoidReg(
404 SPIRV::ModuleAnalysisInfo &MAI) {
405 if (!CachedOpTypeVoidReg.isValid())
406 CachedOpTypeVoidReg = findOrEmitOpTypeVoid(MAI);
407 return CachedOpTypeVoidReg;
408}
409
410MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeInt32Reg(
411 SPIRV::ModuleAnalysisInfo &MAI) {
412 if (!CachedOpTypeInt32Reg.isValid())
413 CachedOpTypeInt32Reg = findOrEmitOpTypeInt32(MAI);
414 return CachedOpTypeInt32Reg;
415}
416
417MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeVoid(
418 SPIRV::ModuleAnalysisInfo &MAI) {
419 for (const MachineInstr *MI : MAI.getMSInstrs(MSType: SPIRV::MB_TypeConstVars)) {
420 if (MI->getOpcode() == SPIRV::OpTypeVoid)
421 return MAI.getRegisterAlias(MF: MI->getMF(), Reg: MI->getOperand(i: 0).getReg());
422 }
423 MCRegister Reg = MAI.getNextIDRegister();
424 MCInst Inst;
425 Inst.setOpcode(SPIRV::OpTypeVoid);
426 Inst.addOperand(Op: MCOperand::createReg(Reg));
427 emitMCInst(Inst);
428 return Reg;
429}
430
431MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeInt32(
432 SPIRV::ModuleAnalysisInfo &MAI) {
433 for (const MachineInstr *MI : MAI.getMSInstrs(MSType: SPIRV::MB_TypeConstVars)) {
434 if (MI->getOpcode() == SPIRV::OpTypeInt &&
435 MI->getOperand(i: 1).getImm() == 32 && MI->getOperand(i: 2).getImm() == 0)
436 return MAI.getRegisterAlias(MF: MI->getMF(), Reg: MI->getOperand(i: 0).getReg());
437 }
438 MCRegister Reg = MAI.getNextIDRegister();
439 MCInst Inst;
440 Inst.setOpcode(SPIRV::OpTypeInt);
441 Inst.addOperand(Op: MCOperand::createReg(Reg));
442 Inst.addOperand(Op: MCOperand::createImm(Val: 32)); // width
443 Inst.addOperand(Op: MCOperand::createImm(Val: 0)); // signedness (unsigned)
444 emitMCInst(Inst);
445 return Reg;
446}
447
448std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypePointer(
449 const DIDerivedType *PT, MCRegister ExtInstSetReg,
450 SPIRV::ModuleAnalysisInfo &MAI) {
451 // A DWARF address space is required to determine the SPIR-V storage class.
452 // Skip pointer types that do not carry one.
453 if (!PT->getDWARFAddressSpace().has_value())
454 return std::nullopt;
455
456 MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
457 MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
458 MCRegister DebugTypePointerFlagsReg =
459 emitOpConstantI32(Value: transDebugFlags(DN: PT), I32TypeReg, MAI);
460
461 // For SPIR-V targets, Clang sets DwarfAddressSpace to the LLVM IR address
462 // space, which addressSpaceToStorageClass expects.
463 const auto &ST = static_cast<const SPIRVSubtarget &>(Asm->getSubtargetInfo());
464 MCRegister StorageClassReg = emitOpConstantI32(
465 Value: addressSpaceToStorageClass(AddrSpace: PT->getDWARFAddressSpace().value(), STI: ST),
466 I32TypeReg, MAI);
467
468 if (const DIType *BaseTy = PT->getBaseType()) {
469 auto BaseIt = DebugTypeRegs.find(Val: BaseTy);
470 if (BaseIt != DebugTypeRegs.end())
471 return emitExtInst(
472 Opcode: SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg,
473 ExtInstSetReg,
474 Operands: {BaseIt->second, StorageClassReg, DebugTypePointerFlagsReg}, MAI);
475 // Unsupported type, no DebugType* id available.
476 return std::nullopt;
477 }
478 // No getBaseType() (typical for void*): use DebugInfoNone as Base Type,
479 // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED
480 // spirv-val run in debug-type-pointer.ll). spirv-val may still reject this
481 // encoding; see https://github.com/KhronosGroup/SPIRV-Registry/pull/287.
482 return emitExtInst(
483 Opcode: SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg,
484 Operands: {CachedDebugInfoNoneReg, StorageClassReg, DebugTypePointerFlagsReg}, MAI);
485}
486
487std::optional<MCRegister>
488SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType(
489 const DISubroutineType *ST, MCRegister ExtInstSetReg,
490 SPIRV::ModuleAnalysisInfo &MAI) {
491 MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
492 MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
493 MCRegister DebugTypeFunctionFlagsReg =
494 emitOpConstantI32(Value: transDebugFlags(DN: ST), I32TypeReg, MAI);
495 DITypeArray TA = ST->getTypeArray();
496 SmallVector<MCRegister, 8> Ops;
497 Ops.push_back(Elt: DebugTypeFunctionFlagsReg);
498 // Empty DI type tuple: no explicit return or parameter slots (hand-written IR
499 // may use !{}). Emit void-only prototype. Same as SPIRV-LLVM-Translator when
500 // DISubroutineType::getTypeArray() has zero elements.
501 if (TA.empty()) {
502 Ops.push_back(Elt: VoidTypeReg);
503 } else {
504 for (unsigned I = 0, E = TA.size(); I != E; ++I) {
505 bool IsReturnType = (I == 0);
506 auto OptReg = mapDISignatureTypeToReg(Ty: TA[I], VoidTypeReg, ReturnType: IsReturnType);
507 // No emitted DebugType* id for this slot (e.g., pointer that
508 // was skipped due missing address space, etc.).
509 if (!OptReg)
510 return std::nullopt;
511 Ops.push_back(Elt: *OptReg);
512 }
513 }
514 return getOrEmitDebugTypeFunction(Ops, VoidTypeReg, ExtInstSetReg, MAI);
515}
516
517// Match SPIRV-LLVM-Translator's selection logic for the Parent operand.
518std::optional<MCRegister>
519SPIRVNonSemanticDebugHandler::resolveDebugFunctionDeclarationParent(
520 const DISubprogram *SP) const {
521 const DIScope *Scope = SP->getScope();
522 if (Scope && !isa<DIFile>(Val: Scope)) {
523 // TODO: Complete with other lookups once other scopes are supported
524 // (subclasses of DIScope).
525 const DIType *Ty = dyn_cast<DIType>(Val: Scope);
526 if (!Ty)
527 return std::nullopt;
528 return lookupOptReg(Map: DebugTypeRegs, Key: Ty);
529 }
530
531 const DICompileUnit *ParentCU = SP->getUnit();
532 if (!ParentCU && !CompileUnits.empty())
533 ParentCU = CompileUnits[0].TheCU;
534 if (!ParentCU)
535 return std::nullopt;
536 return lookupOptReg(Map: CUToCompilationUnitDbgReg, Key: ParentCU);
537}
538
539std::optional<MCRegister>
540SPIRVNonSemanticDebugHandler::emitDebugFunctionDeclaration(
541 const DISubprogram *SP, MCRegister VoidTypeReg, MCRegister I32TypeReg,
542 MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
543 assert(SP && "SP must not be null in emitDebugFunctionDeclaration");
544 assert(!SP->isDefinition() &&
545 "SP must not be a definition in emitDebugFunctionDeclaration");
546
547 // The IR verifier already enforces that this cannot be null.
548 const DISubroutineType *ST = SP->getType();
549
550 auto FnTyRegOpt = lookupOptReg(Map: DebugTypeRegs, Key: ST);
551 if (!FnTyRegOpt)
552 return std::nullopt;
553 MCRegister FnTyReg = *FnTyRegOpt;
554
555 auto ParentRegOpt = resolveDebugFunctionDeclarationParent(SP);
556 if (!ParentRegOpt)
557 return std::nullopt;
558
559 MCRegister ParentReg = *ParentRegOpt;
560
561 MCRegister FileStrReg = getCachedScopePathOpStringReg(Scope: SP);
562
563 MCRegister NameReg = getCachedOpStringReg(S: SP->getName());
564 MCRegister LinkageReg = getCachedOpStringReg(S: SP->getLinkageName());
565 MCRegister SrcReg = getOrEmitDebugSourceForFileStrReg(FileStrReg, VoidTypeReg,
566 ExtInstSetReg, MAI);
567
568 MCRegister LineReg =
569 emitOpConstantI32(Value: static_cast<uint32_t>(SP->getLine()), I32TypeReg, MAI);
570 MCRegister ColReg = emitOpConstantI32(Value: 0, I32TypeReg, MAI);
571
572 uint32_t FlagsVal = transDebugFlags(DN: SP);
573 // TODO: When composite scopes are DebugFunctionDeclaration parents (available
574 // in DebugTypeRegs), sync declaration Flags with SPIRV-LLVM-Translator.
575 FlagsVal &= ~NSDIFlagIsDefinition;
576 MCRegister FlagsReg = emitOpConstantI32(Value: FlagsVal, I32TypeReg, MAI);
577
578 return emitExtInst(Opcode: SPIRV::NonSemanticExtInst::DebugFunctionDeclaration,
579 VoidTypeReg, ExtInstSetReg,
580 Operands: {NameReg, FnTyReg, SrcReg, LineReg, ColReg, ParentReg,
581 LinkageReg, FlagsReg},
582 MAI);
583}
584
585std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg(
586 const DIType *Ty, MCRegister VoidTypeReg, bool ReturnType) {
587 if (!Ty) {
588 if (ReturnType)
589 return VoidTypeReg;
590 assert(CachedDebugInfoNoneReg.isValid() &&
591 "DebugInfoNone must be emitted before DISubroutineType operands");
592 return CachedDebugInfoNoneReg;
593 }
594 return lookupOptReg(Map: DebugTypeRegs, Key: Ty);
595}
596
597MCRegister SPIRVNonSemanticDebugHandler::resolveGlobalVariableParent(
598 const DIGlobalVariable *) const {
599 // TODO: When this backend emits debug instructions for namespace, subprogram,
600 // compilation units, and module scopes return GV->getScope()'s debug id.
601
602 // !CompileUnits.empty() was already checked before staring the emission of
603 // NSDI instructions.
604 assert(!CompileUnits.empty() &&
605 "resolveGlobalVariableParent requires non-empty CompileUnits");
606 std::optional<MCRegister> ParentRegOpt =
607 lookupOptReg(Map: CUToCompilationUnitDbgReg, Key: CompileUnits[0].TheCU);
608 assert(ParentRegOpt && "DebugCompilationUnit must be emitted before "
609 "resolveGlobalVariableParent");
610 // Fallback: first module compile unit (SPIRV-LLVM-Translator default).
611 return *ParentRegOpt;
612}
613
614// Unimplemented no-op; see emitDebugExpression declaration.
615std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugExpression(
616 const DIExpression *, MCRegister, MCRegister, SPIRV::ModuleAnalysisInfo &) {
617 return std::nullopt;
618}
619
620std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugGlobalVariable(
621 const DIGlobalVariable *GV, const GlobalVariableDebugInfo &Info,
622 MCRegister VoidTypeReg, MCRegister I32TypeReg, MCRegister ExtInstSetReg,
623 SPIRV::ModuleAnalysisInfo &MAI) {
624 assert(GV && "GV must not be null in emitDebugGlobalVariable");
625
626 MCRegister ParentReg = resolveGlobalVariableParent(GV);
627
628 // TyReg: DebugInfoNone when GV has no DI type (as done in
629 // SPIRV-LLVM-Translator). Declarations (isDefinition: false) can have null
630 // getType() while definitions must have a non-null one (enforced by the IR
631 // verifier).
632 MCRegister TyReg = CachedDebugInfoNoneReg;
633 if (const DIType *Ty = GV->getType()) {
634 auto TyRegOpt = lookupOptReg(Map: DebugTypeRegs, Key: Ty);
635 if (!TyRegOpt)
636 return std::nullopt;
637 TyReg = *TyRegOpt;
638 }
639
640 std::optional<MCRegister> StaticMemberRegOpt;
641 if (const DIDerivedType *SM = GV->getStaticDataMemberDeclaration()) {
642 StaticMemberRegOpt = lookupOptReg(Map: DebugTypeRegs, Key: SM);
643 if (!StaticMemberRegOpt)
644 return std::nullopt;
645 }
646
647 MCRegister NameReg = getCachedOpStringReg(S: GV->getName());
648 MCRegister LinkageReg = getCachedOpStringReg(S: GV->getLinkageName());
649 MCRegister FileStrReg = getCachedScopePathOpStringReg(
650 Scope: GV->getFile(), /*UseEmptyPathIfNullScope=*/true);
651 MCRegister SrcReg = getOrEmitDebugSourceForFileStrReg(FileStrReg, VoidTypeReg,
652 ExtInstSetReg, MAI);
653
654 MCRegister LineReg =
655 emitOpConstantI32(Value: static_cast<uint32_t>(GV->getLine()), I32TypeReg, MAI);
656 // DIGlobalVariable or DIGlobalVariableExpression metadata carry no column
657 // field. Column is hardcoded to 0 (because it can't be determined), matching
658 // SPIRV-LLVM-Translator.
659 MCRegister ColReg = emitOpConstantI32(Value: 0, I32TypeReg, MAI);
660
661 // Variable: @g OpVariable id when !dbg matches; else a DebugExpression for
662 // the GVE init value when no @g exists; else DebugInfoNone.
663 MCRegister VariableReg = CachedDebugInfoNoneReg;
664 if (const GlobalVariable *LLVMGV = Info.LLVMGV) {
665 MCRegister GVReg = MAI.getGlobalObjReg(GO: LLVMGV);
666 if (GVReg.isValid())
667 VariableReg = GVReg;
668 } else if (Info.Expr) {
669 if (auto ExprReg =
670 emitDebugExpression(Info.Expr, VoidTypeReg, ExtInstSetReg, MAI))
671 VariableReg = *ExprReg;
672 }
673
674 MCRegister FlagsReg = emitOpConstantI32(Value: transDebugFlags(DN: GV), I32TypeReg, MAI);
675
676 SmallVector<MCRegister, 10> Ops = {NameReg, TyReg, SrcReg,
677 LineReg, ColReg, ParentReg,
678 LinkageReg, VariableReg, FlagsReg};
679
680 if (StaticMemberRegOpt)
681 Ops.push_back(Elt: *StaticMemberRegOpt);
682
683 return emitExtInst(Opcode: SPIRV::NonSemanticExtInst::DebugGlobalVariable,
684 VoidTypeReg, ExtInstSetReg, Operands: Ops, MAI);
685}
686
687std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypeVector(
688 const DICompositeType *VT, MCRegister ExtInstSetReg,
689 SPIRV::ModuleAnalysisInfo &MAI) {
690 const auto *BaseTy = dyn_cast_or_null<DIBasicType>(Val: VT->getBaseType());
691 if (!BaseTy)
692 return std::nullopt;
693 auto BTIt = DebugTypeRegs.find(Val: BaseTy);
694 if (BTIt == DebugTypeRegs.end())
695 return std::nullopt;
696
697 // DebugTypeVector models only 1D vectors (multi-subrange types cannot be
698 // encoded).
699 DINodeArray Elements = VT->getElements();
700 if (Elements.size() != 1)
701 return std::nullopt;
702 const auto *SR = cast<DISubrange>(Val: Elements[0]);
703 const auto *CI = dyn_cast_if_present<ConstantInt *>(Val: SR->getCount());
704 if (!CI)
705 return std::nullopt;
706
707 MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
708 MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
709 MCRegister CountReg = emitOpConstantI32(
710 Value: static_cast<uint32_t>(CI->getZExtValue()), I32TypeReg, MAI);
711 return emitExtInst(Opcode: SPIRV::NonSemanticExtInst::DebugTypeVector, VoidTypeReg,
712 ExtInstSetReg, Operands: {BTIt->second, CountReg}, MAI);
713}
714
715void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
716 SPIRV::ModuleAnalysisInfo &MAI) {
717 if (CompileUnits.empty())
718 return;
719 // Check that prepareModuleOutput() registered the extended instruction set.
720 // If the subtarget does not support the extension, neither strings nor ext
721 // insts are emitted.
722 constexpr unsigned NSSet = static_cast<unsigned>(
723 SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100);
724 if (!MAI.getExtInstSetReg(SetNum: NSSet).isValid())
725 return;
726
727 for (const CompileUnitInfo &Info : CompileUnits) {
728 if (Info.TheCU) {
729 MCRegister PathReg = emitOpStringIfNew(S: Info.FilePath, MAI);
730 ScopeToPathOpStringReg[Info.TheCU] = PathReg;
731 if (const DIFile *F = Info.TheCU->getFile())
732 ScopeToPathOpStringReg[F] = PathReg;
733 }
734 }
735
736 for (const DIBasicType *BT : BasicTypes)
737 emitOpStringIfNew(S: BT->getName(), MAI);
738
739 for (const DISubprogram *SP : SubprogramDeclarations) {
740 emitOpStringIfNew(S: SP->getName(), MAI);
741 emitOpStringIfNew(S: SP->getLinkageName(), MAI);
742 ScopeToPathOpStringReg[SP] = emitOpStringIfNew(S: getDebugFullPath(Scope: SP), MAI);
743 }
744
745 for (const auto &[GV, _] : GlobalVariableDebugInfoMap) {
746 emitOpStringIfNew(S: GV->getName(), MAI);
747 emitOpStringIfNew(S: GV->getLinkageName(), MAI);
748 SmallString<128> Path = getDebugFullPath(Scope: GV->getFile());
749 MCRegister PathReg = emitOpStringIfNew(S: Path, MAI);
750 if (const DIFile *F = GV->getFile())
751 ScopeToPathOpStringReg[F] = PathReg;
752 }
753
754 CachedEmptyStringReg = emitOpStringIfNew(S: "", MAI);
755
756#ifndef NDEBUG
757 NonSemanticOpStringsSectionEmitted = true;
758#endif
759}
760
761void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
762 SPIRV::ModuleAnalysisInfo &MAI) {
763 if (GlobalDIEmitted || CompileUnits.empty())
764 return;
765 GlobalDIEmitted = true;
766
767 // Retrieve the ext inst set register allocated by prepareModuleOutput().
768 constexpr unsigned NSSet = static_cast<unsigned>(
769 SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100);
770 MCRegister ExtInstSetReg = MAI.getExtInstSetReg(SetNum: NSSet);
771 if (!ExtInstSetReg.isValid())
772 return; // Extension not available.
773
774#ifndef NDEBUG
775 assert(NonSemanticOpStringsSectionEmitted &&
776 "emitNonSemanticDebugStrings() must run before "
777 "emitNonSemanticGlobalDebugInfo()");
778#endif
779
780 MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
781 MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
782
783 CachedDebugInfoNoneReg = emitExtInst(Opcode: SPIRV::NonSemanticExtInst::DebugInfoNone,
784 VoidTypeReg, ExtInstSetReg, Operands: {}, MAI);
785
786 // Emit integer constants shared across all NSDI instructions. The constant
787 // cache ensures each value is emitted at most once even when referenced from
788 // multiple instructions. All constants are pre-emitted before any DebugSource
789 // so that the output order is: constants, then
790 // DebugSource+DebugCompilationUnit pairs. This keeps OpConstant instructions
791 // grouped before the OpExtInst instructions.
792
793 // The Version operand of DebugCompilationUnit is the version of the
794 // NonSemantic.Shader.DebugInfo instruction set, which is 100 for
795 // "NonSemantic.Shader.DebugInfo.100" (NonSemanticShaderDebugInfo100Version).
796 MCRegister DebugInfoVersionReg = emitOpConstantI32(Value: 100, I32TypeReg, MAI);
797 MCRegister DwarfVersionReg =
798 emitOpConstantI32(Value: static_cast<uint32_t>(DwarfVersion), I32TypeReg, MAI);
799
800 // Pre-emit source language constants for all compile units before entering
801 // the DebugSource loop.
802 SmallVector<MCRegister> SrcLangRegs =
803 map_to_vector(C&: CompileUnits, F: [&](const CompileUnitInfo &Info) {
804 return emitOpConstantI32(Value: Info.SpirvSourceLanguage, I32TypeReg, MAI);
805 });
806
807 // Emit DebugSource and DebugCompilationUnit for each compile unit.
808 for (auto [Info, SrcLangReg] : llvm::zip(t&: CompileUnits, u&: SrcLangRegs)) {
809 MCRegister FileStrReg = ScopeToPathOpStringReg.lookup(Val: Info.TheCU);
810 assert(FileStrReg.isValid() &&
811 "CU path OpString must be emitted in emitNonSemanticDebugStrings");
812 MCRegister DebugSourceReg = getOrEmitDebugSourceForFileStrReg(
813 FileStrReg, VoidTypeReg, ExtInstSetReg, MAI);
814 MCRegister CUDbgReg = emitExtInst(
815 Opcode: SPIRV::NonSemanticExtInst::DebugCompilationUnit, VoidTypeReg,
816 ExtInstSetReg,
817 Operands: {DebugInfoVersionReg, DwarfVersionReg, DebugSourceReg, SrcLangReg},
818 MAI);
819 if (Info.TheCU)
820 CUToCompilationUnitDbgReg[Info.TheCU] = CUDbgReg;
821 }
822
823 // Zero constant used as the Flags operand in DebugTypeBasic and
824 // DebugTypePointer. Cached with other i32 constants.
825 MCRegister I32ZeroReg = emitOpConstantI32(Value: 0, I32TypeReg, MAI);
826
827 DebugTypeRegs.clear();
828
829 for (const DIBasicType *BT : BasicTypes) {
830 MCRegister NameReg = getCachedOpStringReg(S: BT->getName());
831 MCRegister SizeReg = emitOpConstantI32(
832 Value: static_cast<uint32_t>(BT->getSizeInBits()), I32TypeReg, MAI);
833
834 // Map DWARF base type encodings to NSDI encoding codes per
835 // NonSemantic.Shader.DebugInfo.100 specification, section 4.5.
836 unsigned Encoding = 0; // Unspecified
837 switch (BT->getEncoding()) {
838 case dwarf::DW_ATE_address:
839 Encoding = 1;
840 break;
841 case dwarf::DW_ATE_boolean:
842 Encoding = 2;
843 break;
844 case dwarf::DW_ATE_float:
845 Encoding = 3;
846 break;
847 case dwarf::DW_ATE_signed:
848 Encoding = 4;
849 break;
850 case dwarf::DW_ATE_signed_char:
851 Encoding = 5;
852 break;
853 case dwarf::DW_ATE_unsigned:
854 Encoding = 6;
855 break;
856 case dwarf::DW_ATE_unsigned_char:
857 Encoding = 7;
858 break;
859 }
860 MCRegister EncodingReg = emitOpConstantI32(Value: Encoding, I32TypeReg, MAI);
861
862 MCRegister BTReg = emitExtInst(
863 Opcode: SPIRV::NonSemanticExtInst::DebugTypeBasic, VoidTypeReg, ExtInstSetReg,
864 Operands: {NameReg, SizeReg, EncodingReg, I32ZeroReg}, MAI);
865 DebugTypeRegs[BT] = BTReg;
866 }
867
868 // Emit DebugTypeVector for each collected vector type.
869 for (const DICompositeType *VT : VectorTypes) {
870 if (auto VecReg = emitDebugTypeVector(VT, ExtInstSetReg, MAI))
871 DebugTypeRegs[VT] = *VecReg;
872 }
873
874 // Emit DebugTypePointer for each referenced pointer type.
875 for (const DIDerivedType *PT : PointerTypes) {
876 if (auto PtrReg = emitDebugTypePointer(PT, ExtInstSetReg, MAI))
877 DebugTypeRegs[PT] = *PtrReg;
878 }
879
880 // Emit DebugTypeFunction for each distinct DISubroutineType.
881 for (const DISubroutineType *ST : SubroutineTypes) {
882 if (auto FnTyReg =
883 emitDebugTypeFunctionForSubroutineType(ST, ExtInstSetReg, MAI))
884 DebugTypeRegs[ST] = *FnTyReg;
885 }
886
887 // Emit DebugFunctionDeclaration for DISubprogram declarations.
888 for (const DISubprogram *SP : SubprogramDeclarations) {
889 if (auto DeclReg = emitDebugFunctionDeclaration(SP, VoidTypeReg, I32TypeReg,
890 ExtInstSetReg, MAI))
891 DebugFunctionDeclarationRegs[SP] = *DeclReg;
892 }
893
894 // Emit DebugGlobalVariable for each collected DIGlobalVariable.
895 for (const auto &[GV, Info] : GlobalVariableDebugInfoMap)
896 emitDebugGlobalVariable(GV, Info, VoidTypeReg, I32TypeReg, ExtInstSetReg,
897 MAI);
898}
899
900SmallString<128>
901SPIRVNonSemanticDebugHandler::getDebugFullPath(const DIScope *Scope) const {
902 SmallString<128> Out;
903 if (!Scope)
904 return Out;
905 StringRef Filename = Scope->getFilename();
906 const auto Style = sys::path::Style::native;
907 if (sys::path::is_absolute(path: Filename, style: Style))
908 Out.assign(in_start: Filename.begin(), in_end: Filename.end());
909 else {
910 StringRef Dir = Scope->getDirectory();
911 Out.assign(in_start: Dir.begin(), in_end: Dir.end());
912 sys::path::append(path&: Out, style: Style, a: Filename);
913 }
914 return Out;
915}
916
917MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugSourceForFileStrReg(
918 MCRegister FileStrReg, MCRegister VoidTypeReg, MCRegister ExtInstSetReg,
919 SPIRV::ModuleAnalysisInfo &MAI) {
920 const unsigned Key = FileStrReg.id();
921 auto It = DebugSourceRegByFileStr.find(Val: Key);
922 if (It != DebugSourceRegByFileStr.end())
923 return It->second;
924
925 MCRegister DS = emitExtInst(Opcode: SPIRV::NonSemanticExtInst::DebugSource,
926 VoidTypeReg, ExtInstSetReg, Operands: {FileStrReg}, MAI);
927 DebugSourceRegByFileStr[Key] = DS;
928 return DS;
929}
930