Index: llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp =================================================================== --- llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp +++ llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp @@ -101,6 +101,8 @@ namespace { struct AllocaInfo { + using DbgUserVec = SmallVector; + SmallVector DefiningBlocks; SmallVector UsingBlocks; @@ -108,7 +110,7 @@ BasicBlock *OnlyBlock; bool OnlyUsedInOneBlock; - TinyPtrVector DbgDeclares; + DbgUserVec DbgUsers; void clear() { DefiningBlocks.clear(); @@ -116,7 +118,7 @@ OnlyStore = nullptr; OnlyBlock = nullptr; OnlyUsedInOneBlock = true; - DbgDeclares.clear(); + DbgUsers.clear(); } /// Scan the uses of the specified alloca, filling in the AllocaInfo used @@ -149,7 +151,7 @@ } } - DbgDeclares = FindDbgAddrUses(AI); + findDbgUsers(DbgUsers, AI); } }; @@ -247,7 +249,7 @@ /// For each alloca, we keep track of the dbg.declare intrinsic that /// describes it, if any, so that we can convert it to a dbg.value /// intrinsic if the alloca gets promoted. - SmallVector, 8> AllocaDbgDeclares; + SmallVector AllocaDbgUsers; /// The set of basic blocks the renamer has already visited. SmallPtrSet Visited; @@ -421,10 +423,14 @@ // Record debuginfo for the store and remove the declaration's // debuginfo. - for (DbgVariableIntrinsic *DII : Info.DbgDeclares) { - DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false); - ConvertDebugDeclareToDebugValue(DII, Info.OnlyStore, DIB); - DII->eraseFromParent(); + for (DbgVariableIntrinsic *DII : Info.DbgUsers) { + if (DII->isAddressOfVariable()) { + DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false); + ConvertDebugDeclareToDebugValue(DII, Info.OnlyStore, DIB); + DII->eraseFromParent(); + } else if (DII->getExpression()->startsWithDeref()) { + DII->eraseFromParent(); + } } // Remove the (now dead) store and alloca. Info.OnlyStore->eraseFromParent(); @@ -519,9 +525,11 @@ while (!AI->use_empty()) { StoreInst *SI = cast(AI->user_back()); // Record debuginfo for the store before removing it. - for (DbgVariableIntrinsic *DII : Info.DbgDeclares) { - DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false); - ConvertDebugDeclareToDebugValue(DII, SI, DIB); + for (DbgVariableIntrinsic *DII : Info.DbgUsers) { + if (DII->isAddressOfVariable()) { + DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false); + ConvertDebugDeclareToDebugValue(DII, SI, DIB); + } } SI->eraseFromParent(); LBI.deleteValue(SI); @@ -530,8 +538,9 @@ AI->eraseFromParent(); // The alloca's debuginfo can be removed as well. - for (DbgVariableIntrinsic *DII : Info.DbgDeclares) - DII->eraseFromParent(); + for (DbgVariableIntrinsic *DII : Info.DbgUsers) + if (DII->isAddressOfVariable() || DII->getExpression()->startsWithDeref()) + DII->eraseFromParent(); ++NumLocalPromoted; return true; @@ -540,7 +549,7 @@ void PromoteMem2Reg::run() { Function &F = *DT.getRoot()->getParent(); - AllocaDbgDeclares.resize(Allocas.size()); + AllocaDbgUsers.resize(Allocas.size()); AllocaInfo Info; LargeBlockInfo LBI; @@ -598,8 +607,8 @@ } // Remember the dbg.declare intrinsic describing this alloca, if any. - if (!Info.DbgDeclares.empty()) - AllocaDbgDeclares[AllocaNum] = Info.DbgDeclares; + if (!Info.DbgUsers.empty()) + AllocaDbgUsers[AllocaNum] = Info.DbgUsers; // Keep the reverse mapping of the 'Allocas' array for the rename pass. AllocaLookup[Allocas[AllocaNum]] = AllocaNum; @@ -672,9 +681,11 @@ } // Remove alloca's dbg.declare instrinsics from the function. - for (auto &Declares : AllocaDbgDeclares) - for (auto *DII : Declares) - DII->eraseFromParent(); + for (auto &DbgUsers : AllocaDbgUsers) { + for (auto *DII : DbgUsers) + if (DII->isAddressOfVariable() || DII->getExpression()->startsWithDeref()) + DII->eraseFromParent(); + } // Loop over all of the PHI nodes and see if there are any that we can get // rid of because they merge all of the same incoming values. This can @@ -914,8 +925,9 @@ // The currently active variable for this block is now the PHI. IncomingVals[AllocaNo] = APN; - for (DbgVariableIntrinsic *DII : AllocaDbgDeclares[AllocaNo]) - ConvertDebugDeclareToDebugValue(DII, APN, DIB); + for (DbgVariableIntrinsic *DII : AllocaDbgUsers[AllocaNo]) + if (DII->isAddressOfVariable()) + ConvertDebugDeclareToDebugValue(DII, APN, DIB); // Get the next phi node. ++PNI; @@ -974,8 +986,9 @@ // Record debuginfo for the store before removing it. IncomingLocs[AllocaNo] = SI->getDebugLoc(); - for (DbgVariableIntrinsic *DII : AllocaDbgDeclares[ai->second]) - ConvertDebugDeclareToDebugValue(DII, SI, DIB); + for (DbgVariableIntrinsic *DII : AllocaDbgUsers[ai->second]) + if (DII->isAddressOfVariable()) + ConvertDebugDeclareToDebugValue(DII, SI, DIB); BB->getInstList().erase(SI); } } Index: llvm/test/DebugInfo/Generic/mem2reg-promote-alloca-1.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/mem2reg-promote-alloca-1.ll @@ -0,0 +1,77 @@ +; RUN: opt -mem2reg %s -S -o - | FileCheck %s + +;; Check that mem2reg removes dbg.value(%param.addr, DIExpression(DW_OP_deref...)) +;; when promoting the alloca %param.addr. +;; +;; $ clang inlining.c -O2 -g -emit-llvm -S -o tmp.ll -Xclang -disable-llvm-passes +;; $ opt tmp.ll -o - -instcombine -inline -S +;; $ cat inlining.c +;; int g; +;; __attribute__((__always_inline__)) +;; static void use(int* p) { +;; g = *p; +;; } +;; +;; __attribute__((__noinline__)) +;; void fun(int param) { +;; use(¶m); +;; } + +; CHECK: define dso_local void @fun(i32 %param) +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 %param, metadata ![[PARAM:[0-9]+]], metadata !DIExpression()) +; CHECK-NOT: call void @llvm.dbg.value({{.*}}, metadata ![[PARAM]] +; CHECK: ![[PARAM]] = !DILocalVariable(name: "param", + +@g = dso_local global i32 0, align 4, !dbg !0 + +define dso_local void @fun(i32 %param) !dbg !12 { +entry: + %param.addr = alloca i32, align 4 + call void @llvm.dbg.value(metadata i32 %param, metadata !16, metadata !DIExpression()), !dbg !17 + store i32 %param, i32* %param.addr, align 4 + call void @llvm.dbg.value(metadata i32* %param.addr, metadata !16, metadata !DIExpression(DW_OP_deref)), !dbg !17 + call void @llvm.dbg.value(metadata i32* %param.addr, metadata !22, metadata !DIExpression()), !dbg !28 + call void @llvm.dbg.value(metadata i32* %param.addr, metadata !22, metadata !DIExpression()), !dbg !28 + %0 = load i32, i32* %param.addr, align 4, !dbg !30 + store i32 %0, i32* @g, align 4, !dbg !31 + ret void, !dbg !32 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "g", scope: !2, file: !6, line: 8, type: !7, 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: "inlining.c", directory: "/") +!4 = !{} +!5 = !{!0} +!6 = !DIFile(filename: "inlining.c", directory: "/") +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 7, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 12.0.0"} +!12 = distinct !DISubprogram(name: "fun", scope: !6, file: !6, line: 15, type: !13, scopeLine: 15, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{null, !7} +!15 = !{!16} +!16 = !DILocalVariable(name: "param", arg: 1, scope: !12, file: !6, line: 15, type: !7) +!17 = !DILocation(line: 0, scope: !12) +!22 = !DILocalVariable(name: "p", arg: 1, scope: !23, file: !6, line: 10, type: !26) +!23 = distinct !DISubprogram(name: "use", scope: !6, file: !6, line: 10, type: !24, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !27) +!24 = !DISubroutineType(types: !25) +!25 = !{null, !26} +!26 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!27 = !{!22} +!28 = !DILocation(line: 0, scope: !23, inlinedAt: !29) +!29 = distinct !DILocation(line: 16, column: 3, scope: !12) +!30 = !DILocation(line: 11, column: 7, scope: !23, inlinedAt: !29) +!31 = !DILocation(line: 11, column: 5, scope: !23, inlinedAt: !29) +!32 = !DILocation(line: 17, column: 1, scope: !12) +!34 = !DISubroutineType(types: !35) +!35 = !{!7} Index: llvm/test/DebugInfo/Generic/mem2reg-promote-alloca-2.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/mem2reg-promote-alloca-2.ll @@ -0,0 +1,124 @@ +; RUN: opt -mem2reg %s -S -o - | FileCheck %s + +;; Check that mem2reg removes dbg.value(%local, DIExpression(DW_OP_deref...)) +;; that instcombine LowerDbgDeclare inserted before the call to 'esc' when +;; promoting the alloca %local after 'esc' has been inlined. Without this we +;; provide no location for 'local', even though it is provably constant +;; throughout after inlining. +;; +;; $ clang reduce.c -O2 -g -emit-llvm -S -o tmp.ll -Xclang -disable-llvm-passes +;; $ opt tmp.ll -o - -instcombine -inline -S +;; $ cat reduce.c +;; long a; +;; int b; +;; void c(); +;; __attribute__((__always_inline__)) +;; static void esc(long *e) { +;; *e = a; +;; c(); +;; if (b) +;; *e = 0; +;; } +;; +;; void fun() { +;; long local = 0; +;; esc(&local); +;; } + +; CHECK: define dso_local void @fun() +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @llvm.dbg.value(metadata i64 0, metadata ![[LOCAL:[0-9]+]], metadata !DIExpression()) +; CHECK-NOT: call void @llvm.dbg.value({{.*}}, metadata ![[LOCAL]] +; CHECK: ![[LOCAL]] = !DILocalVariable(name: "local", + +@a = dso_local global i64 0, align 8, !dbg !0 +@b = dso_local global i32 0, align 4, !dbg !6 + +define dso_local void @fun() !dbg !14 { +entry: + %e.addr.i = alloca i64*, align 8 + %local = alloca i64, align 8 + %0 = bitcast i64* %local to i8*, !dbg !19 + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0), !dbg !19 + call void @llvm.dbg.value(metadata i64 0, metadata !18, metadata !DIExpression()), !dbg !20 + store i64 0, i64* %local, align 8, !dbg !21 + call void @llvm.dbg.value(metadata i64* %local, metadata !18, metadata !DIExpression(DW_OP_deref)), !dbg !20 + %1 = bitcast i64** %e.addr.i to i8*, !dbg !26 + call void @llvm.lifetime.start.p0i8(i64 8, i8* %1), !dbg !26 + call void @llvm.dbg.value(metadata i64* %local, metadata !32, metadata !DIExpression()), !dbg !26 + store i64* %local, i64** %e.addr.i, align 8 + %2 = load i64, i64* @a, align 8, !dbg !36 + call void @llvm.dbg.value(metadata i64* %local, metadata !32, metadata !DIExpression()), !dbg !26 + store i64 %2, i64* %local, align 8, !dbg !37 + call void (...) @c(), !dbg !38 + %3 = load i32, i32* @b, align 4, !dbg !39 + %tobool.not.i = icmp eq i32 %3, 0, !dbg !39 + br i1 %tobool.not.i, label %esc.exit, label %if.then.i, !dbg !43 + +if.then.i: ; preds = %entry + %4 = load i64*, i64** %e.addr.i, align 8, !dbg !44 + call void @llvm.dbg.value(metadata i64* %4, metadata !32, metadata !DIExpression()), !dbg !26 + store i64 0, i64* %4, align 8, !dbg !45 + br label %esc.exit, !dbg !46 + +esc.exit: ; preds = %entry, %if.then.i + %5 = bitcast i64** %e.addr.i to i8*, !dbg !47 + call void @llvm.lifetime.end.p0i8(i64 8, i8* %5), !dbg !47 + %6 = bitcast i64* %local to i8*, !dbg !48 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %6), !dbg !48 + ret void, !dbg !48 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.dbg.value(metadata, metadata, metadata) +declare !dbg !49 dso_local void @c(...) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!10, !11, !12} +!llvm.ident = !{!13} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "a", scope: !2, file: !3, line: 1, type: !9, 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: "reduce.c", directory: "/") +!4 = !{} +!5 = !{!0, !6} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 2, type: !8, isLocal: false, isDefinition: true) +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!9 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!10 = !{i32 7, !"Dwarf Version", i32 4} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{!"clang version 12.0.0"} +!14 = distinct !DISubprogram(name: "fun", scope: !3, file: !3, line: 12, type: !15, scopeLine: 12, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !17) +!15 = !DISubroutineType(types: !16) +!16 = !{null} +!17 = !{!18} +!18 = !DILocalVariable(name: "local", scope: !14, file: !3, line: 13, type: !9) +!19 = !DILocation(line: 13, column: 3, scope: !14) +!20 = !DILocation(line: 0, scope: !14) +!21 = !DILocation(line: 13, column: 8, scope: !14) +!26 = !DILocation(line: 0, scope: !27, inlinedAt: !33) +!27 = distinct !DISubprogram(name: "esc", scope: !3, file: !3, line: 5, type: !28, scopeLine: 5, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !31) +!28 = !DISubroutineType(types: !29) +!29 = !{null, !30} +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64) +!31 = !{!32} +!32 = !DILocalVariable(name: "e", arg: 1, scope: !27, file: !3, line: 5, type: !30) +!33 = distinct !DILocation(line: 14, column: 3, scope: !14) +!36 = !DILocation(line: 6, column: 8, scope: !27, inlinedAt: !33) +!37 = !DILocation(line: 6, column: 6, scope: !27, inlinedAt: !33) +!38 = !DILocation(line: 7, column: 3, scope: !27, inlinedAt: !33) +!39 = !DILocation(line: 8, column: 7, scope: !40, inlinedAt: !33) +!40 = distinct !DILexicalBlock(scope: !27, file: !3, line: 8, column: 7) +!43 = !DILocation(line: 8, column: 7, scope: !27, inlinedAt: !33) +!44 = !DILocation(line: 9, column: 6, scope: !40, inlinedAt: !33) +!45 = !DILocation(line: 9, column: 8, scope: !40, inlinedAt: !33) +!46 = !DILocation(line: 9, column: 5, scope: !40, inlinedAt: !33) +!47 = !DILocation(line: 10, column: 1, scope: !27, inlinedAt: !33) +!48 = !DILocation(line: 15, column: 1, scope: !14) +!49 = !DISubprogram(name: "c", scope: !3, file: !3, line: 3, type: !50, spFlags: DISPFlagOptimized, retainedNodes: !4) +!50 = !DISubroutineType(types: !51) +!51 = !{null, null} Index: llvm/test/DebugInfo/Generic/mem2reg-promote-alloca-3.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/mem2reg-promote-alloca-3.ll @@ -0,0 +1,79 @@ +; RUN: opt -mem2reg %s -S -o - | FileCheck %s + +;; Check that mem2reg removes dbg.value(%local, DIExpression(DW_OP_deref...)) +;; that instcombine LowerDbgDeclare inserted before the call to 'esc' when +;; promoting the alloca %local after 'esc' has been inlined. Without this we +;; provide no location for 'local', even though it is provably constant +;; throughout after inlining. +;; +;; $ clang reduce.c -O2 -g -emit-llvm -S -o tmp.ll -Xclang -disable-llvm-passes +;; $ opt tmp.ll -o - -instcombine -inline -S +;; $ cat reduce.c +;; __attribute__((__always_inline__)) +;; static void esc(unsigned char **c) { +;; *c += 4; +;; } +;; void fun() { +;; unsigned char *local = 0; +;; esc(&local); +;; } + +; CHECK: define dso_local void @fun() +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @llvm.dbg.value(metadata i8* null, metadata ![[LOCAL:[0-9]+]], metadata !DIExpression()) +; CHECK-NOT: call void @llvm.dbg.value({{.*}}, metadata ![[LOCAL]] +; CHECK: ![[LOCAL]] = !DILocalVariable(name: "local", + +define dso_local void @fun() !dbg !7 { +entry: + %local = alloca i8*, align 8 + %0 = bitcast i8** %local to i8*, !dbg !14 + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) #3, !dbg !14 + call void @llvm.dbg.value(metadata i8* null, metadata !11, metadata !DIExpression()), !dbg !15 + store i8* null, i8** %local, align 8, !dbg !16 + call void @llvm.dbg.value(metadata i8** %local, metadata !11, metadata !DIExpression(DW_OP_deref)), !dbg !15 + call void @llvm.dbg.value(metadata i8** %local, metadata !21, metadata !DIExpression()), !dbg !27 + call void @llvm.dbg.value(metadata i8** %local, metadata !21, metadata !DIExpression()), !dbg !27 + %1 = load i8*, i8** %local, align 8, !dbg !29 + %add.ptr.i = getelementptr inbounds i8, i8* %1, i64 4, !dbg !29 + store i8* %add.ptr.i, i8** %local, align 8, !dbg !29 + %2 = bitcast i8** %local to i8*, !dbg !30 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %2) #3, !dbg !30 + ret void, !dbg !30 +} + +declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 12.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "reduce.c", 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: "fun", scope: !1, file: !1, line: 5, type: !8, scopeLine: 5, flags: 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: 6, type: !12) +!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) +!13 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char) +!14 = !DILocation(line: 6, column: 3, scope: !7) +!15 = !DILocation(line: 0, scope: !7) +!16 = !DILocation(line: 6, column: 18, scope: !7) +!21 = !DILocalVariable(name: "c", arg: 1, scope: !22, file: !1, line: 2, type: !25) +!22 = distinct !DISubprogram(name: "esc", scope: !1, file: !1, line: 2, type: !23, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !26) +!23 = !DISubroutineType(types: !24) +!24 = !{null, !25} +!25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!26 = !{!21} +!27 = !DILocation(line: 0, scope: !22, inlinedAt: !28) +!28 = distinct !DILocation(line: 7, column: 3, scope: !7) +!29 = !DILocation(line: 3, column: 6, scope: !22, inlinedAt: !28) +!30 = !DILocation(line: 8, column: 1, scope: !7)