//===- SPIRVToLLVMDbgTran.cpp - Converts debug info to LLVM -----*- C++ -*-===// // // The LLVM/SPIR-V Translator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // // Copyright (c) 2018 Intel Corporation. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal with the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimers. // Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimers in the documentation // and/or other materials provided with the distribution. // Neither the names of Intel Corporation, nor the names of its // contributors may be used to endorse or promote products derived from this // Software without specific prior written permission. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH // THE SOFTWARE. // //===----------------------------------------------------------------------===// // // This file implements translation of debug info from SPIR-V to LLVM metadata // //===----------------------------------------------------------------------===// #include "SPIRVToLLVMDbgTran.h" #include "SPIRV.debug.h" #include "SPIRVEntry.h" #include "SPIRVFunction.h" #include "SPIRVInstruction.h" #include "SPIRVInternal.h" #include "SPIRVReader.h" #include "SPIRVType.h" #include "llvm/ADT/StringExtras.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DebugProgramInstruction.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Module.h" using namespace SPIRVDebug::Operand; namespace SPIRV { static uint64_t getDerivedSizeInBits(const DIType *Ty) { if (auto Size = Ty->getSizeInBits()) return Size; if (auto *DT = llvm::dyn_cast(Ty)) if (auto *BT = llvm::dyn_cast(DT->getRawBaseType())) return getDerivedSizeInBits(BT); return 0; } SPIRVToLLVMDbgTran::SPIRVToLLVMDbgTran(SPIRVModule *TBM, Module *TM, SPIRVToLLVM *Reader) : BM(TBM), M(TM), SPIRVReader(Reader) { Enable = BM->hasDebugInfo(); } void SPIRVToLLVMDbgTran::addDbgInfoVersion() { if (!Enable) return; M->addModuleFlag(Module::Warning, "Debug Info Version", DEBUG_METADATA_VERSION); } DIFile * SPIRVToLLVMDbgTran::getDIFile(const std::string &FileName, std::optional> CS, std::optional Source) { return getOrInsert(FileMap, FileName, [=]() { SplitFileName Split(FileName); // Use the first builder from the map to crete DIFile since it's // relations with other debug metadata is not going through DICompileUnit if (!Split.BaseName.empty()) return BuilderMap.begin()->second->createFile(Split.BaseName, Split.Path, CS, Source); return static_cast(nullptr); }); } SPIRVExtInst *SPIRVToLLVMDbgTran::getDbgInst(const SPIRVId Id) { SPIRVEntry *E = BM->getEntry(Id); if (isa(E)) { SPIRVExtInst *EI = static_cast(E); if (EI->getExtSetKind() == SPIRV::SPIRVEIS_Debug || EI->getExtSetKind() == SPIRV::SPIRVEIS_OpenCL_DebugInfo_100 || EI->getExtSetKind() == SPIRV::SPIRVEIS_NonSemantic_Shader_DebugInfo_100 || EI->getExtSetKind() == SPIRV::SPIRVEIS_NonSemantic_Shader_DebugInfo_200) return EI; } return nullptr; } // Check if module is generated using NonSemantic.Shader.DebugInfo.XXX // extended instruction set inline bool isNonSemanticDebugInfo(const SPIRVExtInstSetKind Kind) { return (Kind == SPIRVEIS_NonSemantic_Shader_DebugInfo_100 || Kind == SPIRVEIS_NonSemantic_Shader_DebugInfo_200); } // Get integer parameter of debug instruction considering whether it's // Literal or of OpConstant instruction depending on DebugInfo // extended instruction set kind SPIRVWord SPIRVToLLVMDbgTran::getConstantValueOrLiteral(const std::vector &Ops, const SPIRVWord Idx, const SPIRVExtInstSetKind Kind) { if (!isNonSemanticDebugInfo(Kind)) return Ops[Idx]; SPIRVValue *SPVConst = BM->get(Ops[Idx]); assert(isConstantOpCode(SPVConst->getOpCode()) && "NonSemantic Debug instruction's parameters must be OpConstant"); ConstantInt *Const = cast(SPIRVReader->transValue(SPVConst, nullptr, nullptr)); return Const->getZExtValue(); } const std::string &SPIRVToLLVMDbgTran::getString(const SPIRVId Id) { SPIRVString *String = BM->get(Id); assert(String && "Invalid string"); return String->getStr(); } const std::string SPIRVToLLVMDbgTran::getStringSourceContinued(const SPIRVId Id, SPIRVExtInst *DebugInst) { if (!isValidId(Id) || getDbgInst(Id)) return ""; std::string Str = BM->get(Id)->getStr(); using namespace SPIRVDebug::Operand::SourceContinued; for (auto *I : DebugInst->getContinuedInstructions()) { std::string TmpStr = BM->get(I->getArguments()[TextIdx])->getStr(); Str.append(TmpStr); } return Str; } void SPIRVToLLVMDbgTran::transDbgInfo(const SPIRVValue *SV, Value *V) { // Constant samplers and composites do not have a corresponding // SPIRVInstruction, but they may be mapped to an LLVM Instruction. if (SV->getOpCode() == OpConstantSampler || SV->getOpCode() == OpConstantComposite) return; if (Instruction *I = dyn_cast(V)) { if (SV->isInst()) { const SPIRVInstruction *SI = static_cast(SV); I->setDebugLoc(transDebugScope(SI)); } } } DIScope *SPIRVToLLVMDbgTran::getScope(const SPIRVEntry *ScopeInst) { if (ScopeInst->getOpCode() == OpString) return getDIFile(static_cast(ScopeInst)->getStr()); return transDebugInst(static_cast(ScopeInst)); } void SPIRVToLLVMDbgTran::appendToSourceLangLiteral(DICompileUnit *CompileUnit, SPIRVWord SourceLang) { if (!M->getModuleFlag("Source Lang Literal")) { M->addModuleFlag(llvm::Module::Warning, "Source Lang Literal", MDTuple::get(M->getContext(), {})); } auto *SourceLangLiteral = dyn_cast(M->getModuleFlag("Source Lang Literal")); // Copy old content SmallVector Nodes; for (auto &Node : SourceLangLiteral->operands()) { Nodes.push_back(Node); } // Add new entry Nodes.push_back(MDTuple::get( M->getContext(), SmallVector{ CompileUnit, ConstantAsMetadata::get(ConstantInt::get( Type::getInt32Ty(M->getContext()), SourceLang)), })); // Update M->setModuleFlag(llvm::Module::Warning, "Source Lang Literal", MDTuple::get(M->getContext(), Nodes)); } DICompileUnit * SPIRVToLLVMDbgTran::transCompilationUnit(const SPIRVExtInst *DebugInst, const std::string CompilerVersion, const std::string Flags) { // Do nothing in case we have already translated the CU (e.g. during // DebugEntryPoint translation) if (BuilderMap[DebugInst->getId()]) return cast(DebugInstCache[DebugInst]); const SPIRVWordVec &Ops = DebugInst->getArguments(); using namespace SPIRVDebug::Operand::CompilationUnit; assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); // We must preserve only one Dwarf version module level metadata // UpgradeDebugInfo from llvm/lib/IR/AutoUpgrade.cpp has already done all // work for us during linking stage leaving a single Dwarf version in the // module if (!M->getModuleFlag("Dwarf Version")) { SPIRVWord DWARFVersion = getConstantValueOrLiteral( Ops, DWARFVersionIdx, DebugInst->getExtSetKind()); M->addModuleFlag(llvm::Module::Max, "Dwarf Version", DWARFVersion); } SPIRVWord SourceLang = getConstantValueOrLiteral(Ops, LanguageIdx, DebugInst->getExtSetKind()); SPIRVWord OriginalSourceLang = SourceLang; bool InvalidSourceLang = false; if (DebugInst->getExtSetKind() == SPIRVEIS_NonSemantic_Shader_DebugInfo_200) { SourceLang = convertSPIRVSourceLangToDWARFNonSemanticDbgInfo(SourceLang); } else if (isSPIRVSourceLangValid(SourceLang)) { SourceLang = convertSPIRVSourceLangToDWARF(SourceLang); } else { // Some SPIR-V producers generate invalid source language value. In such // case the original value should be preserved in "Source Lang Literal" // module flag for later use by LLVM IR consumers. SourceLang = dwarf::DW_LANG_OpenCL; InvalidSourceLang = true; } BuilderMap[DebugInst->getId()] = std::make_unique(*M); assert(BuilderMap.size() != 0 && "No debug compile units"); if (isNonSemanticDebugInfo(DebugInst->getExtSetKind())) { // Only initialize once if (BuilderMap.size() == 1) setBuildIdentifierAndStoragePath(); // This assertion is a guard/reminder to update the Producer argument to // createCompileUnit() if a new DebugInfo type is ever created assert(DebugInst->getExtSetKind() == SPIRVEIS_NonSemantic_Shader_DebugInfo_100 || DebugInst->getExtSetKind() == SPIRVEIS_NonSemantic_Shader_DebugInfo_200); auto *CompileUnit = BuilderMap[DebugInst->getId()]->createCompileUnit( SourceLang, getFile(Ops[SourceIdx]), DebugInst->getExtSetKind() == SPIRVEIS_NonSemantic_Shader_DebugInfo_100 ? CompilerVersion : getString(Ops[ProducerIdx]), false, Flags, 0, StoragePath, DICompileUnit::DebugEmissionKind::FullDebug, BuildIdentifier); if (InvalidSourceLang) { appendToSourceLangLiteral(CompileUnit, OriginalSourceLang); } return CompileUnit; } // TODO: Remove this workaround once we switch to NonSemantic.Shader.* debug // info by default auto Producer = findModuleProducer(); auto *CompileUnit = BuilderMap[DebugInst->getId()]->createCompileUnit( SourceLang, getFile(Ops[SourceIdx]), Producer, false, Flags, 0); if (InvalidSourceLang) { appendToSourceLangLiteral(CompileUnit, OriginalSourceLang); } return CompileUnit; } DIBasicType *SPIRVToLLVMDbgTran::transTypeBasic(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeBasic; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert((Ops.size() == OperandCountOCL || Ops.size() == OperandCountNonSemantic) && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); auto Tag = static_cast( getConstantValueOrLiteral(Ops, EncodingIdx, DebugInst->getExtSetKind())); unsigned Encoding = SPIRV::DbgEncodingMap::rmap(Tag); if (Encoding == 0) return getDIBuilder(DebugInst).createUnspecifiedType(Name); uint64_t Size = BM->get(Ops[SizeIdx])->getZExtIntValue(); return getDIBuilder(DebugInst).createBasicType(Name, Size, Encoding); } DIDerivedType * SPIRVToLLVMDbgTran::transTypeQualifier(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeQualifier; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() == OperandCount && "Invalid number of operands"); DIType *BaseTy = transDebugInst(BM->get(Ops[BaseTypeIdx])); SPIRVWord Tag = SPIRV::DbgTypeQulifierMap::rmap( static_cast(getConstantValueOrLiteral( Ops, QualifierIdx, DebugInst->getExtSetKind()))); return getDIBuilder(DebugInst).createQualifiedType(Tag, BaseTy); } DIType *SPIRVToLLVMDbgTran::transTypePointer(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypePointer; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() == OperandCount && "Invalid number of operands"); DIType *PointeeTy = nullptr; if (BM->getEntry(Ops[BaseTypeIdx])->getOpCode() != OpTypeVoid) PointeeTy = transDebugInst(BM->get(Ops[BaseTypeIdx])); std::optional AS; SPIRVWord SC = getConstantValueOrLiteral(Ops, StorageClassIdx, DebugInst->getExtSetKind()); if (SC != ~0U) // all ones denote no address space AS = SPIRSPIRVAddrSpaceMap::rmap(static_cast(SC)); DIType *Ty; SPIRVWord Flags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); if (Flags & SPIRVDebug::FlagIsLValueReference) Ty = getDIBuilder(DebugInst).createReferenceType( dwarf::DW_TAG_reference_type, PointeeTy, 0, 0, AS); else if (Flags & SPIRVDebug::FlagIsRValueReference) Ty = getDIBuilder(DebugInst).createReferenceType( dwarf::DW_TAG_rvalue_reference_type, PointeeTy, 0, 0, AS); else Ty = getDIBuilder(DebugInst).createPointerType( PointeeTy, BM->getAddressingModel() * 32, 0, AS); if (Flags & SPIRVDebug::FlagIsObjectPointer) Ty = getDIBuilder(DebugInst).createObjectPointerType(Ty, /*Implicit=*/true); else if (Flags & SPIRVDebug::FlagIsArtificial) Ty = getDIBuilder(DebugInst).createArtificialType(Ty); return Ty; } DICompositeType * SPIRVToLLVMDbgTran::transTypeArray(const SPIRVExtInst *DebugInst) { if (DebugInst->getExtSetKind() == SPIRVEIS_NonSemantic_Shader_DebugInfo_200) return transTypeArrayNonSemantic(DebugInst); // TODO: figure out better naming for transTypeArrayOpenCL since // it also handles SPIRVEIS_NonSemantic_Shader_DebugInfo_100. // Also to consider separating OpenCL from NonSemantic, as OpenCL has several // workarounds return transTypeArrayOpenCL(DebugInst); } DICompositeType * SPIRVToLLVMDbgTran::transTypeArrayOpenCL(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeArray; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); DIType *BaseTy = transNonNullDebugType(BM->get(Ops[BaseTypeIdx])); size_t TotalCount = 1; SmallVector Subscripts; // Ops looks like: { BaseType, count1|upperBound1, count2|upperBound2, ..., // countN|upperBoundN, lowerBound1, lowerBound2, ..., lowerBoundN } for (size_t I = ComponentCountIdx, E = Ops.size() / 2 + 1; I < E; ++I) { if (auto *LocalVar = getDbgInst(Ops[I])) { auto *UpperBound = transDebugInst(LocalVar); SPIRVConstant *C = BM->get(Ops[Ops.size() / 2 + I]); int64_t ConstantAsInt = static_cast(C->getZExtIntValue()); auto *LowerBound = ConstantAsMetadata::get( ConstantInt::get(M->getContext(), APInt(64, ConstantAsInt))); Subscripts.push_back(getDIBuilder(DebugInst).getOrCreateSubrange( nullptr, LowerBound, UpperBound, nullptr)); continue; } if (const SPIRVExtInst * ExprUB, *ExprLB; (ExprUB = getDbgInst(Ops[I])) && (ExprLB = getDbgInst(Ops[Ops.size() / 2 + I]))) { auto *UpperBound = transDebugInst(ExprUB); auto *LowerBound = transDebugInst(ExprLB); Subscripts.push_back(getDIBuilder(DebugInst).getOrCreateSubrange( nullptr, LowerBound, UpperBound, nullptr)); continue; } if (!getDbgInst(Ops[I])) { SPIRVConstant *C = BM->get(Ops[I]); int64_t Count = static_cast(C->getZExtIntValue()); // If the SPIR-V file was generated by an older version of the translator, // Ops may not contain the LowerBound if (Ops.size() / 2 + I < Ops.size()) { C = BM->get(Ops[Ops.size() / 2 + I]); int64_t LowerBound = static_cast(C->getZExtIntValue()); Subscripts.push_back( getDIBuilder(DebugInst).getOrCreateSubrange(LowerBound, Count)); } else { auto *CountAsMD = ConstantAsMetadata::get( ConstantInt::get(M->getContext(), APInt(64, Count))); Subscripts.push_back(getDIBuilder(DebugInst).getOrCreateSubrange( CountAsMD, nullptr, nullptr, nullptr)); } // Count = -1 means that the array is empty TotalCount *= Count > 0 ? static_cast(Count) : 0; continue; } } DINodeArray SubscriptArray = getDIBuilder(DebugInst).getOrCreateArray(Subscripts); size_t Size = getDerivedSizeInBits(BaseTy) * TotalCount; return getDIBuilder(DebugInst).createArrayType(Size, 0 /*align*/, BaseTy, SubscriptArray); } DICompositeType * SPIRVToLLVMDbgTran::transTypeArrayNonSemantic(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeArray; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); DIType *BaseTy = transNonNullDebugType(BM->get(Ops[BaseTypeIdx])); size_t TotalCount = 1; SmallVector Subscripts; if (DebugInst->getExtOp() == SPIRVDebug::TypeArray) { for (size_t I = SubrangesIdx; I < Ops.size(); ++I) { auto *SR = transDebugInst(BM->get(Ops[I])); if (auto *Count = cast(SR->getCount())) TotalCount *= Count->getSExtValue() > 0 ? Count->getSExtValue() : 0; Subscripts.push_back(SR); } } DINodeArray SubscriptArray = getDIBuilder(DebugInst).getOrCreateArray(Subscripts); size_t Size = getDerivedSizeInBits(BaseTy) * TotalCount; return getDIBuilder(DebugInst).createArrayType(Size, 0 /*align*/, BaseTy, SubscriptArray); } DICompositeType * SPIRVToLLVMDbgTran::transTypeArrayDynamic(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeArrayDynamic; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); DIType *BaseTy = transNonNullDebugType(BM->get(Ops[BaseTypeIdx])); size_t TotalCount = 1; SmallVector Subscripts; for (size_t I = SubrangesIdx; I < Ops.size(); ++I) { auto *SR = transDebugInst(BM->get(Ops[I])); if (auto *Count = cast(SR->getCount())) TotalCount *= Count->getSExtValue() > 0 ? Count->getSExtValue() : 0; Subscripts.push_back(SR); } DINodeArray SubscriptArray = getDIBuilder(DebugInst).getOrCreateArray(Subscripts); size_t Size = getDerivedSizeInBits(BaseTy) * TotalCount; auto TransOperand = [&](SPIRVWord Idx) -> PointerUnion { if (!getDbgInst(Ops[Idx])) { if (const auto *GV = getDbgInst(Ops[Idx])) return transDebugInst(GV); if (const auto *LV = getDbgInst(Ops[Idx])) return transDebugInst(LV); if (const auto *DIExpr = getDbgInst(Ops[Idx])) return transDebugInst(DIExpr); } return nullptr; }; PointerUnion DataLocation = TransOperand(DataLocationIdx); PointerUnion Associated = TransOperand(AssociatedIdx); PointerUnion Allocated = TransOperand(AllocatedIdx); PointerUnion Rank = TransOperand(RankIdx); return getDIBuilder(DebugInst).createArrayType(Size, 0 /*align*/, BaseTy, SubscriptArray, DataLocation, Associated, Allocated, Rank); } DICompositeType * SPIRVToLLVMDbgTran::transTypeVector(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeVector; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); DIType *BaseTy = transNonNullDebugType(BM->get(Ops[BaseTypeIdx])); SPIRVWord Count = getConstantValueOrLiteral(Ops, ComponentCountIdx, DebugInst->getExtSetKind()); // Round up to a power of two. // OpenCL/SYCL 3-element vectors // occupy the same amount of memory as 4-element vectors // Clang rounds up the memory size of vectors to a power of 2. // Vulkan allows vec3 to have a memory size of 12, but in RenderDoc memory // size is not derived from debug info. uint64_t Size = getDerivedSizeInBits(BaseTy) * llvm::bit_ceil(Count); SmallVector Subscripts; Subscripts.push_back(getDIBuilder(DebugInst).getOrCreateSubrange(0, Count)); DINodeArray SubscriptArray = getDIBuilder(DebugInst).getOrCreateArray(Subscripts); return getDIBuilder(DebugInst).createVectorType(Size, 0 /*align*/, BaseTy, SubscriptArray); } DICompositeType * SPIRVToLLVMDbgTran::transTypeComposite(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeComposite; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); DIScope *ParentScope = getScope(BM->getEntry(Ops[ParentIdx])); uint64_t Size = 0; SPIRVEntry *SizeEntry = BM->getEntry(Ops[SizeIdx]); if (!(SizeEntry->isExtInst(SPIRVEIS_Debug, SPIRVDebug::DebugInfoNone) || SizeEntry->isExtInst(SPIRVEIS_OpenCL_DebugInfo_100, SPIRVDebug::DebugInfoNone) || SizeEntry->isExtInst(SPIRVEIS_NonSemantic_Shader_DebugInfo_200, SPIRVDebug::DebugInfoNone))) { Size = BM->get(Ops[SizeIdx])->getZExtIntValue(); } uint64_t Align = 0; DIType *DerivedFrom = nullptr; StringRef Identifier; SPIRVEntry *UniqId = BM->get(Ops[LinkageNameIdx]); if (UniqId->getOpCode() == OpString) Identifier = static_cast(UniqId)->getStr(); DINode::DIFlags Flags = DINode::FlagZero; SPIRVWord SPIRVFlags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); if (SPIRVFlags & SPIRVDebug::FlagIsFwdDecl) Flags |= DINode::FlagFwdDecl; if (SPIRVFlags & SPIRVDebug::FlagTypePassByValue) Flags |= DINode::FlagTypePassByValue; if (SPIRVFlags & SPIRVDebug::FlagTypePassByReference) Flags |= DINode::FlagTypePassByReference; DICompositeType *CT = nullptr; switch (getConstantValueOrLiteral(Ops, TagIdx, DebugInst->getExtSetKind())) { case SPIRVDebug::Class: // TODO: should be replaced with createClassType, when bug with creating // ClassType with llvm::dwarf::DW_TAG_struct_type tag will be fixed CT = getDIBuilder(DebugInst).createReplaceableCompositeType( llvm::dwarf::DW_TAG_class_type, Name, ParentScope, File, LineNo, 0, Size, Align, Flags, Identifier); CT = llvm::MDNode::replaceWithDistinct(llvm::TempDICompositeType(CT)); break; case SPIRVDebug::Structure: CT = getDIBuilder(DebugInst).createStructType( ParentScope, Name, File, LineNo, Size, Align, Flags, DerivedFrom, DINodeArray() /*elements*/, 0 /*RunTimeLang*/, nullptr /*VTableHolder*/, Identifier); break; case SPIRVDebug::Union: CT = getDIBuilder(DebugInst).createUnionType( ParentScope, Name, File, LineNo, Size, Align, Flags, DINodeArray(), 0 /*RuntimrLang*/, Identifier); break; default: llvm_unreachable("Unexpected composite type"); break; } DebugInstCache[DebugInst] = CT; SmallVector EltTys; for (size_t I = FirstMemberIdx; I < Ops.size(); ++I) { auto *MemberInst = BM->get(Ops[I]); if (MemberInst->getExtOp() == SPIRVDebug::TypeMember) { auto *SPVMemberInst = BM->get(Ops[I]); DINode *MemberMD = transTypeMember(SPVMemberInst, DebugInst, cast(CT)); EltTys.push_back(MemberMD); DebugInstCache[SPVMemberInst] = MemberMD; } else if (MemberInst->getExtOp() == SPIRVDebug::TypeInheritance) { auto *SPVMemberInst = BM->get(Ops[I]); DINode *MemberMD = transTypeInheritance(SPVMemberInst, cast(CT)); EltTys.push_back(MemberMD); DebugInstCache[SPVMemberInst] = MemberMD; } else { EltTys.emplace_back(transDebugInst(BM->get(Ops[I]))); } } DINodeArray Elements = getDIBuilder(DebugInst).getOrCreateArray(EltTys); getDIBuilder(DebugInst).replaceArrays(CT, Elements); assert(CT && "Composite type translation failed."); return CT; } DISubrange * SPIRVToLLVMDbgTran::transTypeSubrange(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeSubrange; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert((Ops.size() == MinOperandCount || Ops.size() == MaxOperandCount) && "Invalid number of operands"); std::vector TranslatedOps(MaxOperandCount, nullptr); auto TransOperand = [&Ops, &TranslatedOps, this](int Idx) -> void { if (!getDbgInst(Ops[Idx])) { if (auto *GlobalVar = getDbgInst(Ops[Idx])) { TranslatedOps[Idx] = cast(transDebugInst(GlobalVar)); } else if (auto *LocalVar = getDbgInst(Ops[Idx])) { TranslatedOps[Idx] = cast(transDebugInst(LocalVar)); } else if (auto *Expr = getDbgInst(Ops[Idx])) { TranslatedOps[Idx] = cast(transDebugInst(Expr)); } else if (auto *Const = BM->get(Ops[Idx])) { int64_t ConstantAsInt = static_cast(Const->getZExtIntValue()); TranslatedOps[Idx] = cast(ConstantAsMetadata::get( ConstantInt::get(M->getContext(), APInt(64, ConstantAsInt)))); } } }; for (size_t Idx = 0; Idx < Ops.size(); ++Idx) TransOperand(Idx); return getDIBuilder(DebugInst).getOrCreateSubrange( TranslatedOps[CountIdx], TranslatedOps[LowerBoundIdx], TranslatedOps[UpperBoundIdx], TranslatedOps[StrideIdx]); } DIStringType * SPIRVToLLVMDbgTran::transTypeString(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeString; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); unsigned Encoding = 0; if (!getDbgInst((Ops[BaseTypeIdx]))) { DIBasicType *BaseTy = transTypeBasic(BM->get(Ops[BaseTypeIdx])); Encoding = BaseTy->getEncoding(); } DIExpression *StrLocationExp = nullptr; if (!getDbgInst(Ops[DataLocationIdx])) { if (const auto *DIExpr = getDbgInst(Ops[DataLocationIdx])) StrLocationExp = transDebugInst(DIExpr); } uint64_t SizeInBits = BM->get(Ops[SizeIdx])->getZExtIntValue(); DIExpression *StringLengthExp = nullptr; DIVariable *StringLengthVar = nullptr; if (!getDbgInst(Ops[LengthAddrIdx])) { if (const auto *GV = getDbgInst(Ops[LengthAddrIdx])) StringLengthVar = transDebugInst(GV); if (const auto *LV = getDbgInst(Ops[LengthAddrIdx])) StringLengthVar = transDebugInst(LV); if (const auto *DIExpr = getDbgInst(Ops[LengthAddrIdx])) StringLengthExp = transDebugInst(DIExpr); } return DIStringType::get(M->getContext(), dwarf::DW_TAG_string_type, Name, cast_or_null(StringLengthVar), cast_or_null(StringLengthExp), cast_or_null(StrLocationExp), SizeInBits, 0 /*AlignInBits*/, Encoding); } DINode *SPIRVToLLVMDbgTran::transTypeMember(const SPIRVExtInst *DebugInst, const SPIRVExtInst *ParentInst, DIScope *Scope) { if (isNonSemanticDebugInfo(DebugInst->getExtSetKind())) // In NonSemantic spec TypeMember doesn't have Scope parameter return transTypeMemberNonSemantic(DebugInst, ParentInst, Scope); return transTypeMemberOpenCL(DebugInst); } DINode * SPIRVToLLVMDbgTran::transTypeMemberOpenCL(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeMember::OpenCL; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); StringRef Name = getString(Ops[NameIdx]); DIScope *Scope = getScope(BM->getEntry(Ops[ParentIdx])); DIType *BaseType = transNonNullDebugType(BM->get(Ops[TypeIdx])); uint64_t OffsetInBits = BM->get(Ops[OffsetIdx])->getZExtIntValue(); SPIRVWord SPIRVFlags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); DINode::DIFlags Flags = DINode::FlagZero; if ((SPIRVDebug::FlagAccess & SPIRVFlags) == SPIRVDebug::FlagIsPublic) { Flags |= DINode::FlagPublic; } else if (SPIRVFlags & SPIRVDebug::FlagIsProtected) { Flags |= DINode::FlagProtected; } else if (SPIRVFlags & SPIRVDebug::FlagIsPrivate) { Flags |= DINode::FlagPrivate; } if (SPIRVFlags & SPIRVDebug::FlagIsStaticMember) Flags |= DINode::FlagStaticMember; if (Flags & DINode::FlagStaticMember) { llvm::Value *Val = nullptr; if (Ops.size() > MinOperandCount) { SPIRVValue *ConstVal = BM->get(Ops[ValueIdx]); assert(isConstantOpCode(ConstVal->getOpCode()) && "Static member must be a constant"); Val = SPIRVReader->transValue(ConstVal, nullptr, nullptr); } auto Tag = M->getDwarfVersion() >= 5 ? llvm::dwarf::DW_TAG_variable : llvm::dwarf::DW_TAG_member; return getDIBuilder(DebugInst).createStaticMemberType( Scope, Name, File, LineNo, BaseType, Flags, cast_or_null(Val), Tag); } uint64_t Size = BM->get(Ops[SizeIdx])->getZExtIntValue(); uint64_t Alignment = 0; return getDIBuilder(DebugInst).createMemberType(Scope, Name, File, LineNo, Size, Alignment, OffsetInBits, Flags, BaseType); } DINode * SPIRVToLLVMDbgTran::transTypeMemberNonSemantic(const SPIRVExtInst *DebugInst, const SPIRVExtInst *ParentInst, DIScope *Scope) { if (!Scope) // Will be translated later when processing TypeMember's parent return nullptr; using namespace SPIRVDebug::Operand::TypeMember::NonSemantic; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); StringRef Name = getString(Ops[NameIdx]); DIType *BaseType = transNonNullDebugType(BM->get(Ops[TypeIdx])); uint64_t OffsetInBits = BM->get(Ops[OffsetIdx])->getZExtIntValue(); SPIRVWord SPIRVFlags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); DINode::DIFlags Flags = DINode::FlagZero; if ((SPIRVDebug::FlagAccess & SPIRVFlags) == SPIRVDebug::FlagIsPublic) { Flags |= DINode::FlagPublic; } else if (SPIRVFlags & SPIRVDebug::FlagIsProtected) { Flags |= DINode::FlagProtected; } else if (SPIRVFlags & SPIRVDebug::FlagIsPrivate) { Flags |= DINode::FlagPrivate; } if (SPIRVFlags & SPIRVDebug::FlagIsStaticMember) Flags |= DINode::FlagStaticMember; if (SPIRVFlags & SPIRVDebug::FlagBitField) Flags |= DINode::FlagBitField; if (Flags & DINode::FlagStaticMember) { llvm::Value *Val = nullptr; if (Ops.size() > MinOperandCount) { SPIRVValue *ConstVal = BM->get(Ops[ValueIdx]); assert(isConstantOpCode(ConstVal->getOpCode()) && "Static member must be a constant"); Val = SPIRVReader->transValue(ConstVal, nullptr, nullptr); } auto Tag = M->getDwarfVersion() >= 5 ? llvm::dwarf::DW_TAG_variable : llvm::dwarf::DW_TAG_member; return getDIBuilder(DebugInst).createStaticMemberType( Scope, Name, File, LineNo, BaseType, Flags, cast_or_null(Val), Tag); } uint64_t Size = BM->get(Ops[SizeIdx])->getZExtIntValue(); uint64_t Alignment = 0; return getDIBuilder(ParentInst) .createMemberType(Scope, Name, File, LineNo, Size, Alignment, OffsetInBits, Flags, BaseType); } DINode *SPIRVToLLVMDbgTran::transTypeEnum(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeEnum; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); DIScope *Scope = getScope(BM->getEntry(Ops[ParentIdx])); uint64_t SizeInBits = BM->get(Ops[SizeIdx])->getZExtIntValue(); SPIRVWord AlignInBits = 0; SPIRVWord Flags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); if (Flags & SPIRVDebug::FlagIsFwdDecl) { return getDIBuilder(DebugInst).createForwardDecl( dwarf::DW_TAG_enumeration_type, Name, Scope, File, LineNo, AlignInBits, SizeInBits); } else { SmallVector Elts; for (size_t I = FirstEnumeratorIdx, E = Ops.size(); I < E; I += 2) { uint64_t Val = BM->get(Ops[I])->getZExtIntValue(); StringRef Name = getString(Ops[I + 1]); Elts.push_back(getDIBuilder(DebugInst).createEnumerator(Name, Val)); } DINodeArray Enumerators = getDIBuilder(DebugInst).getOrCreateArray(Elts); DIType *UnderlyingType = nullptr; SPIRVEntry *E = BM->getEntry(Ops[UnderlyingTypeIdx]); if (!isa(E)) UnderlyingType = transDebugInst(static_cast(E)); return getDIBuilder(DebugInst).createEnumerationType( Scope, Name, File, LineNo, SizeInBits, AlignInBits, Enumerators, UnderlyingType, 0, "", Flags & SPIRVDebug::FlagIsEnumClass); } } DINode *SPIRVToLLVMDbgTran::transTypeFunction(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeFunction; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); SPIRVWord SPIRVFlags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); DINode::DIFlags Flags = DINode::FlagZero; if (SPIRVFlags & SPIRVDebug::FlagIsLValueReference) Flags |= llvm::DINode::FlagLValueReference; if (SPIRVFlags & SPIRVDebug::FlagIsRValueReference) Flags |= llvm::DINode::FlagRValueReference; SPIRVEntry *E = BM->getEntry(Ops[ReturnTypeIdx]); MDNode *RT = isa(E) ? nullptr : transDebugInst(BM->get(Ops[ReturnTypeIdx])); SmallVector Elements{RT}; for (size_t I = FirstParameterIdx, E = Ops.size(); I < E; ++I) { SPIRVEntry *P = BM->getEntry(Ops[I]); MDNode *Param = isa(P) ? nullptr : transDebugInst(BM->get(Ops[I])); Elements.push_back(Param); } DITypeRefArray ArgTypes = getDIBuilder(DebugInst).getOrCreateTypeArray(Elements); return getDIBuilder(DebugInst).createSubroutineType(ArgTypes, Flags); } DINode * SPIRVToLLVMDbgTran::transTypePtrToMember(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypePtrToMember; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= OperandCount && "Invalid number of operands"); SPIRVExtInst *Member = BM->get(Ops[MemberTypeIdx]); DIType *PointeeTy = transNonNullDebugType(Member); SPIRVExtInst *ContainingTy = BM->get(Ops[ParentIdx]); DIType *BaseTy = transNonNullDebugType(ContainingTy); return getDIBuilder(DebugInst).createMemberPointerType(PointeeTy, BaseTy, 0); } DINode *SPIRVToLLVMDbgTran::transLexicalBlock(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::LexicalBlock; const SPIRVWordVec &Ops = DebugInst->getArguments(); DIScope *ParentScope = getScope(BM->getEntry(Ops[ParentIdx])); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); if (Ops.size() > MinOperandCount) { StringRef Name = getString(Ops[NameIdx]); bool InlinedNamespace = false; if (DebugInst->getExtSetKind() == SPIRVEIS_NonSemantic_Shader_DebugInfo_200) { SPIRVValue *V = BM->get(Ops[InlineNamespaceIdx]); Value *Var = SPIRVReader->transValue(V, nullptr, nullptr); InlinedNamespace = cast(Var)->isOne(); } return getDIBuilder(DebugInst).createNameSpace(ParentScope, Name, InlinedNamespace); } unsigned Column = Ops[ColumnIdx]; return getDIBuilder(DebugInst).createLexicalBlock(ParentScope, File, LineNo, Column); } DINode *SPIRVToLLVMDbgTran::transLexicalBlockDiscriminator( const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::LexicalBlockDiscriminator; const SPIRVWordVec &Ops = DebugInst->getArguments(); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord Disc = getConstantValueOrLiteral(Ops, DiscriminatorIdx, DebugInst->getExtSetKind()); DIScope *ParentScope = getScope(BM->getEntry(Ops[ParentIdx])); return getDIBuilder(DebugInst).createLexicalBlockFile(ParentScope, File, Disc); } DINode *SPIRVToLLVMDbgTran::transFunction(const SPIRVExtInst *DebugInst, bool IsMainSubprogram) { using namespace SPIRVDebug::Operand::Function; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCountNonSem && "Invalid number of operands"); if (!isNonSemanticDebugInfo(DebugInst->getExtSetKind())) assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); DISubroutineType *Ty = transDebugInst(BM->get(Ops[TypeIdx])); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); DIScope *Scope = getScope(BM->getEntry(Ops[ParentIdx])); StringRef LinkageName = getString(Ops[LinkageNameIdx]); SPIRVWord SPIRVDebugFlags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); DINode::DIFlags Flags = DINode::FlagZero; if (SPIRVDebugFlags & SPIRVDebug::FlagIsArtificial) Flags |= llvm::DINode::FlagArtificial; if (SPIRVDebugFlags & SPIRVDebug::FlagIsExplicit) Flags |= llvm::DINode::FlagExplicit; if (SPIRVDebugFlags & SPIRVDebug::FlagIsPrototyped) Flags |= llvm::DINode::FlagPrototyped; if (SPIRVDebugFlags & SPIRVDebug::FlagIsLValueReference) Flags |= llvm::DINode::FlagLValueReference; if (SPIRVDebugFlags & SPIRVDebug::FlagIsRValueReference) Flags |= llvm::DINode::FlagRValueReference; if ((SPIRVDebugFlags & SPIRVDebug::FlagAccess) == SPIRVDebug::FlagIsPublic) Flags |= llvm::DINode::FlagPublic; if (SPIRVDebugFlags & SPIRVDebug::FlagIsProtected) Flags |= llvm::DINode::FlagProtected; if (SPIRVDebugFlags & SPIRVDebug::FlagIsPrivate) Flags |= llvm::DINode::FlagPrivate; bool IsDefinition = SPIRVDebugFlags & SPIRVDebug::FlagIsDefinition; bool IsOptimized = SPIRVDebugFlags & SPIRVDebug::FlagIsOptimized; bool IsLocal = SPIRVDebugFlags & SPIRVDebug::FlagIsLocal; bool IsMainSubprogramFlag = IsMainSubprogram || (!isNonSemanticDebugInfo(DebugInst->getExtSetKind()) && BM->isEntryPoint(spv::ExecutionModelKernel, Ops[FunctionIdIdx])); DISubprogram::DISPFlags SPFlags = DISubprogram::toSPFlags( IsLocal, IsDefinition, IsOptimized, DISubprogram::SPFlagNonvirtual, IsMainSubprogramFlag); SPIRVWord ScopeLine = getConstantValueOrLiteral(Ops, ScopeLineIdx, DebugInst->getExtSetKind()); // Function declaration descriptor DISubprogram *FD = nullptr; if (isNonSemanticDebugInfo(DebugInst->getExtSetKind()) && Ops.size() > DeclarationNonSemIdx) { FD = transDebugInst( BM->get(Ops[DeclarationNonSemIdx])); } else if (Ops.size() > DeclarationIdx) { FD = transDebugInst( BM->get(Ops[DeclarationIdx])); } // Here we create fake array of template parameters. If it was plain nullptr, // the template parameter operand would be removed in DISubprogram::getImpl. // But we want it to be there, because if there is DebugTypeTemplate // instruction refering to this function, transTypeTemplate method must be // able to replace the template parameter operand, thus it must be in the // operands list. SmallVector Elts; DINodeArray TParams = getDIBuilder(DebugInst).getOrCreateArray(Elts); llvm::DITemplateParameterArray TParamsArray = TParams.get(); DISubprogram *DIS = nullptr; if (Scope && (isa(Scope) || isa(Scope)) && !IsDefinition) DIS = getDIBuilder(DebugInst).createMethod(Scope, Name, LinkageName, File, LineNo, Ty, 0, 0, nullptr, Flags, SPFlags, TParamsArray); else { // Create targetFuncName mostly for Fortran trampoline function if it is // the case StringRef TargetFunction = ""; if (DebugInst->getExtSetKind() == SPIRVEIS_NonSemantic_Shader_DebugInfo_200 && Ops.size() > TargetFunctionNameIdx) { TargetFunction = getString(Ops[TargetFunctionNameIdx]); } DIS = getDIBuilder(DebugInst).createFunction( Scope, Name, LinkageName, File, LineNo, Ty, ScopeLine, Flags, SPFlags, TParamsArray, FD, /*ThrownTypes*/ nullptr, /*Annotations*/ nullptr, TargetFunction); } DebugInstCache[DebugInst] = DIS; // At this point, we don't have info about the function definition for // NonSemantic.Shader debug info. If function definition is present, it'll be // translated later within the function scope. // For "default" debug info we do translate function body here. if (!isNonSemanticDebugInfo(DebugInst->getExtSetKind())) transFunctionBody(DIS, Ops[FunctionIdIdx]); return DIS; } void SPIRVToLLVMDbgTran::transFunctionBody(DISubprogram *DIS, SPIRVId FuncId) { FuncMap[FuncId] = DIS; SPIRVEntry *E = BM->getEntry(FuncId); if (E->getOpCode() == OpFunction) { SPIRVFunction *BF = static_cast(E); llvm::Function *F = SPIRVReader->transFunction(BF); assert(F && "Translation of function failed!"); if (!F->hasMetadata("dbg")) F->setMetadata("dbg", DIS); } } DINode * SPIRVToLLVMDbgTran::transFunctionDefinition(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::FunctionDefinition; const SPIRVWordVec &Ops = DebugInst->getArguments(); SPIRVExtInst *Func = BM->get(Ops[FunctionIdx]); DISubprogram *LLVMFunc = cast(DebugInstCache[Func]); transFunctionBody(LLVMFunc, Ops[DefinitionIdx]); return nullptr; } DINode *SPIRVToLLVMDbgTran::transFunctionDecl(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::FunctionDeclaration; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() == OperandCount && "Invalid number of operands"); // Scope DIScope *Scope = getScope(BM->getEntry(Ops[ParentIdx])); StringRef Name = getString(Ops[NameIdx]); StringRef LinkageName = getString(Ops[LinkageNameIdx]); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); DISubroutineType *Ty = transDebugInst(BM->get(Ops[TypeIdx])); SPIRVWord SPIRVDebugFlags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); bool IsDefinition = SPIRVDebugFlags & SPIRVDebug::FlagIsDefinition; bool IsOptimized = SPIRVDebugFlags & SPIRVDebug::FlagIsOptimized; bool IsLocal = SPIRVDebugFlags & SPIRVDebug::FlagIsLocal; DINode::DIFlags Flags = DINode::FlagZero; if (SPIRVDebugFlags & SPIRVDebug::FlagIsArtificial) Flags |= llvm::DINode::FlagArtificial; if (SPIRVDebugFlags & SPIRVDebug::FlagIsExplicit) Flags |= llvm::DINode::FlagExplicit; if (SPIRVDebugFlags & SPIRVDebug::FlagIsPrototyped) Flags |= llvm::DINode::FlagPrototyped; if (SPIRVDebugFlags & SPIRVDebug::FlagIsLValueReference) Flags |= llvm::DINode::FlagLValueReference; if (SPIRVDebugFlags & SPIRVDebug::FlagIsRValueReference) Flags |= llvm::DINode::FlagRValueReference; if ((SPIRVDebugFlags & SPIRVDebug::FlagAccess) == SPIRVDebug::FlagIsPublic) Flags |= llvm::DINode::FlagPublic; if (SPIRVDebugFlags & SPIRVDebug::FlagIsProtected) Flags |= llvm::DINode::FlagProtected; if (SPIRVDebugFlags & SPIRVDebug::FlagIsPrivate) Flags |= llvm::DINode::FlagPrivate; // Here we create fake array of template parameters. If it was plain nullptr, // the template parameter operand would be removed in DISubprogram::getImpl. // But we want it to be there, because if there is DebugTypeTemplate // instruction refering to this function, transTypeTemplate method must be // able to replace the template parameter operand, thus it must be in the // operands list. SmallVector Elts; DINodeArray TParams = getDIBuilder(DebugInst).getOrCreateArray(Elts); llvm::DITemplateParameterArray TParamsArray = TParams.get(); DISubprogram *DIS = nullptr; DISubprogram::DISPFlags SPFlags = DISubprogram::toSPFlags(IsLocal, IsDefinition, IsOptimized); if (isa(Scope) || isa(Scope)) DIS = getDIBuilder(DebugInst).createMethod(Scope, Name, LinkageName, File, LineNo, Ty, 0, 0, nullptr, Flags, SPFlags, TParamsArray); else { // Since a function declaration doesn't have any retained nodes, resolve // the temporary placeholder for them immediately. DIS = getDIBuilder(DebugInst).createTempFunctionFwdDecl( Scope, Name, LinkageName, File, LineNo, Ty, 0, Flags, SPFlags, TParamsArray); llvm::TempMDNode FwdDecl(cast(DIS)); DIS = getDIBuilder(DebugInst).replaceTemporary(std::move(FwdDecl), DIS); } DebugInstCache[DebugInst] = DIS; return DIS; } MDNode *SPIRVToLLVMDbgTran::transEntryPoint(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::EntryPoint; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() == OperandCount && "Invalid number of operands"); SPIRVExtInst *EP = BM->get(Ops[EntryPointIdx]); SPIRVExtInst *CU = BM->get(Ops[CompilationUnitIdx]); std::string Producer = getString(Ops[CompilerSignatureIdx]); std::string CLArgs = getString(Ops[CommandLineArgsIdx]); DICompileUnit *C = transCompilationUnit(CU, Producer, CLArgs); // NOLINT DebugInstCache[CU] = C; return transFunction(EP, true /*IsMainSubprogram*/); } MDNode *SPIRVToLLVMDbgTran::transGlobalVariable(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::GlobalVariable; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); DIType *Ty = transNonNullDebugType(BM->get(Ops[TypeIdx])); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); DIScope *Parent = getScope(BM->getEntry(Ops[ParentIdx])); StringRef LinkageName = getString(Ops[LinkageNameIdx]); DIDerivedType *StaticMemberDecl = nullptr; if (Ops.size() > MinOperandCount) { StaticMemberDecl = transDebugInst( BM->get(Ops[StaticMemberDeclarationIdx])); } DIExpression *DIExpr = nullptr; // Check if Ops[VariableIdx] is not being used to hold a variable operand. // Instead it is being used to hold an Expression that holds the initial // value of the GlobalVariable. if (getDbgInst(Ops[VariableIdx])) DIExpr = transDebugInst(BM->get(Ops[VariableIdx])); SPIRVWord Flags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); bool IsLocal = Flags & SPIRVDebug::FlagIsLocal; bool IsDefinition = Flags & SPIRVDebug::FlagIsDefinition; MDNode *VarDecl = nullptr; if (IsDefinition) { VarDecl = getDIBuilder(DebugInst).createGlobalVariableExpression( Parent, Name, LinkageName, File, LineNo, Ty, IsLocal, IsDefinition, DIExpr, StaticMemberDecl); } else { VarDecl = getDIBuilder(DebugInst).createTempGlobalVariableFwdDecl( Parent, Name, LinkageName, File, LineNo, Ty, IsLocal, StaticMemberDecl); // replaceAllUsesWith call makes VarDecl non-temp. // Otherwise DIBuilder will crash at finalization. llvm::TempMDNode TMP(VarDecl); VarDecl = getDIBuilder(DebugInst).replaceTemporary(std::move(TMP), VarDecl); } // Ops[VariableIdx] was not used to hold an Expression with the initial value // for the GlobalVariable if (!DIExpr) { // If the variable has no initializer Ops[VariableIdx] is OpDebugInfoNone. // Otherwise Ops[VariableIdx] may be a global variable or a constant(C++ // static const). if (VarDecl && !getDbgInst(Ops[VariableIdx])) { SPIRVValue *V = BM->get(Ops[VariableIdx]); Value *Var = SPIRVReader->transValue(V, nullptr, nullptr); llvm::GlobalVariable *GV = dyn_cast_or_null(Var); if (GV && !GV->hasMetadata("dbg")) GV->addMetadata("dbg", *VarDecl); } } return VarDecl; } DINode *SPIRVToLLVMDbgTran::transLocalVariable(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::LocalVariable; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); DIScope *Scope = getScope(BM->getEntry(Ops[ParentIdx])); StringRef Name = getString(Ops[NameIdx]); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); DIType *Ty = transNonNullDebugType(BM->get(Ops[TypeIdx])); DINode::DIFlags Flags = DINode::FlagZero; SPIRVWord SPIRVFlags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); if (SPIRVFlags & SPIRVDebug::FlagIsArtificial) Flags |= DINode::FlagArtificial; if (SPIRVFlags & SPIRVDebug::FlagIsObjectPointer) Flags |= DINode::FlagObjectPointer; if (Ops.size() > ArgNumberIdx) return getDIBuilder(DebugInst).createParameterVariable( Scope, Name, Ops[ArgNumberIdx], File, LineNo, Ty, true, Flags); return getDIBuilder(DebugInst).createAutoVariable(Scope, Name, File, LineNo, Ty, true, Flags); } DINode *SPIRVToLLVMDbgTran::transTypedef(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::Typedef; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= OperandCount && "Invalid number of operands"); DIFile *File = getFile(Ops[SourceIdx]); SPIRVWord LineNo = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); StringRef Alias = getString(Ops[NameIdx]); SPIRVEntry *TypeInst = BM->getEntry(Ops[BaseTypeIdx]); DIType *Ty = transDebugInst(static_cast(TypeInst)); DIScope *Scope = getScope(BM->getEntry(Ops[ParentIdx])); assert(Scope && "Typedef should have a parent scope"); return getDIBuilder(DebugInst).createTypedef(Ty, Alias, File, LineNo, Scope); } DINode *SPIRVToLLVMDbgTran::transTypeInheritance(const SPIRVExtInst *DebugInst, DIType *ChildClass) { using namespace SPIRVDebug::Operand::TypeInheritance; // The value is used when assertions are enabled [[maybe_unused]] unsigned OperandCount; unsigned ParentIdx, OffsetIdx, FlagsIdx; if (isNonSemanticDebugInfo(DebugInst->getExtSetKind())) { if (!ChildClass) { // Will be translated later when processing TypeMember's parent return nullptr; } OperandCount = NonSemantic::OperandCount; ParentIdx = NonSemantic::ParentIdx; OffsetIdx = NonSemantic::OffsetIdx; FlagsIdx = NonSemantic::FlagsIdx; } else { OperandCount = OpenCL::OperandCount; ParentIdx = OpenCL::ParentIdx; OffsetIdx = OpenCL::OffsetIdx; FlagsIdx = OpenCL::FlagsIdx; } const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() == OperandCount && "Invalid number of operands"); DIType *Parent = transDebugInst(BM->get(Ops[ParentIdx])); DINode::DIFlags Flags = DINode::FlagZero; SPIRVWord SPIRVFlags = getConstantValueOrLiteral(Ops, FlagsIdx, DebugInst->getExtSetKind()); if ((SPIRVFlags & SPIRVDebug::FlagAccess) == SPIRVDebug::FlagIsPublic) Flags |= llvm::DINode::FlagPublic; if ((SPIRVFlags & SPIRVDebug::FlagAccess) == SPIRVDebug::FlagIsProtected) Flags |= llvm::DINode::FlagProtected; if ((SPIRVFlags & SPIRVDebug::FlagAccess) == SPIRVDebug::FlagIsPrivate) Flags |= llvm::DINode::FlagPrivate; uint64_t OffsetVal = BM->get(Ops[OffsetIdx])->getZExtIntValue(); DIType *Child; if (isNonSemanticDebugInfo(DebugInst->getExtSetKind())) { Child = ChildClass; } else { Child = transDebugInst(BM->get(Ops[OpenCL::ChildIdx])); } return getDIBuilder(DebugInst).createInheritance(Child, Parent, OffsetVal, 0, Flags); } DINode * SPIRVToLLVMDbgTran::transTypeTemplateParameter(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeTemplateParameter; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= OperandCount && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); SPIRVEntry *ActualType = BM->getEntry(Ops[TypeIdx]); DIType *Ty = nullptr; if (!isa(ActualType)) Ty = transDebugInst(static_cast(ActualType)); DIScope *Context = nullptr; if (!getDbgInst(Ops[ValueIdx])) { SPIRVValue *Val = BM->get(Ops[ValueIdx]); Value *V = SPIRVReader->transValue(Val, nullptr, nullptr); return getDIBuilder(DebugInst).createTemplateValueParameter( Context, Name, Ty, false, cast(V)); } return getDIBuilder(DebugInst).createTemplateTypeParameter(Context, Name, Ty, false); } DINode *SPIRVToLLVMDbgTran::transTypeTemplateTemplateParameter( const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeTemplateTemplateParameter; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= OperandCount && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); StringRef TemplName = getString(Ops[TemplateNameIdx]); DIScope *Context = nullptr; return getDIBuilder(DebugInst).createTemplateTemplateParameter( Context, Name, nullptr, TemplName); } DINode *SPIRVToLLVMDbgTran::transTypeTemplateParameterPack( const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeTemplateParameterPack; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); StringRef Name = getString(Ops[NameIdx]); SmallVector Elts; for (size_t I = FirstParameterIdx, E = Ops.size(); I < E; ++I) { Elts.push_back(transDebugInst(BM->get(Ops[I]))); } DINodeArray Pack = getDIBuilder(DebugInst).getOrCreateArray(Elts); DIScope *Context = nullptr; return getDIBuilder(DebugInst).createTemplateParameterPack(Context, Name, nullptr, Pack); } MDNode *SPIRVToLLVMDbgTran::transTypeTemplate(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::TypeTemplate; const SPIRVWordVec &Ops = DebugInst->getArguments(); const size_t NumOps = Ops.size(); assert(NumOps >= MinOperandCount && "Invalid number of operands"); auto *Templ = BM->get(Ops[TargetIdx]); MDNode *D = transDebugInst(Templ); SmallVector Elts; for (size_t I = FirstParameterIdx; I < NumOps; ++I) { Elts.push_back(transDebugInst(BM->get(Ops[I]))); } DINodeArray TParams = getDIBuilder(DebugInst).getOrCreateArray(Elts); if (DICompositeType *Comp = dyn_cast(D)) { getDIBuilder(DebugInst).replaceArrays(Comp, Comp->getElements(), TParams); return Comp; } if (isa(D)) { // This constant matches with one used in // DISubprogram::getRawTemplateParams() const unsigned TemplateParamsIndex = 9; D->replaceOperandWith(TemplateParamsIndex, TParams.get()); return D; } llvm_unreachable("Invalid template"); } DINode *SPIRVToLLVMDbgTran::transImportedEntry(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::ImportedEntity; const SPIRVWordVec &Ops = DebugInst->getArguments(); // FIXME: 'OpenCL/bugged' version is kept because it's hard to remove it // It's W/A for missing 2nd index in OpenCL's implementation const SPIRVWord OffsetIdx = static_cast(isNonSemanticDebugInfo(DebugInst->getExtSetKind())); assert(Ops.size() == (OpenCL::OperandCount - OffsetIdx) && "Invalid number of operands"); DIScope *Scope = getScope(BM->getEntry(Ops[OpenCL::ParentIdx - OffsetIdx])); SPIRVWord Line = getConstantValueOrLiteral(Ops, OpenCL::LineIdx - OffsetIdx, DebugInst->getExtSetKind()); DIFile *File = getFile(Ops[OpenCL::SourceIdx - OffsetIdx]); auto *Entity = transDebugInst( BM->get(Ops[OpenCL::EntityIdx - OffsetIdx])); SPIRVWord Tag = getConstantValueOrLiteral(Ops, OpenCL::TagIdx, DebugInst->getExtSetKind()); if (Tag == SPIRVDebug::ImportedModule) { if (!Entity) return getDIBuilder(DebugInst).createImportedModule( Scope, static_cast(nullptr), File, Line); if (DIModule *DM = dyn_cast(Entity)) return getDIBuilder(DebugInst).createImportedModule(Scope, DM, File, Line); if (DIImportedEntity *IE = dyn_cast(Entity)) return getDIBuilder(DebugInst).createImportedModule(Scope, IE, File, Line); if (DINamespace *NS = dyn_cast(Entity)) return getDIBuilder(DebugInst).createImportedModule(Scope, NS, File, Line); } if (Tag == SPIRVDebug::ImportedDeclaration) { StringRef Name = getString(Ops[OpenCL::NameIdx]); if (DIGlobalVariableExpression *GVE = dyn_cast(Entity)) return getDIBuilder(DebugInst).createImportedDeclaration( Scope, GVE->getVariable(), File, Line, Name); return getDIBuilder(DebugInst).createImportedDeclaration(Scope, Entity, File, Line, Name); } llvm_unreachable("Unexpected kind of imported entity!"); } DINode *SPIRVToLLVMDbgTran::transModule(const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::ModuleINTEL; const SPIRVWordVec &Ops = DebugInst->getArguments(); assert(Ops.size() >= OperandCount && "Invalid number of operands"); DIScope *Scope = getScope(BM->getEntry(Ops[ParentIdx])); // In case we translate DebugModuleINTEL instruction (not DebugModule of // NonSemantic.Shader.200 spec), we should not apply the rule "literals are // not allowed for NonSemantic specification". This is a hack to avoid getting // constant value instead of literal in such case. const SPIRVExtInstSetKind ExtKind = DebugInst->getExtOp() == SPIRVDebug::Instruction::ModuleINTEL ? SPIRVEIS_OpenCL_DebugInfo_100 : DebugInst->getExtSetKind(); SPIRVWord Line = getConstantValueOrLiteral(Ops, LineIdx, ExtKind); DIFile *File = getFile(Ops[SourceIdx]); StringRef Name = getString(Ops[NameIdx]); StringRef ConfigMacros = getString(Ops[ConfigMacrosIdx]); StringRef IncludePath = getString(Ops[IncludePathIdx]); StringRef ApiNotes = getString(Ops[ApiNotesIdx]); bool IsDecl = getConstantValueOrLiteral(Ops, IsDeclIdx, ExtKind); return getDIBuilder(DebugInst).createModule( Scope, Name, ConfigMacros, IncludePath, ApiNotes, File, Line, IsDecl); } MDNode *SPIRVToLLVMDbgTran::transExpression(const SPIRVExtInst *DebugInst) { const SPIRVWordVec &Args = DebugInst->getArguments(); std::vector Ops; for (SPIRVId A : Args) { using namespace SPIRVDebug::Operand::Operation; SPIRVExtInst *O = BM->get(A); const SPIRVWordVec &Operands = O->getArguments(); auto OpCode = static_cast(getConstantValueOrLiteral( Operands, OpCodeIdx, DebugInst->getExtSetKind())); Ops.push_back(SPIRV::DbgExpressionOpCodeMap::rmap(OpCode)); for (unsigned I = 1, E = Operands.size(); I < E; ++I) { Ops.push_back( getConstantValueOrLiteral(Operands, I, DebugInst->getExtSetKind())); } } ArrayRef Addr(Ops.data(), Ops.size()); return getDIBuilder(DebugInst).createExpression(Addr); } DIType * SPIRVToLLVMDbgTran::transNonNullDebugType(const SPIRVExtInst *DebugInst) { if (DebugInst->getExtOp() != SPIRVDebug::DebugInfoNone) return transDebugInst(DebugInst); return getDIBuilder(DebugInst).createUnspecifiedType("SPIRV unknown type"); } MDNode *SPIRVToLLVMDbgTran::transDebugInstImpl(const SPIRVExtInst *DebugInst) { switch (DebugInst->getExtOp()) { case SPIRVDebug::DebugInfoNone: return nullptr; case SPIRVDebug::CompilationUnit: return transCompilationUnit(DebugInst); case SPIRVDebug::TypeBasic: return transTypeBasic(DebugInst); case SPIRVDebug::TypeQualifier: return transTypeQualifier(DebugInst); case SPIRVDebug::TypePointer: return transTypePointer(DebugInst); case SPIRVDebug::TypeArray: return transTypeArray(DebugInst); case SPIRVDebug::TypeSubrange: return transTypeSubrange(DebugInst); case SPIRVDebug::TypeString: return transTypeString(DebugInst); case SPIRVDebug::TypeVector: return transTypeVector(DebugInst); case SPIRVDebug::TypeComposite: return transTypeComposite(DebugInst); case SPIRVDebug::TypeMember: return transTypeMember(DebugInst); case SPIRVDebug::TypePtrToMember: return transTypePtrToMember(DebugInst); case SPIRVDebug::TypeEnum: return transTypeEnum(DebugInst); case SPIRVDebug::TypeFunction: return transTypeFunction(DebugInst); case SPIRVDebug::LexicalBlock: return transLexicalBlock(DebugInst); case SPIRVDebug::LexicalBlockDiscriminator: return transLexicalBlockDiscriminator(DebugInst); case SPIRVDebug::Function: return transFunction(DebugInst); case SPIRVDebug::FunctionDeclaration: return transFunctionDecl(DebugInst); case SPIRVDebug::FunctionDefinition: return transFunctionDefinition(DebugInst); case SPIRVDebug::EntryPoint: return transEntryPoint(DebugInst); case SPIRVDebug::GlobalVariable: return transGlobalVariable(DebugInst); case SPIRVDebug::LocalVariable: return transLocalVariable(DebugInst); case SPIRVDebug::Typedef: return transTypedef(DebugInst); case SPIRVDebug::InlinedAt: return transDebugInlined(DebugInst); case SPIRVDebug::TypeInheritance: return transTypeInheritance(DebugInst); case SPIRVDebug::TypeTemplateParameter: return transTypeTemplateParameter(DebugInst); case SPIRVDebug::TypeTemplateTemplateParameter: return transTypeTemplateTemplateParameter(DebugInst); case SPIRVDebug::TypeTemplateParameterPack: return transTypeTemplateParameterPack(DebugInst); case SPIRVDebug::TypeTemplate: return transTypeTemplate(DebugInst); case SPIRVDebug::ImportedEntity: return transImportedEntry(DebugInst); case SPIRVDebug::Module: case SPIRVDebug::ModuleINTEL: return transModule(DebugInst); case SPIRVDebug::Operation: // To be translated with transExpression case SPIRVDebug::Source: // To be used by other instructions case SPIRVDebug::SourceContinued: case SPIRVDebug::BuildIdentifier: // To be used by transCompilationUnit case SPIRVDebug::StoragePath: // To be used by transCompilationUnit return nullptr; case SPIRVDebug::Expression: return transExpression(DebugInst); case SPIRVDebug::TypeArrayDynamic: return transTypeArrayDynamic(DebugInst); default: llvm_unreachable("Not implemented SPIR-V debug instruction!"); } } DbgInstPtr SPIRVToLLVMDbgTran::transDebugIntrinsic(const SPIRVExtInst *DebugInst, BasicBlock *BB) { auto GetLocalVar = [&](SPIRVId Id) -> std::pair { auto *LV = transDebugInst(BM->get(Id)); DebugLoc DL = DILocation::get(M->getContext(), LV->getLine(), /*Column=*/0, LV->getScope()); return std::make_pair(LV, DL); }; auto GetValue = [&](SPIRVId Id) -> Value * { auto *V = BM->get(Id); return SPIRVReader->transValue(V, BB->getParent(), BB); }; auto GetExpression = [&](SPIRVId Id) -> DIExpression * { return transDebugInst(BM->get(Id)); }; SPIRVWordVec Ops = DebugInst->getArguments(); switch (DebugInst->getExtOp()) { case SPIRVDebug::Scope: case SPIRVDebug::NoScope: case SPIRVDebug::FunctionDefinition: return nullptr; case SPIRVDebug::Declare: { using namespace SPIRVDebug::Operand::DebugDeclare; auto LocalVar = GetLocalVar(Ops[DebugLocalVarIdx]); DebugLoc Loc = transDebugScope(DebugInst); if (!Loc) Loc = LocalVar.second; DIBuilder &DIB = getDIBuilder(DebugInst); if (getDbgInst(Ops[VariableIdx])) { auto *Null = ConstantPointerNull::get(PointerType::get(M->getContext(), 0)); DbgInstPtr DbgDeclare = DIB.insertDeclare( Null, LocalVar.first, GetExpression(Ops[ExpressionIdx]), Loc, BB); return DbgDeclare; } return DIB.insertDeclare(GetValue(Ops[VariableIdx]), LocalVar.first, GetExpression(Ops[ExpressionIdx]), Loc, BB); } case SPIRVDebug::Value: { using namespace SPIRVDebug::Operand::DebugValue; auto LocalVar = GetLocalVar(Ops[DebugLocalVarIdx]); Value *Val = GetValue(Ops[ValueIdx]); DIExpression *Expr = GetExpression(Ops[ExpressionIdx]); DebugLoc Loc = transDebugScope(DebugInst); DbgInstPtr DbgValIntr = getDIBuilder(DebugInst).insertDbgValueIntrinsic( Val, LocalVar.first, Expr, Loc, BB); std::vector MDs; for (size_t I = 0; I != Expr->getNumLocationOperands(); ++I) { MDs.emplace_back(ValueAsMetadata::get(Val)); } if (!MDs.empty()) { DIArgList *AL = DIArgList::get(M->getContext(), MDs); cast(cast(DbgValIntr)) ->setRawLocation(AL); } return DbgValIntr; } default: llvm_unreachable("Unknown debug intrinsic!"); } } DebugLoc SPIRVToLLVMDbgTran::transDebugScope(const SPIRVInstruction *Inst) { unsigned Line = 0; unsigned Col = 0; MDNode *Scope = nullptr; MDNode *InlinedAt = nullptr; // If DebugLine and OpLine are both active give DebugLine priority if (auto DL = Inst->getDebugLine()) { using namespace SPIRVDebug::Operand::DebugLine; SPIRVWordVec DebugLineArgs = DL->getArguments(); Line = getConstantValueOrLiteral(DebugLineArgs, StartIdx, DL->getExtSetKind()); Col = getConstantValueOrLiteral(DebugLineArgs, ColumnStartIdx, DL->getExtSetKind()); } else if (auto L = Inst->getLine()) { Line = L->getLine(); Col = L->getColumn(); } if (SPIRVEntry *S = Inst->getDebugScope()) { using namespace SPIRVDebug::Operand::Scope; SPIRVExtInst *DbgScope = static_cast(S); SPIRVWordVec Ops = DbgScope->getArguments(); Scope = getScope(BM->getEntry(Ops[ScopeIdx])); if (Ops.size() > InlinedAtIdx) InlinedAt = transDebugInst(BM->get(Ops[InlinedAtIdx])); return DILocation::get(M->getContext(), Line, Col, Scope, InlinedAt); } return DebugLoc(); } MDNode *SPIRVToLLVMDbgTran::transDebugInlined(const SPIRVExtInst *DebugInst) { // There is a Column operand in NonSemantic.Shader.200 spec if (DebugInst->getExtSetKind() == SPIRV::SPIRVEIS_NonSemantic_Shader_DebugInfo_200) return transDebugInlinedNonSemanticShader200(DebugInst); using namespace SPIRVDebug::Operand::InlinedAt::OpenCL; SPIRVWordVec Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); SPIRVWord Line = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); unsigned Col = 0; // DebugInlinedAt instruction has no column operand DILocalScope *Scope = cast(getScope(BM->getEntry(Ops[ScopeIdx]))); DILocation *InlinedAt = nullptr; if (Ops.size() > InlinedIdx) { InlinedAt = transDebugInst(BM->get(Ops[InlinedIdx])); } return DILocation::getDistinct(M->getContext(), Line, Col, Scope, InlinedAt); } MDNode *SPIRVToLLVMDbgTran::transDebugInlinedNonSemanticShader200( const SPIRVExtInst *DebugInst) { using namespace SPIRVDebug::Operand::InlinedAt::NonSemantic; SPIRVWordVec Ops = DebugInst->getArguments(); assert(Ops.size() >= MinOperandCount && "Invalid number of operands"); SPIRVWord Line = getConstantValueOrLiteral(Ops, LineIdx, DebugInst->getExtSetKind()); unsigned Col = getConstantValueOrLiteral(Ops, ColumnIdx, DebugInst->getExtSetKind()); DILocalScope *Scope = cast(getScope(BM->getEntry(Ops[ScopeIdx]))); DILocation *InlinedAt = nullptr; if (Ops.size() > InlinedIdx) { InlinedAt = transDebugInst(BM->get(Ops[InlinedIdx])); } return DILocation::getDistinct(M->getContext(), Line, Col, Scope, InlinedAt); } void SPIRVToLLVMDbgTran::finalize() { if (!Enable) return; for (const auto &Builder : BuilderMap) Builder.second->finalize(); } DIFile *SPIRVToLLVMDbgTran::getFile(const SPIRVId SourceId) { using namespace SPIRVDebug::Operand::Source; SPIRVExtInst *Source = BM->get(SourceId); assert(Source->getExtOp() == SPIRVDebug::Source && "DebugSource instruction is expected"); SPIRVWordVec SourceArgs = Source->getArguments(); assert(SourceArgs.size() >= MinOperandCount && "Invalid number of operands"); if (SourceArgs.size() == MinOperandCount) return getDIFile(getString(SourceArgs[FileIdx])); if (!isNonSemanticDebugInfo(Source->getExtSetKind())) { std::string ChecksumStr = getDbgInst(SourceArgs[TextIdx]) ? "" : getString(SourceArgs[TextIdx]); return getDIFile(getString(SourceArgs[FileIdx]), ParseChecksum(ChecksumStr)); } std::optional> CS; SPIRVWord StrIdx = SourceArgs[TextIdx]; if (Source->getExtSetKind() == SPIRVEIS_NonSemantic_Shader_DebugInfo_200) { if (SourceArgs.size() >= MaxOperandCount - 1) { // 2 optional parameters are ChecksumKind and ChecksumValue - they should // go together if (!getDbgInst(SourceArgs[ChecksumKind]) && !getDbgInst(SourceArgs[ChecksumValue])) { llvm::DIFile::ChecksumKind Kind = SPIRV::DbgChecksumKindMap::rmap( static_cast( BM->get(SourceArgs[ChecksumKind]) ->getZExtIntValue())); StringRef Checksum = getString(SourceArgs[ChecksumValue]); size_t ChecksumEndPos = Checksum.find_if_not(llvm::isHexDigit); CS.emplace(Kind, Checksum.substr(0, ChecksumEndPos)); } } // Among optional parameters - text is always the last one (either 1st or // 3rd) if (SourceArgs.size() == MaxOperandCount || SourceArgs.size() == MaxOperandCount - 2) StrIdx = SourceArgs[TextNonSemIdx]; else StrIdx = SPIRVID_INVALID; } return getDIFile(getString(SourceArgs[FileIdx]), CS, getStringSourceContinued(StrIdx, Source)); } void SPIRVToLLVMDbgTran::setBuildIdentifierAndStoragePath() { #ifndef NDEBUG bool FoundBuildIdentifier{false}; bool FoundStoragePath{false}; #endif for (SPIRVExtInst *EI : BM->getDebugInstVec()) { if (EI->getExtOp() == SPIRVDebug::BuildIdentifier) { using namespace SPIRVDebug::Operand::BuildIdentifier; SPIRVWordVec BuildIdentifierArgs = EI->getArguments(); assert(BuildIdentifierArgs.size() == OperandCount && "Invalid number of operands"); assert(!FoundBuildIdentifier && "More than one BuildIdentifier instruction not allowed"); BuildIdentifier = strtoull( getString(BuildIdentifierArgs[IdentifierIdx]).c_str(), NULL, 10); #ifndef NDEBUG FoundBuildIdentifier = true; #endif } else if (EI->getExtOp() == SPIRVDebug::StoragePath) { using namespace SPIRVDebug::Operand::StoragePath; SPIRVWordVec StoragePathArgs = EI->getArguments(); assert(StoragePathArgs.size() == OperandCount && "Invalid number of operands"); assert(!FoundStoragePath && "More than one StoragePath instruction not allowed"); StoragePath = getString(StoragePathArgs[PathIdx]); #ifndef NDEBUG FoundStoragePath = true; #endif } } assert(((FoundBuildIdentifier && FoundStoragePath) || (!FoundBuildIdentifier && !FoundStoragePath)) && "BuildIdentifier and StoragePath must both be set or both unset"); } DIBuilder &SPIRVToLLVMDbgTran::getDIBuilder(const SPIRVExtInst *DebugInst) { assert(BuilderMap.size() != 0 && "No debug compile units"); if (BuilderMap.size() == 1) return *BuilderMap.begin()->second; while (DebugInst->getExtOp() != SPIRVDebug::CompilationUnit) { if (DebugInst->getExtOp() == SPIRVDebug::DebugInfoNone) return *BuilderMap.begin()->second; const SPIRVWordVec &Ops = DebugInst->getArguments(); SPIRVWord ParentScopeIdx = 0; if (!hasDbgInstParentScopeIdx(DebugInst->getExtOp(), ParentScopeIdx, DebugInst->getExtSetKind())) return *BuilderMap.begin()->second; if (SPIRVEntry *Scope = BM->getEntry(Ops[ParentScopeIdx])) { DebugInst = static_cast(Scope); continue; } return *BuilderMap.begin()->second; } return *BuilderMap[DebugInst->getId()]; } SPIRVToLLVMDbgTran::SplitFileName::SplitFileName(const std::string &FileName) { auto Loc = FileName.find_last_of("/\\"); if (Loc != std::string::npos) { BaseName = FileName.substr(Loc + 1); Path = FileName.substr(0, Loc); } else { BaseName = FileName; Path = "."; } } std::string SPIRVToLLVMDbgTran::findModuleProducer() { for (const auto &I : BM->getModuleProcessedVec()) { if (I->getProcessStr().find(SPIRVDebug::ProducerPrefix) != std::string::npos) { return I->getProcessStr().substr(SPIRVDebug::ProducerPrefix.size()); } } return "spirv"; } std::optional> SPIRVToLLVMDbgTran::ParseChecksum(StringRef Text) { // Example of "Text" variable: // "SomeInfo//__CSK_MD5:7bb56387968a9caa6e9e35fff94eaf7b:OtherInfo" std::optional> CS; auto KindPos = Text.find(SPIRVDebug::ChecksumKindPrefx); if (KindPos != StringRef::npos) { auto ColonPos = Text.find(":", KindPos); KindPos += std::string("//__").size(); auto KindStr = Text.substr(KindPos, ColonPos - KindPos); auto Checksum = Text.substr(ColonPos).ltrim(':'); if (auto Kind = DIFile::getChecksumKind(KindStr)) { size_t ChecksumEndPos = Checksum.find_if_not(llvm::isHexDigit); CS.emplace(Kind.value(), Checksum.substr(0, ChecksumEndPos)); } } return CS; } } // namespace SPIRV