Skip to content

Conversation

@aadeshps-mcw
Copy link
Contributor

--Added support for the extension SPV_KHR_non_semantic_info
--Added 19 instructions from the documentation.
--Added supporting tests for the same.

@aadeshps-mcw aadeshps-mcw marked this pull request as draft October 27, 2025 19:28
@llvmbot
Copy link
Member

llvmbot commented Oct 27, 2025

@llvm/pr-subscribers-backend-spir-v

Author: Aadesh Premkumar (aadeshps-mcw)

Changes

--Added support for the extension SPV_KHR_non_semantic_info
--Added 19 instructions from the documentation.
--Added supporting tests for the same.


Patch is 659.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/165302.diff

18 Files Affected:

  • (modified) llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp (+1408-168)
  • (modified) llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h (+11)
  • (modified) llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp (+26-2)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-build-identifier-storagepath.ll (+56)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-function.ll (+54)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-imported-entity.ll (+66)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-lexical-scope.ll (+60)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-macro-def-undef.ll (+88)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-qualifier.ll (+58)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-source-continued.ll (+52)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-type-array.ll (+74)
  • (modified) llvm/test/CodeGen/SPIRV/debug-info/debug-type-basic.ll (+1)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-type-composite.ll (+69)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-type-inheritance.ll (+88)
  • (modified) llvm/test/CodeGen/SPIRV/debug-info/debug-type-pointer.ll (+2-2)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-type-ptrtomember.ll (+78)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-type-template.ll (+85)
  • (added) llvm/test/CodeGen/SPIRV/debug-info/debug-typedef.ll (+86)
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp
index 318ef0679ba03..416a87559d6b4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitNonSemanticDI.cpp
@@ -18,6 +18,7 @@
 #include "llvm/CodeGen/MachineOperand.h"
 #include "llvm/CodeGen/MachineRegisterInfo.h"
 #include "llvm/CodeGen/Register.h"
+#include "llvm/Config/llvm-config.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/DebugProgramInstruction.h"
 #include "llvm/IR/Metadata.h"
@@ -29,6 +30,71 @@
 using namespace llvm;
 
 namespace {
+struct SPIRVCodeGenContext {
+  MachineIRBuilder &MIRBuilder;
+  MachineRegisterInfo &MRI;
+  SPIRVGlobalRegistry *GR;
+  const SPIRVType *VoidTy;
+  const SPIRVType *I32Ty;
+  const SPIRVInstrInfo *TII;
+  const SPIRVRegisterInfo *TRI;
+  const RegisterBankInfo *RBI;
+  MachineFunction &MF;
+  const Register &I32ZeroReg;
+  SPIRVTargetMachine *TM;
+  SmallVector<std::pair<const DIFile *const, const Register>, 12>
+      &SourceRegPairs;
+  SmallVector<std::pair<const DIScope *const, const Register>, 12>
+      &ScopeRegPairs;
+  SmallVector<std::pair<const DISubroutineType *const, const Register>, 12>
+      &SubRoutineTypeRegPairs;
+  SmallVector<std::pair<const DIBasicType *const, const Register>, 12>
+      &BasicTypeRegPairs;
+  SmallVector<std::pair<const DICompositeType *const, const Register>, 12>
+      &CompositeTypeRegPairs;
+
+  SPIRVCodeGenContext(
+      MachineIRBuilder &Builder, MachineRegisterInfo &RegInfo,
+      SPIRVGlobalRegistry *Registry, const SPIRVType *VTy,
+      const SPIRVType *I32Ty, const SPIRVInstrInfo *TI,
+      const SPIRVRegisterInfo *TR, const RegisterBankInfo *RB,
+      MachineFunction &Function, const Register &ZeroReg,
+      SPIRVTargetMachine *TargetMachine,
+      SmallVector<std::pair<const DIFile *const, const Register>, 12>
+          &SourceRegisterPairs,
+      SmallVector<std::pair<const DIScope *const, const Register>, 12>
+          &ScopeRegisterPairs,
+      SmallVector<std::pair<const DISubroutineType *const, const Register>, 12>
+          &SubRoutineTypeRegisterPairs,
+      SmallVector<std::pair<const DIBasicType *const, const Register>, 12>
+          &BasicTypePairs,
+      SmallVector<std::pair<const DICompositeType *const, const Register>, 12>
+          &CompositeTypePairs)
+      : MIRBuilder(Builder), MRI(RegInfo), GR(Registry), VoidTy(VTy),
+        I32Ty(I32Ty), TII(TI), TRI(TR), RBI(RB), MF(Function),
+        I32ZeroReg(ZeroReg), TM(TargetMachine),
+        SourceRegPairs(SourceRegisterPairs), ScopeRegPairs(ScopeRegisterPairs),
+        SubRoutineTypeRegPairs(SubRoutineTypeRegisterPairs),
+        BasicTypeRegPairs(BasicTypePairs),
+        CompositeTypeRegPairs(CompositeTypePairs) {}
+};
+struct DebugInfoCollector {
+  SmallPtrSet<DIBasicType *, 12> BasicTypes;
+  SmallPtrSet<DIDerivedType *, 12> PointerDerivedTypes;
+  SmallPtrSet<DIDerivedType *, 12> QualifiedDerivedTypes;
+  SmallPtrSet<DIDerivedType *, 12> TypedefTypes;
+  SmallPtrSet<DIDerivedType *, 12> InheritedTypes;
+  SmallPtrSet<DIDerivedType *, 12> PtrToMemberTypes;
+  SmallVector<const DIImportedEntity *, 5> ImportedEntities;
+  SmallPtrSet<DISubprogram *, 12> SubPrograms;
+  SmallPtrSet<DISubroutineType *, 12> SubRoutineTypes;
+  SmallPtrSet<DIScope *, 12> LexicalScopes;
+  SmallPtrSet<DICompositeType *, 12> ArrayTypes;
+  SmallPtrSet<const DICompositeType *, 8> CompositeTypesWithTemplates;
+  SmallPtrSet<const DICompositeType *, 8> CompositeTypes;
+  SmallPtrSet<const DICompositeType *, 8> EnumTypes;
+  DenseSet<const DIType *> visitedTypes;
+};
 struct SPIRVEmitNonSemanticDI : public MachineFunctionPass {
   static char ID;
   SPIRVTargetMachine *TM;
@@ -40,6 +106,115 @@ struct SPIRVEmitNonSemanticDI : public MachineFunctionPass {
 private:
   bool IsGlobalDIEmitted = false;
   bool emitGlobalDI(MachineFunction &MF);
+  Register EmitOpString(StringRef, SPIRVCodeGenContext &Ctx);
+  uint32_t transDebugFlags(const DINode *DN);
+  uint32_t mapTagToCompositeEncoding(const DICompositeType *CT);
+  uint32_t mapTagToQualifierEncoding(unsigned Tag);
+  uint32_t mapDebugFlags(DINode::DIFlags DFlags);
+  uint32_t mapImportedTagToEncoding(const DIImportedEntity *Imported);
+
+  Register EmitDIInstruction(SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst,
+                             ArrayRef<Register> Operands,
+                             SPIRVCodeGenContext &Ctx);
+
+  Register findEmittedBasicTypeReg(
+      const DIType *BaseType,
+      const SmallVectorImpl<std::pair<const DIBasicType *const, const Register>>
+          &BasicTypeRegPairs);
+
+  Register findEmittedCompositeTypeReg(
+      const DIType *BaseType,
+      const SmallVectorImpl<std::pair<const DICompositeType *const,
+                                      const Register>> &CompositeTypeRegPairs);
+
+  void extractTypeMetadata(DIType *Ty, DebugInfoCollector &Collector);
+
+  void handleCompositeType(DICompositeType *CT, DebugInfoCollector &Collector);
+  void handleDerivedType(DIDerivedType *DT, DebugInfoCollector &Collector);
+
+  void emitDebugBuildIdentifier(StringRef BuildIdentifier,
+                                SPIRVCodeGenContext &Ctx);
+
+  void emitDebugStoragePath(StringRef BuildStoragePath,
+                            SPIRVCodeGenContext &Ctx);
+
+  void emitDebugBasicTypes(const SmallPtrSetImpl<DIBasicType *> &BasicTypes,
+                           SPIRVCodeGenContext &Ctx);
+
+  void emitDebugPointerTypes(
+      const SmallPtrSetImpl<DIDerivedType *> &PointerDerivedTypes,
+      SPIRVCodeGenContext &Ctx);
+
+  void emitSingleCompilationUnit(StringRef FilePath, int64_t SourceLanguage,
+                                 SPIRVCodeGenContext &Ctx,
+                                 Register DebugInfoVersionReg,
+                                 Register DwarfVersionReg,
+                                 Register &DebugSourceResIdReg,
+                                 Register &DebugCompUnitResIdReg);
+
+  // void emitDebugTypeInheritance(
+  //     const SmallPtrSetImpl<DIDerivedType *> &InheritedTypes,
+  //     SPIRVCodeGenContext &Ctx);
+
+  void emitLexicalScopes(const SmallPtrSetImpl<DIScope *> &LexicalScopes,
+                         SPIRVCodeGenContext &Ctx);
+
+  void emitDebugArrayTypes(const SmallPtrSetImpl<DICompositeType *> &ArrayTypes,
+                           SPIRVCodeGenContext &Ctx);
+
+  void emitDebugVectorTypes(DICompositeType *ArrayTy, Register BaseTypeReg,
+                            SPIRVCodeGenContext &Ctx);
+
+  void emitDebugTypeComposite(const DICompositeType *CompTy,
+                              SPIRVCodeGenContext &Ctx);
+
+  void emitAllTemplateDebugInstructions(
+      const SmallPtrSetImpl<const DICompositeType *> &TemplatedTypes,
+      SPIRVCodeGenContext &Ctx);
+
+  void emitAllDebugTypeComposites(
+      const SmallPtrSetImpl<const DICompositeType *> &CompositeTypes,
+      SPIRVCodeGenContext &Ctx);
+
+  void emitSubroutineTypes(
+      const SmallPtrSet<DISubroutineType *, 12> &SubRoutineTypes,
+      SPIRVCodeGenContext &Ctx);
+
+  void emitSubprograms(const SmallPtrSet<DISubprogram *, 12> &SubPrograms,
+                       SPIRVCodeGenContext &Ctx);
+
+  void emitDebugTypeMember(const DIDerivedType *Member,
+                           SPIRVCodeGenContext &Ctx,
+                           const Register &CompositeReg,
+                           SmallVectorImpl<Register> &MemberRegs,
+                           Register DebugSourceReg);
+
+  void emitDebugMacroDefs(MachineFunction &MF, SPIRVCodeGenContext &Ctx);
+
+  void emitDebugMacroUndef(const DIMacro *MacroUndef, StringRef FileName,
+                           SPIRVCodeGenContext &Ctx,
+                           const DenseMap<StringRef, Register> &MacroDefRegs);
+
+  void emitDebugQualifiedTypes(
+      const SmallPtrSetImpl<DIDerivedType *> &QualifiedDerivedTypes,
+      SPIRVCodeGenContext &Ctx);
+
+  void emitDebugTypedefs(const SmallPtrSetImpl<DIDerivedType *> &TypedefTypes,
+                         SPIRVCodeGenContext &Ctx);
+
+  void emitDebugImportedEntities(
+      const SmallVectorImpl<const DIImportedEntity *> &ImportedEntities,
+      SPIRVCodeGenContext &Ctx);
+
+  Register emitDebugGlobalVariable(const DIGlobalVariableExpression *GVE,
+                                   SPIRVCodeGenContext &Ctx);
+
+  void emitAllDebugGlobalVariables(MachineFunction &MF,
+                                   SPIRVCodeGenContext &Ctx);
+
+  void emitDebugTypePtrToMember(
+      const SmallPtrSetImpl<DIDerivedType *> &PtrToMemberTypes,
+      SPIRVCodeGenContext &Ctx);
 };
 } // anonymous namespace
 
@@ -64,6 +239,12 @@ enum BaseTypeAttributeEncoding {
   UnsignedChar = 7
 };
 
+enum CompositeTypeAttributeEncoding { Class = 0, Struct = 1, Union = 2 };
+enum ImportedEnityAttributeEncoding {
+  ImportedModule = 0,
+  ImportedDeclaration = 1
+};
+
 enum SourceLanguage {
   Unknown = 0,
   ESSL = 1,
@@ -77,9 +258,53 @@ enum SourceLanguage {
   NZSL = 9,
   WGSL = 10,
   Slang = 11,
-  Zig = 12
+  Zig = 12,
+  CPP = 13
+};
+
+enum QualifierTypeAttributeEncoding {
+  ConstType = 0,
+  VolatileType = 1,
+  RestrictType = 2,
+  AtomicType = 3
+};
+
+enum Flag {
+  FlagIsProtected = 1 << 0,
+  FlagIsPrivate = 1 << 1,
+  FlagIsPublic = FlagIsPrivate | FlagIsProtected,
+  FlagAccess = FlagIsPublic,
+  FlagIsLocal = 1 << 2,
+  FlagIsDefinition = 1 << 3,
+  FlagIsFwdDecl = 1 << 4,
+  FlagIsArtificial = 1 << 5,
+  FlagIsExplicit = 1 << 6,
+  FlagIsPrototyped = 1 << 7,
+  FlagIsObjectPointer = 1 << 8,
+  FlagIsStaticMember = 1 << 9,
+  FlagIsIndirectVariable = 1 << 10,
+  FlagIsLValueReference = 1 << 11,
+  FlagIsRValueReference = 1 << 12,
+  FlagIsOptimized = 1 << 13,
+  FlagIsEnumClass = 1 << 14,
+  FlagTypePassByValue = 1 << 15,
+  FlagTypePassByReference = 1 << 16,
+  FlagUnknownPhysicalLayout = 1 << 17,
+  FlagBitField = 1 << 18
 };
 
+template <typename T, typename Container>
+Register findRegisterFromMap(const T *DIType, const Container &RegPairs,
+                             Register DefaultReg = Register()) {
+  if (!DIType)
+    return DefaultReg;
+  for (const auto &[DefinedType, Reg] : RegPairs) {
+    if (DefinedType == DIType)
+      return Reg;
+  }
+  return DefaultReg;
+}
+
 bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) {
   // If this MachineFunction doesn't have any BB repeat procedure
   // for the next
@@ -88,16 +313,17 @@ bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) {
     return false;
   }
 
-  // Required variables to get from metadata search
   LLVMContext *Context;
   SmallVector<SmallString<128>> FilePaths;
   SmallVector<int64_t> LLVMSourceLanguages;
   int64_t DwarfVersion = 0;
   int64_t DebugInfoVersion = 0;
-  SmallPtrSet<DIBasicType *, 12> BasicTypes;
-  SmallPtrSet<DIDerivedType *, 12> PointerDerivedTypes;
-  // Searching through the Module metadata to find nescessary
-  // information like DwarfVersion or SourceLanguage
+  SmallString<128> BuildIdentifier;
+  SmallString<128> BuildStoragePath;
+  Register DebugCompUnitResIdReg;
+  Register DebugSourceResIdReg;
+  DebugInfoCollector Collector;
+
   {
     const MachineModuleInfo &MMI =
         getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
@@ -108,6 +334,22 @@ bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) {
       return false;
     for (const auto *Op : DbgCu->operands()) {
       if (const auto *CompileUnit = dyn_cast<DICompileUnit>(Op)) {
+        if (CompileUnit->getDWOId())
+          BuildIdentifier = std::to_string(CompileUnit->getDWOId());
+        if (!CompileUnit->getSplitDebugFilename().empty())
+          BuildStoragePath = CompileUnit->getSplitDebugFilename();
+
+        for (auto *GVE : CompileUnit->getGlobalVariables()) {
+          if (auto *DIGV = dyn_cast<DIGlobalVariable>(GVE->getVariable())) {
+            extractTypeMetadata(DIGV->getType(), Collector);
+          }
+        }
+        for (const auto *IE : CompileUnit->getImportedEntities()) {
+          if (const auto *Imported = dyn_cast<DIImportedEntity>(IE)) {
+            Collector.ImportedEntities.push_back(Imported);
+          }
+        }
+
         DIFile *File = CompileUnit->getFile();
         FilePaths.emplace_back();
         sys::path::append(FilePaths.back(), File->getDirectory(),
@@ -135,25 +377,25 @@ bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) {
     // This traversal is the only supported way to access
     // instruction related DI metadata like DIBasicType
     for (auto &F : *M) {
+      if (DISubprogram *SP = F.getSubprogram()) {
+        Collector.SubPrograms.insert(SP);
+        if (auto *SubType = dyn_cast<DISubroutineType>(SP->getType()))
+          Collector.SubRoutineTypes.insert(SubType);
+      }
       for (auto &BB : F) {
         for (auto &I : BB) {
           for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange())) {
-            DILocalVariable *LocalVariable = DVR.getVariable();
-            if (auto *BasicType =
-                    dyn_cast<DIBasicType>(LocalVariable->getType())) {
-              BasicTypes.insert(BasicType);
-            } else if (auto *DerivedType =
-                           dyn_cast<DIDerivedType>(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 (auto *BT = dyn_cast_or_null<DIBasicType>(
-                        DerivedType->getBaseType()))
-                  BasicTypes.insert(BT);
-              }
+            if (DILocalVariable *LocalVariable = DVR.getVariable())
+              extractTypeMetadata(LocalVariable->getType(), Collector);
+          }
+          if (const DebugLoc &DL = I.getDebugLoc()) {
+            if (const DILocation *Loc = DL.get()) {
+              DIScope *Scope = Loc->getScope();
+              if (auto *SP = dyn_cast<DISubprogram>(Scope))
+                Collector.SubPrograms.insert(SP);
+              else if (isa<DILexicalBlock>(Scope) ||
+                       isa<DILexicalBlockFile>(Scope))
+                Collector.LexicalScopes.insert(Scope);
             }
           }
         }
@@ -175,185 +417,1183 @@ bool SPIRVEmitNonSemanticDI::emitGlobalDI(MachineFunction &MF) {
     // 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;
-    };
+    SmallVector<std::pair<const DIBasicType *const, const Register>, 12>
+        BasicTypeRegPairs;
+    SmallVector<std::pair<const DICompositeType *const, const Register>, 12>
+        CompositeTypeRegPairs;
+    SmallVector<std::pair<const DIFile *const, const Register>, 12>
+        SourceRegPairs;
+    SmallVector<std::pair<const DIScope *const, const Register>, 12>
+        ScopeRegPairs;
+    SmallVector<std::pair<const DISubroutineType *const, const Register>, 12>
+        SubRoutineTypeRegPairs;
 
     const SPIRVType *VoidTy =
         GR->getOrCreateSPIRVType(Type::getVoidTy(*Context), MIRBuilder,
                                  SPIRV::AccessQualifier::ReadWrite, false);
-
-    const auto EmitDIInstruction =
-        [&](SPIRV::NonSemanticExtInst::NonSemanticExtInst Inst,
-            std::initializer_list<Register> 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<int64_t>(
-                      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;
-        };
-
     const SPIRVType *I32Ty =
         GR->getOrCreateSPIRVType(Type::getInt32Ty(*Context), MIRBuilder,
                                  SPIRV::AccessQualifier::ReadWrite, false);
 
+    const Register I32ZeroReg =
+        GR->buildConstantInt(1, MIRBuilder, I32Ty, false);
+
     const Register DwarfVersionReg =
         GR->buildConstantInt(DwarfVersion, MIRBuilder, I32Ty, false);
 
     const Register DebugInfoVersionReg =
         GR->buildConstantInt(DebugInfoVersion, MIRBuilder, I32Ty, false);
 
+    SPIRVCodeGenContext Ctx(MIRBuilder, MRI, GR, VoidTy, I32Ty, TII, TRI, RBI,
+                            MF, I32ZeroReg, TM, SourceRegPairs, ScopeRegPairs,
+                            SubRoutineTypeRegPairs, BasicTypeRegPairs,
+                            CompositeTypeRegPairs);
+
     for (unsigned Idx = 0; Idx < LLVMSourceLanguages.size(); ++Idx) {
-      const Register FilePathStrReg = EmitOpString(FilePaths[Idx]);
-
-      const Register DebugSourceResIdReg = EmitDIInstruction(
-          SPIRV::NonSemanticExtInst::DebugSource, {FilePathStrReg});
-
-      SourceLanguage SpirvSourceLanguage = SourceLanguage::Unknown;
-      switch (LLVMSourceLanguages[Idx]) {
-      case dwarf::DW_LANG_OpenCL:
-        SpirvSourceLanguage = SourceLanguage::OpenCL_C;
-        break;
-      case dwarf::DW_LANG_OpenCL_CPP:
-        SpirvSourceLanguage = SourceLanguage::OpenCL_CPP;
-        break;
-      case dwarf::DW_LANG_CPP_for_OpenCL:
-        SpirvSourceLanguage = SourceLanguage::CPP_for_OpenCL;
-        break;
-      case dwarf::DW_LANG_GLSL:
-        SpirvSourceLanguage = SourceLanguage::GLSL;
-        break;
-      case dwarf::DW_LANG_HLSL:
-        SpirvSourceLanguage = SourceLanguage::HLSL;
-        break;
-      case dwarf::DW_LANG_SYCL:
-        SpirvSourceLanguage = SourceLanguage::SYCL;
-        break;
-      case dwarf::DW_LANG_Zig:
-        SpirvSourceLanguage = SourceLanguage::Zig;
+      emitSingleCompilationUnit(FilePaths[Idx], LLVMSourceLanguages[Idx], Ctx,
+                                DebugInfoVersionReg, DwarfVersionReg,
+                                DebugSourceResIdReg, DebugCompUnitResIdReg);
+
+      if (const DISubprogram *SP = Ctx.MF.getFunction().getSubprogram()) {
+        if (const DIFile *File = SP->getFile())
+          Ctx.GR->addDebugValue(File, DebugCompUnitResIdReg);
+        if (const DICompileUnit *Unit = SP->getUnit())
+          Ctx.GR->addDebugValue(Unit, DebugCompUnitResIdReg);
+      }
+    }
+    emitDebugMacroDefs(MF, Ctx);
+    emitSubroutineTypes(Collector.SubRoutineTypes, Ctx);
+    emitSubprograms(Collector.SubPrograms, Ctx);
+    emitLexicalScopes(Collector.LexicalScopes, Ctx);
+    emitDebugBuildIdentifier(BuildIdentifier, Ctx);
+    emitDebugStoragePath(BuildStoragePath, Ctx);
+    emitDebugBasicTypes(Collector.BasicTypes, Ctx);
+    emitDebugPointerTypes(Collector.PointerDerivedTypes, Ctx);
+    emitDebugArrayTypes(Collector.ArrayTypes, Ctx);
+    emitAllDebugTypeComposites(Collector.CompositeTypes, Ctx);
+    emitAllTemplateDebugInstructions(Collector.CompositeTypesWithTemplates,
+                                     Ctx);
+    emitAllDebugGlobalVariables(MF, Ctx);
+    emitDebugQualifiedTypes(Collector.QualifiedDerivedTypes, Ctx);
+    emitDebugTypedefs(Collector.TypedefTypes, Ctx);
+    emitDebugImportedEntities(Collector.ImportedEntities, Ctx);
+    // emitDebugTypeInheritance(Collector.InheritedTypes, Ctx);
+    emitDebugTypePtrToMember(Collector.PtrToMemberTypes, Ctx);
+  }
+  return true;
+}
+
+bool SPIRVEmitNonSemanticDI::runOnMachineFunction(MachineFunction &MF) {
+  bool Res = false;
+  if (!IsGlobalDIEmitted) {
+    IsGlobalDIEmitted = true;
+    Res = emitGlobalDI(MF);
+  }
+  return Res;
+}
+
+Register SPIRVEmitNonSemanticDI::EmitOpString(StringRef SR,
+                                              SPIRVCodeGenContext &Ctx) {
+  const Register StrReg = Ctx.MRI.createVirtual...
[truncated]

@github-actions
Copy link

github-actions bot commented Oct 27, 2025

✅ With the latest revision this PR passed the undef deprecator.

@aadeshps-mcw aadeshps-mcw force-pushed the semantic_info branch 10 times, most recently from 3830905 to 62e262c Compare October 28, 2025 07:27
@Keenuts
Copy link
Contributor

Keenuts commented Oct 28, 2025

You might want to take a look at:
https://github.khronos.org/SPIRV-Registry/extensions/KHR/SPV_KHR_relaxed_extended_instruction.html

and

https://github.com/KhronosGroup/SPIRV-Tools/pull/5698/files

When declaring a class with methods, you can end-up with forward references in non-semantic info. In such case, you need to generate OpExtInstWithForwardRef instruction instead, and user this extension.

@aadeshps-mcw
Copy link
Contributor Author

aadeshps-mcw commented Oct 28, 2025

Is this extension needed for the instructions I am trying to implement in this PR @Keenuts ?

@Keenuts
Copy link
Contributor

Keenuts commented Oct 28, 2025

This extension is required to use OpExtInstWithForwardRefsKHR, which is not used as-is in this PR.
However, if you implement debug instructions for class methods, this will be required as those adds a circular dependency on debug instructions operands (spirv-val should complain if this happens)

@Keenuts
Copy link
Contributor

Keenuts commented Oct 28, 2025

Seems like having a method yields a crash on this PR:
(Compiled in 2 steps, HLSL -> IR, IR -> SPIR-V + debug)

// RUN: %clang --driver-mode=dxc %s -T cs_6_8 -E main -O3 -spirv -g

class A {
  int foo(int value) {
    return value * 2;
  }
};

RWStructuredBuffer<uint> buffer;

[numthreads(1, 1, 1)]
void main() {
  A a;
  buffer[0] = a.foo(buffer[0]);
}
; RUN: %llc --spv-emit-nonsemantic-debug-info --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown /tmp/a.ll -o -

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G10"
target triple = "spirv1.6-unknown-vulkan1.3-compute"

@.str = private unnamed_addr constant [7 x i8] c"buffer\00", align 1

; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none)
define void @main() local_unnamed_addr #0 {
entry:
    #dbg_value(i32 0, !69, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !78)
    #dbg_value(i32 0, !72, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !78)
    #dbg_value(i32 1, !73, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !78)
    #dbg_value(i32 0, !74, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !78)
    #dbg_value(ptr @.str, !75, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !78)
    #dbg_value(i32 1, !76, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !78)
  %0 = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefromimplicitbinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str), !dbg !78
    #dbg_value(ptr poison, !87, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !91)
    #dbg_value(ptr undef, !90, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !91)
    #dbg_value(ptr poison, !93, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !97)
    #dbg_value(i32 0, !96, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !97)
  %1 = tail call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %0, i32 0), !dbg !97
  %2 = load i32, ptr addrspace(11) %1, align 4, !dbg !109, !tbaa !65
    #dbg_value(ptr undef, !110, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !115)
    #dbg_value(i32 %2, !113, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !115)
  %mul.i = shl nsw i32 %2, 1, !dbg !117
    #dbg_value(ptr poison, !93, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !118)
    #dbg_value(i32 0, !96, !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef), !118)
  store i32 %mul.i, ptr addrspace(11) %1, align 4, !dbg !120, !tbaa !65
  ret void
}

; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
declare target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefromimplicitbinding.tspirv.VulkanBuffer_a0i32_12_1t(i32, i32, i32, i32, ptr) #1

; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none)
declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1), i32) #1

attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-infs-fp-math"="true" "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
attributes #1 = { mustprogress nocallback nofree nosync nounwind willreturn memory(none) }

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!60, !61, !62, !63}
!llvm.ident = !{!64}
!llvm.errno.tbaa = !{!65}

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_11, file: !1, producer: "clang version 22.0.0git (/home/user/projects/llvm-project/clang 07b96bbf70c7773e44e31e51b4f1aac63b815a1f)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, globals: !57, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "/home/user/projects/llvm-project/repro.hlsl", directory: "/home/user/projects/llvm-project")
!2 = !{!3}
!3 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "RWStructuredBuffer<unsigned int>", scope: !4, size: 128, flags: DIFlagTypePassByReference | DIFlagNonTrivial, elements: !5, templateParams: !55, identifier: "_ZTSN4hlsl18RWStructuredBufferIjEE")
!4 = !DINamespace(name: "hlsl", scope: null)
!5 = !{!6, !10, !11, !15, !20, !24, !32, !33, !39, !43, !46, !49, !50}
!6 = !DIDerivedType(tag: DW_TAG_member, name: "__handle", scope: !3, file: !7, line: 15, baseType: !8, size: 64)
!7 = !DIFile(filename: "repro.hlsl", directory: "/home/user/projects/llvm-project")
!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64)
!9 = !DICompositeType(tag: DW_TAG_structure_type, name: "__hlsl_resource_t", file: !1, flags: DIFlagFwdDecl)
!10 = !DIDerivedType(tag: DW_TAG_member, name: "__counter_handle", scope: !3, file: !7, line: 15, baseType: !8, size: 64, offset: 64)
!11 = !DISubprogram(name: "RWStructuredBuffer", linkageName: "_ZN4hlsl18RWStructuredBufferIjEC4Ev", scope: !3, file: !7, type: !12, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!12 = !DISubroutineType(types: !13)
!13 = !{null, !14}
!14 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !3, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer, dwarfAddressSpace: 0)
!15 = !DISubprogram(name: "RWStructuredBuffer", linkageName: "_ZN4hlsl18RWStructuredBufferIjEC4ERKS1_", scope: !3, file: !7, type: !16, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!16 = !DISubroutineType(types: !17)
!17 = !{null, !14, !18}
!18 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !19, size: 64, dwarfAddressSpace: 0)
!19 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !3)
!20 = !DISubprogram(name: "operator=", linkageName: "_ZN4hlsl18RWStructuredBufferIjEaSERKS1_", scope: !3, file: !7, type: !21, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!21 = !DISubroutineType(types: !22)
!22 = !{!23, !14, !18}
!23 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !3, size: 64, dwarfAddressSpace: 0)
!24 = !DISubprogram(name: "__createFromBindingWithImplicitCounter", linkageName: "_ZN4hlsl18RWStructuredBufferIjE38__createFromBindingWithImplicitCounterEjjijPKcj", scope: !3, file: !7, type: !25, flags: DIFlagPublic | DIFlagPrototyped | DIFlagStaticMember, spFlags: DISPFlagOptimized)
!25 = !DISubroutineType(types: !26)
!26 = !{!3, !27, !27, !28, !27, !29, !27}
!27 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
!28 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !30, size: 64, dwarfAddressSpace: 0)
!30 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !31)
!31 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!32 = !DISubprogram(name: "__createFromImplicitBindingWithImplicitCounter", linkageName: "_ZN4hlsl18RWStructuredBufferIjE46__createFromImplicitBindingWithImplicitCounterEjjijPKcj", scope: !3, file: !7, type: !25, flags: DIFlagPublic | DIFlagPrototyped | DIFlagStaticMember, spFlags: DISPFlagOptimized)
!33 = !DISubprogram(name: "operator[]", linkageName: "_ZNK4hlsl18RWStructuredBufferIjEixEj", scope: !3, file: !7, type: !34, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!34 = !DISubroutineType(types: !35)
!35 = !{!36, !38, !27}
!36 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !37, size: 64, dwarfAddressSpace: 11)
!37 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !27)
!38 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !19, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer, dwarfAddressSpace: 0)
!39 = !DISubprogram(name: "operator[]", linkageName: "_ZN4hlsl18RWStructuredBufferIjEixEj", scope: !3, file: !7, type: !40, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!40 = !DISubroutineType(types: !41)
!41 = !{!42, !14, !27}
!42 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !27, size: 64, dwarfAddressSpace: 11)
!43 = !DISubprogram(name: "Load", linkageName: "_ZN4hlsl18RWStructuredBufferIjE4LoadEj", scope: !3, file: !7, type: !44, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!44 = !DISubroutineType(types: !45)
!45 = !{!27, !14, !27}
!46 = !DISubprogram(name: "IncrementCounter", linkageName: "_ZN4hlsl18RWStructuredBufferIjE16IncrementCounterEv", scope: !3, file: !7, type: !47, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!47 = !DISubroutineType(types: !48)
!48 = !{!27, !14}
!49 = !DISubprogram(name: "DecrementCounter", linkageName: "_ZN4hlsl18RWStructuredBufferIjE16DecrementCounterEv", scope: !3, file: !7, type: !47, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!50 = !DISubprogram(name: "GetDimensions", linkageName: "_ZN4hlsl18RWStructuredBufferIjE13GetDimensionsERjS2_", scope: !3, file: !7, type: !51, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!51 = !DISubroutineType(types: !52)
!52 = !{null, !14, !53, !53}
!53 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !54)
!54 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !27, size: 64, dwarfAddressSpace: 0)
!55 = !{!56}
!56 = !DITemplateTypeParameter(name: "element_type", type: !27)
!57 = !{!58}
!58 = !DIGlobalVariableExpression(var: !59, expr: !DIExpression(DW_OP_constu, 0, DW_OP_swap, DW_OP_xderef))
!59 = distinct !DIGlobalVariable(name: "buffer", linkageName: "_ZL6buffer", scope: !0, file: !7, line: 9, type: !3, isLocal: true, isDefinition: true)
!60 = !{i32 7, !"Dwarf Version", i32 4}
!61 = !{i32 2, !"Debug Info Version", i32 3}
!62 = !{i32 1, !"wchar_size", i32 4}
!63 = !{i32 7, !"frame-pointer", i32 2}
!64 = !{!"clang version 22.0.0git (/home/user/projects/llvm-project/clang 07b96bbf70c7773e44e31e51b4f1aac63b815a1f)"}
!65 = !{!66, !66, i64 0}
!66 = !{!"int", !67, i64 0}
!67 = !{!"omnipotent char", !68, i64 0}
!68 = !{!"Simple C++ TBAA"}
!69 = !DILocalVariable(name: "orderId", arg: 1, scope: !70, file: !7, type: !27)
!70 = distinct !DISubprogram(name: "__createFromImplicitBindingWithImplicitCounter", linkageName: "_ZN4hlsl18RWStructuredBufferIjE46__createFromImplicitBindingWithImplicitCounterEjjijPKcj", scope: !3, file: !7, line: 15, type: !25, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !32, retainedNodes: !71)
!71 = !{!69, !72, !73, !74, !75, !76, !77}
!72 = !DILocalVariable(name: "spaceNo", arg: 2, scope: !70, file: !7, type: !27)
!73 = !DILocalVariable(name: "range", arg: 3, scope: !70, file: !7, type: !28)
!74 = !DILocalVariable(name: "index", arg: 4, scope: !70, file: !7, type: !27)
!75 = !DILocalVariable(name: "name", arg: 5, scope: !70, file: !7, type: !29)
!76 = !DILocalVariable(name: "counterOrderId", arg: 6, scope: !70, file: !7, type: !27)
!77 = !DILocalVariable(name: "tmp", scope: !70, file: !7, type: !3)
!78 = !DILocation(line: 0, scope: !70, inlinedAt: !79)
!79 = distinct !DILocation(line: 0, scope: !80, inlinedAt: !83)
!80 = distinct !DISubprogram(name: "__cxx_global_var_init", scope: !7, file: !7, type: !81, flags: DIFlagArtificial | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0)
!81 = !DISubroutineType(types: !82)
!82 = !{null}
!83 = distinct !DILocation(line: 0, scope: !84)
!84 = distinct !DISubprogram(linkageName: "_GLOBAL__sub_I_repro.hlsl", scope: !7, file: !7, type: !85, flags: DIFlagArtificial | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0)
!85 = !DISubroutineType(types: !86)
!86 = !{}
!87 = !DILocalVariable(name: "this", arg: 1, scope: !88, type: !23, flags: DIFlagArtificial | DIFlagObjectPointer)
!88 = distinct !DISubprogram(name: "RWStructuredBuffer", linkageName: "_ZN4hlsl18RWStructuredBufferIjEC2ERKS1_", scope: !3, file: !7, line: 15, type: !16, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !15, retainedNodes: !89)
!89 = !{!87, !90}
!90 = !DILocalVariable(name: "other", arg: 2, scope: !88, file: !7, type: !18)
!91 = !DILocation(line: 0, scope: !88, inlinedAt: !92)
!92 = distinct !DILocation(line: 0, scope: !70, inlinedAt: !79)
!93 = !DILocalVariable(name: "this", arg: 1, scope: !94, type: !23, flags: DIFlagArtificial | DIFlagObjectPointer)
!94 = distinct !DISubprogram(name: "operator[]", linkageName: "_ZN4hlsl18RWStructuredBufferIjEixEj", scope: !3, file: !7, line: 6, type: !40, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !39, retainedNodes: !95)
!95 = !{!93, !96}
!96 = !DILocalVariable(name: "Index", arg: 2, scope: !94, file: !7, type: !27)
!97 = !DILocation(line: 0, scope: !94, inlinedAt: !98)
!98 = distinct !DILocation(line: 14, column: 21, scope: !99)
!99 = distinct !DISubprogram(name: "main", linkageName: "_Z4mainv", scope: !7, file: !7, line: 12, type: !100, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !101)
!100 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !82)
!101 = !{!102}
!102 = !DILocalVariable(name: "a", scope: !99, file: !7, line: 13, type: !103)
!103 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "A", file: !7, line: 3, size: 8, flags: DIFlagTypePassByValue, elements: !104, identifier: "_ZTS1A")
!104 = !{!105}
!105 = !DISubprogram(name: "foo", linkageName: "_ZN1A3fooEi", scope: !103, file: !7, line: 4, type: !106, scopeLine: 4, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized)
!106 = !DISubroutineType(cc: DW_CC_LLVM_SpirFunction, types: !107)
!107 = !{!28, !108, !28}
!108 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !103, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer, dwarfAddressSpace: 0)
!109 = !DILocation(line: 14, column: 21, scope: !99)
!110 = !DILocalVariable(name: "this", arg: 1, scope: !111, type: !114, flags: DIFlagArtificial | DIFlagObjectPointer)
!111 = distinct !DISubprogram(name: "foo", linkageName: "_ZN1A3fooEi", scope: !103, file: !7, line: 4, type: !106, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !105, retainedNodes: !112)
!112 = !{!110, !113}
!113 = !DILocalVariable(name: "value", arg: 2, scope: !111, file: !7, line: 4, type: !28)
!114 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !103, size: 64, dwarfAddressSpace: 0)
!115 = !DILocation(line: 0, scope: !111, inlinedAt: !116)
!116 = distinct !DILocation(line: 14, column: 17, scope: !99)
!117 = !DILocation(line: 5, column: 18, scope: !111, inlinedAt: !116)
!118 = !DILocation(line: 0, scope: !94, inlinedAt: !119)
!119 = distinct !DILocation(line: 14, column: 3, scope: !99)
!120 = !DILocation(line: 14, column: 13, scope: !99)

@github-actions
Copy link

github-actions bot commented Oct 29, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@aadeshps-mcw aadeshps-mcw changed the title [SPIRV] Addition of extension SPV_KHR_non_semantic_info [SPIRV] Addition of extension SPV_KHR_non_semantic_info and SPV_KHR_relaxed_extended_instruction Oct 30, 2025
@aadeshps-mcw aadeshps-mcw marked this pull request as ready for review October 30, 2025 20:46
@aadeshps-mcw
Copy link
Contributor Author

Have added the extension SPV_KHR_relaxed_extended_instructions for the instructions @Keenuts.

@Keenuts
Copy link
Contributor

Keenuts commented Nov 6, 2025

Have added the extension SPV_KHR_relaxed_extended_instructions for the instructions @Keenuts.

This PR seem to refactorize a bit the existing code and adds a lot of new bits.
Would it be possible to make it more reviewer-friendly by:

  • having an initial PR doing the rafactoring of the current EmitNonSemanticDI file
  • then small PRs adding one or a few similar debug instructions bit by bit (those should be non-controversial once the first has been merged)

@aadeshps-mcw
Copy link
Contributor Author

@Keenuts So i will close this PR and then put the new PR's.
Is that alright?

@Keenuts
Copy link
Contributor

Keenuts commented Nov 6, 2025

@Keenuts So i will close this PR and then put the new PR's. Is that alright?

As you prefer

Copy link
Contributor

@MrSidims MrSidims left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good. As of now I'm checking metadata to SPIR-V lowering correctness.

First set of comments, dedicated just to DebugInfoNone

Comment on lines 621 to 625
if (!UnderlyingTypeReg.isValid()) {
UnderlyingTypeReg = EmitDIInstruction(
SPIRV::NonSemanticExtInst::DebugInfoNone, {}, Ctx, false);
UnderlyingTypeIsFwd = false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per spec it's not allowed to have DebugInfoNone to be Source. Note: llvm-spirv may be has such lowering, if so - it's a bug there as well as in other cases, where it relaxes rules for DebugInfoNone.

Correct behaviour is to skip debug info instruction.

Comment on lines 642 to 646
if (!ParentReg.isValid()) {
ParentReg = EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone,
{}, Ctx, false);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment for DebugInfoNone

Comment on lines 911 to 904
if (!BaseTypeReg.isValid()) {
llvm::errs() << "Warning: Failed to find or create placeholder for base "
"type of pointer.\n";
BaseTypeReg = EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone,
{}, Ctx, false);
HasForwardRef = false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grey area, while here I see a value of DebugInfoNone - yet spec doesn't allow it. I think we can leave DebugInfoNone as a way to represent void*, especially when this part is a refactoring of #109287

Copy link
Contributor Author

@aadeshps-mcw aadeshps-mcw Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MrSidims
But in the test file debug-type-pointer.ll, we have a pointer type with baseType as null. So how to handle such cases then?

!7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 32, dwarfAddressSpace: 1)

If not handled, it is giving out a blank register.
The old method handled it as emitting out DebugInfoNone in those cases, which i have attached below.

// If the Pointer is representing a void type it's getBaseType
        // is a nullptr
        const auto *MaybeNestedBasicType =
            dyn_cast_or_null<DIBasicType>(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});
            }
          }
        } else {
          const Register DebugInfoNoneReg =
              EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone, {});
          [[maybe_unused]]
          const Register DebugPointerTypeReg = EmitDIInstruction(
              SPIRV::NonSemanticExtInst::DebugTypePointer,
              {DebugInfoNoneReg, StorageClassReg, I32ZeroReg});
        }
      }
    }
  }

Copy link
Contributor

@MrSidims MrSidims Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can leave it as DebugInfoNone for types.

Also, in case if you see null representing variable in Debug(Local/Global)Variable - it should be fine to translate it as DebugInfoNone. AFAIK NonSemantic DebugInfo spec now allowed DebugInfoNone used as variable for DebugGlobalVariable (this was allowed some time ago) and doesn't allow DebugInfoNone for LocalVariables, which is a specification bug.

My main concern about misuse of DebugInfoNone goes to Scopes/Parents of debug info instructions. Spec doesn't allow this and it's hard to imagine, what debugger tools could do with debug info without knowing the Scopes. Hence for this cases it's better to skip translation of the instruction.

Comment on lines 1105 to 1111
if (!BaseTypeReg.isValid()) {
llvm::errs()
<< "Warning: Could not find base type for DebugTypeQualifier.\n";
BaseTypeReg = EmitDIInstruction(
SPIRV::NonSemanticExtInst::DebugInfoNone, {}, Ctx, false);
IsForwardRef = false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grey area. Fine with leaving it as is for void*

Comment on lines 1137 to 1143
if (!BaseTypeReg.isValid()) {
llvm::errs() << "Warning: Could not find base type for Typedef: "
<< TypedefDT->getName() << "\n";
BaseTypeReg = EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone,
{}, Ctx, false);
HasForwardRef = false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grey area. Fine with leaving it as is for void*

Comment on lines +1174 to +1146
// TODO: Handle Entity as there are no current instructions for DINamespace,
// so replaced by DebugInfoNone
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it's not necessarily DINamespace. Imported entity can be a type declaration, e.g. DIDerivedType.

Comment on lines 1249 to 1219
if (ParentScope) {
ParentReg = Ctx.GR->getDebugValue(ParentScope);
if (!ParentReg.isValid()) {
llvm::errs() << "Warning: Could not find parent scope register for "
"Global Variable.\n";
ParentReg = EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone,
{}, Ctx, false);
}
} else {
llvm::errs() << "Warning: DIGlobalVariable has no parent scope\n";
ParentReg = EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone, {},
Ctx, false);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misuse of DebugInfoNone

Comment on lines 1238 to 1244
if (!TypeReg.isValid()) {
llvm::errs() << "Warning: Could not find type for Global Variable: " << Name
<< "\n";
TypeReg = EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone, {},
Ctx, false);
HasForwardRef = false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grey area, fine with leaving as is. Basically applied to every type handling in the PR.

Comment on lines 1666 to 1672
if (!ParentReg.isValid()) {
llvm::errs()
<< "Warning: Could not find Parent Type for PtrToMember.\n";
ParentReg = EmitDIInstruction(SPIRV::NonSemanticExtInst::DebugInfoNone,
{}, Ctx, false);
ParentTypeIsFwd = false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misuse of DebugInfoNone

--Added 19 instructions from the documentation.
--Added supporting tests for the same.
--Modified the SPIRVEmitNonSemanticDI.cpp file for forward referencing
--Modified the test files to reflect the changes made in SPIRVEmitNonSemanticDI.cpp.
--Modified the SPIRVEmitNonSemanticDI.cpp file for forward referencing
--Modified the test files to reflect the changes made in SPIRVEmitNonSemanticDI.cpp.
--Modified the SPIRVEmitNonSemanticDI.cpp file for forward referencing
--Modified the test files to reflect the changes made in SPIRVEmitNonSemanticDI.cpp.
--Modified the SPIRVEmitNonSemanticDI.cpp file for forward referencing
--Modified the test files to reflect the changes made in SPIRVEmitNonSemanticDI.cpp.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants