[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