diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp index d3e323efaee91..90d629210e75b 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp @@ -5,17 +5,15 @@ #include "SPIRVRegisterInfo.h" #include "SPIRVTargetMachine.h" #include "SPIRVUtils.h" -#include "llvm/ADT/SmallPtrSet.h" + #include "llvm/ADT/SmallString.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineFunctionPass.h" -#include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineModuleInfo.h" -#include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/Register.h" #include "llvm/IR/DebugInfoMetadata.h" @@ -27,42 +25,9 @@ #define DEBUG_TYPE "spirv-nonsemantic-debug-info" -namespace llvm { -struct SPIRVEmitNonSemanticDI : public MachineFunctionPass { - static char ID; - SPIRVTargetMachine *TM; - SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM); - SPIRVEmitNonSemanticDI(); - - bool runOnMachineFunction(MachineFunction &MF) override; - -private: - bool IsGlobalDIEmitted = false; - bool emitGlobalDI(MachineFunction &MF); -}; -} // namespace llvm - +namespace { using namespace llvm; -INITIALIZE_PASS(SPIRVEmitNonSemanticDI, DEBUG_TYPE, - "SPIRV NonSemantic.Shader.DebugInfo.100 emitter", false, false) - -char SPIRVEmitNonSemanticDI::ID = 0; - -MachineFunctionPass * -llvm::createSPIRVEmitNonSemanticDIPass(SPIRVTargetMachine *TM) { - return new SPIRVEmitNonSemanticDI(TM); -} - -SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM) - : MachineFunctionPass(ID), TM(TM) { - initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry()); -} - -SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI() : MachineFunctionPass(ID) { - initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry()); -} - enum BaseTypeAttributeEncoding { Unspecified = 0, Address = 1, @@ -90,149 +55,469 @@ enum SourceLanguage { Zig = 12 }; -bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) { - // If this MachineFunction doesn't have any BB repeat procedure - // for the next - if (MF.begin() == MF.end()) { - IsGlobalDIEmitted = false; - return false; +enum TypesMapping { + PrimitiveIntArray, + PrimitiveStringArray, + DebugSourceArray, + DebugCompilationUnitArray, + DebugTypeBasicArray, + DebugTypePointerArray, + DebugInfoNoneArray, + DebugLineArray +}; + +struct DebugSource { + size_t FileId; + + explicit constexpr DebugSource(const size_t FileId) noexcept + : FileId(FileId) {} + + explicit constexpr DebugSource(const ArrayRef AR) : FileId(AR[0]) {} + + friend bool operator==(const DebugSource &Lhs, const DebugSource &Rhs) { + return Lhs.FileId == Rhs.FileId; } +}; - // Required variables to get from metadata search - LLVMContext *Context; - SmallVector> FilePaths; - SmallVector LLVMSourceLanguages; - int64_t DwarfVersion = 0; - int64_t DebugInfoVersion = 0; - SmallPtrSet BasicTypes; - SmallPtrSet PointerDerivedTypes; - // Searching through the Module metadata to find nescessary - // information like DwarfVersion or SourceLanguage - { - const MachineModuleInfo &MMI = - getAnalysis().getMMI(); - const Module *M = MMI.getModule(); - Context = &M->getContext(); - const NamedMDNode *DbgCu = M->getNamedMetadata("llvm.dbg.cu"); - if (!DbgCu) - return false; - for (const auto *Op : DbgCu->operands()) { - if (const auto *CompileUnit = dyn_cast(Op)) { - DIFile *File = CompileUnit->getFile(); - FilePaths.emplace_back(); - sys::path::append(FilePaths.back(), File->getDirectory(), - File->getFilename()); - LLVMSourceLanguages.push_back(CompileUnit->getSourceLanguage()); - } +struct DebugCompilationUnit { + size_t DebugInfoVersionId; + size_t DwarfVersionId; + size_t DebugSourceId; + size_t LanguageId; + + constexpr DebugCompilationUnit(const size_t DebugInfoVersionId, + const size_t DwarfVersionId, + const size_t DebugSourceId, + const size_t LanguageId) noexcept + : DebugInfoVersionId(DebugInfoVersionId), DwarfVersionId(DwarfVersionId), + DebugSourceId(DebugSourceId), LanguageId(LanguageId) {} + + explicit constexpr DebugCompilationUnit(const ArrayRef AR) noexcept + : DebugInfoVersionId(AR[0]), DwarfVersionId(AR[1]), DebugSourceId(AR[2]), + LanguageId(AR[3]) {} + + friend bool operator==(const DebugCompilationUnit &Lhs, + const DebugCompilationUnit &Rhs) { + return Lhs.DebugInfoVersionId == Rhs.DebugInfoVersionId & + Lhs.DwarfVersionId == Rhs.DwarfVersionId & + Lhs.DebugSourceId == Rhs.DebugSourceId & + Lhs.LanguageId == Rhs.LanguageId; + } +}; + +struct DebugTypeBasic { + size_t NameId; + size_t SizeId; + size_t BaseTypeEncodingId; + size_t FlagsId; + + explicit constexpr DebugTypeBasic(const ArrayRef AR) + : NameId(AR[0]), SizeId(AR[1]), BaseTypeEncodingId(AR[2]), + FlagsId(AR[3]) {} + + friend bool operator==(const DebugTypeBasic &Lhs, const DebugTypeBasic &Rhs) { + return Lhs.NameId == Rhs.NameId && Lhs.SizeId == Rhs.SizeId && + Lhs.BaseTypeEncodingId == Rhs.BaseTypeEncodingId && + Lhs.FlagsId == Rhs.FlagsId; + } +}; + +struct DebugTypePointer { + size_t BaseTypeId; + size_t StorageClassId; + size_t FlagsId; + + DebugTypePointer(const size_t BaseTypeId, const size_t StorageClassId, + const size_t FlagsId) noexcept + : BaseTypeId(BaseTypeId), StorageClassId(StorageClassId), + FlagsId(FlagsId) {} + + explicit DebugTypePointer(const ArrayRef AR) noexcept + : BaseTypeId(AR[0]), StorageClassId(AR[1]), FlagsId(AR[2]) {} + + friend bool operator==(const DebugTypePointer &Lhs, + const DebugTypePointer &Rhs) { + return Lhs.BaseTypeId == Rhs.BaseTypeId && + Lhs.StorageClassId == Rhs.StorageClassId && + Lhs.FlagsId == Rhs.FlagsId; + } +}; + +struct DebugLine { + size_t DebugSourceId; + size_t LineStartId; + size_t LineEndId; + size_t ColumnStartId; + size_t ColumnEndId; + + explicit DebugLine(const ArrayRef AR) + : DebugSourceId(AR[0]), LineStartId(AR[1]), LineEndId(AR[2]), + ColumnStartId(AR[3]), ColumnEndId(AR[4]) {} + + friend bool operator==(const DebugLine &Lhs, const DebugLine &Rhs) { + return Lhs.DebugSourceId == Rhs.DebugSourceId && + Lhs.LineStartId == Rhs.LineStartId && + Lhs.LineEndId == Rhs.LineEndId && + Lhs.ColumnStartId == Rhs.ColumnStartId && + Lhs.ColumnEndId == Rhs.ColumnEndId; + } +}; + +struct DebugInfoNone; + +template struct DebugTypeContainer; + +template <> struct DebugTypeContainer { + static constexpr TypesMapping TM = PrimitiveIntArray; +}; + +template <> struct DebugTypeContainer { + static constexpr TypesMapping TM = PrimitiveStringArray; +}; + +template <> struct DebugTypeContainer { + static constexpr TypesMapping TM = DebugSourceArray; + static constexpr SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst = + SPIRV::NonSemanticExtInst::DebugSource; +}; + +template <> struct DebugTypeContainer { + static constexpr TypesMapping TM = DebugCompilationUnitArray; + static constexpr SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst = + SPIRV::NonSemanticExtInst::DebugCompilationUnit; +}; + +template <> struct DebugTypeContainer { + static constexpr TypesMapping TM = DebugTypeBasicArray; + static constexpr SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst = + SPIRV::NonSemanticExtInst::DebugTypeBasic; +}; + +template <> struct DebugTypeContainer { + static constexpr TypesMapping TM = DebugTypePointerArray; + static constexpr SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst = + SPIRV::NonSemanticExtInst::DebugTypePointer; +}; + +template <> struct DebugTypeContainer { + static constexpr TypesMapping TM = DebugInfoNoneArray; + static constexpr SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst = + SPIRV::NonSemanticExtInst::DebugInfoNone; +}; + +template <> struct DebugTypeContainer { + static constexpr TypesMapping TM = DebugLineArray; + static constexpr SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst = + SPIRV::NonSemanticExtInst::DebugLine; +}; + +template struct DebugLiveContainer { + SmallVector Values; + SmallVector BackIdx; +}; +/// @class LiveRepository +/// @brief Container for the connection graph between newly emitted Debug +/// instructions. +/// +/// The architecture consists of two tiers: +/// - The **Main tier**, represented by an array of `Instructions`. +/// - The **Concrete Type tier**, represented by separate arrays for each +/// corresponding debug type. +/// +/// The `Instructions` array contains records that include: +/// - `Type`: An enum value pointing to the specific debug type array in the +/// Concrete Type tier. +/// - `Id`: An index into the corresponding array in the Concrete Type tier. +/// +/// The class is designed to ensure safe and controlled access to its internal +/// state. All interactions with the repository are done through the +/// `getOrCreateIdx` function, which restricts direct manipulation of the +/// underlying data structures, thus minimizing the risk of memory errors or +/// logical mistakes. +/// +/// Users of this class interact exclusively with the indexes of the +/// `Instructions` array and never directly access the internal concrete type +/// arrays. This property flattens the instruction access, +// making it easily retrievable. +/// +/// +/// getOrCreateIdx() <--+ Iidx +/// V | +/// +-------------------------+-+ +/// |[Instructions]--{Cidx,Type}|<----+ +/// +------+--------------+-----+ | +/// v v | +/// [PrimitiveType] [ConcreteType_0] | +/// {Data} {Instruction} | +/// (i32)(i32) (Iidx) (Iidx) | +/// | | | +/// +----+-----+ +/// +/// Class is designed to be easily removable by simply replacing the +/// `getOrConstructIdx` method with another mechanism. +class LiveRepository { + DebugLiveContainer PrimitiveInts; + DebugLiveContainer PrimitiveStrings; + DebugLiveContainer DebugSources; + DebugLiveContainer DebugLines; + DebugLiveContainer DebugCompilationUnits; + DebugLiveContainer DebugTypeBasics; + DebugLiveContainer DebugTypePointers; + + SmallVector Registers; + SmallVector> Instructions; + + template constexpr SmallVector &values() { + if constexpr (std::is_same_v) { + return PrimitiveInts.Values; + } else if constexpr (std::is_same_v) { + return PrimitiveStrings.Values; + } else if constexpr (std::is_same_v) { + return DebugLines.Values; + } else if constexpr (std::is_same_v) { + return DebugSources.Values; + } else if constexpr (std::is_same_v) { + return DebugCompilationUnits.Values; + } else if constexpr (std::is_same_v) { + return DebugTypeBasics.Values; + } else if constexpr (std::is_same_v) { + return DebugTypePointers.Values; } - const NamedMDNode *ModuleFlags = M->getNamedMetadata("llvm.module.flags"); - for (const auto *Op : ModuleFlags->operands()) { - const MDOperand &MaybeStrOp = Op->getOperand(1); - if (MaybeStrOp.equalsStr("Dwarf Version")) - DwarfVersion = - cast( - cast(Op->getOperand(2))->getValue()) - ->getSExtValue(); - else if (MaybeStrOp.equalsStr("Debug Info Version")) - DebugInfoVersion = - cast( - cast(Op->getOperand(2))->getValue()) - ->getSExtValue(); + llvm_unreachable("unreachable"); + } + + template constexpr SmallVector &backIdx() { + if constexpr (std::is_same_v) { + return PrimitiveInts.BackIdx; + } else if constexpr (std::is_same_v) { + return PrimitiveStrings.BackIdx; + } else if constexpr (std::is_same_v) { + return DebugLines.BackIdx; + } else if constexpr (std::is_same_v) { + return DebugSources.BackIdx; + } else if constexpr (std::is_same_v) { + return DebugCompilationUnits.BackIdx; + } else if constexpr (std::is_same_v) { + return DebugTypeBasics.BackIdx; + } else if constexpr (std::is_same_v) { + return DebugTypePointers.BackIdx; } + llvm_unreachable("unreachable"); + } - // This traversal is the only supported way to access - // instruction related DI metadata like DIBasicType - for (auto &F : *M) { - for (auto &BB : F) { - for (auto &I : BB) { - for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) { - DILocalVariable *LocalVariable = DVR.getVariable(); - if (auto *BasicType = - dyn_cast(LocalVariable->getType())) { - BasicTypes.insert(BasicType); - } else if (auto *DerivedType = - dyn_cast(LocalVariable->getType())) { - if (DerivedType->getTag() == dwarf::DW_TAG_pointer_type) { - PointerDerivedTypes.insert(DerivedType); - // DIBasicType can be unreachable from DbgRecord and only - // pointed on from other DI types - // DerivedType->getBaseType is null when pointer - // is representing a void type - if (DerivedType->getBaseType()) - BasicTypes.insert( - cast(DerivedType->getBaseType())); - } - } - } - } + template + static std::pair + emplaceOrReturnDuplicate(T Val, SmallVectorImpl &SV) { + for (unsigned Idx = 0; Idx < SV.size(); ++Idx) { + if (Val == SV[Idx]) { + return {Idx, true}; } } + SV.emplace_back(Val); + return {SV.size() - 1, false}; } - // NonSemantic.Shader.DebugInfo.100 global DI instruction emitting - { - // Required LLVM variables for emitting logic + + static Register emitOpString(const StringRef SR, + MachineIRBuilder &MIRBuilder) { + MachineRegisterInfo *MRI = MIRBuilder.getMRI(); + const Register StrReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); + MRI->setType(StrReg, LLT::scalar(32)); + MachineInstrBuilder MIB = MIRBuilder.buildInstr(SPIRV::OpString); + MIB.addDef(StrReg); + addStringImm(SR, MIB); + return StrReg; + } + + Register emitDIInstruction(SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst, + const ArrayRef Ids, + MachineIRBuilder &MIRBuilder, + const SPIRVTargetMachine *const TM) { const SPIRVInstrInfo *TII = TM->getSubtargetImpl()->getInstrInfo(); const SPIRVRegisterInfo *TRI = TM->getSubtargetImpl()->getRegisterInfo(); const RegisterBankInfo *RBI = TM->getSubtargetImpl()->getRegBankInfo(); SPIRVGlobalRegistry *GR = TM->getSubtargetImpl()->getSPIRVGlobalRegistry(); - MachineRegisterInfo &MRI = MF.getRegInfo(); - MachineBasicBlock &MBB = *MF.begin(); - - // To correct placement of a OpLabel instruction during SPIRVAsmPrinter - // emission all new instructions needs to be placed after OpFunction - // and before first terminator - MachineIRBuilder MIRBuilder(MBB, MBB.getFirstTerminator()); - - const auto EmitOpString = [&](StringRef SR) { - const Register StrReg = MRI.createVirtualRegister(&SPIRV::IDRegClass); - MRI.setType(StrReg, LLT::scalar(32)); - MachineInstrBuilder MIB = MIRBuilder.buildInstr(SPIRV::OpString); - MIB.addDef(StrReg); - addStringImm(SR, MIB); - return StrReg; - }; - - const SPIRVType *VoidTy = - GR->getOrCreateSPIRVType(Type::getVoidTy(*Context), MIRBuilder); - - const auto EmitDIInstruction = - [&](SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst, - std::initializer_list Registers) { - const Register InstReg = - MRI.createVirtualRegister(&SPIRV::IDRegClass); - MRI.setType(InstReg, LLT::scalar(32)); - MachineInstrBuilder MIB = - MIRBuilder.buildInstr(SPIRV::OpExtInst) - .addDef(InstReg) - .addUse(GR->getSPIRVTypeID(VoidTy)) - .addImm(static_cast( - SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100)) - .addImm(Inst); - for (auto Reg : Registers) { - MIB.addUse(Reg); - } - MIB.constrainAllUses(*TII, *TRI, *RBI); - GR->assignSPIRVTypeToVReg(VoidTy, InstReg, MF); - return InstReg; - }; + MachineRegisterInfo *MRI = MIRBuilder.getMRI(); + const SPIRVType *VoidTy = GR->getOrCreateSPIRVType( + Type::getVoidTy(MIRBuilder.getContext()), MIRBuilder); + const Register InstReg = MRI->createVirtualRegister(&SPIRV::IDRegClass); + MRI->setType(InstReg, LLT::scalar(32)); + MachineInstrBuilder MIB = + MIRBuilder.buildInstr(SPIRV::OpExtInst) + .addDef(InstReg) + .addUse(GR->getSPIRVTypeID(VoidTy)) + .addImm(SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100) + .addImm(Inst); + for (const auto Idx : Ids) { + MIB.addUse(Registers[Idx]); + } + MIB.constrainAllUses(*TII, *TRI, *RBI); + GR->assignSPIRVTypeToVReg(VoidTy, InstReg, MIRBuilder.getMF()); + return InstReg; + } - const SPIRVType *I32Ty = - GR->getOrCreateSPIRVType(Type::getInt32Ty(*Context), MIRBuilder); +public: + /// @brief Retrieves or creates an index for the int64_t primitive type. + /// + /// It's backed by OpConstantI instruction. + /// It either retrieves an existing index or creates a new one based on the + /// provided parameters. The function currently has a linear, cache-friendly + /// search time for duplicate entries. It assumes that no single type of + /// instruction will become so dominant in the module and every entry of that + /// type will be unique. + /// + /// @param[in] Val A int64_t value to retreive or construct. + /// @param[in] MIRBuilder A reference to the `MachineIRBuilder` used for + /// constructing IR. + /// @param[in] TM A pointer to the target machine (`SPIRVTargetMachine`) used + /// for this operation. + /// + /// @return The index (`size_t`) of the retrieved duplicate or newly created + /// entry. + /// + size_t getOrCreateIdx(const int64_t Val, MachineIRBuilder &MIRBuilder, + const SPIRVTargetMachine *TM) { + auto &SV = values(); + const auto [ConcreteIdx, IsDuplicate] = emplaceOrReturnDuplicate(Val, SV); + if (IsDuplicate) { + return backIdx()[ConcreteIdx]; + } + Instructions.emplace_back(DebugTypeContainer::TM, ConcreteIdx); + SPIRVGlobalRegistry *GR = TM->getSubtargetImpl()->getSPIRVGlobalRegistry(); + const SPIRVType *I32Ty = GR->getOrCreateSPIRVType( + Type::getInt32Ty(MIRBuilder.getContext()), MIRBuilder); + Registers.emplace_back(GR->buildConstantInt(Val, MIRBuilder, I32Ty, false)); + backIdx().emplace_back(Instructions.size() - 1); + return Instructions.size() - 1; + } - const Register DwarfVersionReg = - GR->buildConstantInt(DwarfVersion, MIRBuilder, I32Ty, false); + /// @brief Retrieves or creates an index for the StringRef primitive type. + /// + /// It's backed by OpString instruction. + /// It either retrieves an existing index or creates a new one based on the + /// provided parameters. The function currently has a linear, cache-friendly + /// search time for duplicate entries. It assumes that no single type of + /// instruction will become so dominant in the module and every entry of that + /// type will be unique. + /// + /// @param[in] Val A StringRef value to retreive or construct. + /// @param[in] MIRBuilder A reference to the `MachineIRBuilder` used for + /// constructing IR. + /// @param[in] TM A pointer to the target machine (`SPIRVTargetMachine`) used + /// for this operation. + /// + /// @return The index (`size_t`) of the retrieved duplicate or newly created + /// entry. + /// + size_t getOrCreateIdx(const StringRef Val, MachineIRBuilder &MIRBuilder) { + auto &SV = values(); + const auto [ConcreteIdx, IsDuplicate] = emplaceOrReturnDuplicate(Val, SV); + if (IsDuplicate) { + return backIdx()[ConcreteIdx]; + } + Instructions.emplace_back(DebugTypeContainer::TM, ConcreteIdx); + Registers.emplace_back(emitOpString(Val, MIRBuilder)); + backIdx().emplace_back(Instructions.size() - 1); + return Instructions.size() - 1; + } - const Register DebugInfoVersionReg = - GR->buildConstantInt(DebugInfoVersion, MIRBuilder, I32Ty, false); + /// @brief Retrieves or creates an index for the specified type. + /// It either retrieves an existing index or creates a new one based on the + /// provided parameters. The function currently has a linear, cache-friendly + /// search time for duplicate entries. It assumes that no single type of + /// instruction will become so dominant in the module and every entry of that + /// type will be unique.:w + /// + /// @tparam T The specific type being used for this instantiation. + /// + /// @param[in] arrayRef A reference to an array containing indices (of type + /// `size_t`). + /// @param[in] MIRBuilder A reference to the `MachineIRBuilder` used for + /// constructing IR. + /// @param[in] TM A pointer to the target machine (`SPIRVTargetMachine`) used + /// for this operation. + /// + /// @return The index (`size_t`) of the retrieved duplicate or newly created + /// entry. + /// + template + constexpr size_t getOrCreateIdx(ArrayRef Args, + MachineIRBuilder &MIRBuilder, + const SPIRVTargetMachine *TM) { + auto &SV = values(); + const auto [ConcreteIdx, IsDuplicate] = + emplaceOrReturnDuplicate(T(Args), SV); + if (IsDuplicate) { + return backIdx()[ConcreteIdx]; + } + Instructions.emplace_back(DebugTypeContainer::TM, ConcreteIdx); + Registers.emplace_back( + emitDIInstruction(DebugTypeContainer::Inst, Args, MIRBuilder, TM)); + backIdx().emplace_back(Instructions.size() - 1); + return Instructions.size() - 1; + } +}; - for (unsigned Idx = 0; Idx < LLVMSourceLanguages.size(); ++Idx) { - const Register FilePathStrReg = EmitOpString(FilePaths[Idx]); +template <> +size_t +LiveRepository::getOrCreateIdx(ArrayRef, + MachineIRBuilder &MIRBuilder, + const SPIRVTargetMachine *TM) { + static std::optional DebugInfoNoneIdx = std::nullopt; + if (!DebugInfoNoneIdx.has_value()) { + Instructions.emplace_back(DebugTypeContainer::TM, 0); + Registers.emplace_back(emitDIInstruction( + DebugTypeContainer::Inst, {}, MIRBuilder, TM)); + DebugInfoNoneIdx.emplace(Instructions.size() - 1); + } + return DebugInfoNoneIdx.value(); +} - const Register DebugSourceResIdReg = EmitDIInstruction( - SPIRV::NonSemanticExtInst::DebugSource, {FilePathStrReg}); +size_t emitDebugSource(const DIFile *File, MachineIRBuilder &MIRBuilder, + SPIRVTargetMachine *TM, LiveRepository &LR) { + SmallString<128> FilePath; + sys::path::append(FilePath, File->getDirectory(), File->getFilename()); + const size_t FilePathId = + LR.getOrCreateIdx(StringRef(FilePath.c_str()), MIRBuilder); + return LR.getOrCreateIdx({FilePathId}, MIRBuilder, TM); +} - SourceLanguage SpirvSourceLanguage = SourceLanguage::Unknown; - switch (LLVMSourceLanguages[Idx]) { +size_t emitDebugCompilationUnits(const Module *M, MachineIRBuilder &MIRBuilder, + const SPIRVTargetMachine *TM, + LiveRepository &LR) { + std::optional DwarfVersionId = std::nullopt; + std::optional DebugInfoVersionId = std::nullopt; + const NamedMDNode *ModuleFlags = M->getNamedMetadata("llvm.module.flags"); + for (const auto *Op : ModuleFlags->operands()) { + const MDOperand &MaybeStrOp = Op->getOperand(1); + if (MaybeStrOp.equalsStr("Dwarf Version")) { + const int64_t DwarfVersion = + cast( + cast(Op->getOperand(2))->getValue()) + ->getSExtValue(); + DwarfVersionId = LR.getOrCreateIdx(DwarfVersion, MIRBuilder, TM); + } else if (MaybeStrOp.equalsStr("Debug Info Version")) { + const int64_t DebugInfoVersion = + cast( + cast(Op->getOperand(2))->getValue()) + ->getSExtValue(); + DebugInfoVersionId = LR.getOrCreateIdx(DebugInfoVersion, MIRBuilder, TM); + } + } + assert(DwarfVersionId.has_value() && DebugInfoVersionId.has_value()); + const NamedMDNode *DbgCu = M->getNamedMetadata("llvm.dbg.cu"); + for (const auto *Op : DbgCu->operands()) { + if (const auto *CompileUnit = dyn_cast(Op)) { + const DIFile *File = CompileUnit->getFile(); + SmallString<128> FilePath; + sys::path::append(FilePath, File->getDirectory(), File->getFilename()); + + const size_t FilePathId = + LR.getOrCreateIdx(StringRef(FilePath.c_str()), MIRBuilder); + const size_t DebugSourceId = + LR.getOrCreateIdx({FilePathId}, MIRBuilder, TM); + + SourceLanguage SpirvSourceLanguage; + switch (CompileUnit->getSourceLanguage()) { case dwarf::DW_LANG_OpenCL: SpirvSourceLanguage = SourceLanguage::OpenCL_C; break; @@ -253,99 +538,179 @@ bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) { break; case dwarf::DW_LANG_Zig: SpirvSourceLanguage = SourceLanguage::Zig; + break; + default: + SpirvSourceLanguage = SourceLanguage::Unknown; } - const Register SourceLanguageReg = - GR->buildConstantInt(SpirvSourceLanguage, MIRBuilder, I32Ty, false); - - [[maybe_unused]] - const Register DebugCompUnitResIdReg = - EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugCompilationUnit, - {DebugInfoVersionReg, DwarfVersionReg, - DebugSourceResIdReg, SourceLanguageReg}); + const size_t SpirvSourceLanguageId = + LR.getOrCreateIdx(SpirvSourceLanguage, MIRBuilder, TM); + LR.getOrCreateIdx( + {DebugInfoVersionId.value(), DwarfVersionId.value(), DebugSourceId, + SpirvSourceLanguageId}, + MIRBuilder, TM); } + } + return 0; +} - // We aren't extracting any DebugInfoFlags now so we - // emitting zero to use as Flags argument for DebugBasicType - const Register I32ZeroReg = - GR->buildConstantInt(0, MIRBuilder, I32Ty, false); - - // We need to store pairs because further instructions reference - // the DIBasicTypes and size will be always small so there isn't - // need for any kind of map - SmallVector, 12> - BasicTypeRegPairs; - for (auto *BasicType : BasicTypes) { - const Register BasicTypeStrReg = EmitOpString(BasicType->getName()); - - const Register ConstIntBitwidthReg = GR->buildConstantInt( - BasicType->getSizeInBits(), MIRBuilder, I32Ty, false); - - uint64_t AttributeEncoding = BaseTypeAttributeEncoding::Unspecified; - switch (BasicType->getEncoding()) { - case dwarf::DW_ATE_signed: - AttributeEncoding = BaseTypeAttributeEncoding::Signed; - break; - case dwarf::DW_ATE_unsigned: - AttributeEncoding = BaseTypeAttributeEncoding::Unsigned; - break; - case dwarf::DW_ATE_unsigned_char: - AttributeEncoding = BaseTypeAttributeEncoding::UnsignedChar; - break; - case dwarf::DW_ATE_signed_char: - AttributeEncoding = BaseTypeAttributeEncoding::SignedChar; - break; - case dwarf::DW_ATE_float: - AttributeEncoding = BaseTypeAttributeEncoding::Float; - break; - case dwarf::DW_ATE_boolean: - AttributeEncoding = BaseTypeAttributeEncoding::Boolean; - break; - case dwarf::DW_ATE_address: - AttributeEncoding = BaseTypeAttributeEncoding::Address; - } +size_t emitDebugTypeBasic(const DIBasicType *BT, size_t I32ZeroIdx, + MachineIRBuilder &MIRBuilder, + const SPIRVTargetMachine *TM, LiveRepository &LR) { + + const size_t BasicTypeStrId = LR.getOrCreateIdx(BT->getName(), MIRBuilder); + + const size_t ConstIntBitWidthId = + LR.getOrCreateIdx(BT->getSizeInBits(), MIRBuilder, TM); + + uint64_t AttributeEncoding; + switch (BT->getEncoding()) { + case dwarf::DW_ATE_signed: + AttributeEncoding = BaseTypeAttributeEncoding::Signed; + break; + case dwarf::DW_ATE_unsigned: + AttributeEncoding = BaseTypeAttributeEncoding::Unsigned; + break; + case dwarf::DW_ATE_unsigned_char: + AttributeEncoding = BaseTypeAttributeEncoding::UnsignedChar; + break; + case dwarf::DW_ATE_signed_char: + AttributeEncoding = BaseTypeAttributeEncoding::SignedChar; + break; + case dwarf::DW_ATE_float: + AttributeEncoding = BaseTypeAttributeEncoding::Float; + break; + case dwarf::DW_ATE_boolean: + AttributeEncoding = BaseTypeAttributeEncoding::Boolean; + break; + case dwarf::DW_ATE_address: + AttributeEncoding = BaseTypeAttributeEncoding::Address; + break; + default: + AttributeEncoding = BaseTypeAttributeEncoding::Unspecified; + } - const Register AttributeEncodingReg = - GR->buildConstantInt(AttributeEncoding, MIRBuilder, I32Ty, false); + const size_t AttributeEncodingId = + LR.getOrCreateIdx(AttributeEncoding, MIRBuilder, TM); - const Register BasicTypeReg = - EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugTypeBasic, - {BasicTypeStrReg, ConstIntBitwidthReg, - AttributeEncodingReg, I32ZeroReg}); - BasicTypeRegPairs.emplace_back(BasicType, BasicTypeReg); - } + return LR.getOrCreateIdx( + {BasicTypeStrId, ConstIntBitWidthId, AttributeEncodingId, I32ZeroIdx}, + MIRBuilder, TM); +} + +size_t emitDebugTypePointer(const DIDerivedType *DT, const size_t BasicTypeIdx, + const size_t I32ZeroIdx, + MachineIRBuilder &MIRBuilder, + const SPIRVTargetMachine *TM, LiveRepository &LR) { + assert(DT->getDWARFAddressSpace().has_value()); + + size_t StorageClassIdx = LR.getOrCreateIdx( + addressSpaceToStorageClass(DT->getDWARFAddressSpace().value(), + *TM->getSubtargetImpl()), + MIRBuilder, TM); + + return LR.getOrCreateIdx( + {BasicTypeIdx, StorageClassIdx, I32ZeroIdx}, MIRBuilder, TM); +} + +size_t emitDebugTypePointer(const DIDerivedType *DT, const size_t I32ZeroIdx, + MachineIRBuilder &MIRBuilder, + const SPIRVTargetMachine *TM, LiveRepository &LR) { + assert(DT->getDWARFAddressSpace().has_value()); + + size_t StorageClassIdx = LR.getOrCreateIdx( + addressSpaceToStorageClass(DT->getDWARFAddressSpace().value(), + *TM->getSubtargetImpl()), + MIRBuilder, TM); + size_t DebugInfoNoneIdx = + LR.getOrCreateIdx({}, MIRBuilder, TM); + return LR.getOrCreateIdx( + {DebugInfoNoneIdx, StorageClassIdx, I32ZeroIdx}, MIRBuilder, TM); +} +} // namespace + +namespace llvm { +struct SPIRVEmitNonSemanticDI : MachineFunctionPass { + static char ID; + SPIRVTargetMachine *TM = nullptr; + SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM); + SPIRVEmitNonSemanticDI(); + + bool runOnMachineFunction(MachineFunction &MF) override; + +private: + bool IsGlobalDIEmitted = false; + bool emitGlobalDI(MachineFunction &MF, const Module *M, + LiveRepository &LR) const; + bool emitLineDI(MachineFunction &MF, LiveRepository &LR) const; +}; +} // namespace llvm + +using namespace llvm; + +INITIALIZE_PASS(SPIRVEmitNonSemanticDI, DEBUG_TYPE, + "SPIRV NonSemantic.Shader.DebugInfo.100 emitter", false, false) + +char SPIRVEmitNonSemanticDI::ID = 0; + +MachineFunctionPass * +llvm::createSPIRVEmitNonSemanticDIPass(SPIRVTargetMachine *TM) { + return new SPIRVEmitNonSemanticDI(TM); +} - if (PointerDerivedTypes.size()) { - for (const auto *PointerDerivedType : PointerDerivedTypes) { - - assert(PointerDerivedType->getDWARFAddressSpace().has_value()); - const Register StorageClassReg = GR->buildConstantInt( - addressSpaceToStorageClass( - PointerDerivedType->getDWARFAddressSpace().value(), - *TM->getSubtargetImpl()), - MIRBuilder, I32Ty, false); - - // If the Pointer is representing a void type it's getBaseType - // is a nullptr - const auto *MaybeNestedBasicType = - cast_or_null(PointerDerivedType->getBaseType()); - if (MaybeNestedBasicType) { - for (const auto &BasicTypeRegPair : BasicTypeRegPairs) { - const auto &[DefinedBasicType, BasicTypeReg] = BasicTypeRegPair; - if (DefinedBasicType == MaybeNestedBasicType) { - [[maybe_unused]] - const Register DebugPointerTypeReg = EmitDIInstruction( - SPIRV::NonSemanticExtInst::DebugTypePointer, - {BasicTypeReg, StorageClassReg, I32ZeroReg}); +SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI(SPIRVTargetMachine *TM) + : MachineFunctionPass(ID), TM(TM) { + initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry()); +} + +SPIRVEmitNonSemanticDI::SPIRVEmitNonSemanticDI() : MachineFunctionPass(ID) { + initializeSPIRVEmitNonSemanticDIPass(*PassRegistry::getPassRegistry()); +} + +bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF, const Module *M, + LiveRepository &LR) const { + MachineBasicBlock &MBB = *MF.begin(); + + // To ensure correct placement of an OpLabel instruction during + // SPIRVAsmPrinter emission, all new instructions must be positioned after + // OpFunction and before the first terminator. + MachineIRBuilder MIRBuilder(MBB, MBB.getFirstTerminator()); + + emitDebugCompilationUnits(M, MIRBuilder, TM, LR); + + for (auto &F : *M) { + for (auto &BB : F) { + for (auto &I : BB) { + for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) { + const DILocalVariable *LocalVariable = DVR.getVariable(); + if (const auto *BasicType = + dyn_cast(LocalVariable->getType())) { + // Currently, we are not extracting any DebugInfoFlags, + // so we emit zero as the Flags argument for DebugBasicType. + const size_t I32ZeroIdx = LR.getOrCreateIdx(0, MIRBuilder, TM); + emitDebugTypeBasic(BasicType, I32ZeroIdx, MIRBuilder, TM, LR); + continue; + } + if (const auto *DerivedType = + dyn_cast(LocalVariable->getType())) { + if (DerivedType->getTag() == dwarf::DW_TAG_pointer_type) { + const size_t I32ZeroIdx = LR.getOrCreateIdx(0, MIRBuilder, TM); + // DIBasicType may be unreachable from DbgRecord and can only be + // referenced by other Debug Information (DI) types. Note: + // DerivedType->getBaseType returns null when the pointer + // represents a void type. + if (const auto *BasicType = dyn_cast_if_present( + DerivedType->getBaseType())) { + const size_t BTIdx = emitDebugTypeBasic(BasicType, I32ZeroIdx, + MIRBuilder, TM, LR); + emitDebugTypePointer(DerivedType, BTIdx, I32ZeroIdx, MIRBuilder, + TM, LR); + } else { + emitDebugTypePointer(DerivedType, I32ZeroIdx, MIRBuilder, TM, + LR); + } } } - } else { - const Register DebugInfoNoneReg = - EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone, {}); - [[maybe_unused]] - const Register DebugPointerTypeReg = EmitDIInstruction( - SPIRV::NonSemanticExtInst::DebugTypePointer, - {DebugInfoNoneReg, StorageClassReg, I32ZeroReg}); } } } @@ -353,13 +718,50 @@ bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) { return true; } +bool SPIRVEmitNonSemanticDI::emitLineDI(MachineFunction &MF, + LiveRepository &LR) const { + bool IsModified = false; + for (auto &MBB : MF) { + for (auto &MI : MBB) { + if (MI.getDebugLoc().get()) { + MachineIRBuilder MIRBuilder(MBB, MI); + DebugLoc DL = MI.getDebugLoc(); + assert(DL.getScope() && "DL.getScope() must exist and be DISubprogram"); + const auto *File = cast(DL.getScope())->getFile(); + const size_t ScopeIdx = emitDebugSource(File, MIRBuilder, TM, LR); + const size_t LineIdx = LR.getOrCreateIdx(DL.getLine(), MIRBuilder, TM); + const size_t ColIdx = LR.getOrCreateIdx(DL.getCol(), MIRBuilder, TM); + LR.getOrCreateIdx( + {ScopeIdx, LineIdx, LineIdx, ColIdx, ColIdx}, MIRBuilder, TM); + IsModified = true; + } + } + } + return IsModified; +} + bool SPIRVEmitNonSemanticDI::runOnMachineFunction(MachineFunction &MF) { - bool Res = false; - // emitGlobalDI needs to be executed only once to avoid - // emitting duplicates - if (!IsGlobalDIEmitted) { - IsGlobalDIEmitted = true; - Res = emitGlobalDI(MF); + if (MF.begin() == MF.end()) { + return false; + } + static bool IsDIInModule = true; + bool IsFunctionModified = false; + if (IsDIInModule) { + LiveRepository LR; + // emitGlobalDI should be executed only once to prevent + // the emission of duplicate entries. + if (!IsGlobalDIEmitted) { + IsGlobalDIEmitted = true; + const MachineModuleInfo &MMI = + getAnalysis().getMMI(); + const Module *M = MMI.getModule(); + if (!M || !M->getNamedMetadata("llvm.dbg.cu")) { + IsDIInModule = false; + return false; + } + IsFunctionModified = emitGlobalDI(MF, M, LR); + } + IsFunctionModified |= emitLineDI(MF, LR); } - return Res; + return IsFunctionModified; } diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-line.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-line.ll new file mode 100644 index 0000000000000..58f09ef43a3c3 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-line.ll @@ -0,0 +1,144 @@ +; RUN: llc --verify-machineinstrs --spv-emit-nonsemantic-debug-info --spirv-ext=+SPV_KHR_non_semantic_info --print-after=spirv-nonsemantic-debug-info -O0 -mtriple=spirv64-unknown-unknown %s -o - 2>&1 | FileCheck %s --check-prefix=CHECK-MIR +; RUN: llc --verify-machineinstrs --spv-emit-nonsemantic-debug-info --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV +; +; TODO: spirv-val emits "line 42: NonSemantic.Shader.DebugInfo.100 DebugCompilationUnit: +; expected operand Language must be a result id of 32-bit unsigned OpConstant" that's because +; OpConstant with zero is substituted by OpConstantNull in resulting SPIR-V output. It's unrelated +; to debug-info. +; DISABLED: %if spirv-tools %{ llc --verify-machineinstrs --spv-emit-nonsemantic-debug-info --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %} + +; CHECK-MIR-DAG: [[void:%[0-9]+\:[a-z\(\)0-9]+]] = OpTypeVoid +; CHECK-MIR-DAG: [[i32:%[0-9]+\:[a-z\(\)0-9]+]] = OpTypeInt 32, 0 +; CHECK-MIR-DAG: [[i32_1:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 1 +; CHECK-MIR-DAG: [[i32_6:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 6 +; CHECK-MIR-DAG: [[i32_10:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 10 +; CHECK-MIR-DAG: [[i32_5:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 5 +; CHECK-MIR-DAG: [[i32_3:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 3 +; CHECK-MIR-DAG: [[i32_32:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 32 +; CHECK-MIR-DAG: [[i32_4:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 4 +; CHECK-MIR-DAG: [[i32_11:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 11 +; CHECK-MIR-DAG: [[i32_2:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 2 +; CHECK-MIR-DAG: [[i32_7:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 7 +; CHECK-MIR-DAG: [[i32_14:%[0-9]+\:[a-z\(\)0-9]+]] = OpConstantI [[i32]], 14 +; CHECK-MIR-DAG: [[file_scope:%[0-9]+\:[a-z\(\)0-9]+]] = OpExtInst [[void]], 3, 35 +; CHECK-MIR-DAG: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_1]], [[i32_1]], [[i32_14]], [[i32_14]] +; CHECK-MIR-DAG: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_2]], [[i32_2]], [[i32_7]], [[i32_7]] +; CHECK-MIR-DAG: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_2]], [[i32_2]], [[i32_11]], [[i32_11]] +; CHECK-MIR-DAG: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_3]], [[i32_3]], [[i32_7]], [[i32_7]] +; CHECK-MIR-DAG: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_4]], [[i32_4]], [[i32_7]], [[i32_7]] +; CHECK-MIR-DAG: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_5]], [[i32_5]], [[i32_7]], [[i32_7]] +; CHECK-MIR-DAG: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_6]], [[i32_6]], [[i32_10]], [[i32_10]] +; CHECK-MIR-DAG: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_6]], [[i32_6]], [[i32_3]], [[i32_3]] +; +; CHECK-MIR-NOT: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_1]], [[i32_1]], [[i32_14]], [[i32_14]] +; CHECK-MIR-NOT: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_2]], [[i32_2]], [[i32_7]], [[i32_7]] +; CHECK-MIR-NOT: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_2]], [[i32_2]], [[i32_11]], [[i32_11]] +; CHECK-MIR-NOT: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_3]], [[i32_3]], [[i32_7]], [[i32_7]] +; CHECK-MIR-NOT: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_4]], [[i32_4]], [[i32_7]], [[i32_7]] +; CHECK-MIR-NOT: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_5]], [[i32_5]], [[i32_7]], [[i32_7]] +; CHECK-MIR-NOT: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_6]], [[i32_6]], [[i32_10]], [[i32_10]] +; CHECK-MIR-NOT: OpExtInst [[void]], 3, 103, [[file_scope]], [[i32_6]], [[i32_6]], [[i32_3]], [[i32_3]] + +; CHECK-SPIRV-DAG: [[nonsemantic_di:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100" +; CHECK-SPIRV-DAG: [[i32:%[0-9]+]] = OpTypeInt 32 0 +; CHECK-SPIRV-DAG: [[void:%[0-9]+]] = OpTypeVoid +; CHECK-SPIRV-DAG: [[i32_1:%[0-9]+]] = OpConstant [[i32]] 1 +; CHECK-SPIRV-DAG: [[i32_5:%[0-9]+]] = OpConstant [[i32]] 5 +; CHECK-SPIRV-DAG: [[i32_3:%[0-9]+]] = OpConstant [[i32]] 3 +; CHECK-SPIRV-DAG: [[i32_32:%[0-9]+]] = OpConstant [[i32]] 32 +; CHECK-SPIRV-DAG: [[i32_4:%[0-9]+]] = OpConstant [[i32]] 4 +; CHECK-SPIRV-DAG: [[i32_14:%[0-9]+]] = OpConstant [[i32]] 14 +; CHECK-SPIRV-DAG: [[i32_2:%[0-9]+]] = OpConstant [[i32]] 2 +; CHECK-SPIRV-DAG: [[i32_7:%[0-9]+]] = OpConstant [[i32]] 7 +; CHECK-SPIRV-DAG: [[i32_11:%[0-9]+]] = OpConstant [[i32]] 11 +; CHECK-SPIRV-DAG: [[i32_6:%[0-9]+]] = OpConstant [[i32]] 6 +; CHECK-SPIRV-DAG: [[i32_10:%[0-9]+]] = OpConstant [[i32]] 10 +; CHECK-SPIRV: [[file_source:%[0-9]+]] = OpExtInst [[void]] [[nonsemantic_di]] DebugSource +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_1]] [[i32_1]] [[i32_14]] [[i32_14]] +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_2]] [[i32_2]] [[i32_7]] [[i32_7]] +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_2]] [[i32_2]] [[i32_11]] [[i32_11]] +; CHECK-SPIRV: OpLoad +; CHECK-SPIRV: OpIAdd +; CHECK-SPIRV: OpStore +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_3]] [[i32_3]] [[i32_7]] [[i32_7]] +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_3]] [[i32_3]] [[i32_11]] [[i32_11]] +; CHECK-SPIRV: OpLoad +; CHECK-SPIRV: OpIAdd +; CHECK-SPIRV: OpStore +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_4]] [[i32_4]] [[i32_7]] [[i32_7]] +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_4]] [[i32_4]] [[i32_11]] [[i32_11]] +; CHECK-SPIRV: OpLoad +; CHECK-SPIRV: OpIAdd +; CHECK-SPIRV: OpStore +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_5]] [[i32_5]] [[i32_7]] [[i32_7]] +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_5]] [[i32_5]] [[i32_11]] [[i32_11]] +; CHECK-SPIRV: OpLoad +; CHECK-SPIRV: OpIAdd +; CHECK-SPIRV: OpStore +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_6]] [[i32_6]] [[i32_10]] [[i32_10]] +; CHECK-SPIRV: OpLoad +; CHECK-SPIRV: OpExtInst [[void]] [[nonsemantic_di]] DebugLine [[file_source]] [[i32_6]] [[i32_6]] [[i32_3]] [[i32_3]] + +define dso_local spir_func i32 @test(i32 noundef %a) #0 !dbg !6 { +entry: + %a.addr = alloca i32, align 4 + %b = alloca i32, align 4 + %c = alloca i32, align 4 + %d = alloca i32, align 4 + %e = alloca i32, align 4 + store i32 %a, ptr %a.addr, align 4 + #dbg_declare(ptr %a.addr, !12, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !13) + #dbg_declare(ptr %b, !14, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !15) + %0 = load i32, ptr %a.addr, align 4, !dbg !16 + %add = add nsw i32 %0, 1, !dbg !17 + store i32 %add, ptr %b, align 4, !dbg !15 + #dbg_declare(ptr %c, !18, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !19) + %1 = load i32, ptr %b, align 4, !dbg !20 + %add1 = add nsw i32 %1, 1, !dbg !21 + store i32 %add1, ptr %c, align 4, !dbg !19 + #dbg_declare(ptr %d, !22, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !23) + %2 = load i32, ptr %c, align 4, !dbg !24 + %add2 = add nsw i32 %2, 1, !dbg !25 + store i32 %add2, ptr %d, align 4, !dbg !23 + #dbg_declare(ptr %e, !26, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !27) + %3 = load i32, ptr %d, align 4, !dbg !28 + %add3 = add nsw i32 %3, 1, !dbg !29 + store i32 %add3, ptr %e, align 4, !dbg !27 + %4 = load i32, ptr %e, align 4, !dbg !30 + ret i32 %4, !dbg !31 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4} + +!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version XX.X.Xgit (GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG 9999999999999999999999999999999999999999)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "YYYYYYY", directory: "/XXXXXXXXXXXXXXXX/XXXXXXXXXXXXXXXX/XXXXXXXXXXX", checksumkind: CSK_MD5, checksum: "66666666666666666666666666666666") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!6 = distinct !DISubprogram(name: "test", scope: !7, file: !7, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !11) +!7 = !DIFile(filename: "ZZZZZ", directory: "YYYYYYYYY/YYYYYYYYYYYY/YYYYYYYYYYYYY/YYYYYYYYY", checksumkind: CSK_MD5, checksum: "77777777777777777777777777777777") +!8 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !9) +!9 = !{!10, !10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{} +!12 = !DILocalVariable(name: "a", arg: 1, scope: !6, file: !7, line: 1, type: !10) +!13 = !DILocation(line: 1, column: 14, scope: !6) +!14 = !DILocalVariable(name: "b", scope: !6, file: !7, line: 2, type: !10) +!15 = !DILocation(line: 2, column: 7, scope: !6) +!16 = !DILocation(line: 2, column: 11, scope: !6) +!17 = !DILocation(line: 2, column: 13, scope: !6) +!18 = !DILocalVariable(name: "c", scope: !6, file: !7, line: 3, type: !10) +!19 = !DILocation(line: 3, column: 7, scope: !6) +!20 = !DILocation(line: 3, column: 11, scope: !6) +!21 = !DILocation(line: 3, column: 13, scope: !6) +!22 = !DILocalVariable(name: "d", scope: !6, file: !7, line: 4, type: !10) +!23 = !DILocation(line: 4, column: 7, scope: !6) +!24 = !DILocation(line: 4, column: 11, scope: !6) +!25 = !DILocation(line: 4, column: 13, scope: !6) +!26 = !DILocalVariable(name: "e", scope: !6, file: !7, line: 5, type: !10) +!27 = !DILocation(line: 5, column: 7, scope: !6) +!28 = !DILocation(line: 5, column: 11, scope: !6) +!29 = !DILocation(line: 5, column: 13, scope: !6) +!30 = !DILocation(line: 6, column: 10, scope: !6) +!31 = !DILocation(line: 6, column: 3, scope: !6) diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer.ll index b7e6e95f366cf..d9ef3f59da357 100644 --- a/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer.ll +++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer.ll @@ -28,19 +28,22 @@ ; CHECK-MIR-DAG: [[float:%[0-9]+\:id\(s32\)]] = OpExtInst [[void_type]], 3, 2, {{%[0-9]+\:[a-z0-9\(\)]+}}, [[i32_32]], [[enc_float]], [[i32_0]] ; CHECK-MIR-DAG: [[double:%[0-9]+\:id\(s32\)]] = OpExtInst [[void_type]], 3, 2, {{%[0-9]+\:[a-z0-9\(\)]+}}, [[i32_64]], [[enc_float]], [[i32_0]] ; CHECK-MIR-DAG: [[int:%[0-9]+\:id\(s32\)]] = OpExtInst [[void_type]], 3, 2, {{%[0-9]+\:[a-z0-9\(\)]+}}, [[i32_32]], [[enc_signed]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[bool]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[short]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[char]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[long]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[unsigned_int]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[unsigned_short]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[unsigned_char]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[unsigned_long]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[float]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[double]], [[i32_8]], [[i32_0]] -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[int]], [[i32_5]], [[i32_0]] -; CHECK-MIR: [[debug_info_none:%[0-9]+\:id\(s32\)]] = OpExtInst [[void_type]], 3, 0 -; CHECK-MIR: OpExtInst [[void_type]], 3, 3, [[debug_info_none]], [[i32_5]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[bool]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[short]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[char]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[long]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[unsigned_int]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[unsigned_short]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[unsigned_char]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[unsigned_long]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[float]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[double]], [[i32_8]], [[i32_0]] +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[int]], [[i32_5]], [[i32_0]] +; CHECK-MIR-DAG: [[debug_info_none:%[0-9]+\:id\(s32\)]] = OpExtInst [[void_type]], 3, 0 +; CHECK-MIR-DAG: OpExtInst [[void_type]], 3, 3, [[debug_info_none]], [[i32_5]], [[i32_0]] + +; Duplicates check +; CHECK-MIR-NOT: OpExtInst [[void_type]], 3, 3 ; CHECK-SPIRV: [[i32type:%[0-9]+]] = OpTypeInt 32 0 ; CHECK-SPIRV-DAG: [[i32_8:%[0-9]+]] = OpConstant [[i32type]] 8 @@ -79,6 +82,9 @@ ; CHECK-SPIRV-DAG: [[debug_info_none:%[0-9]+]] = OpExtInst {{%[0-9]+ %[0-9]+}} DebugInfoNone ; CHECK-SPIRV-DAG: OpExtInst {{%[0-9]+ %[0-9]+}} DebugTypePointer [[debug_info_none]] [[i32_5]] [[i32_0]] +; Duplicates check +; CHECK-SPIRV-NOT: OpExtInst {{%[0-9]+ %[0-9]+}} DebugTypePointer + ; CHECK-OPTION-NOT: DebugTypePointer @gi0 = dso_local addrspace(1) global ptr addrspace(4) null, align 4, !dbg !0