Index: llvm/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/lib/Transforms/Utils/Local.cpp +++ llvm/lib/Transforms/Utils/Local.cpp @@ -1110,6 +1110,42 @@ LLVM_DEBUG(dbgs() << "Killing Trivial BB: \n" << *BB); + // Sink linked dbg.assign intrinsics and make them undef. Treat unlinked + // dbg.assigns like dbg.values (delete them) for consistency. + { + SmallVector ToSink; + SmallVector ToErase; + // Don't touch Seen directly, use IsFirstCopyOf. + using IDTuple = std::tuple; + DenseSet Seen; + auto IsFirstCopyOf = [&Seen](DbgAssignIntrinsic *DAI) { + auto ID = std::make_tuple(DebugVariable(DAI), DAI->getAssignId(), + DAI->getAddress()); + return Seen.insert(ID).second; + }; + + // Scan backwards because we're going to ignore earlier duplicates. This + // doesn't remove redundant dbg.assigns optimally but helps a lot cheaply. + for (auto &I : reverse(*BB)) { + if (auto *DAI = dyn_cast(&I)) { + if (IsFirstCopyOf(DAI) && !at::getAssignmentInsts(DAI).empty()) + ToSink.push_back(DAI); + else + ToErase.push_back(DAI); + } + } + + // Reverse ToSink to get original insertion order. + for (auto *DAI : reverse(ToSink)) { + LLVM_DEBUG(dbgs() << " Sinking & making undef: " << *DAI << "\n"); + DAI->setUndef(); + DAI->removeFromParent(); + DAI->insertBefore(Succ->getFirstNonPHI()); + } + for (auto *DAI : ToErase) + DAI->eraseFromParent(); + } + SmallVector Updates; if (DTU) { // To avoid processing the same predecessor more than once. @@ -2873,9 +2909,11 @@ for (BasicBlock::iterator II = BB->begin(), IE = BB->end(); II != IE;) { Instruction *I = &*II; I->dropUndefImplyingAttrsAndUnknownMetadata(); - if (I->isUsedByMetadata()) - dropDebugUsers(*I); - if (I->isDebugOrPseudoInst()) { + if (auto *DAI = dyn_cast(I)) { + // Hoist dbg.assign intrinsics and make the value component undef since + // we currently can't conditionally pick a value for this branch. + DAI->setUndef(); + } else if (I->isDebugOrPseudoInst()) { // Remove DbgInfo and pseudo probe Intrinsics. II = I->eraseFromParent(); continue; Index: llvm/lib/Transforms/Utils/SimplifyCFG.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -41,6 +41,7 @@ #include "llvm/IR/ConstantRange.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/GlobalValue.h" @@ -1890,9 +1891,14 @@ } // Finally nuke all instructions apart from the common instruction. - for (auto *I : Insts) - if (I != I0) - I->eraseFromParent(); + for (auto *I : Insts) { + if (I == I0) + continue; + // The remaining uses are debug users, replace those with the common inst. + // In most (all?) cases this just introduces a use-before-def. + I->replaceAllUsesWith(I0); + I->eraseFromParent(); + } return true; } @@ -2530,6 +2536,31 @@ SpeculatedStore->setOperand(0, S); SpeculatedStore->applyMergedLocation(BI->getDebugLoc(), SpeculatedStore->getDebugLoc()); + // The value stored is still conditional, but the store itself is now + // unconditonally run, so we must be sure that any linked dbg.assign + // intrinsics are tracking the new stored value (the result of the + // select). If we don't, and the store were to be removed by another pass + // (e.g. DSE), then we'd eventually end up emitting a location describing + // the conditional value, unconditionally. + // + // === Before this transformation === + // pred: + // store %one, %x.dest, !DIAssignID !1 + // dbg.assign %one, "x", ..., !1, ... + // br %cond if.then + // + // if.then: + // store %two, %x.dest, !DIAssignID !2 + // dbg.assign %two, "x", ..., !2, ... + // + // === After this transformation === + // pred: + // dbg.assign %one, "x", ..., !2 + // %merge = select %cond, %two, %one + // store %merge, %x.dest, !DIAssignID !2 + // dbg.assign %merge, "x", ..., !2 + for (auto *DAI : at::getAssignmentMarkers(SpeculatedStore)) + DAI->setValue(S); } // Metadata can be dependent on the condition we are hoisting above. @@ -2539,8 +2570,11 @@ // Similarly strip attributes that maybe dependent on condition we are // hoisting above. for (auto &I : *ThenBB) { - if (!SpeculatedStoreValue || &I != SpeculatedStore) - I.setDebugLoc(DebugLoc()); + if (!SpeculatedStoreValue || &I != SpeculatedStore) { + // Don't update the DILocation of dbg.assign intrinsics. + if (!isa(&I)) + I.setDebugLoc(DebugLoc()); + } I.dropUndefImplyingAttrsAndUnknownMetadata(); } @@ -2574,8 +2608,12 @@ // Remove speculated dbg intrinsics. // FIXME: Is it possible to do this in a more elegant way? Moving/merging the // dbg value for the different flows and inserting it after the select. - for (Instruction *I : SpeculatedDbgIntrinsics) - I->eraseFromParent(); + for (Instruction *I : SpeculatedDbgIntrinsics) { + // We still want to know that an assignment took place so don't remove + // dbg.assign intrinsics. + if (!isa(I)) + I->eraseFromParent(); + } ++NumSpeculations; return true; Index: llvm/test/DebugInfo/Generic/assignment-tracking/simplifycfg/empty-block.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/simplifycfg/empty-block.ll @@ -0,0 +1,143 @@ +; RUN: opt -S %s -simplifycfg -o - -experimental-assignment-tracking | FileCheck %s + +;; $ cat test.cpp +;; class a {}; +;; void operator*(a, float &); +;; class b { +;; public: +;; a c; +;; }; +;; int d; +;; class e { +;; b g[3]; +;; float f; +;; void i(); +;; }; +;; void e::i() { +;; float h; +;; g[d].c *h; +;; if (h) +;; h = f; +;; else +;; h = f; +;; } +;; Generated by grabbing IR before simplifycfg in: +;; $ clang++ -O2 -g -c test.cpp -Xclang -fexperimental-assignment-tracking + +;; if.then and if.else each only have a dbg.assign and br instruction. +;; SimplifyCFG will remove these blocks. Check that the dbg.assign intrinsics +;; are sunk into the succ beforehand. + +; CHECK: entry: +;; -- alloca dbg.assign +; CHECK: call void @llvm.dbg.assign(metadata i1 undef +;; -- sunk dbg.assigns +; CHECK: call void @llvm.dbg.assign(metadata float undef, metadata ![[var:[0-9]+]], metadata !DIExpression(), metadata ![[id:[0-9]+]], metadata float* %h, metadata !DIExpression()), !dbg +; CHECK-NEXT: call void @llvm.dbg.assign(metadata float undef, metadata ![[var]], metadata !DIExpression(), metadata ![[id]], metadata float* %h, metadata !DIExpression()), !dbg +; CHECK-NEXT: %storemerge.in = getelementptr +; CHECK-NEXT: %storemerge = load float +; CHECK-NEXT: store float %storemerge, float* %h, align 4{{.+}}!DIAssignID ![[id]] +; CHECK: ret void + +%class.e = type { [3 x %class.b], float } +%class.b = type { %class.a } +%class.a = type { i8 } + +@d = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 + +; Function Attrs: uwtable +define dso_local void @_ZN1e1iEv(%class.e* %this) local_unnamed_addr #0 align 2 !dbg !11 { +entry: + %h = alloca float, align 4, !DIAssignID !32 + call void @llvm.dbg.assign(metadata i1 undef, metadata !31, metadata !DIExpression(), metadata !32, metadata float* %h, metadata !DIExpression()), !dbg !33 + %0 = bitcast float* %h to i8*, !dbg !34 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #4, !dbg !34 + call void @_Zml1aRf(float* nonnull align 4 dereferenceable(4) %h), !dbg !35 + %1 = load float, float* %h, align 4, !dbg !36, !tbaa !38 + %tobool = fcmp une float %1, 0.000000e+00, !dbg !36 + br i1 %tobool, label %if.then, label %if.else, !dbg !42 + +if.then: ; preds = %entry + call void @llvm.dbg.assign(metadata float undef, metadata !31, metadata !DIExpression(), metadata !43, metadata float* %h, metadata !DIExpression()), !dbg !33 + br label %if.end, !dbg !44 + +if.else: ; preds = %entry + call void @llvm.dbg.assign(metadata float undef, metadata !31, metadata !DIExpression(), metadata !43, metadata float* %h, metadata !DIExpression()), !dbg !33 + br label %if.end + +if.end: ; preds = %if.else, %if.then + %storemerge.in = getelementptr inbounds %class.e, %class.e* %this, i64 0, i32 1, !dbg !45 + %storemerge = load float, float* %storemerge.in, align 4, !dbg !45, !tbaa !46 + store float %storemerge, float* %h, align 4, !dbg !45, !tbaa !38, !DIAssignID !43 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #4, !dbg !48 + ret void, !dbg !48 +} + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 + +declare !dbg !49 dso_local void @_Zml1aRf(float* nonnull align 4 dereferenceable(4)) local_unnamed_addr #2 + +; Function Attrs: argmemonly nofree nosync nounwind willreturn +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +!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: 7, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, 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: "i", linkageName: "_ZN1e1iEv", scope: !12, file: !3, line: 13, type: !25, scopeLine: 13, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, declaration: !24, retainedNodes: !28) +!12 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "e", file: !3, line: 8, size: 64, flags: DIFlagTypePassByValue, elements: !13, identifier: "_ZTS1e") +!13 = !{!14, !22, !24} +!14 = !DIDerivedType(tag: DW_TAG_member, name: "g", scope: !12, file: !3, line: 9, baseType: !15, size: 24) +!15 = !DICompositeType(tag: DW_TAG_array_type, baseType: !16, size: 24, elements: !20) +!16 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "b", file: !3, line: 3, size: 8, flags: DIFlagTypePassByValue, elements: !17, identifier: "_ZTS1b") +!17 = !{!18} +!18 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !16, file: !3, line: 5, baseType: !19, size: 8, flags: DIFlagPublic) +!19 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "a", file: !3, line: 1, size: 8, flags: DIFlagTypePassByValue, elements: !4, identifier: "_ZTS1a") +!20 = !{!21} +!21 = !DISubrange(count: 3) +!22 = !DIDerivedType(tag: DW_TAG_member, name: "f", scope: !12, file: !3, line: 10, baseType: !23, size: 32, offset: 32) +!23 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!24 = !DISubprogram(name: "i", linkageName: "_ZN1e1iEv", scope: !12, file: !3, line: 11, type: !25, scopeLine: 11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) +!25 = !DISubroutineType(types: !26) +!26 = !{null, !27} +!27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!28 = !{!29, !31} +!29 = !DILocalVariable(name: "this", arg: 1, scope: !11, type: !30, flags: DIFlagArtificial | DIFlagObjectPointer) +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!31 = !DILocalVariable(name: "h", scope: !11, file: !3, line: 14, type: !23) +!32 = distinct !DIAssignID() +!33 = !DILocation(line: 0, scope: !11) +!34 = !DILocation(line: 14, column: 3, scope: !11) +!35 = !DILocation(line: 15, column: 10, scope: !11) +!36 = !DILocation(line: 16, column: 7, scope: !37) +!37 = distinct !DILexicalBlock(scope: !11, file: !3, line: 16, column: 7) +!38 = !{!39, !39, i64 0} +!39 = !{!"float", !40, i64 0} +!40 = !{!"omnipotent char", !41, i64 0} +!41 = !{!"Simple C++ TBAA"} +!42 = !DILocation(line: 16, column: 7, scope: !11) +!43 = distinct !DIAssignID() +!44 = !DILocation(line: 17, column: 5, scope: !37) +!45 = !DILocation(line: 0, scope: !37) +!46 = !{!47, !39, i64 4} +!47 = !{!"_ZTS1e", !40, i64 0, !39, i64 4} +!48 = !DILocation(line: 20, column: 1, scope: !11) +!49 = !DISubprogram(name: "operator*", linkageName: "_Zml1aRf", scope: !3, file: !3, line: 2, type: !50, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !4) +!50 = !DISubroutineType(types: !51) +!51 = !{null, !19, !52} +!52 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !23, size: 64) Index: llvm/test/DebugInfo/Generic/assignment-tracking/simplifycfg/phi-fold.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/simplifycfg/phi-fold.ll @@ -0,0 +1,179 @@ +; RUN: opt -S %s -sink-common-insts -simplifycfg -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg.assign" + +;; $ cat test.cpp +;; class a { +;; float b; +;; }; +;; class c { +;; public: +;; a d(); +;; }; +;; class e { +;; public: +;; c &f(); +;; }; +;; class g { +;; public: +;; void h(a &); +;; }; +;; class i { +;; g j; +;; e k; +;; e l; +;; bool m; +;; void n(); +;; }; +;; void i::n() { +;; a o; +;; if (m) +;; o = k.f().d(); // <- Sink common tails & fold two-entry-phi. +;; else // <- +;; o = l.f().d(); // <- +;; j.h(o); +;; } +;; $ clang -O2 -g -Xclang -fexperimental-assignment-tracking + +;; Check that SimplifyCFG doesn't incorrectly delete dbg.assign intrinsics, and +;; that the hoisted dbg.assign intrinsics' value components are made undef. + +; CHECK: dbg.assign(metadata i1 undef +; CHECK: dbg.assign(metadata float undef +; CHECK: dbg.assign(metadata float undef + +%class.i = 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 @_ZN1i1nEv(%class.i* %this) local_unnamed_addr #0 align 2 !dbg !7 { +entry: + %o = alloca %class.a, align 4, !DIAssignID !47 + call void @llvm.dbg.assign(metadata i1 undef, metadata !46, metadata !DIExpression(), metadata !47, metadata %class.a* %o, metadata !DIExpression()), !dbg !48 + %0 = bitcast %class.a* %o to i8*, !dbg !49 + call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #4, !dbg !49 + %m = getelementptr inbounds %class.i, %class.i* %this, i64 0, i32 3, !dbg !50 + %1 = load i8, i8* %m, align 1, !dbg !50, !tbaa !52, !range !59 + %tobool.not = icmp eq i8 %1, 0, !dbg !50 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !60 + +if.then: ; preds = %entry + %k = getelementptr inbounds %class.i, %class.i* %this, i64 0, i32 1, !dbg !61 + %call = tail call nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e* nonnull %k), !dbg !62 + %call2 = tail call float @_ZN1c1dEv(%class.c* nonnull %call), !dbg !63 + call void @llvm.dbg.assign(metadata float %call2, metadata !46, metadata !DIExpression(), metadata !64, metadata float* %2, metadata !DIExpression()), !dbg !48 + br label %if.end, !dbg !65 + +if.else: ; preds = %entry + %l = getelementptr inbounds %class.i, %class.i* %this, i64 0, i32 2, !dbg !66 + %call4 = tail call nonnull align 1 dereferenceable(1) %class.c* @_ZN1e1fEv(%class.e* nonnull %l), !dbg !67 + %call5 = tail call float @_ZN1c1dEv(%class.c* nonnull %call4), !dbg !68 + call void @llvm.dbg.assign(metadata float %call5, metadata !46, metadata !DIExpression(), metadata !64, metadata float* %2, metadata !DIExpression()), !dbg !48 + br label %if.end + +if.end: ; preds = %if.else, %if.then + %call2.sink = phi float [ %call5, %if.else ], [ %call2, %if.then ], !dbg !70 + %2 = getelementptr inbounds %class.a, %class.a* %o, i64 0, i32 0, !dbg !71 + store float %call2.sink, float* %2, align 4, !dbg !71, !DIAssignID !64 + %j = getelementptr inbounds %class.i, %class.i* %this, i64 0, i32 0, !dbg !72 + call void @_ZN1g1hER1a(%class.g* %j, %class.a* nonnull align 4 dereferenceable(4) %o), !dbg !73 + call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #4, !dbg !74 + ret void, !dbg !74 +} + +; 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: "n", linkageName: "_ZN1i1nEv", 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: "i", file: !1, line: 16, size: 32, flags: DIFlagTypePassByValue, elements: !9, identifier: "_ZTS1i") +!9 = !{!10, !22, !36, !37, !39} +!10 = !DIDerivedType(tag: DW_TAG_member, name: "j", 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: "k", 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: "l", scope: !8, file: !1, line: 19, baseType: !23, size: 8, offset: 16) +!37 = !DIDerivedType(tag: DW_TAG_member, name: "m", 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: "n", linkageName: "_ZN1i1nEv", 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} +!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: "o", scope: !7, file: !1, line: 24, type: !18) +!47 = distinct !DIAssignID() +!48 = !DILocation(line: 0, scope: !7) +!49 = !DILocation(line: 24, column: 3, scope: !7) +!50 = !DILocation(line: 25, column: 7, scope: !51) +!51 = distinct !DILexicalBlock(scope: !7, file: !1, line: 25, column: 7) +!52 = !{!53, !56, i64 3} +!53 = !{!"_ZTS1i", !54, i64 0, !55, i64 1, !55, i64 2, !56, i64 3} +!54 = !{!"_ZTS1g"} +!55 = !{!"_ZTS1e"} +!56 = !{!"bool", !57, i64 0} +!57 = !{!"omnipotent char", !58, i64 0} +!58 = !{!"Simple C++ TBAA"} +!59 = !{i8 0, i8 2} +!60 = !DILocation(line: 25, column: 7, scope: !7) +!61 = !DILocation(line: 26, column: 9, scope: !51) +!62 = !DILocation(line: 26, column: 11, scope: !51) +!63 = !DILocation(line: 26, column: 15, scope: !51) +!64 = distinct !DIAssignID() +!65 = !DILocation(line: 26, column: 5, scope: !51) +!66 = !DILocation(line: 28, column: 9, scope: !51) +!67 = !DILocation(line: 28, column: 11, scope: !51) +!68 = !DILocation(line: 28, column: 15, scope: !51) +!69 = distinct !DIAssignID() +!70 = !DILocation(line: 0, scope: !51) +!71 = !DILocation(line: 28, column: 7, scope: !51) +!72 = !DILocation(line: 29, column: 3, scope: !7) +!73 = !DILocation(line: 29, column: 5, scope: !7) +!74 = !DILocation(line: 30, column: 1, scope: !7) Index: llvm/test/DebugInfo/Generic/assignment-tracking/simplifycfg/sink-erase.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/simplifycfg/sink-erase.ll @@ -0,0 +1,137 @@ +; RUN: opt %s -S -o - -experimental-assignment-tracking | FileCheck %s + +;; Reduced from function cpmx_calc_new_bk in +;; MultiSource/Benchmarks/mafft/tddis.c in the llvm test suite. +;; +;; $ cat reduce.cpp +;; int a; +;; void b() { +;; int c = 0; +;; for (; c < 6; c++) +;; for (; a;) +;; ; +;; } +;; +;; IR grabbed before simplifycfg in: +;; clang++ -O2 -g -Xclang -fexperimental-assignment-tracking +;; +;; simplifycfg will transform this cfg into: +;; +;; [entry] +;; | | +---+ +;; | v v | +;; | [for.cond1]-+ +;; v +;; [for.inc.split.5] +;; +;; Check that there is only one dbg.assign (for c) in the final block. +; CHECK: for.inc.split.5: +; CHECK-COUNT-1: call void @llvm.dbg.assign + +@a = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 + +; Function Attrs: norecurse nounwind readonly uwtable mustprogress +define dso_local void @_Z1bv() local_unnamed_addr #0 !dbg !11 { +entry: + call void @llvm.dbg.assign(metadata i1 undef, metadata !15, metadata !DIExpression(), metadata !16, metadata i32* undef, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.assign(metadata i32 0, metadata !15, metadata !DIExpression(), metadata !18, metadata i32* undef, metadata !DIExpression()), !dbg !17 + %.pr = load i32, i32* @a, align 4, !tbaa !19 + %tobool.not = icmp eq i32 %.pr, 0 + call void @llvm.dbg.value(metadata i32 0, metadata !15, metadata !DIExpression()), !dbg !17 + br i1 %tobool.not, label %for.inc.split, label %for.cond1.preheader, !dbg !23 + +for.cond1.preheader: ; preds = %for.inc.split.4.for.cond1.preheader_crit_edge, %for.inc.split.3.for.cond1.preheader_crit_edge, %for.inc.split.2.for.cond1.preheader_crit_edge, %for.inc.split.1.for.cond1.preheader_crit_edge, %for.inc.split.for.cond1.preheader_crit_edge, %entry + br label %for.cond1, !dbg !28 + +for.cond1: ; preds = %for.cond1.preheader, %for.cond1 + br label %for.cond1, !dbg !28 + +for.inc.split: ; preds = %entry + call void @llvm.dbg.assign(metadata i32 1, metadata !15, metadata !DIExpression(), metadata !29, metadata i32* undef, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 1, metadata !15, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 1, metadata !15, metadata !DIExpression()), !dbg !17 + br i1 true, label %for.inc.split.1, label %for.inc.split.for.cond1.preheader_crit_edge, !dbg !23 + +for.inc.split.for.cond1.preheader_crit_edge: ; preds = %for.inc.split + br label %for.cond1.preheader, !dbg !23 + +for.inc.split.1: ; preds = %for.inc.split + call void @llvm.dbg.assign(metadata i32 2, metadata !15, metadata !DIExpression(), metadata !29, metadata i32* undef, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 2, metadata !15, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 2, metadata !15, metadata !DIExpression()), !dbg !17 + br i1 true, label %for.inc.split.2, label %for.inc.split.1.for.cond1.preheader_crit_edge, !dbg !23 + +for.inc.split.1.for.cond1.preheader_crit_edge: ; preds = %for.inc.split.1 + br label %for.cond1.preheader, !dbg !23 + +for.inc.split.2: ; preds = %for.inc.split.1 + call void @llvm.dbg.assign(metadata i32 3, metadata !15, metadata !DIExpression(), metadata !29, metadata i32* undef, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 3, metadata !15, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 3, metadata !15, metadata !DIExpression()), !dbg !17 + br i1 true, label %for.inc.split.3, label %for.inc.split.2.for.cond1.preheader_crit_edge, !dbg !23 + +for.inc.split.2.for.cond1.preheader_crit_edge: ; preds = %for.inc.split.2 + br label %for.cond1.preheader, !dbg !23 + +for.inc.split.3: ; preds = %for.inc.split.2 + call void @llvm.dbg.assign(metadata i32 4, metadata !15, metadata !DIExpression(), metadata !29, metadata i32* undef, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 4, metadata !15, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 4, metadata !15, metadata !DIExpression()), !dbg !17 + br i1 true, label %for.inc.split.4, label %for.inc.split.3.for.cond1.preheader_crit_edge, !dbg !23 + +for.inc.split.3.for.cond1.preheader_crit_edge: ; preds = %for.inc.split.3 + br label %for.cond1.preheader, !dbg !23 + +for.inc.split.4: ; preds = %for.inc.split.3 + call void @llvm.dbg.assign(metadata i32 5, metadata !15, metadata !DIExpression(), metadata !29, metadata i32* undef, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 5, metadata !15, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 5, metadata !15, metadata !DIExpression()), !dbg !17 + br i1 true, label %for.inc.split.5, label %for.inc.split.4.for.cond1.preheader_crit_edge, !dbg !23 + +for.inc.split.4.for.cond1.preheader_crit_edge: ; preds = %for.inc.split.4 + br label %for.cond1.preheader, !dbg !23 + +for.inc.split.5: ; preds = %for.inc.split.4 + call void @llvm.dbg.assign(metadata i32 6, metadata !15, metadata !DIExpression(), metadata !29, metadata i32* undef, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 6, metadata !15, metadata !DIExpression()), !dbg !17 + ret void, !dbg !30 +} + +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #1 +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!7, !8, !9} +!llvm.ident = !{!10} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", 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: "reduce.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: "b", linkageName: "_Z1bv", scope: !3, file: !3, line: 2, type: !12, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !14) +!12 = !DISubroutineType(types: !13) +!13 = !{null} +!14 = !{!15} +!15 = !DILocalVariable(name: "c", scope: !11, file: !3, line: 3, type: !6) +!16 = distinct !DIAssignID() +!17 = !DILocation(line: 0, scope: !11) +!18 = distinct !DIAssignID() +!19 = !{!20, !20, i64 0} +!20 = !{!"int", !21, i64 0} +!21 = !{!"omnipotent char", !22, i64 0} +!22 = !{!"Simple C++ TBAA"} +!23 = !DILocation(line: 5, column: 12, scope: !24) +!24 = distinct !DILexicalBlock(scope: !25, file: !3, line: 5, column: 5) +!25 = distinct !DILexicalBlock(scope: !26, file: !3, line: 5, column: 5) +!26 = distinct !DILexicalBlock(scope: !27, file: !3, line: 4, column: 3) +!27 = distinct !DILexicalBlock(scope: !11, file: !3, line: 4, column: 3) +!28 = !DILocation(line: 5, column: 5, scope: !25) +!29 = distinct !DIAssignID() +!30 = !DILocation(line: 7, column: 1, scope: !11) Index: llvm/test/DebugInfo/Generic/assignment-tracking/simplifycfg/sink-gep.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/simplifycfg/sink-gep.ll @@ -0,0 +1,137 @@ +; RUN: opt %s -sink-common-insts -simplifycfg -verify -S -o - -experimental-assignment-tracking \ +; RUN: | FileCheck %s + +;; $ cat test.c +;; int a; +;; char b[]; +;; char c; +;; void d() { +;; char e[8]; +;; if (a) { +;; snprintf(e, sizeof(e), &c); +;; e[sizeof 1] = 0; +;; } else +;; e[1] = 0; +;; sprintf(b, e); +;; } +;; +;; Before simplifycfg runs there is a dbg.assign in if.then and another in +;; if.else. These mark assignments to different offsets into %e. The stores and +;; geps are sunk and merged; the store DIAssignIDs are merged and the value is +;; stored to an offset picked by a gep using a phi. Make sure that all +;; dbg.assign(addr) uses of the merged values are replaced with the merged +;; value. This may create use-before-defs, as it does in this example. This is +;; fine as use-before-def is legal in debug intrinsics and is essentially the +;; same as "undef", but it gives us more to work with than if we just let it +;; become undef naturally after the defining instruction is deleted. + +;; Note that if.else is removed after this transformation because it contains +;; no non-debug intrstructions. + +; CHECK: if.then: +; CHECK-NEXT: %call = call i32 (i8*, i64, i8*, ...) @snprintf + +;; Ensure the address component is the sunken gep (%arrayidx1). +; CHECK-NEXT: @llvm.dbg.assign({{.+}}, {{.+}}, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 8), {{.+}}, metadata i8* %arrayidx1, metadata !DIExpression()), !dbg + +; CHECK: if.end: +; CHECK-NEXT: %.sink = phi i64 [ 4, %if.then ], [ 1, %entry ] + +;; This dbg.assign has been sunk from the now deleted if.else. +; CHECK-NEXT: @llvm.dbg.assign({{.+}}, {{.+}}, metadata !DIExpression(DW_OP_LLVM_fragment, 8, 8), {{.+}}, metadata i8* %arrayidx1, metadata !DIExpression()), !dbg + +; CHECK-NEXT: %arrayidx1 = getelementptr inbounds [8 x i8], [8 x i8]* %e, i64 0, i64 %.sink, !dbg + +@a = dso_local local_unnamed_addr global i32 0, align 4, !dbg !0 +@c = dso_local global i8 0, align 1, !dbg !12 +@b = dso_local global [1 x i8] zeroinitializer, align 1, !dbg !6 + +; Function Attrs: nofree nounwind uwtable +define dso_local void @d() local_unnamed_addr #0 !dbg !19 { +entry: + %e = alloca [8 x i8], align 1, !DIAssignID !27 + call void @llvm.dbg.assign(metadata i1 undef, metadata !23, metadata !DIExpression(), metadata !27, metadata [8 x i8]* %e, metadata !DIExpression()), !dbg !28 + %0 = getelementptr inbounds [8 x i8], [8 x i8]* %e, i64 0, i64 0, !dbg !29 + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) #4, !dbg !29 + %1 = load i32, i32* @a, align 4, !dbg !30, !tbaa !32 + %tobool.not = icmp eq i32 %1, 0, !dbg !30 + br i1 %tobool.not, label %if.else, label %if.then, !dbg !36 + +if.then: ; preds = %entry + %call = call i32 (i8*, i64, i8*, ...) @snprintf(i8* nonnull %0, i64 8, i8* nonnull @c), !dbg !37 + %arrayidx = getelementptr inbounds [8 x i8], [8 x i8]* %e, i64 0, i64 4, !dbg !39 + store i8 0, i8* %arrayidx, align 1, !dbg !40, !tbaa !41, !DIAssignID !42 + call void @llvm.dbg.assign(metadata i8 0, metadata !23, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 8), metadata !42, metadata i8* %arrayidx, metadata !DIExpression()), !dbg !28 + br label %if.end, !dbg !43 + +if.else: ; preds = %entry + %arrayidx1 = getelementptr inbounds [8 x i8], [8 x i8]* %e, i64 0, i64 1, !dbg !44 + store i8 0, i8* %arrayidx1, align 1, !dbg !45, !tbaa !41, !DIAssignID !46 + call void @llvm.dbg.assign(metadata i8 0, metadata !23, metadata !DIExpression(DW_OP_LLVM_fragment, 8, 8), metadata !46, metadata i8* %arrayidx1, metadata !DIExpression()), !dbg !28 + br label %if.end + +if.end: ; preds = %if.else, %if.then + %call3 = call i32 (i8*, i8*, ...) @sprintf(i8* nonnull dereferenceable(1) getelementptr inbounds ([1 x i8], [1 x i8]* @b, i64 0, i64 0), i8* nonnull %0), !dbg !47 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) #4, !dbg !48 + ret void, !dbg !48 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 +declare dso_local noundef i32 @snprintf(i8* noalias nocapture noundef writeonly, i64 noundef, i8* nocapture noundef readonly, ...) local_unnamed_addr #2 +declare dso_local noundef i32 @sprintf(i8* noalias nocapture noundef writeonly, i8* nocapture noundef readonly, ...) local_unnamed_addr #2 +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) #3 + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!15, !16, !17} +!llvm.ident = !{!18} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !14, 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, !12} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 2, type: !8, isLocal: false, isDefinition: true) +!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 8, elements: !10) +!9 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!10 = !{!11} +!11 = !DISubrange(count: 1) +!12 = !DIGlobalVariableExpression(var: !13, expr: !DIExpression()) +!13 = distinct !DIGlobalVariable(name: "c", scope: !2, file: !3, line: 3, type: !9, isLocal: false, isDefinition: true) +!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!15 = !{i32 7, !"Dwarf Version", i32 4} +!16 = !{i32 2, !"Debug Info Version", i32 3} +!17 = !{i32 1, !"wchar_size", i32 4} +!18 = !{!"clang version 12.0.0)"} +!19 = distinct !DISubprogram(name: "d", scope: !3, file: !3, line: 4, type: !20, scopeLine: 4, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !22) +!20 = !DISubroutineType(types: !21) +!21 = !{null} +!22 = !{!23} +!23 = !DILocalVariable(name: "e", scope: !19, file: !3, line: 5, type: !24) +!24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 64, elements: !25) +!25 = !{!26} +!26 = !DISubrange(count: 8) +!27 = distinct !DIAssignID() +!28 = !DILocation(line: 0, scope: !19) +!29 = !DILocation(line: 5, column: 3, scope: !19) +!30 = !DILocation(line: 6, column: 7, scope: !31) +!31 = distinct !DILexicalBlock(scope: !19, file: !3, line: 6, column: 7) +!32 = !{!33, !33, i64 0} +!33 = !{!"int", !34, i64 0} +!34 = !{!"omnipotent char", !35, i64 0} +!35 = !{!"Simple C/C++ TBAA"} +!36 = !DILocation(line: 6, column: 7, scope: !19) +!37 = !DILocation(line: 7, column: 5, scope: !38) +!38 = distinct !DILexicalBlock(scope: !31, file: !3, line: 6, column: 10) +!39 = !DILocation(line: 8, column: 5, scope: !38) +!40 = !DILocation(line: 8, column: 17, scope: !38) +!41 = !{!34, !34, i64 0} +!42 = distinct !DIAssignID() +!43 = !DILocation(line: 9, column: 3, scope: !38) +!44 = !DILocation(line: 10, column: 5, scope: !31) +!45 = !DILocation(line: 10, column: 10, scope: !31) +!46 = distinct !DIAssignID() +!47 = !DILocation(line: 11, column: 3, scope: !19) +!48 = !DILocation(line: 12, column: 1, scope: !19)