| //===-- SPIRVNonSemanticDebugHandler.cpp - NSDI AsmPrinter handler -*- 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 "SPIRVNonSemanticDebugHandler.h" |
| #include "MCTargetDesc/SPIRVMCTargetDesc.h" |
| #include "SPIRVSubtarget.h" |
| #include "SPIRVUtils.h" |
| #include "llvm/ADT/SmallVectorExtras.h" |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "llvm/CodeGen/AsmPrinter.h" |
| #include "llvm/IR/DebugInfo.h" |
| #include "llvm/IR/DebugInfoMetadata.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/MC/MCInst.h" |
| #include "llvm/MC/MCStreamer.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/Path.h" |
| #include <cassert> |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| /// Partition \p Ty into \p BasicTypes, \p PointerTypes, \p SubroutineTypes, |
| /// and \p VectorTypes for NSDI emission. Used when iterating |
| /// DebugInfoFinder.types(); each DI node is seen once, so no recursion into |
| /// pointer bases. Other composites and non-pointer derived kinds are ignored |
| /// because they are not yet supported. Only types that are supported (later |
| /// used) are partitioned. |
| static void |
| partitionTypes(const DIType *Ty, SmallVector<const DIBasicType *> &BasicTypes, |
| SmallVector<const DIDerivedType *> &PointerTypes, |
| SmallVector<const DISubroutineType *> &SubroutineTypes, |
| SmallVector<const DICompositeType *> &VectorTypes) { |
| if (const auto *BT = dyn_cast<DIBasicType>(Ty)) { |
| BasicTypes.push_back(BT); |
| return; |
| } |
| if (const auto *ST = dyn_cast<DISubroutineType>(Ty)) { |
| SubroutineTypes.push_back(ST); |
| return; |
| } |
| if (const auto *CT = dyn_cast<DICompositeType>(Ty)) { |
| if (CT->getTag() == dwarf::DW_TAG_array_type && CT->isVector()) |
| VectorTypes.push_back(CT); |
| return; |
| } |
| const auto *DT = dyn_cast<DIDerivedType>(Ty); |
| if (DT && DT->getTag() == dwarf::DW_TAG_pointer_type) |
| PointerTypes.push_back(DT); |
| } |
| |
| enum : uint32_t { |
| NSDIFlagIsProtected = 1u << 0, |
| NSDIFlagIsPrivate = 1u << 1, |
| NSDIFlagIsPublic = NSDIFlagIsPrivate | NSDIFlagIsProtected, |
| NSDIFlagIsLocal = 1u << 2, |
| NSDIFlagIsDefinition = 1u << 3, |
| NSDIFlagFwdDecl = 1u << 4, |
| NSDIFlagArtificial = 1u << 5, |
| NSDIFlagExplicit = 1u << 6, |
| NSDIFlagPrototyped = 1u << 7, |
| NSDIFlagObjectPointer = 1u << 8, |
| NSDIFlagStaticMember = 1u << 9, |
| NSDIFlagIndirectVariable = 1u << 10, |
| NSDIFlagLValueReference = 1u << 11, |
| NSDIFlagRValueReference = 1u << 12, |
| NSDIFlagIsOptimized = 1u << 13, |
| NSDIFlagIsEnumClass = 1u << 14, |
| NSDIFlagTypePassByValue = 1u << 15, |
| NSDIFlagTypePassByReference = 1u << 16, |
| NSDIFlagUnknownPhysicalLayout = 1u << 17, |
| }; |
| |
| static uint32_t mapDIFlagsToNonSemantic(DINode::DIFlags DFlags) { |
| uint32_t Flags = 0; |
| if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPublic) |
| Flags |= NSDIFlagIsPublic; |
| if ((DFlags & DINode::FlagAccessibility) == DINode::FlagProtected) |
| Flags |= NSDIFlagIsProtected; |
| if ((DFlags & DINode::FlagAccessibility) == DINode::FlagPrivate) |
| Flags |= NSDIFlagIsPrivate; |
| if (DFlags & DINode::FlagFwdDecl) |
| Flags |= NSDIFlagFwdDecl; |
| if (DFlags & DINode::FlagArtificial) |
| Flags |= NSDIFlagArtificial; |
| if (DFlags & DINode::FlagExplicit) |
| Flags |= NSDIFlagExplicit; |
| if (DFlags & DINode::FlagPrototyped) |
| Flags |= NSDIFlagPrototyped; |
| if (DFlags & DINode::FlagObjectPointer) |
| Flags |= NSDIFlagObjectPointer; |
| if (DFlags & DINode::FlagStaticMember) |
| Flags |= NSDIFlagStaticMember; |
| if (DFlags & DINode::FlagLValueReference) |
| Flags |= NSDIFlagLValueReference; |
| if (DFlags & DINode::FlagRValueReference) |
| Flags |= NSDIFlagRValueReference; |
| if (DFlags & DINode::FlagTypePassByValue) |
| Flags |= NSDIFlagTypePassByValue; |
| if (DFlags & DINode::FlagTypePassByReference) |
| Flags |= NSDIFlagTypePassByReference; |
| if (DFlags & DINode::FlagEnumClass) |
| Flags |= NSDIFlagIsEnumClass; |
| return Flags; |
| } |
| |
| static uint32_t transDebugFlags(const DINode *DN) { |
| uint32_t Flags = 0; |
| if (const auto *GV = dyn_cast<DIGlobalVariable>(DN)) { |
| if (GV->isLocalToUnit()) |
| Flags |= NSDIFlagIsLocal; |
| if (GV->isDefinition()) |
| Flags |= NSDIFlagIsDefinition; |
| } |
| if (const auto *SP = dyn_cast<DISubprogram>(DN)) { |
| if (SP->isLocalToUnit()) |
| Flags |= NSDIFlagIsLocal; |
| if (SP->isOptimized()) |
| Flags |= NSDIFlagIsOptimized; |
| if (SP->isDefinition()) |
| Flags |= NSDIFlagIsDefinition; |
| Flags |= mapDIFlagsToNonSemantic(SP->getFlags()); |
| } |
| if (DN->getTag() == dwarf::DW_TAG_reference_type) |
| Flags |= NSDIFlagLValueReference; |
| if (DN->getTag() == dwarf::DW_TAG_rvalue_reference_type) |
| Flags |= NSDIFlagRValueReference; |
| if (const auto *Ty = dyn_cast<DIType>(DN)) |
| Flags |= mapDIFlagsToNonSemantic(Ty->getFlags()); |
| if (const auto *LV = dyn_cast<DILocalVariable>(DN)) |
| Flags |= mapDIFlagsToNonSemantic(LV->getFlags()); |
| return Flags; |
| } |
| |
| } // namespace |
| |
| SPIRVNonSemanticDebugHandler::SPIRVNonSemanticDebugHandler(AsmPrinter &AP) |
| : DebugHandlerBase(&AP) {} |
| |
| // Map DWARF source language codes to NonSemantic.Shader.DebugInfo.100 source |
| // language codes. Values are from the SourceLanguage enum in the |
| // NonSemantic.Shader.DebugInfo.100 specification, section 4.3. |
| unsigned SPIRVNonSemanticDebugHandler::toNSDISrcLang(unsigned DwarfSrcLang) { |
| switch (DwarfSrcLang) { |
| case dwarf::DW_LANG_OpenCL: |
| return 3; // OpenCL_C |
| case dwarf::DW_LANG_OpenCL_CPP: |
| return 4; // OpenCL_CPP |
| case dwarf::DW_LANG_CPP_for_OpenCL: |
| return 6; // CPP_for_OpenCL |
| case dwarf::DW_LANG_GLSL: |
| return 2; // GLSL |
| case dwarf::DW_LANG_HLSL: |
| return 5; // HLSL |
| case dwarf::DW_LANG_SYCL: |
| return 7; // SYCL |
| case dwarf::DW_LANG_Zig: |
| return 12; // Zig |
| default: |
| return 0; // Unknown |
| } |
| } |
| |
| void SPIRVNonSemanticDebugHandler::beginModule(Module *M) { |
| // The base class sets Asm = nullptr when the module has no compile units, |
| // and initializes lexical scope tracking otherwise. |
| DebugHandlerBase::beginModule(M); |
| |
| if (!Asm) |
| return; |
| |
| CompileUnits.clear(); |
| BasicTypes.clear(); |
| PointerTypes.clear(); |
| SubroutineTypes.clear(); |
| VectorTypes.clear(); |
| DebugTypeRegs.clear(); |
| OpStringContentCache.clear(); |
| I32ConstantCache.clear(); |
| DebugTypeFunctionCache.clear(); |
| GlobalDIEmitted = false; |
| #ifndef NDEBUG |
| NonSemanticOpStringsSectionEmitted = false; |
| #endif |
| CachedDebugInfoNoneReg = MCRegister(); |
| CachedOpTypeVoidReg = MCRegister(); |
| CachedOpTypeInt32Reg = MCRegister(); |
| |
| // Collect compile-unit info: file paths and source languages. |
| for (const DICompileUnit *CU : M->debug_compile_units()) { |
| const DIFile *File = CU->getFile(); |
| CompileUnitInfo Info; |
| if (sys::path::is_absolute(File->getFilename())) |
| Info.FilePath = File->getFilename(); |
| else |
| sys::path::append(Info.FilePath, File->getDirectory(), |
| File->getFilename()); |
| // getName() returns the language code regardless of whether the name is |
| // versioned. getUnversionedName() would assert on versioned names. |
| Info.SpirvSourceLanguage = toNSDISrcLang(CU->getSourceLanguage().getName()); |
| CompileUnits.push_back(std::move(Info)); |
| } |
| |
| // Collect DWARF version from module flags. For CodeView modules there is no |
| // "Dwarf Version" flag; DwarfVersion remains 0, which is the correct value |
| // for the DebugCompilationUnit DWARF Version operand in that case. |
| if (const NamedMDNode *Flags = M->getNamedMetadata("llvm.module.flags")) { |
| for (const auto *Op : Flags->operands()) { |
| const MDOperand &NameOp = Op->getOperand(1); |
| if (NameOp.equalsStr("Dwarf Version")) |
| DwarfVersion = |
| cast<ConstantInt>( |
| cast<ConstantAsMetadata>(Op->getOperand(2))->getValue()) |
| ->getSExtValue(); |
| } |
| } |
| |
| // Find all debug info types that may be referenced by NSDI instructions. |
| DebugInfoFinder Finder; |
| Finder.processModule(*M); |
| llvm::for_each(Finder.types(), [&](DIType *Ty) { |
| partitionTypes(Ty, BasicTypes, PointerTypes, SubroutineTypes, VectorTypes); |
| }); |
| } |
| |
| void SPIRVNonSemanticDebugHandler::prepareModuleOutput( |
| const SPIRVSubtarget &ST, SPIRV::ModuleAnalysisInfo &MAI) { |
| if (CompileUnits.empty()) |
| return; |
| if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_non_semantic_info)) |
| return; |
| |
| // Add the extension to requirements so OpExtension is output. |
| MAI.Reqs.addExtension(SPIRV::Extension::SPV_KHR_non_semantic_info); |
| |
| // Add the NonSemantic.Shader.DebugInfo.100 entry to ExtInstSetMap so that |
| // outputOpExtInstImports() emits the OpExtInstImport instruction. Allocate a |
| // fresh result ID for it now; the same ID is used in emitExtInst() operands. |
| constexpr unsigned NSSet = static_cast<unsigned>( |
| SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100); |
| if (!MAI.ExtInstSetMap.count(NSSet)) |
| MAI.ExtInstSetMap[NSSet] = MAI.getNextIDRegister(); |
| } |
| |
| void SPIRVNonSemanticDebugHandler::emitMCInst(MCInst &Inst) { |
| Asm->OutStreamer->emitInstruction(Inst, Asm->getSubtargetInfo()); |
| } |
| |
| MCRegister |
| SPIRVNonSemanticDebugHandler::emitOpString(StringRef S, |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| MCRegister Reg = MAI.getNextIDRegister(); |
| MCInst Inst; |
| Inst.setOpcode(SPIRV::OpString); |
| Inst.addOperand(MCOperand::createReg(Reg)); |
| addStringImm(S, Inst); |
| emitMCInst(Inst); |
| return Reg; |
| } |
| |
| void SPIRVNonSemanticDebugHandler::emitOpStringIfNew( |
| StringRef S, SPIRV::ModuleAnalysisInfo &MAI) { |
| #ifndef NDEBUG |
| assert(!NonSemanticOpStringsSectionEmitted && |
| "emitOpStringIfNew is only valid while emitting SPIR-V section 7"); |
| #endif |
| auto [It, Inserted] = OpStringContentCache.try_emplace(S, MCRegister()); |
| if (!Inserted) |
| return; |
| |
| It->second = emitOpString(S, MAI); |
| } |
| |
| MCRegister SPIRVNonSemanticDebugHandler::getCachedOpStringReg(StringRef S) { |
| #ifndef NDEBUG |
| assert(NonSemanticOpStringsSectionEmitted && |
| "getCachedOpStringReg requires emitNonSemanticDebugStrings() first"); |
| #endif |
| auto It = OpStringContentCache.find(S); |
| assert(It != OpStringContentCache.end() && |
| "NSDI OpString missing from cache; emitNonSemanticDebugStrings must " |
| "cache every string used in section 10"); |
| return It->second; |
| } |
| |
| MCRegister SPIRVNonSemanticDebugHandler::emitOpConstantI32( |
| uint32_t Value, MCRegister I32TypeReg, SPIRV::ModuleAnalysisInfo &MAI) { |
| auto [It, Inserted] = I32ConstantCache.try_emplace(Value); |
| if (!Inserted) |
| return It->second; |
| |
| MCRegister Reg = MAI.getNextIDRegister(); |
| It->second = Reg; |
| MCInst Inst; |
| Inst.setOpcode(SPIRV::OpConstantI); |
| Inst.addOperand(MCOperand::createReg(Reg)); |
| Inst.addOperand(MCOperand::createReg(I32TypeReg)); |
| Inst.addOperand(MCOperand::createImm(static_cast<int64_t>(Value))); |
| emitMCInst(Inst); |
| return Reg; |
| } |
| |
| MCRegister SPIRVNonSemanticDebugHandler::emitExtInst( |
| SPIRV::NonSemanticExtInst::NonSemanticExtInst Opcode, |
| MCRegister VoidTypeReg, MCRegister ExtInstSetReg, |
| ArrayRef<MCRegister> Operands, SPIRV::ModuleAnalysisInfo &MAI) { |
| MCRegister Reg = MAI.getNextIDRegister(); |
| MCInst Inst; |
| Inst.setOpcode(SPIRV::OpExtInst); |
| Inst.addOperand(MCOperand::createReg(Reg)); |
| Inst.addOperand(MCOperand::createReg(VoidTypeReg)); |
| Inst.addOperand(MCOperand::createReg(ExtInstSetReg)); |
| Inst.addOperand(MCOperand::createImm(static_cast<int64_t>(Opcode))); |
| for (MCRegister R : Operands) |
| Inst.addOperand(MCOperand::createReg(R)); |
| emitMCInst(Inst); |
| return Reg; |
| } |
| |
| MCRegister SPIRVNonSemanticDebugHandler::getOrEmitDebugTypeFunction( |
| ArrayRef<MCRegister> Ops, MCRegister VoidTypeReg, MCRegister ExtInstSetReg, |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| auto [It, Inserted] = |
| DebugTypeFunctionCache.try_emplace(SmallVector<MCRegister, 8>(Ops)); |
| if (!Inserted) |
| return It->second; |
| |
| MCRegister Reg = emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeFunction, |
| VoidTypeReg, ExtInstSetReg, Ops, MAI); |
| It->second = Reg; |
| return Reg; |
| } |
| |
| MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeVoidReg( |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| if (!CachedOpTypeVoidReg.isValid()) |
| CachedOpTypeVoidReg = findOrEmitOpTypeVoid(MAI); |
| return CachedOpTypeVoidReg; |
| } |
| |
| MCRegister SPIRVNonSemanticDebugHandler::getOrEmitOpTypeInt32Reg( |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| if (!CachedOpTypeInt32Reg.isValid()) |
| CachedOpTypeInt32Reg = findOrEmitOpTypeInt32(MAI); |
| return CachedOpTypeInt32Reg; |
| } |
| |
| MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeVoid( |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars)) { |
| if (MI->getOpcode() == SPIRV::OpTypeVoid) |
| return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg()); |
| } |
| MCRegister Reg = MAI.getNextIDRegister(); |
| MCInst Inst; |
| Inst.setOpcode(SPIRV::OpTypeVoid); |
| Inst.addOperand(MCOperand::createReg(Reg)); |
| emitMCInst(Inst); |
| return Reg; |
| } |
| |
| MCRegister SPIRVNonSemanticDebugHandler::findOrEmitOpTypeInt32( |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| for (const MachineInstr *MI : MAI.getMSInstrs(SPIRV::MB_TypeConstVars)) { |
| if (MI->getOpcode() == SPIRV::OpTypeInt && |
| MI->getOperand(1).getImm() == 32 && MI->getOperand(2).getImm() == 0) |
| return MAI.getRegisterAlias(MI->getMF(), MI->getOperand(0).getReg()); |
| } |
| MCRegister Reg = MAI.getNextIDRegister(); |
| MCInst Inst; |
| Inst.setOpcode(SPIRV::OpTypeInt); |
| Inst.addOperand(MCOperand::createReg(Reg)); |
| Inst.addOperand(MCOperand::createImm(32)); // width |
| Inst.addOperand(MCOperand::createImm(0)); // signedness (unsigned) |
| emitMCInst(Inst); |
| return Reg; |
| } |
| |
| std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypePointer( |
| const DIDerivedType *PT, MCRegister ExtInstSetReg, |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| // A DWARF address space is required to determine the SPIR-V storage class. |
| // Skip pointer types that do not carry one. |
| if (!PT->getDWARFAddressSpace().has_value()) |
| return std::nullopt; |
| |
| MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI); |
| MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI); |
| MCRegister DebugTypePointerFlagsReg = |
| emitOpConstantI32(transDebugFlags(PT), I32TypeReg, MAI); |
| |
| // For SPIR-V targets, Clang sets DwarfAddressSpace to the LLVM IR address |
| // space, which addressSpaceToStorageClass expects. |
| const auto &ST = static_cast<const SPIRVSubtarget &>(Asm->getSubtargetInfo()); |
| MCRegister StorageClassReg = emitOpConstantI32( |
| addressSpaceToStorageClass(PT->getDWARFAddressSpace().value(), ST), |
| I32TypeReg, MAI); |
| |
| if (const DIType *BaseTy = PT->getBaseType()) { |
| auto BaseIt = DebugTypeRegs.find(BaseTy); |
| if (BaseIt != DebugTypeRegs.end()) |
| return emitExtInst( |
| SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, |
| ExtInstSetReg, |
| {BaseIt->second, StorageClassReg, DebugTypePointerFlagsReg}, MAI); |
| // Unsupported type, no DebugType* id available. |
| return std::nullopt; |
| } |
| // No getBaseType() (typical for void*): use DebugInfoNone as Base Type, |
| // same as SPIRV-LLVM-Translator (see issue #109287 and the DISABLED |
| // spirv-val run in debug-type-pointer.ll). spirv-val may still reject this |
| // encoding; see https://github.com/KhronosGroup/SPIRV-Registry/pull/287. |
| return emitExtInst( |
| SPIRV::NonSemanticExtInst::DebugTypePointer, VoidTypeReg, ExtInstSetReg, |
| {CachedDebugInfoNoneReg, StorageClassReg, DebugTypePointerFlagsReg}, MAI); |
| } |
| |
| std::optional<MCRegister> |
| SPIRVNonSemanticDebugHandler::emitDebugTypeFunctionForSubroutineType( |
| const DISubroutineType *ST, MCRegister ExtInstSetReg, |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI); |
| MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI); |
| MCRegister DebugTypeFunctionFlagsReg = |
| emitOpConstantI32(transDebugFlags(ST), I32TypeReg, MAI); |
| DITypeArray TA = ST->getTypeArray(); |
| SmallVector<MCRegister, 8> Ops; |
| Ops.push_back(DebugTypeFunctionFlagsReg); |
| // Empty DI type tuple: no explicit return or parameter slots (hand-written IR |
| // may use !{}). Emit void-only prototype. Same as SPIRV-LLVM-Translator when |
| // DISubroutineType::getTypeArray() has zero elements. |
| if (TA.empty()) { |
| Ops.push_back(VoidTypeReg); |
| } else { |
| for (unsigned I = 0, E = TA.size(); I != E; ++I) { |
| bool IsReturnType = (I == 0); |
| auto OptReg = mapDISignatureTypeToReg(TA[I], VoidTypeReg, IsReturnType); |
| // No emitted DebugType* id for this slot (e.g., pointer that |
| // was skipped due missing address space, etc.). |
| if (!OptReg) |
| return std::nullopt; |
| Ops.push_back(*OptReg); |
| } |
| } |
| return getOrEmitDebugTypeFunction(Ops, VoidTypeReg, ExtInstSetReg, MAI); |
| } |
| |
| std::optional<MCRegister> SPIRVNonSemanticDebugHandler::mapDISignatureTypeToReg( |
| const DIType *Ty, MCRegister VoidTypeReg, bool ReturnType) { |
| if (!Ty) { |
| if (ReturnType) |
| return VoidTypeReg; |
| assert(CachedDebugInfoNoneReg.isValid() && |
| "DebugInfoNone must be emitted before DISubroutineType operands"); |
| return CachedDebugInfoNoneReg; |
| } |
| auto It = DebugTypeRegs.find(Ty); |
| if (It != DebugTypeRegs.end()) |
| return It->second; |
| |
| return std::nullopt; |
| } |
| |
| std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypeVector( |
| const DICompositeType *VT, MCRegister ExtInstSetReg, |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| const auto *BaseTy = dyn_cast_or_null<DIBasicType>(VT->getBaseType()); |
| if (!BaseTy) |
| return std::nullopt; |
| auto BTIt = DebugTypeRegs.find(BaseTy); |
| if (BTIt == DebugTypeRegs.end()) |
| return std::nullopt; |
| |
| // DebugTypeVector models only 1D vectors (multi-subrange types cannot be |
| // encoded). |
| DINodeArray Elements = VT->getElements(); |
| if (Elements.size() != 1) |
| return std::nullopt; |
| const auto *SR = cast<DISubrange>(Elements[0]); |
| const auto *CI = dyn_cast_if_present<ConstantInt *>(SR->getCount()); |
| if (!CI) |
| return std::nullopt; |
| |
| MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI); |
| MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI); |
| MCRegister CountReg = emitOpConstantI32( |
| static_cast<uint32_t>(CI->getZExtValue()), I32TypeReg, MAI); |
| return emitExtInst(SPIRV::NonSemanticExtInst::DebugTypeVector, VoidTypeReg, |
| ExtInstSetReg, {BTIt->second, CountReg}, MAI); |
| } |
| |
| void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings( |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| if (CompileUnits.empty()) |
| return; |
| // Check that prepareModuleOutput() registered the extended instruction set. |
| // If the subtarget does not support the extension, neither strings nor ext |
| // insts are emitted. |
| constexpr unsigned NSSet = static_cast<unsigned>( |
| SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100); |
| if (!MAI.getExtInstSetReg(NSSet).isValid()) |
| return; |
| |
| for (const CompileUnitInfo &Info : CompileUnits) |
| emitOpStringIfNew(Info.FilePath, MAI); |
| |
| for (const DIBasicType *BT : BasicTypes) |
| emitOpStringIfNew(BT->getName(), MAI); |
| |
| #ifndef NDEBUG |
| NonSemanticOpStringsSectionEmitted = true; |
| #endif |
| } |
| |
| void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo( |
| SPIRV::ModuleAnalysisInfo &MAI) { |
| if (GlobalDIEmitted || CompileUnits.empty()) |
| return; |
| GlobalDIEmitted = true; |
| |
| // Retrieve the ext inst set register allocated by prepareModuleOutput(). |
| constexpr unsigned NSSet = static_cast<unsigned>( |
| SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100); |
| MCRegister ExtInstSetReg = MAI.getExtInstSetReg(NSSet); |
| if (!ExtInstSetReg.isValid()) |
| return; // Extension not available. |
| |
| #ifndef NDEBUG |
| assert(NonSemanticOpStringsSectionEmitted && |
| "emitNonSemanticDebugStrings() must run before " |
| "emitNonSemanticGlobalDebugInfo()"); |
| #endif |
| |
| MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI); |
| MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI); |
| |
| CachedDebugInfoNoneReg = emitExtInst(SPIRV::NonSemanticExtInst::DebugInfoNone, |
| VoidTypeReg, ExtInstSetReg, {}, MAI); |
| |
| // Emit integer constants shared across all NSDI instructions. The constant |
| // cache ensures each value is emitted at most once even when referenced from |
| // multiple instructions. All constants are pre-emitted before any DebugSource |
| // so that the output order is: constants, then |
| // DebugSource+DebugCompilationUnit pairs. This keeps OpConstant instructions |
| // grouped before the OpExtInst instructions. |
| |
| // The Version operand of DebugCompilationUnit is the version of the |
| // NonSemantic.Shader.DebugInfo instruction set, which is 100 for |
| // "NonSemantic.Shader.DebugInfo.100" (NonSemanticShaderDebugInfo100Version). |
| MCRegister DebugInfoVersionReg = emitOpConstantI32(100, I32TypeReg, MAI); |
| MCRegister DwarfVersionReg = |
| emitOpConstantI32(static_cast<uint32_t>(DwarfVersion), I32TypeReg, MAI); |
| |
| // Pre-emit source language constants for all compile units before entering |
| // the DebugSource loop. |
| SmallVector<MCRegister> SrcLangRegs = |
| map_to_vector(CompileUnits, [&](const CompileUnitInfo &Info) { |
| return emitOpConstantI32(Info.SpirvSourceLanguage, I32TypeReg, MAI); |
| }); |
| |
| // Emit DebugSource and DebugCompilationUnit for each compile unit. |
| for (auto [Info, SrcLangReg] : llvm::zip(CompileUnits, SrcLangRegs)) { |
| MCRegister FileStrReg = getCachedOpStringReg(Info.FilePath); |
| MCRegister DebugSourceReg = |
| emitExtInst(SPIRV::NonSemanticExtInst::DebugSource, VoidTypeReg, |
| ExtInstSetReg, {FileStrReg}, MAI); |
| emitExtInst( |
| SPIRV::NonSemanticExtInst::DebugCompilationUnit, VoidTypeReg, |
| ExtInstSetReg, |
| {DebugInfoVersionReg, DwarfVersionReg, DebugSourceReg, SrcLangReg}, |
| MAI); |
| } |
| |
| // Zero constant used as the Flags operand in DebugTypeBasic and |
| // DebugTypePointer. Cached with other i32 constants. |
| MCRegister I32ZeroReg = emitOpConstantI32(0, I32TypeReg, MAI); |
| |
| DebugTypeRegs.clear(); |
| |
| for (const DIBasicType *BT : BasicTypes) { |
| MCRegister NameReg = getCachedOpStringReg(BT->getName()); |
| MCRegister SizeReg = emitOpConstantI32( |
| static_cast<uint32_t>(BT->getSizeInBits()), I32TypeReg, MAI); |
| |
| // Map DWARF base type encodings to NSDI encoding codes per |
| // NonSemantic.Shader.DebugInfo.100 specification, section 4.5. |
| unsigned Encoding = 0; // Unspecified |
| switch (BT->getEncoding()) { |
| case dwarf::DW_ATE_address: |
| Encoding = 1; |
| break; |
| case dwarf::DW_ATE_boolean: |
| Encoding = 2; |
| break; |
| case dwarf::DW_ATE_float: |
| Encoding = 3; |
| break; |
| case dwarf::DW_ATE_signed: |
| Encoding = 4; |
| break; |
| case dwarf::DW_ATE_signed_char: |
| Encoding = 5; |
| break; |
| case dwarf::DW_ATE_unsigned: |
| Encoding = 6; |
| break; |
| case dwarf::DW_ATE_unsigned_char: |
| Encoding = 7; |
| break; |
| } |
| MCRegister EncodingReg = emitOpConstantI32(Encoding, I32TypeReg, MAI); |
| |
| MCRegister BTReg = emitExtInst( |
| SPIRV::NonSemanticExtInst::DebugTypeBasic, VoidTypeReg, ExtInstSetReg, |
| {NameReg, SizeReg, EncodingReg, I32ZeroReg}, MAI); |
| DebugTypeRegs[BT] = BTReg; |
| } |
| |
| // Emit DebugTypeVector for each collected vector type. |
| for (const DICompositeType *VT : VectorTypes) { |
| if (auto VecReg = emitDebugTypeVector(VT, ExtInstSetReg, MAI)) |
| DebugTypeRegs[VT] = *VecReg; |
| } |
| |
| // Emit DebugTypePointer for each referenced pointer type. |
| for (const DIDerivedType *PT : PointerTypes) { |
| if (auto PtrReg = emitDebugTypePointer(PT, ExtInstSetReg, MAI)) |
| DebugTypeRegs[PT] = *PtrReg; |
| } |
| |
| // Emit DebugTypeFunction for each distinct DISubroutineType. |
| for (const DISubroutineType *ST : SubroutineTypes) { |
| if (auto FnTyReg = |
| emitDebugTypeFunctionForSubroutineType(ST, ExtInstSetReg, MAI)) |
| DebugTypeRegs[ST] = *FnTyReg; |
| } |
| } |