Index: llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -37,6 +37,7 @@ #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" @@ -206,6 +207,7 @@ S->setMetadata(LLVMContext::MD_mem_parallel_loop_access, LoopMemParallelMD); if (AccessGroupMD) S->setMetadata(LLVMContext::MD_access_group, AccessGroupMD); + S->copyMetadata(*MI, LLVMContext::MD_DIAssignID); if (auto *MT = dyn_cast(MI)) { // non-atomics can be volatile @@ -269,8 +271,14 @@ // Extract the fill value and store. uint64_t Fill = FillC->getZExtValue()*0x0101010101010101ULL; - StoreInst *S = Builder.CreateStore(ConstantInt::get(ITy, Fill), Dest, - MI->isVolatile()); + auto *FillVal = ConstantInt::get(ITy, Fill); + StoreInst *S = Builder.CreateStore(FillVal, Dest, MI->isVolatile()); + S->copyMetadata(*MI, LLVMContext::MD_DIAssignID); + for (auto *DAI : at::getAssignmentMarkers(S)) { + if (any_of(DAI->location_ops(), [&](Value *V) { return V == FillC; })) + DAI->replaceVariableLocationOp(FillC, FillVal); + } + S->setAlignment(Alignment); if (isa(MI)) S->setOrdering(AtomicOrdering::Unordered); Index: llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -16,6 +16,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/PatternMatch.h" #include "llvm/Support/KnownBits.h" #include "llvm/Transforms/InstCombine/InstCombiner.h" @@ -164,6 +165,8 @@ New->setAlignment(AI.getAlign()); New->takeName(&AI); New->setUsedWithInAlloca(AI.isUsedWithInAlloca()); + New->setMetadata(LLVMContext::MD_DIAssignID, + AI.getMetadata(LLVMContext::MD_DIAssignID)); // If the allocation has multiple real uses, insert a cast and change all // things that used it to use the new cast. This will also hack on CI, but it Index: llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -510,6 +510,7 @@ // here. switch (ID) { case LLVMContext::MD_dbg: + case LLVMContext::MD_DIAssignID: case LLVMContext::MD_tbaa: case LLVMContext::MD_prof: case LLVMContext::MD_fpmath: @@ -1546,6 +1547,7 @@ SI.getOrdering(), SI.getSyncScopeID()); InsertNewInstBefore(NewSI, *BBI); NewSI->setDebugLoc(MergedLoc); + NewSI->mergeDIAssignID({&SI, OtherStore}); // If the two stores had AA tags, merge them. AAMDNodes AATags = SI.getAAMetadata(); Index: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3939,6 +3939,11 @@ if (!SunkVariables.insert(DbgUserVariable).second) continue; + // Leave dbg.assign intrinsics in their original positions and there should + // be no need to insert a clone. + if (isa(User)) + continue; + DIIClones.emplace_back(cast(User->clone())); if (isa(User) && isa(I)) DIIClones.back()->replaceVariableLocationOp(I, I->getOperand(0)); Index: llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/alloca-bitcast.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/alloca-bitcast.ll @@ -0,0 +1,70 @@ +; RUN: opt -instcombine -S %s -o - -experimental-assignment-tracking | FileCheck %s + +;; Check that allocas generated in InstCombine's PromoteCastOfAllocation +;; have DIAssignID copied from the original alloca. +;; +;; $ cat reduce.cpp +;; struct c { +;; c(int); +;; int a, b; +;; }; +;; c d() { +;; c e(1); +;; return e; +;; } +;; $ clang -O2 -c -g reduce.cpp -fno-inline -Xclang -disable-llvm-passes -emit-llvm -S \ +;; | opt -passes=declare-to-assign -S + +; CHECK: entry: +; CHECK-NEXT: %retval = alloca i64, align 8, !DIAssignID ![[ID:[0-9]+]] +; CHECK-NEXT: %tmpcast = bitcast i64* %retval to %struct.c* +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[e:[0-9]+]], metadata !DIExpression(), metadata ![[ID]], metadata %struct.c* %tmpcast, metadata !DIExpression()), !dbg +; CHECK: ![[e]] = !DILocalVariable(name: "e", + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + +%struct.c = type { i32, i32 } + +; Function Attrs: noinline uwtable mustprogress +define dso_local i64 @_Z1dv() !dbg !7 { +entry: + %retval = alloca %struct.c, align 4, !DIAssignID !21 + call void @llvm.dbg.assign(metadata i1 undef, metadata !20, metadata !DIExpression(), metadata !21, metadata %struct.c* %retval, metadata !DIExpression()), !dbg !22 + call void @_ZN1cC1Ei(%struct.c* %retval, i32 1), !dbg !23 + %0 = bitcast %struct.c* %retval to i64*, !dbg !24 + %1 = load i64, i64* %0, align 4, !dbg !24 + ret i64 %1, !dbg !24 +} + +declare dso_local void @_ZN1cC1Ei(%struct.c*, i32) unnamed_addr +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduce.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "d", linkageName: "_Z1dv", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !19) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "c", file: !1, line: 1, size: 64, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !11, identifier: "_ZTS1c") +!11 = !{!12, !14, !15} +!12 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !10, file: !1, line: 3, baseType: !13, size: 32) +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !10, file: !1, line: 3, baseType: !13, size: 32, offset: 32) +!15 = !DISubprogram(name: "c", scope: !10, file: !1, line: 2, type: !16, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!16 = !DISubroutineType(types: !17) +!17 = !{null, !18, !13} +!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!19 = !{!20} +!20 = !DILocalVariable(name: "e", scope: !7, file: !1, line: 6, type: !10) +!21 = distinct !DIAssignID() +!22 = !DILocation(line: 0, scope: !7) +!23 = !DILocation(line: 6, column: 5, scope: !7) +!24 = !DILocation(line: 7, column: 3, scope: !7) Index: llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/memset.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/memset.ll @@ -0,0 +1,86 @@ +; RUN: opt %s -S -passes=instcombine -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; $ cat test.cpp +;; void esc(int*); +;; void fun() { +;; int local[5]; +;; __builtin_memset(local, 8, 4 * 4); +;; __builtin_memset(local, 0, 2 * 4); +;; esc(local); +;; } +;; IR grabbed before instcombine in: +;; clang++ -O2 -g -Xclang -fexperimental-assignment-tracking + +;; Instcombine is going to turn th second memset into a store. Check that it +;; inherits the DIAssignID from the memset and that the dbg.assign's value +;; component is correct. + +; CHECK: store i64 0, i64* %2, align 16{{.*}}, !DIAssignID ![[ID:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i64 0, metadata !{{.*}}, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata ![[ID]], metadata i8* %1, metadata !DIExpression()) + +define dso_local void @_Z3funv() local_unnamed_addr #0 !dbg !7 { +entry: + %local = alloca [5 x i32], align 16, !DIAssignID !16 + call void @llvm.dbg.assign(metadata i1 undef, metadata !11, metadata !DIExpression(), metadata !16, metadata [5 x i32]* %local, metadata !DIExpression()), !dbg !17 + %0 = bitcast [5 x i32]* %local to i8*, !dbg !18 + call void @llvm.lifetime.start.p0i8(i64 20, i8* %0) #5, !dbg !18 + %arraydecay = getelementptr inbounds [5 x i32], [5 x i32]* %local, i64 0, i64 0, !dbg !19 + %1 = bitcast i32* %arraydecay to i8*, !dbg !19 + call void @llvm.memset.p0i8.i64(i8* align 16 %1, i8 8, i64 16, i1 false), !dbg !19, !DIAssignID !20 + call void @llvm.dbg.assign(metadata i8 8, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 128), metadata !20, metadata i8* %1, metadata !DIExpression()), !dbg !17 + call void @llvm.memset.p0i8.i64(i8* align 16 %1, i8 0, i64 8, i1 false), !dbg !21, !DIAssignID !22 + call void @llvm.dbg.assign(metadata i8 0, metadata !11, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64), metadata !22, metadata i8* %1, metadata !DIExpression()), !dbg !17 + call void @_Z3escPi(i32* noundef %arraydecay), !dbg !23 + call void @llvm.lifetime.end.p0i8(i64 20, i8* %0) #5, !dbg !24 + ret void, !dbg !24 +} + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: argmemonly mustprogress nofree nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #2 + +declare !dbg !25 dso_local void @_Z3escPi(i32* noundef) local_unnamed_addr #3 + +; Function Attrs: argmemonly mustprogress nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: mustprogress nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #4 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 5} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{!"clang version 14.0.0"} +!7 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11} +!11 = !DILocalVariable(name: "local", scope: !7, file: !1, line: 3, type: !12) +!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 160, elements: !14) +!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!14 = !{!15} +!15 = !DISubrange(count: 5) +!16 = distinct !DIAssignID() +!17 = !DILocation(line: 0, scope: !7) +!18 = !DILocation(line: 3, column: 3, scope: !7) +!19 = !DILocation(line: 4, column: 3, scope: !7) +!20 = distinct !DIAssignID() +!21 = !DILocation(line: 5, column: 3, scope: !7) +!22 = distinct !DIAssignID() +!23 = !DILocation(line: 6, column: 3, scope: !7) +!24 = !DILocation(line: 7, column: 1, scope: !7) +!25 = !DISubprogram(name: "esc", linkageName: "_Z3escPi", scope: !1, file: !1, line: 1, type: !26, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !29) +!26 = !DISubroutineType(types: !27) +!27 = !{null, !28} +!28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) +!29 = !{} Index: llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/sink-store.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/sink-store.ll @@ -0,0 +1,108 @@ +; RUN: opt %s -S -instcombine -experimental-assignment-tracking | FileCheck %s + +;; Check that instcombine merges the DIAssignID metadata when merging two +;; stores into a successor. Filecheck directives inline. +;; +;; Generated from the following source: +;; int c; +;; void esc(int*); +;; int get(); +;; void fun() { +;; int local; +;; if (c) { +;; get(); +;; local = 2; +;; } else { +;; local = 2; +;; } +;; esc(&local); +;; } + +; CHECK: if.then: +; CHECK-NEXT: %call = call +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 2, metadata ![[LOCAL:[0-9]+]], metadata !DIExpression(), metadata ![[MERGED_ID:[0-9]+]], metadata i32* %local, metadata !DIExpression()), !dbg + +; CHECK: if.else: +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 2, metadata ![[LOCAL]], metadata !DIExpression(), metadata ![[MERGED_ID]], metadata i32* %local, metadata !DIExpression()), !dbg + +; CHECK: if.end: +; CHECK-NEXT: store i32 2, i32* %local{{.*}}!DIAssignID ![[MERGED_ID]] + +; CHECK: ![[LOCAL]] = !DILocalVariable(name: "local", + +@c = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 + +; Function Attrs: uwtable mustprogress +define dso_local void @_Z3funv() local_unnamed_addr !dbg !11 { +entry: + %local = alloca i32, align 4 + %0 = bitcast i32* %local to i8*, !dbg !16 + call void @llvm.lifetime.start.p0i8(i64 4, i8* %0), !dbg !16 + %1 = load i32, i32* @c, align 4, !dbg !17 + %tobool = icmp ne i32 %1, 0, !dbg !17 + br i1 %tobool, label %if.then, label %if.else, !dbg !23 + +if.then: ; preds = %entry + %call = call i32 @_Z3getv(), !dbg !24 + store i32 2, i32* %local, align 4, !dbg !26, !DIAssignID !27 + call void @llvm.dbg.assign(metadata i32 2, metadata !15, metadata !DIExpression(), metadata !27, metadata i32* %local, metadata !DIExpression()), !dbg !26 + br label %if.end, !dbg !28 + +if.else: ; preds = %entry + store i32 2, i32* %local, align 4, !dbg !29, !DIAssignID !31 + call void @llvm.dbg.assign(metadata i32 2, metadata !15, metadata !DIExpression(), metadata !31, metadata i32* %local, metadata !DIExpression()), !dbg !29 + br label %if.end + +if.end: ; preds = %if.else, %if.then + call void @_Z3escPi(i32* %local), !dbg !32 + call void @llvm.lifetime.end.p0i8(i64 4, i8* %0), !dbg !33 + ret void, !dbg !33 +} + +declare !dbg !34 dso_local i32 @_Z3getv() local_unnamed_addr +declare !dbg !37 dso_local void @_Z3escPi(i32*) local_unnamed_addr +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "c", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.cpp", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 7, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{!"clang version 12.0.0"} +!11 = distinct !DISubprogram(name: "fun", linkageName: "_Z3funv", scope: !3, file: !3, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !14) +!12 = !DISubroutineType(types: !13) +!13 = !{null} +!14 = !{!15} +!15 = !DILocalVariable(name: "local", scope: !11, file: !3, line: 5, type: !6) +!16 = !DILocation(line: 5, column: 3, scope: !11) +!17 = !DILocation(line: 6, column: 7, scope: !18) +!18 = distinct !DILexicalBlock(scope: !11, file: !3, line: 6, column: 7) +!23 = !DILocation(line: 6, column: 7, scope: !11) +!24 = !DILocation(line: 7, column: 5, scope: !25) +!25 = distinct !DILexicalBlock(scope: !18, file: !3, line: 6, column: 10) +!26 = !DILocation(line: 8, column: 11, scope: !25) +!27 = distinct !DIAssignID() +!28 = !DILocation(line: 9, column: 3, scope: !25) +!29 = !DILocation(line: 10, column: 11, scope: !30) +!30 = distinct !DILexicalBlock(scope: !18, file: !3, line: 9, column: 10) +!31 = distinct !DIAssignID() +!32 = !DILocation(line: 12, column: 3, scope: !11) +!33 = !DILocation(line: 13, column: 1, scope: !11) +!34 = !DISubprogram(name: "get", linkageName: "_Z3getv", scope: !3, file: !3, line: 3, type: !35, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !4) +!35 = !DISubroutineType(types: !36) +!36 = !{!6} +!37 = !DISubprogram(name: "esc", linkageName: "_Z3escPi", scope: !3, file: !3, line: 2, type: !38, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !4) +!38 = !DISubroutineType(types: !39) +!39 = !{null, !40} +!40 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64) Index: llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/sink.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/sink.ll @@ -0,0 +1,143 @@ +; RUN: opt %s -S -instcombine -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; Check that when instcombine sinks an instruction used by a dbg.assign, the +;; usual debug intrinsic updating doesn't take place (i.e. do not +;; clone-and-sink a dbg.assign). Assignment tracking should be able to handle +;; this gracefully for variables that still have a stack home. For fully +;; promoted variables we may need to revisit this. + +;; $ cat test.c +;; struct a { +;; char *b; +;; } c() __attribute__((noreturn)); +;; int d; +;; int e(); +;; int f() { +;; if (e()) +;; return d; +;; c(); +;; } +;; void g(); +;; void h() { +;; struct a i; +;; i.b = 0; +;; f(); +;; i.b = 0; +;; g(&i); +;; } +;; $ clang -O2 -g -Xclang -fexperimental-assignment-tracking + +; CHECK: f.exit: +; CHECK-NEXT: %b = getelementptr inbounds %struct.a, %struct.a* %i, i64 0, i32 0, !dbg +; CHECK-NEXT: store i8* null, i8** %b, align 8,{{.+}}, !DIAssignID ![[ID:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign({{.+}}, {{.+}}, {{.+}}, metadata ![[ID]], metadata i8** %b, {{.+}}), !dbg + +%struct.a = type { i8* } + +@d = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 + +; Function Attrs: nounwind uwtable +define dso_local i32 @f() local_unnamed_addr #0 !dbg !11 { +entry: + %call = tail call i32 (...) @e() #5, !dbg !14 + %tobool.not = icmp eq i32 %call, 0, !dbg !14 + br i1 %tobool.not, label %if.end, label %if.then, !dbg !16 + +if.then: ; preds = %entry + %0 = load i32, i32* @d, align 4, !dbg !17 + ret i32 %0, !dbg !22 + +if.end: ; preds = %entry + %call1 = tail call i8* (...) @c() #6, !dbg !23 + unreachable, !dbg !23 +} + +declare !dbg !24 dso_local i32 @e(...) local_unnamed_addr #1 + +; Function Attrs: noreturn +declare !dbg !27 dso_local i8* @c(...) local_unnamed_addr #2 + +; Function Attrs: nounwind uwtable +define dso_local void @h() local_unnamed_addr #0 !dbg !35 { +entry: + %i = alloca %struct.a, align 8, !DIAssignID !40 + call void @llvm.dbg.assign(metadata i1 undef, metadata !39, metadata !DIExpression(), metadata !40, metadata %struct.a* %i, metadata !DIExpression()), !dbg !41 + %0 = bitcast %struct.a* %i to i8*, !dbg !42 + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) #5, !dbg !42 + %b = getelementptr inbounds %struct.a, %struct.a* %i, i64 0, i32 0, !dbg !43 + call void @llvm.dbg.assign(metadata i8* null, metadata !39, metadata !DIExpression(), metadata !44, metadata i8** %b, metadata !DIExpression()), !dbg !41 + %call.i = tail call i32 (...) @e() #5, !dbg !45 + %tobool.not.i = icmp eq i32 %call.i, 0, !dbg !45 + br i1 %tobool.not.i, label %if.end.i, label %f.exit, !dbg !47 + +if.end.i: ; preds = %entry + %call1.i = tail call i8* (...) @c() #6, !dbg !48 + unreachable, !dbg !48 + +f.exit: ; preds = %entry + store i8* null, i8** %b, align 8, !dbg !49, !DIAssignID !53 + call void @llvm.dbg.assign(metadata i8* null, metadata !39, metadata !DIExpression(), metadata !53, metadata i8** %b, metadata !DIExpression()), !dbg !41 + call void (%struct.a*, ...) bitcast (void (...)* @g to void (%struct.a*, ...)*)(%struct.a* nonnull %i) #5, !dbg !54 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) #5, !dbg !55 + ret void, !dbg !55 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #3 +declare dso_local void @g(...) local_unnamed_addr #1 +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #3 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #4 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "d", scope: !2, file: !3, line: 4, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "test.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = !{i32 7, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{i32 1, !"wchar_size", i32 4} +!10 = !{!"clang version 12.0.0"} +!11 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 6, type: !12, scopeLine: 6, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !4) +!12 = !DISubroutineType(types: !13) +!13 = !{!6} +!14 = !DILocation(line: 7, column: 7, scope: !15) +!15 = distinct !DILexicalBlock(scope: !11, file: !3, line: 7, column: 7) +!16 = !DILocation(line: 7, column: 7, scope: !11) +!17 = !DILocation(line: 8, column: 12, scope: !15) +!22 = !DILocation(line: 8, column: 5, scope: !15) +!23 = !DILocation(line: 9, column: 3, scope: !11) +!24 = !DISubprogram(name: "e", scope: !3, file: !3, line: 5, type: !25, spFlags: DISPFlagOptimized, retainedNodes: !4) +!25 = !DISubroutineType(types: !26) +!26 = !{!6, null} +!27 = !DISubprogram(name: "c", scope: !3, file: !3, line: 3, type: !28, flags: DIFlagNoReturn, spFlags: DISPFlagOptimized, retainedNodes: !4) +!28 = !DISubroutineType(types: !29) +!29 = !{!30, null} +!30 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "a", file: !3, line: 1, size: 64, elements: !31) +!31 = !{!32} +!32 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !30, file: !3, line: 2, baseType: !33, size: 64) +!33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !34, size: 64) +!34 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!35 = distinct !DISubprogram(name: "h", scope: !3, file: !3, line: 12, type: !36, scopeLine: 12, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !38) +!36 = !DISubroutineType(types: !37) +!37 = !{null} +!38 = !{!39} +!39 = !DILocalVariable(name: "i", scope: !35, file: !3, line: 13, type: !30) +!40 = distinct !DIAssignID() +!41 = !DILocation(line: 0, scope: !35) +!42 = !DILocation(line: 13, column: 3, scope: !35) +!43 = !DILocation(line: 14, column: 5, scope: !35) +!44 = distinct !DIAssignID() +!45 = !DILocation(line: 7, column: 7, scope: !15, inlinedAt: !46) +!46 = distinct !DILocation(line: 15, column: 3, scope: !35) +!47 = !DILocation(line: 7, column: 7, scope: !11, inlinedAt: !46) +!48 = !DILocation(line: 9, column: 3, scope: !11, inlinedAt: !46) +!49 = !DILocation(line: 16, column: 7, scope: !35) +!53 = distinct !DIAssignID() +!54 = !DILocation(line: 17, column: 3, scope: !35) +!55 = !DILocation(line: 18, column: 1, scope: !35) Index: llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/store-new-type.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/store-new-type.ll @@ -0,0 +1,67 @@ +; RUN: opt %s -S -instcombine -experimental-assignment-tracking | FileCheck %s + +;; Check that instcombine carries over DIAssignID metadata to the new store +;; when it changes the type of the pointer operand. +;; +;; Generated from the following source: +;; void esc(char**); +;; void fun(int *in) { +;; char *local = (char *)∈ +;; esc(&local); +;; } + +;; The DIAssignID should match that used in the dbg.assign that follows. +; CHECK: store i32**{{.*}}, i32***{{.*}},{{.*}}!DIAssignID ![[ID:[0-9]+]] +; CHECK-NEXT: call void @llvm.dbg.assign({{.*}},{{.*}},{{.*}}, metadata ![[ID]],{{.*}}) + +define dso_local void @fun(i32* %in) local_unnamed_addr !dbg !10 { +entry: + %in.addr = alloca i32*, align 8 + %local = alloca i8*, align 8 + store i32* %in, i32** %in.addr, align 8, !DIAssignID !22 + call void @llvm.dbg.assign(metadata i32* %in, metadata !16, metadata !DIExpression(), metadata !22, metadata i32** %in.addr, metadata !DIExpression()), !dbg !23 + %0 = bitcast i8** %local to i8*, !dbg !24 + %1 = bitcast i32** %in.addr to i8*, !dbg !25 + store i8* %1, i8** %local, align 8, !dbg !26, !DIAssignID !27 + call void @llvm.dbg.assign(metadata i8* %1, metadata !17, metadata !DIExpression(), metadata !27, metadata i8** %local, metadata !DIExpression()), !dbg !26 + call void @esc(i8** %local), !dbg !28 + ret void, !dbg !29 +} + +declare !dbg !30 dso_local void @esc(i8**) local_unnamed_addr +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!6, !7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduce.c", directory: "/") +!2 = !{} +!3 = !{!4} +!4 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64) +!5 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!6 = !{i32 7, !"Dwarf Version", i32 4} +!7 = !{i32 2, !"Debug Info Version", i32 3} +!8 = !{i32 1, !"wchar_size", i32 4} +!9 = !{!"clang version 12.0.0"} +!10 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 2, type: !11, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !15) +!11 = !DISubroutineType(types: !12) +!12 = !{null, !13} +!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64) +!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!15 = !{!16, !17} +!16 = !DILocalVariable(name: "in", arg: 1, scope: !10, file: !1, line: 2, type: !13) +!17 = !DILocalVariable(name: "local", scope: !10, file: !1, line: 3, type: !4) +!22 = distinct !DIAssignID() +!23 = !DILocation(line: 0, scope: !10) +!24 = !DILocation(line: 3, column: 3, scope: !10) +!25 = !DILocation(line: 3, column: 17, scope: !10) +!26 = !DILocation(line: 3, column: 9, scope: !10) +!27 = distinct !DIAssignID() +!28 = !DILocation(line: 4, column: 3, scope: !10) +!29 = !DILocation(line: 5, column: 1, scope: !10) +!30 = !DISubprogram(name: "esc", scope: !1, file: !1, line: 1, type: !31, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!31 = !DISubroutineType(types: !32) +!32 = !{null, !33} +!33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !4, size: 64) Index: llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/storemerge.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/instcombine/storemerge.ll @@ -0,0 +1,252 @@ +; RUN: opt %s -S -instcombine -o - -experimental-assignment-tracking | FileCheck %s + +;; $ cat test.cpp +;; class a { +;; float b; +;; }; +;; class c { +;; public: +;; a d(); +;; }; +;; class e { +;; public: +;; c &f(); +;; }; +;; class g { +;; public: +;; void h(a &); +;; }; +;; class j { +;; g k; +;; e l; +;; e m; +;; bool n; +;; void o(); +;; }; +;; void j::o() { +;; int i; +;; a p; +;; i = 0; +;; for (; i < 3; i++) +;; if (n) +;; p = l.f().d(); +;; else +;; p = m.f().d(); +;; k.h(p); +;; } +;; +;; Generated by grabbing IR before instcombine in: +;; $ clang++ -O2 -g -c test.cpp -Xclang -fexperimental-assignment-tracking + +;; Before instcombine runs we have an unrolled loop (3 iterations). Each +;; unrolled section is an if-diamond with a store in if.then and if.else +;; block. The "same" stores in each unrolled section have the same +;; DIAssignID. Instcombine is going to sink the stores from if.then and if.else +;; into if.end for each unrolled section. This involves merging the DIAssignID +;; of the two stores. Check that each merge updates all linked instructions +;; with the same DIAssignID attachments too. + +; CHECK: if.then: +; CHECK: call void @llvm.dbg.assign(metadata float %call2, metadata ![[var:[0-9]+]], metadata !DIExpression(), metadata ![[id:[0-9]+]], metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg +; CHECK: br label %for.inc + +; CHECK: if.else: +; CHECK: call void @llvm.dbg.assign(metadata float %call5, metadata ![[var]], metadata !DIExpression(), metadata ![[id]], metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg +; CHECK: br label %for.inc + +; CHECK: for.inc: +; CHECK-NEXT: %storemerge = phi float [ %call2, %if.then ], [ %call5, %if.else ] +; CHECK-NEXT: store float %storemerge, float* %ref.tmp.sroa.0.0..sroa_idx, align 4{{.+}}!DIAssignID ![[id]] + +; CHECK: if.then.1: +; CHECK: call void @llvm.dbg.assign(metadata float %call2.1, metadata ![[var]], metadata !DIExpression(), metadata ![[id]], metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg +; CHECK: br label %for.inc.1 + +; CHECK: if.else.1: +; CHECK: call void @llvm.dbg.assign(metadata float %call5.1, metadata ![[var]], metadata !DIExpression(), metadata ![[id]], metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg +; CHECK: br label %for.inc.1 + +; CHECK: for.inc.1: +; CHECK-NEXT: %storemerge1 = phi float [ %call2.1, %if.then.1 ], [ %call5.1, %if.else.1 ] +; CHECK-NEXT: store float %storemerge1, float* %ref.tmp.sroa.0.0..sroa_idx, align 4{{.+}}!DIAssignID ![[id]] + +; CHECK: if.then.2: +; CHECK: call void @llvm.dbg.assign(metadata float %call2.2, metadata ![[var]], metadata !DIExpression(), metadata ![[id]], metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg +; CHECK: br label %for.inc.2 + +; CHECK: if.else.2: +; CHECK: call void @llvm.dbg.assign(metadata float %call5.2, metadata ![[var]], metadata !DIExpression(), metadata ![[id]], metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg +; CHECK: br label %for.inc.2 + +; CHECK: for.inc.2: +; CHECK-NEXT: %storemerge2 = phi float [ %call2.2, %if.then.2 ], [ %call5.2, %if.else.2 ] +; CHECK-NEXT: store float %storemerge2, float* %ref.tmp.sroa.0.0..sroa_idx, align 4{{.+}}!DIAssignID ![[id]] + +%class.j = type { %class.g, %class.e, %class.e, i8 } +%class.g = type { i8 } +%class.e = type { i8 } +%class.a = type { float } +%class.c = type { i8 } + +; Function Attrs: uwtable +define dso_local void @_ZN1j1oEv(%class.j* %this) local_unnamed_addr #0 align 2 !dbg !7 { +entry: + %p = alloca %class.a, align 4, !DIAssignID !49 + call void @llvm.dbg.assign(metadata i1 undef, metadata !48, metadata !DIExpression(), metadata !49, metadata %class.a* %p, metadata !DIExpression()), !dbg !50 + %0 = bitcast %class.a* %p to i8*, !dbg !51 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #4, !dbg !51 + %n = getelementptr inbounds %class.j, %class.j* %this, i64 0, i32 3 + %l = getelementptr inbounds %class.j, %class.j* %this, i64 0, i32 1 + %ref.tmp.sroa.0.0..sroa_idx = getelementptr inbounds %class.a, %class.a* %p, i64 0, i32 0 + %m = getelementptr inbounds %class.j, %class.j* %this, i64 0, i32 2 + %1 = load i8, i8* %n, align 1, !dbg !52 + %tobool.not = icmp eq i8 %1, 0, !dbg !52 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !64 + +if.then: ; preds = %entry + %call = tail call nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e* nonnull %l), !dbg !65 + %call2 = tail call float @_ZN1c1dEv(%class.c* nonnull %call), !dbg !66 + store float %call2, float* %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !67, !DIAssignID !71 + call void @llvm.dbg.assign(metadata float %call2, metadata !48, metadata !DIExpression(), metadata !71, metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 + br label %for.inc, !dbg !72 + +if.else: ; preds = %entry + %call4 = tail call nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e* nonnull %m), !dbg !73 + %call5 = tail call float @_ZN1c1dEv(%class.c* nonnull %call4), !dbg !74 + store float %call5, float* %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !75, !DIAssignID !76 + call void @llvm.dbg.assign(metadata float %call5, metadata !48, metadata !DIExpression(), metadata !76, metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 + br label %for.inc + +for.inc: ; preds = %if.then, %if.else + %2 = load i8, i8* %n, align 1, !dbg !52 + %tobool.not.1 = icmp eq i8 %2, 0, !dbg !52 + br i1 %tobool.not.1, label %if.else.1, label %if.then.1, !dbg !64 + +if.then.1: ; preds = %for.inc + %call.1 = tail call nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e* nonnull %l), !dbg !65 + %call2.1 = tail call float @_ZN1c1dEv(%class.c* nonnull %call.1), !dbg !66 + store float %call2.1, float* %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !67, !DIAssignID !71 + call void @llvm.dbg.assign(metadata float %call2.1, metadata !48, metadata !DIExpression(), metadata !71, metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 + br label %for.inc.1, !dbg !72 + +if.else.1: ; preds = %for.inc + %call4.1 = tail call nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e* nonnull %m), !dbg !73 + %call5.1 = tail call float @_ZN1c1dEv(%class.c* nonnull %call4.1), !dbg !74 + store float %call5.1, float* %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !75, !DIAssignID !76 + call void @llvm.dbg.assign(metadata float %call5.1, metadata !48, metadata !DIExpression(), metadata !76, metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 + br label %for.inc.1 + +for.inc.1: ; preds = %if.else.1, %if.then.1 + %3 = load i8, i8* %n, align 1, !dbg !52 + %tobool.not.2 = icmp eq i8 %3, 0, !dbg !52 + br i1 %tobool.not.2, label %if.else.2, label %if.then.2, !dbg !64 + +if.then.2: ; preds = %for.inc.1 + %call.2 = tail call nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e* nonnull %l), !dbg !65 + %call2.2 = tail call float @_ZN1c1dEv(%class.c* nonnull %call.2), !dbg !66 + store float %call2.2, float* %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !67, !DIAssignID !71 + call void @llvm.dbg.assign(metadata float %call2.2, metadata !48, metadata !DIExpression(), metadata !71, metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 + br label %for.inc.2, !dbg !72 + +if.else.2: ; preds = %for.inc.1 + %call4.2 = tail call nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e* nonnull %m), !dbg !73 + %call5.2 = tail call float @_ZN1c1dEv(%class.c* nonnull %call4.2), !dbg !74 + store float %call5.2, float* %ref.tmp.sroa.0.0..sroa_idx, align 4, !dbg !75, !DIAssignID !76 + call void @llvm.dbg.assign(metadata float %call5.2, metadata !48, metadata !DIExpression(), metadata !76, metadata float* %ref.tmp.sroa.0.0..sroa_idx, metadata !DIExpression()), !dbg !50 + br label %for.inc.2 + +for.inc.2: ; preds = %if.else.2, %if.then.2 + %k = getelementptr inbounds %class.j, %class.j* %this, i64 0, i32 0, !dbg !77 + call void @_ZN1g1hER1a(%class.g* %k, %class.a* nonnull align 4 dereferenceable(4) %p), !dbg !78 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #4, !dbg !79 + ret void, !dbg !79 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +declare dso_local nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e*) local_unnamed_addr #2 + +declare dso_local float @_ZN1c1dEv(%class.c*) local_unnamed_addr #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +declare dso_local void @_ZN1g1hER1a(%class.g*, %class.a* nonnull align 4 dereferenceable(4)) local_unnamed_addr #2 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 12.0.0"} +!7 = distinct !DISubprogram(name: "o", linkageName: "_ZN1j1oEv", scope: !8, file: !1, line: 23, type: !40, scopeLine: 23, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, declaration: !39, retainedNodes: !43) +!8 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "j", file: !1, line: 16, size: 32, flags: DIFlagTypePassByValue, elements: !9, identifier: "_ZTS1j") +!9 = !{!10, !22, !36, !37, !39} +!10 = !DIDerivedType(tag: DW_TAG_member, name: "k", scope: !8, file: !1, line: 17, baseType: !11, size: 8) +!11 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "g", file: !1, line: 12, size: 8, flags: DIFlagTypePassByValue, elements: !12, identifier: "_ZTS1g") +!12 = !{!13} +!13 = !DISubprogram(name: "h", linkageName: "_ZN1g1hER1a", scope: !11, file: !1, line: 14, type: !14, scopeLine: 14, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!14 = !DISubroutineType(types: !15) +!15 = !{null, !16, !17} +!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !11, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!17 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !18, size: 64) +!18 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !1, line: 1, size: 32, flags: DIFlagTypePassByValue, elements: !19, identifier: "_ZTS1a") +!19 = !{!20} +!20 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !18, file: !1, line: 2, baseType: !21, size: 32) +!21 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!22 = !DIDerivedType(tag: DW_TAG_member, name: "l", scope: !8, file: !1, line: 18, baseType: !23, size: 8, offset: 8) +!23 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "e", file: !1, line: 8, size: 8, flags: DIFlagTypePassByValue, elements: !24, identifier: "_ZTS1e") +!24 = !{!25} +!25 = !DISubprogram(name: "f", linkageName: "_ZN1e1fEv", scope: !23, file: !1, line: 10, type: !26, scopeLine: 10, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!26 = !DISubroutineType(types: !27) +!27 = !{!28, !35} +!28 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !29, size: 64) +!29 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "c", file: !1, line: 4, size: 8, flags: DIFlagTypePassByValue, elements: !30, identifier: "_ZTS1c") +!30 = !{!31} +!31 = !DISubprogram(name: "d", linkageName: "_ZN1c1dEv", scope: !29, file: !1, line: 6, type: !32, scopeLine: 6, flags: DIFlagPublic | DIFlagPrototyped, spFlags: DISPFlagOptimized) +!32 = !DISubroutineType(types: !33) +!33 = !{!18, !34} +!34 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !29, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!35 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!36 = !DIDerivedType(tag: DW_TAG_member, name: "m", scope: !8, file: !1, line: 19, baseType: !23, size: 8, offset: 16) +!37 = !DIDerivedType(tag: DW_TAG_member, name: "n", scope: !8, file: !1, line: 20, baseType: !38, size: 8, offset: 24) +!38 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean) +!39 = !DISubprogram(name: "o", linkageName: "_ZN1j1oEv", scope: !8, file: !1, line: 21, type: !40, scopeLine: 21, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!40 = !DISubroutineType(types: !41) +!41 = !{null, !42} +!42 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!43 = !{!44, !46, !48} +!44 = !DILocalVariable(name: "this", arg: 1, scope: !7, type: !45, flags: DIFlagArtificial | DIFlagObjectPointer) +!45 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) +!46 = !DILocalVariable(name: "i", scope: !7, file: !1, line: 24, type: !47) +!47 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!48 = !DILocalVariable(name: "p", scope: !7, file: !1, line: 25, type: !18) +!49 = distinct !DIAssignID() +!50 = !DILocation(line: 0, scope: !7) +!51 = !DILocation(line: 25, column: 3, scope: !7) +!52 = !DILocation(line: 28, column: 9, scope: !53) +!53 = distinct !DILexicalBlock(scope: !54, file: !1, line: 28, column: 9) +!54 = distinct !DILexicalBlock(scope: !55, file: !1, line: 27, column: 3) +!55 = distinct !DILexicalBlock(scope: !7, file: !1, line: 27, column: 3) +!64 = !DILocation(line: 28, column: 9, scope: !54) +!65 = !DILocation(line: 29, column: 13, scope: !53) +!66 = !DILocation(line: 29, column: 17, scope: !53) +!67 = !DILocation(line: 29, column: 9, scope: !53) +!71 = distinct !DIAssignID() +!72 = !DILocation(line: 29, column: 7, scope: !53) +!73 = !DILocation(line: 31, column: 13, scope: !53) +!74 = !DILocation(line: 31, column: 17, scope: !53) +!75 = !DILocation(line: 31, column: 9, scope: !53) +!76 = distinct !DIAssignID() +!77 = !DILocation(line: 32, column: 3, scope: !7) +!78 = !DILocation(line: 32, column: 5, scope: !7) +!79 = !DILocation(line: 33, column: 1, scope: !7)