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