Index: debuginfo-tests/dexter-tests/memvars/two-inlined-calls.c =================================================================== --- debuginfo-tests/dexter-tests/memvars/two-inlined-calls.c +++ debuginfo-tests/dexter-tests/memvars/two-inlined-calls.c @@ -1,15 +1,12 @@ -//// XFAIL:* -//// RemoveRedundantDbgInstrs is removing the second dbg.value+DW_OP_deref. - // REQUIRES: lldb // UNSUPPORTED: system-windows // RUN: %dexter --fail-lt 1.0 -w --debugger lldb \ // RUN: --builder clang-c --cflags "-O2 -glldb" -- %s // -//// The alloca 'param' uses can be promoted after inlining, and the final -//// store to it is redundant and will be removed. Check that 'param' can still -//// be read and has the expeted values throughout 'fun', and inlined calls to -//// 'use'. +//// See discussion here https://bugs.llvm.org/show_bug.cgi?id=47946#c3. The +//// alloca 'param' uses can be promoted after inlining, and the final store to +//// it is redundant and will be removed. Check that 'param' can still be read +//// and has the expected values throughout 'fun', and inlined calls to 'use'. int g; __attribute__((__always_inline__)) Index: llvm/lib/Transforms/Utils/BasicBlockUtils.cpp =================================================================== --- llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -405,23 +405,35 @@ /// - Keep track of non-overlapping fragments. static bool removeRedundantDbgInstrsUsingForwardScan(BasicBlock *BB) { SmallVector ToBeRemoved; - DenseMap > VariableMap; + DenseMap> VariableMap; + + auto ShouldRemove = [&](DebugVariable Var, const DbgValueInst *DVI) { + auto VMI = VariableMap.find(Var); + // Check if this is the first time we've encountered this variable. + if (VMI == VariableMap.end()) + return false; + // Check if this is a new value for an existing variable. + if (VMI->second.first != DVI->getValue() || + VMI->second.second != DVI->getExpression()) + return false; + // Check if this is a dbg.value+deref. Identical consecutive indirect + // locations may mark the assignment of different values to the + // location. In addition, InlineLowerDbgDeclare currently relies on finding + // dbg.value+deref inserted before calls by LowerDbgDeclare which may + // otherwise be removed by this function. + if (DVI->getExpression()->startsWithDeref()) + return false; + return true; + }; + for (auto &I : *BB) { if (DbgValueInst *DVI = dyn_cast(&I)) { - DebugVariable Key(DVI->getVariable(), - NoneType(), + DebugVariable Key(DVI->getVariable(), NoneType(), DVI->getDebugLoc()->getInlinedAt()); - auto VMI = VariableMap.find(Key); - // Update the map if we found a new value/expression describing the - // variable, or if the variable wasn't mapped already. - if (VMI == VariableMap.end() || - VMI->second.first != DVI->getValue() || - VMI->second.second != DVI->getExpression()) { - VariableMap[Key] = { DVI->getValue(), DVI->getExpression() }; - continue; - } - // Found an identical mapping. Remember the instruction for later removal. - ToBeRemoved.push_back(DVI); + if (ShouldRemove(Key, DVI)) + ToBeRemoved.push_back(DVI); + else + VariableMap[Key] = {DVI->getValue(), DVI->getExpression()}; } } Index: llvm/test/DebugInfo/Generic/dont-remove-redundant-dbg-derefs.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/dont-remove-redundant-dbg-derefs.ll @@ -0,0 +1,63 @@ +; RUN: opt -S -redundant-dbg-inst-elim %s | FileCheck %s + +;; See https://bugs.llvm.org/show_bug.cgi?id=47946#c3 +;; Check that RemoveRedundantDbgInstrs doesn't remove consecutive indirect +;; dbg.value+derefs in the forward scan. + +;; Generated from the following, with some metadata stripped out. +;; $ clang test.c -O2 -g -Xclang -disable-llvm-passes -S -emit-llvm -o tmp.ll +;; $ opt -S tmp.ll -o -instcombine +;; $ cat test.c +;; void use(int* p); +;; void fun(int param) { +;; use(¶m); +;; use(¶m); +;; } + +; CHECK: call void @llvm.dbg.value(metadata i32 %param, metadata ![[PARAM:[0-9]+]], metadata !DIExpression()) +; CHECK: call void @llvm.dbg.value(metadata i32* %param.addr, metadata ![[PARAM]], metadata !DIExpression(DW_OP_deref) +; CHECK-NEXT: call void @use +; CHECK: call void @llvm.dbg.value(metadata i32* %param.addr, metadata ![[PARAM]], metadata !DIExpression(DW_OP_deref) +; CHECK-NEXT: call void @use +; CHECK: ![[PARAM]] = !DILocalVariable(name: "param", + +define dso_local void @fun(i32 %param) !dbg !7 { +entry: + %param.addr = alloca i32, align 4 + call void @llvm.dbg.value(metadata i32 %param, metadata !12, metadata !DIExpression()), !dbg !13 + store i32 %param, i32* %param.addr, align 4 + call void @llvm.dbg.value(metadata i32* %param.addr, metadata !12, metadata !DIExpression(DW_OP_deref)), !dbg !13 + call void @use(i32* nonnull %param.addr), !dbg !18 + call void @llvm.dbg.value(metadata i32* %param.addr, metadata !12, metadata !DIExpression(DW_OP_deref)), !dbg !13 + call void @use(i32* nonnull %param.addr), !dbg !19 + ret void, !dbg !20 +} + +declare !dbg !21 dso_local void @use(i32*) +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: "test.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: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{null, !10} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DILocalVariable(name: "param", arg: 1, scope: !7, file: !1, line: 2, type: !10) +!13 = !DILocation(line: 0, scope: !7) +!18 = !DILocation(line: 3, column: 3, scope: !7) +!19 = !DILocation(line: 4, column: 3, scope: !7) +!20 = !DILocation(line: 5, column: 1, scope: !7) +!21 = !DISubprogram(name: "use", scope: !1, file: !1, line: 1, type: !22, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!22 = !DISubroutineType(types: !23) +!23 = !{null, !24} +!24 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64)