From 36c04a15f30ed7d25ff4fced1ee2dc1f30099957 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Thu, 10 Sep 2026 13:25:19 +0200 Subject: [PATCH] Fix debug info nondeterminism in LowerStaticGlobalIntoAlloca lowerStaticGlobalIntoAlloca() collects the instructions using a static global into a DenseMap keyed on Instruction*, then iterates it to rewrite each use to the per-function alloca. A DenseMap walks its buckets, so that order is pointer-derived and moves with heap addresses from run to run. That used to be unobservable, but since #7799 added a DILocalVariable per inlined DISubprogram for these localized globals, PatchDebugInfo() walks AI->users() to decide which subprograms are relevant and in which order it creates that debug info. Rewriting the uses in pointer-derived order mutates AI's use list in that order, so the debug info comes out permuted. The result is a compiler that emits a different ILDB part for byte-identical input, twice in the same process: the same METADATA_LOCAL_VAR and DEBUG_LOC records with identical operands in a different order, and the metadata renumbering that follows. The DXIL part itself is unaffected, so this only breaks reproducible builds and content-addressed caching of shader artifacts, not codegen. Use a MapVector so the uses are rewritten in the order they were collected, which is the module's use-list order. The regression test runs the pass in separate processes and compares the outputs, as test/DXC/deterministic_output_resource_array.hlsl does, reusing the module from local_global_inline_scope.ll which already covers this path with three users of the global across three inlined scopes. That relies on the runs laying out the heap differently, so it detects the regression with high probability rather than with certainty; the test comment says so. Co-Authored-By: Claude Opus 5 --- docs/ReleaseNotes.md | 4 + .../Scalar/ScalarReplAggregatesHLSL.cpp | 11 +- .../nondet_local_global_inline_scope.ll | 256 ++++++++++++++++++ 3 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 tools/clang/test/HLSLFileCheckLit/passes/dxil/static_global_to_alloca/nondet_local_global_inline_scope.ll diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index 0ef8fc9adb..33fa0d64b8 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -36,6 +36,10 @@ line upon naming the release. Refer to previous for appropriate section names. - SPIR-V: Fixed a crash when writing to a bitfield member through a `vk::BufferPointer` [#8402](https://github.com/microsoft/DirectXShaderCompiler/issues/8402). +- Fixed non-deterministic debug information for static globals localized into + allocas, which made the embedded debug module differ between compilations of + byte-identical input + [#8899](https://github.com/microsoft/DirectXShaderCompiler/pull/8899). #### HLSL Language diff --git a/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp b/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp index d80a678651..c7db2c0fd1 100644 --- a/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp +++ b/lib/Transforms/Scalar/ScalarReplAggregatesHLSL.cpp @@ -27,6 +27,7 @@ #include "dxc/HLSL/HLUtil.h" #include "dxc/HlslIntrinsicOp.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" @@ -6808,7 +6809,7 @@ static void PatchDebugInfo(GVDebugInfoPatchCache &Cache, // instruction. This requires recursion to unwrap nested constant operators // using the GV. static void collectGVInstUsers(Value *V, - DenseMap &InstUserMap) { + MapVector &InstUserMap) { for (User *U : V->users()) { if (Instruction *I = dyn_cast(U)) { InstUserMap[I] = V; @@ -6856,7 +6857,13 @@ bool LowerStaticGlobalIntoAlloca::lowerStaticGlobalIntoAlloca( } } - DenseMap InstUserMap; + // Iteration order of this map is observable: rewriting a use below mutates + // AI's use list, and PatchDebugInfo() walks AI->users() to decide the order in + // which it creates a DILocalVariable per inlined DISubprogram. A DenseMap keyed + // on Instruction* walks its buckets, so the order is pointer-derived and moves + // with heap addresses, which made that debug info come out permuted between + // runs of the same compilation. Keep insertion order. + MapVector InstUserMap; collectGVInstUsers(GV, InstUserMap); for (auto it : InstUserMap) { diff --git a/tools/clang/test/HLSLFileCheckLit/passes/dxil/static_global_to_alloca/nondet_local_global_inline_scope.ll b/tools/clang/test/HLSLFileCheckLit/passes/dxil/static_global_to_alloca/nondet_local_global_inline_scope.ll new file mode 100644 index 0000000000..7cedacfb55 --- /dev/null +++ b/tools/clang/test/HLSLFileCheckLit/passes/dxil/static_global_to_alloca/nondet_local_global_inline_scope.ll @@ -0,0 +1,256 @@ +; RUN: opt %s -hlsl-passes-resume -static-global-to-alloca -S -o %t1 +; RUN: opt %s -hlsl-passes-resume -static-global-to-alloca -S -o %t2 +; RUN: opt %s -hlsl-passes-resume -static-global-to-alloca -S -o %t3 +; RUN: diff %t1 %t2 +; RUN: diff %t1 %t3 +; RUN: FileCheck %s < %t1 + +; Check that the debug info for a localized global is emitted in a deterministic +; order, in addition to being present for every inlined scope. +; +; lowerStaticGlobalIntoAlloca() used to collect the instructions using the global +; into a DenseMap keyed on Instruction*, and iterate that to rewrite each use onto +; the alloca. DenseMap walks its buckets, so the order was pointer-derived and moved +; with heap addresses. PatchDebugInfo() then walks AI->users() to decide the order in +; which it creates a DILocalVariable per inlined DISubprogram, so the debug info came +; out permuted between compilations of identical input. +; +; The pass is run in separate processes and the outputs compared, as in +; test/DXC/deterministic_output_resource_array.hlsl, rather than with -run-twice: +; that flag clones the module after the first run has already lowered the global +; away, so the second run is a no-op and would compare equal either way. +; +; Note this relies on the runs laying out the heap differently, so it detects the +; regression with high probability rather than with certainty; there are three +; instructions using @g_cond here and three inlined scopes, and a third comparison +; is added to shrink the chance of a coincidental match. + +; CHECK: @main +; CHECK: %[[alloca:.+]] = alloca i32 + +; CHECK-DAG: ![[main_scope:[0-9]+]] = !DISubprogram(name: "main" +; CHECK-DAG: ![[main_var:[0-9]+]] = !DILocalVariable(tag: DW_TAG_arg_variable, name: "global.g_cond", arg: {{[0-9]+}}, scope: ![[main_scope]], +; CHECK-DAG: call void @llvm.dbg.declare(metadata i32* %[[alloca]], metadata ![[main_var]], + +; CHECK-DAG: ![[foo_scope:[0-9]+]] = !DISubprogram(name: "foo" +; CHECK-DAG: ![[foo_var:[0-9]+]] = !DILocalVariable(tag: DW_TAG_arg_variable, name: "global.g_cond", arg: {{[0-9]+}}, scope: ![[foo_scope]], +; CHECK-DAG: call void @llvm.dbg.declare(metadata i32* %[[alloca]], metadata ![[foo_var]], + +; CHECK-DAG: ![[bar_scope:[0-9]+]] = !DISubprogram(name: "bar" +; CHECK-DAG: ![[bar_var:[0-9]+]] = !DILocalVariable(tag: DW_TAG_arg_variable, name: "global.g_cond", arg: {{[0-9]+}}, scope: ![[bar_scope]], +; CHECK-DAG: call void @llvm.dbg.declare(metadata i32* %[[alloca]], metadata ![[bar_var]], + +target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +%"class.Texture2D >" = type { <4 x float>, %"class.Texture2D >::mips_type" } +%"class.Texture2D >::mips_type" = type { i32 } +%ConstantBuffer = type opaque +%dx.types.Handle = type { i8* } +%dx.types.ResourceProperties = type { i32, i32 } + +@"\01?tex0@@3V?$Texture2D@V?$vector@M$03@@@@A" = external global %"class.Texture2D >", align 4 +@"\01?tex1@@3V?$Texture2D@V?$vector@M$03@@@@A" = external global %"class.Texture2D >", align 4 +@g_cond = internal global i32 0, align 4 +@"$Globals" = external constant %ConstantBuffer + +; Function Attrs: nounwind +define <4 x float> @main(i32 %a) #0 { +entry: + %retval.i.i = alloca <4 x float>, align 4, !dx.temp !2 + %retval.i = alloca <4 x float>, align 4, !dx.temp !2 + %ret.i = alloca <4 x float>, align 4 + %retval = alloca <4 x float>, align 4, !dx.temp !2 + %a.addr = alloca i32, align 4, !dx.temp !2 + store i32 %a, i32* %a.addr, align 4 + call void @llvm.dbg.declare(metadata i32* %a.addr, metadata !61, metadata !62), !dbg !63 + %0 = load i32, i32* %a.addr, align 4, !dbg !64 + %cmp = icmp ne i32 %0, 0, !dbg !65 + %tobool = icmp ne i1 %cmp, false, !dbg !65 + %frombool = zext i1 %tobool to i32, !dbg !66 + call void @dx.noop(), !dbg !66 + store i32 %frombool, i32* @g_cond, align 4, !dbg !66 + call void @dx.noop(), !dbg !67 + call void @dx.noop() #0, !dbg !68 + %1 = load i32, i32* @g_cond, align 4, !dbg !68 + %tobool.i = icmp ne i32 %1, 0, !dbg !68 + br i1 %tobool.i, label %cond.true.i, label %cond.false.i, !dbg !68 + +cond.true.i: ; preds = %entry + %2 = load %"class.Texture2D >", %"class.Texture2D >"* @"\01?tex0@@3V?$Texture2D@V?$vector@M$03@@@@A", !dbg !70 + %3 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22class.Texture2D >\22)"(i32 0, %"class.Texture2D >" %2) #0, !dbg !70 + %4 = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %\22class.Texture2D >\22)"(i32 14, %dx.types.Handle %3, %dx.types.ResourceProperties { i32 2, i32 1033 }, %"class.Texture2D >" undef) #0, !dbg !70 + %5 = call <4 x float> @"dx.hl.op.ro.<4 x float> (i32, %dx.types.Handle, <3 x i32>)"(i32 231, %dx.types.Handle %4, <3 x i32> zeroinitializer) #0, !dbg !70 + br label %cond.end.i, !dbg !68 + +cond.false.i: ; preds = %entry + %6 = load %"class.Texture2D >", %"class.Texture2D >"* @"\01?tex1@@3V?$Texture2D@V?$vector@M$03@@@@A", !dbg !71 + %7 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22class.Texture2D >\22)"(i32 0, %"class.Texture2D >" %6) #0, !dbg !71 + %8 = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %\22class.Texture2D >\22)"(i32 14, %dx.types.Handle %7, %dx.types.ResourceProperties { i32 2, i32 1033 }, %"class.Texture2D >" undef) #0, !dbg !71 + %9 = call <4 x float> @"dx.hl.op.ro.<4 x float> (i32, %dx.types.Handle, <3 x i32>)"(i32 231, %dx.types.Handle %8, <3 x i32> zeroinitializer) #0, !dbg !71 + br label %cond.end.i, !dbg !68 + +cond.end.i: ; preds = %cond.false.i, %cond.true.i + %cond.i = phi <4 x float> [ %5, %cond.true.i ], [ %9, %cond.false.i ], !dbg !68 + call void @dx.noop() #0, !dbg !72 + store <4 x float> %cond.i, <4 x float>* %ret.i, align 4, !dbg !72 + call void @dx.noop() #0, !dbg !73 + call void @dx.noop() #0, !dbg !74 + %10 = load i32, i32* @g_cond, align 4, !dbg !74 + %tobool.i.i = icmp ne i32 %10, 0, !dbg !74 + br i1 %tobool.i.i, label %if.then.i.i, label %if.end.i.i, !dbg !77 + +if.then.i.i: ; preds = %cond.end.i + %11 = load %"class.Texture2D >", %"class.Texture2D >"* @"\01?tex0@@3V?$Texture2D@V?$vector@M$03@@@@A", !dbg !78 + %12 = call %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22class.Texture2D >\22)"(i32 0, %"class.Texture2D >" %11) #0, !dbg !78 + %13 = call %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %\22class.Texture2D >\22)"(i32 14, %dx.types.Handle %12, %dx.types.ResourceProperties { i32 2, i32 1033 }, %"class.Texture2D >" undef) #0, !dbg !78 + %14 = call <4 x float> @"dx.hl.op.ro.<4 x float> (i32, %dx.types.Handle, <3 x i32>)"(i32 231, %dx.types.Handle %13, <3 x i32> ) #0, !dbg !78 + store <4 x float> %14, <4 x float>* %retval.i.i, !dbg !79 + br label %"\01?foo@@YA?AV?$vector@M$03@@XZ.exit", !dbg !79 + +if.end.i.i: ; preds = %cond.end.i + store <4 x float> zeroinitializer, <4 x float>* %retval.i.i, !dbg !80 + br label %"\01?foo@@YA?AV?$vector@M$03@@XZ.exit", !dbg !80 + +"\01?foo@@YA?AV?$vector@M$03@@XZ.exit": ; preds = %if.then.i.i, %if.end.i.i + %15 = load <4 x float>, <4 x float>* %retval.i.i, !dbg !81 + call void @dx.noop() #0, !dbg !81 + %16 = load <4 x float>, <4 x float>* %ret.i, align 4, !dbg !82 + %add.i = fadd <4 x float> %16, %15, !dbg !82 + call void @dx.noop() #0, !dbg !82 + store <4 x float> %add.i, <4 x float>* %ret.i, align 4, !dbg !82 + %17 = load <4 x float>, <4 x float>* %ret.i, align 4, !dbg !83 + store <4 x float> %17, <4 x float>* %retval.i, !dbg !84 + %18 = load <4 x float>, <4 x float>* %retval.i, !dbg !84 + call void @dx.noop() #0, !dbg !84 + store <4 x float> %18, <4 x float>* %retval, !dbg !85 + %19 = load <4 x float>, <4 x float>* %retval, !dbg !85 + call void @dx.noop(), !dbg !85 + call void @llvm.dbg.declare(metadata <4 x float>* %ret.i, metadata !86, metadata !62), !dbg !72 + ret <4 x float> %19, !dbg !85 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: nounwind readonly +declare <4 x float> @"dx.hl.op.ro.<4 x float> (i32, %dx.types.Handle, <3 x i32>)"(i32, %dx.types.Handle, <3 x i32>) #2 + +; Function Attrs: nounwind readnone +declare %dx.types.Handle @"dx.hl.createhandle..%dx.types.Handle (i32, %\22class.Texture2D >\22)"(i32, %"class.Texture2D >") #1 + +; Function Attrs: nounwind readnone +declare %dx.types.Handle @"dx.hl.annotatehandle..%dx.types.Handle (i32, %dx.types.Handle, %dx.types.ResourceProperties, %\22class.Texture2D >\22)"(i32, %dx.types.Handle, %dx.types.ResourceProperties, %"class.Texture2D >") #1 + +; Function Attrs: convergent +declare void @dx.noop() #3 + +attributes #0 = { nounwind } +attributes #1 = { nounwind readnone } +attributes #2 = { nounwind readonly } +attributes #3 = { convergent } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!33, !34} +!pauseresume = !{!35} +!llvm.ident = !{!36} +!dx.source.contents = !{!37} +!dx.source.defines = !{!2} +!dx.source.mainFileName = !{!38} +!dx.source.args = !{!39} +!dx.version = !{!40} +!dx.valver = !{!41} +!dx.shaderModel = !{!42} +!dx.typeAnnotations = !{!43} +!dx.entryPoints = !{!49} +!dx.fnprops = !{!57} +!dx.options = !{!58, !59} +!dx.rootSignature = !{!60} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "dxc(private) 1.8.0.5063 (local_global_var_debug_info, 3464c49d3-dirty)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, subprograms: !3, globals: !25) +!1 = !DIFile(filename: "C:\5Cdxc\5Ctools\5Cclang\5Ctest\5CHLSLFileCheck\5Cdxil\5Cdebug\5Clocal_global_inline_scope.hlsl", directory: "") +!2 = !{} +!3 = !{!4, !21, !24} +!4 = !DISubprogram(name: "main", scope: !1, file: !1, line: 37, type: !5, isLocal: false, isDefinition: true, scopeLine: 37, flags: DIFlagPrototyped, isOptimized: false, function: <4 x float> (i32)* @main) +!5 = !DISubroutineType(types: !6) +!6 = !{!7, !19} +!7 = !DIDerivedType(tag: DW_TAG_typedef, name: "float4", file: !1, baseType: !8) +!8 = !DICompositeType(tag: DW_TAG_class_type, name: "vector", file: !1, size: 128, align: 32, elements: !9, templateParams: !15) +!9 = !{!10, !12, !13, !14} +!10 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !8, file: !1, baseType: !11, size: 32, align: 32, flags: DIFlagPublic) +!11 = !DIBasicType(name: "float", size: 32, align: 32, encoding: DW_ATE_float) +!12 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !8, file: !1, baseType: !11, size: 32, align: 32, offset: 32, flags: DIFlagPublic) +!13 = !DIDerivedType(tag: DW_TAG_member, name: "z", scope: !8, file: !1, baseType: !11, size: 32, align: 32, offset: 64, flags: DIFlagPublic) +!14 = !DIDerivedType(tag: DW_TAG_member, name: "w", scope: !8, file: !1, baseType: !11, size: 32, align: 32, offset: 96, flags: DIFlagPublic) +!15 = !{!16, !17} +!16 = !DITemplateTypeParameter(name: "element", type: !11) +!17 = !DITemplateValueParameter(name: "element_count", type: !18, value: i32 4) +!18 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!19 = !DIDerivedType(tag: DW_TAG_typedef, name: "uint", file: !1, baseType: !20) +!20 = !DIBasicType(name: "unsigned int", size: 32, align: 32, encoding: DW_ATE_unsigned) +!21 = !DISubprogram(name: "foo", linkageName: "\01?foo@@YA?AV?$vector@M$03@@XZ", scope: !1, file: !1, line: 30, type: !22, isLocal: false, isDefinition: true, scopeLine: 30, flags: DIFlagPrototyped, isOptimized: false) +!22 = !DISubroutineType(types: !23) +!23 = !{!7} +!24 = !DISubprogram(name: "bar", linkageName: "\01?bar@@YA?AV?$vector@M$03@@XZ", scope: !1, file: !1, line: 24, type: !22, isLocal: false, isDefinition: true, scopeLine: 24, flags: DIFlagPrototyped, isOptimized: false) +!25 = !{!26, !30, !31} +!26 = !DIGlobalVariable(name: "tex0", linkageName: "\01?tex0@@3V?$Texture2D@V?$vector@M$03@@@@A", scope: !0, file: !1, line: 21, type: !27, isLocal: false, isDefinition: true, variable: %"class.Texture2D >"* @"\01?tex0@@3V?$Texture2D@V?$vector@M$03@@@@A") +!27 = !DICompositeType(tag: DW_TAG_class_type, name: "Texture2D >", file: !1, line: 21, size: 160, align: 32, elements: !2, templateParams: !28) +!28 = !{!29} +!29 = !DITemplateTypeParameter(name: "element", type: !8) +!30 = !DIGlobalVariable(name: "tex1", linkageName: "\01?tex1@@3V?$Texture2D@V?$vector@M$03@@@@A", scope: !0, file: !1, line: 22, type: !27, isLocal: false, isDefinition: true, variable: %"class.Texture2D >"* @"\01?tex1@@3V?$Texture2D@V?$vector@M$03@@@@A") +!31 = !DIGlobalVariable(name: "g_cond", scope: !0, file: !1, line: 19, type: !32, isLocal: true, isDefinition: true, variable: i32* @g_cond) +!32 = !DIBasicType(name: "bool", size: 32, align: 32, encoding: DW_ATE_boolean) +!33 = !{i32 2, !"Dwarf Version", i32 4} +!34 = !{i32 2, !"Debug Info Version", i32 3} +!35 = !{!"hlsl-hlemit", !"hlsl-hlensure"} +!36 = !{!"dxc(private) 1.8.0.5063 (local_global_var_debug_info, 3464c49d3-dirty)"} +!37 = !{!"C:\5Cdxc\5Ctools\5Cclang\5Ctest\5CHLSLFileCheck\5Cdxil\5Cdebug\5Clocal_global_inline_scope.hlsl", !""} +!38 = !{!"C:\5Cdxc\5Ctools\5Cclang\5Ctest\5CHLSLFileCheck\5Cdxil\5Cdebug\5Clocal_global_inline_scope.hlsl"} +!39 = !{!"-E", !"main", !"-T", !"ps_6_0", !"/Zi", !"/Od", !"-Qembed_debug"} +!40 = !{i32 1, i32 0} +!41 = !{i32 1, i32 9} +!42 = !{!"ps", i32 6, i32 0} +!43 = !{i32 1, <4 x float> (i32)* @main, !44} +!44 = !{!45, !47} +!45 = !{i32 1, !46, !2} +!46 = !{i32 4, !"sv_target", i32 7, i32 9} +!47 = !{i32 0, !48, !2} +!48 = !{i32 4, !"A", i32 7, i32 5} +!49 = !{<4 x float> (i32)* @main, !"main", null, !50, null} +!50 = !{!51, null, !55, null} +!51 = !{!52, !54} +!52 = !{i32 0, %"class.Texture2D >"* @"\01?tex0@@3V?$Texture2D@V?$vector@M$03@@@@A", !"tex0", i32 0, i32 0, i32 1, i32 2, i32 0, !53} +!53 = !{i32 0, i32 9} +!54 = !{i32 1, %"class.Texture2D >"* @"\01?tex1@@3V?$Texture2D@V?$vector@M$03@@@@A", !"tex1", i32 0, i32 1, i32 1, i32 2, i32 0, !53} +!55 = !{!56} +!56 = !{i32 0, %ConstantBuffer* @"$Globals", !"$Globals", i32 0, i32 -1, i32 1, i32 0, null} +!57 = !{<4 x float> (i32)* @main, i32 0, i1 false} +!58 = !{i32 -2147483576} +!59 = !{i32 -1} +!60 = !{[68 x i8] c"\02\00\00\00\01\00\00\00\18\00\00\00\00\00\00\00D\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00$\00\00\00\01\00\00\00,\00\00\00\00\00\00\00\02\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\FF\FF\FF"} +!61 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "a", arg: 1, scope: !4, file: !1, line: 37, type: !19) +!62 = !DIExpression() +!63 = !DILocation(line: 37, column: 18, scope: !4) +!64 = !DILocation(line: 38, column: 12, scope: !4) +!65 = !DILocation(line: 38, column: 14, scope: !4) +!66 = !DILocation(line: 38, column: 10, scope: !4) +!67 = !DILocation(line: 39, column: 10, scope: !4) +!68 = !DILocation(line: 31, column: 16, scope: !21, inlinedAt: !69) +!69 = distinct !DILocation(line: 39, column: 10, scope: !4) +!70 = !DILocation(line: 31, column: 25, scope: !21, inlinedAt: !69) +!71 = !DILocation(line: 31, column: 40, scope: !21, inlinedAt: !69) +!72 = !DILocation(line: 31, column: 10, scope: !21, inlinedAt: !69) +!73 = !DILocation(line: 32, column: 10, scope: !21, inlinedAt: !69) +!74 = !DILocation(line: 25, column: 7, scope: !75, inlinedAt: !76) +!75 = distinct !DILexicalBlock(scope: !24, file: !1, line: 25, column: 7) +!76 = distinct !DILocation(line: 32, column: 10, scope: !21, inlinedAt: !69) +!77 = !DILocation(line: 25, column: 7, scope: !24, inlinedAt: !76) +!78 = !DILocation(line: 26, column: 12, scope: !75, inlinedAt: !76) +!79 = !DILocation(line: 26, column: 5, scope: !75, inlinedAt: !76) +!80 = !DILocation(line: 27, column: 3, scope: !24, inlinedAt: !76) +!81 = !DILocation(line: 28, column: 1, scope: !24, inlinedAt: !76) +!82 = !DILocation(line: 32, column: 7, scope: !21, inlinedAt: !69) +!83 = !DILocation(line: 33, column: 10, scope: !21, inlinedAt: !69) +!84 = !DILocation(line: 33, column: 3, scope: !21, inlinedAt: !69) +!85 = !DILocation(line: 39, column: 3, scope: !4) +!86 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "ret", scope: !21, file: !1, line: 31, type: !7)