[llvm-branch-commits] [llvm] [SPIRV] Add support for NSDI DebugLocalVariable. (PR #221706)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Sep 7 04:22:12 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

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

Author: Manuel Carrasco (mgcarrasco)

<details>
<summary>Changes</summary>

This PR adds support for [DebugLocalVariable](https://registry.khronos.org/SPIR-V/specs/unified1/DebugInfo.html#DebugLocalVariable).

---

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


7 Files Affected:

- (modified) llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp (+69-11) 
- (modified) llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h (+20) 
- (added) llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-dbg-value.ll (+40) 
- (added) llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-records-and-retained.ll (+42) 
- (added) llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-retained-nodes.ll (+40) 
- (added) llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-skip-type.ll (+32) 
- (added) llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable.ll (+58) 


``````````diff
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index bb311d61f59c0..42d40ef4f8c57 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -257,21 +257,30 @@ unsigned SPIRVNonSemanticDebugHandler::toNSDISrcLang(unsigned DwarfSrcLang) {
   }
 }
 
-// Collect distinct DILocations from LLVM IR. DebugLine pre-emission and MIR
-// lookups assume every machine-instruction debug location already appeared
-// here; a codegen-only location would not be collected and emission will be
-// skipped.
-static void collectUniqueDebugLocations(const Module &M,
-                                        SetVector<const DILocation *> &Out) {
+// Collect distinct DILocations and DILocalVariables from LLVM IR. DebugLine
+// pre-emission and MIR lookups assume every machine-instruction debug location
+// already appeared here; a codegen-only location would not be collected and
+// emission will be skipped.
+static void collectDebugLocationsAndLocalVariables(
+    const Module &M, SetVector<const DILocation *> &Locations,
+    SetVector<const DILocalVariable *> &LVs) {
   for (const Function &F : M) {
-    if (!F.getSubprogram())
+    const DISubprogram *SP = F.getSubprogram();
+    if (!SP)
       continue;
+    for (const MDNode *N : SP->getRetainedNodes())
+      if (const auto *LV = dyn_cast_or_null<DILocalVariable>(N))
+        LVs.insert(LV);
     for (const Instruction &I : instructions(F)) {
       if (const DILocation *DL = I.getDebugLoc().get())
-        Out.insert(DL);
-      for (DbgRecord &DR : I.getDbgRecordRange())
+        Locations.insert(DL);
+      for (DbgRecord &DR : I.getDbgRecordRange()) {
         if (const DILocation *DL = DR.getDebugLoc().get())
-          Out.insert(DL);
+          Locations.insert(DL);
+        if (const auto *DVR = dyn_cast<DbgVariableRecord>(&DR))
+          if (const DILocalVariable *LV = DVR->getVariable())
+            LVs.insert(LV);
+      }
     }
   }
 }
@@ -310,6 +319,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   SubprogramDefinitions.clear();
   UniqueDebugLocations.clear();
   GlobalVariableDebugInfoMap.clear();
+  LocalVariables.clear();
   LexicalBlocks.clear();
   DebugScopeRegs.clear();
   DebugInlinedAtRegs.clear();
@@ -393,7 +403,8 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
         GV, GlobalVariableDebugInfo{Expr, DIGVToLLVMGV.lookup(GV)});
   }
 
-  collectUniqueDebugLocations(*M, UniqueDebugLocations);
+  collectDebugLocationsAndLocalVariables(*M, UniqueDebugLocations,
+                                         LocalVariables);
 
   // DILexicalBlock and DINamespace scopes are lowered to DebugLexicalBlock.
   // Collect them in parent-before-child order so they can be later emitted in a
@@ -908,6 +919,43 @@ std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugGlobalVariable(
                      VoidTypeReg, ExtInstSetReg, Ops, MAI);
 }
 
+std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugLocalVariable(
+    const DILocalVariable *LV, MCRegister VoidTypeReg, MCRegister I32TypeReg,
+    MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI) {
+  assert(LV && "LV must not be null in emitDebugLocalVariable");
+
+  auto ParentRegOpt = resolveScope(LV->getScope());
+  if (!ParentRegOpt)
+    return std::nullopt;
+
+  MCRegister TyReg = CachedDebugInfoNoneReg;
+  if (const DIType *Ty = LV->getType()) {
+    auto TyRegOpt = lookupOptReg(DebugScopeRegs, Ty);
+    if (!TyRegOpt)
+      return std::nullopt;
+    TyReg = *TyRegOpt;
+  }
+
+  MCRegister NameReg = getCachedOpStringReg(LV->getName());
+  MCRegister FileStrReg = getCachedScopePathOpStringReg(
+      LV->getFile(), /*UseEmptyPathIfNullScope=*/true);
+  MCRegister SrcReg = getOrEmitDebugSourceForFileStrReg(FileStrReg, VoidTypeReg,
+                                                        ExtInstSetReg, MAI);
+  MCRegister LineReg =
+      emitOpConstantI32(static_cast<uint32_t>(LV->getLine()), I32TypeReg, MAI);
+  // DILocalVariable has no column field. Column is hardcoded to 0.
+  MCRegister ColReg = emitOpConstantI32(0, I32TypeReg, MAI);
+  MCRegister FlagsReg = emitOpConstantI32(transDebugFlags(LV), I32TypeReg, MAI);
+
+  SmallVector<MCRegister, 8> Ops = {NameReg, TyReg,         SrcReg, LineReg,
+                                    ColReg,  *ParentRegOpt, FlagsReg};
+  if (unsigned Arg = LV->getArg())
+    Ops.push_back(emitOpConstantI32(Arg, I32TypeReg, MAI));
+
+  return emitExtInst(SPIRV::NonSemanticExtInst::DebugLocalVariable, VoidTypeReg,
+                     ExtInstSetReg, Ops, MAI);
+}
+
 std::optional<MCRegister> SPIRVNonSemanticDebugHandler::emitDebugTypeVector(
     const DICompositeType *VT, MCRegister ExtInstSetReg,
     SPIRV::ModuleAnalysisInfo &MAI) {
@@ -1141,6 +1189,11 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
     emitAndCacheScopePathOpStringReg(GV->getFile(), MAI);
   }
 
+  for (const DILocalVariable *LV : LocalVariables) {
+    emitOpStringIfNew(LV->getName(), MAI);
+    emitAndCacheScopePathOpStringReg(LV->getFile(), MAI);
+  }
+
   // Cache the path OpString each DebugLexicalBlock uses (source file), plus
   // the Name OpString for the DINamespace case.
   for (const DIScope *S : LexicalBlocks) {
@@ -1671,6 +1724,11 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
       DebugScopeRegs[S] = *LBReg;
   }
 
+  // Emit DebugLocalVariable after DebugFunction and their lexical blocks so the
+  // Parent operand can resolve.
+  for (const DILocalVariable *LV : LocalVariables)
+    emitDebugLocalVariable(LV, VoidTypeReg, I32TypeReg, ExtInstSetReg, MAI);
+
   // Emit DebugGlobalVariable for each collected DIGlobalVariable.
   for (const auto &[GV, Info] : GlobalVariableDebugInfoMap)
     emitDebugGlobalVariable(GV, Info, VoidTypeReg, I32TypeReg, ExtInstSetReg,
diff --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index ce02b274af75e..b709a37b42da1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -104,6 +104,10 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   DenseMap<const DIGlobalVariable *, GlobalVariableDebugInfo>
       GlobalVariableDebugInfoMap;
 
+  // Distinct DILocalVariable nodes collected in beginModule() from dbg
+  // records, dbg intrinsics, and subprogram retained nodes.
+  SetVector<const DILocalVariable *> LocalVariables;
+
   // Distinct DILexicalBlock and DINamespace scopes, parent-before-child
   // order, collected in beginModule() for DebugLexicalBlock emission.
   SetVector<const DIScope *> LexicalBlocks;
@@ -348,6 +352,22 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
                                               MCRegister ExtInstSetReg,
                                               SPIRV::ModuleAnalysisInfo &MAI);
 
+  /// Emit \c DebugLocalVariable for the source local variable \p LV:
+  /// Name, Type, Source, Line, Column, Parent, Flags, and an optional Arg
+  /// Number. Line, Column, Flags, and Arg Number are emitted as \c OpConstant
+  /// ids as required for non-semantic debug info. Column is always 0:
+  /// \c DILocalVariable has no column field.
+  ///
+  /// Arg Number is appended when \p LV is a parameter.
+  ///
+  /// \returns The result id register on success. Returns \c std::nullopt and
+  /// emits nothing if \p LV's scope is not an emitted local scope,
+  /// if a non-null type was not emitted in \c DebugScopeRegs, or if
+  /// \c resolveScope returns no id for the Parent operand.
+  std::optional<MCRegister> emitDebugLocalVariable(
+      const DILocalVariable *LV, MCRegister VoidTypeReg, MCRegister I32TypeReg,
+      MCRegister ExtInstSetReg, SPIRV::ModuleAnalysisInfo &MAI);
+
   /// Emit \c DebugGlobalVariable for the source global variable \p GV.
   ///
   /// (\c SPIRVDebug::Operand::GlobalVariable): Name, Type, Source, Line,
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-dbg-value.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-dbg-value.ll
new file mode 100644
index 0000000000000..9f503153b9317
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-dbg-value.ll
@@ -0,0 +1,40 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Collect DILocalVariable from a #dbg_value record. No retainedNodes.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-local-variable-dbg-value.c"
+; CHECK-DAG: [[XNAME:%[0-9]+]] = OpString "x"
+; CHECK-DAG: [[INTNAME:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0
+; CHECK-DAG: [[C1:%[0-9]+]] = OpConstant [[I32T]] 1
+; CHECK-DAG: [[C8:%[0-9]+]] = OpConstant [[I32T]] 8
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[INT:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[INTNAME]] {{.*}} [[C0]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugLocalVariable [[XNAME]] [[INT]] [[DS]] [[C8]] [[C0]] [[DF]] [[C0]] [[C1]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @passthrough(i32 %x) !dbg !5 {
+entry:
+    #dbg_value(i32 %x, !9, !DIExpression(), !10)
+  ret i32 %x, !dbg !10
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-local-variable-dbg-value.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!5 = distinct !DISubprogram(name: "passthrough", linkageName: "passthrough", scope: !1, file: !1, line: 8, type: !4, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!9 = !DILocalVariable(name: "x", arg: 1, scope: !5, file: !1, line: 8, type: !7)
+!10 = !DILocation(line: 8, column: 1, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-records-and-retained.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-records-and-retained.ll
new file mode 100644
index 0000000000000..81b4ce830eed3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-records-and-retained.ll
@@ -0,0 +1,42 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; The same DILocalVariable is listed in retainedNodes and referenced by a #dbg_declare.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-local-variable-records-and-retained.c"
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "dup"
+; CHECK-DAG: [[INTNAME:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0
+; CHECK-DAG: [[C3:%[0-9]+]] = OpConstant [[I32T]] 3
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[INT:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[INTNAME]] {{.*}} [[C0]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugLocalVariable [[NAME]] [[INT]] [[DS]] [[C3]] [[C0]] [[DF]] [[C0]]
+; CHECK-NOT: DebugLocalVariable
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @f() !dbg !5 {
+entry:
+  %dup = alloca i32, align 4
+    #dbg_declare(ptr %dup, !9, !DIExpression(), !10)
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-local-variable-records-and-retained.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !DISubroutineType(types: !6)
+!6 = !{null}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!5 = distinct !DISubprogram(name: "f", linkageName: "f", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8)
+!8 = !{!9}
+!9 = !DILocalVariable(name: "dup", scope: !5, file: !1, line: 3, type: !7)
+!10 = !DILocation(line: 3, column: 1, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-retained-nodes.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-retained-nodes.ll
new file mode 100644
index 0000000000000..47a2dff9b7015
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-retained-nodes.ll
@@ -0,0 +1,40 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Collect DILocalVariable only from DISubprogram retainedNodes. There are no
+; #dbg_declare / #dbg_value records.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-local-variable-retained-nodes.c"
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "gone"
+; CHECK-DAG: [[INTNAME:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0
+; CHECK-DAG: [[C9:%[0-9]+]] = OpConstant [[I32T]] 9
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[INT:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[INTNAME]] {{.*}} [[C0]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugLocalVariable [[NAME]] [[INT]] [[DS]] [[C9]] [[C0]] [[DF]] [[C0]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @f() !dbg !5 {
+entry:
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-local-variable-retained-nodes.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !DISubroutineType(types: !6)
+!6 = !{null}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!5 = distinct !DISubprogram(name: "f", linkageName: "f", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !8)
+!8 = !{!9}
+!9 = !DILocalVariable(name: "gone", scope: !5, file: !1, line: 9, type: !7)
+!10 = !DILocation(line: 1, column: 1, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-skip-type.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-skip-type.ll
new file mode 100644
index 0000000000000..ca496aadf4224
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable-skip-type.ll
@@ -0,0 +1,32 @@
+; RUN: llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; A DILocalVariable whose type is not yet supported.
+; The DebugLocalVariable is skipped.
+
+; CHECK-DAG: OpExtInst {{.*}} DebugCompilationUnit
+; CHECK-NOT: DebugLocalVariable
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @f() !dbg !5 {
+entry:
+  %p = alloca ptr, align 8
+    #dbg_declare(ptr %p, !9, !DIExpression(), !10)
+  ret void, !dbg !10
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "t.c", directory: "/tmp")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !DISubroutineType(types: !6)
+!6 = !{null}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64)
+!5 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!9 = !DILocalVariable(name: "p", scope: !5, file: !1, line: 2, type: !8)
+!10 = !DILocation(line: 2, column: 1, scope: !5)
diff --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable.ll
new file mode 100644
index 0000000000000..3711c8b6778cd
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-local-variable.ll
@@ -0,0 +1,58 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; A function parameter and a local variable.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32T:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-local-variable.c"
+; CHECK-DAG: [[FNNAME:%[0-9]+]] = OpString "add_one"
+; CHECK-DAG: [[INTNAME:%[0-9]+]] = OpString "int"
+; CHECK-DAG: [[VALUE:%[0-9]+]] = OpString "value"
+; CHECK-DAG: [[RESULT:%[0-9]+]] = OpString "result"
+; The trailing anchors keep e.g. [[C1]] from binding to "OpConstant %3 100".
+; CHECK-DAG: [[C0:%[0-9]+]] = OpConstant [[I32T]] 0{{ *$}}
+; CHECK-DAG: [[C1:%[0-9]+]] = OpConstant [[I32T]] 1{{ *$}}
+; CHECK-DAG: [[C7:%[0-9]+]] = OpConstant [[I32T]] 7{{ *$}}
+; CHECK-DAG: [[C11:%[0-9]+]] = OpConstant [[I32T]] 11{{ *$}}
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[INT:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeBasic [[INTNAME]] {{.*}} [[C0]]
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[FNNAME]] {{.*}} [[DS]] {{.*}}
+
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugLocalVariable [[VALUE]] [[INT]] [[DS]] [[C7]] [[C0]] [[DF]] [[C0]] [[C1]]
+; CHECK-DAG: OpExtInst [[VOID]] [[EXT]] DebugLocalVariable [[RESULT]] [[INT]] [[DS]] [[C11]] [[C0]] [[DF]] [[C0]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @add_one(i32 %value) !dbg !5 {
+entry:
+  %value.addr = alloca i32, align 4
+  %result = alloca i32, align 4
+  store i32 %value, ptr %value.addr, align 4
+    #dbg_declare(ptr %value.addr, !9, !DIExpression(), !11)
+  %0 = load i32, ptr %value.addr, align 4, !dbg !11
+  %add = add nsw i32 %0, %0, !dbg !11
+  store i32 %add, ptr %result, align 4, !dbg !11
+    #dbg_declare(ptr %result, !10, !DIExpression(), !12)
+  %1 = load i32, ptr %result, align 4, !dbg !12
+  ret i32 %1, !dbg !12
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompil...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/221706


More information about the llvm-branch-commits mailing list