Index: llvm/lib/Transforms/Utils/BasicBlockUtils.cpp =================================================================== --- llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -25,6 +25,7 @@ #include "llvm/IR/BasicBlock.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" @@ -373,11 +374,22 @@ DVI->getExpression(), DVI->getDebugLoc()->getInlinedAt()); auto R = VariableSet.insert(Key); + // If the variable fragment hasn't been seen before then we don't want + // to remove this dbg intrinsic. + if (R.second) + continue; + + if (auto *DAI = dyn_cast(DVI)) { + // Don't delete dbg.assign intrinsics that are linked to instructions. + if (!at::getAssignmentInsts(DAI).empty()) + continue; + // Unlinked dbg.assign intrinsics can be treated like dbg.values. + } + // If the same variable fragment is described more than once it is enough // to keep the last one (i.e. the first found since we for reverse // iteration). - if (!R.second) - ToBeRemoved.push_back(DVI); + ToBeRemoved.push_back(DVI); continue; } // Sequence with consecutive dbg.value instrs ended. Clear the map to @@ -415,20 +427,58 @@ SmallVector ToBeRemoved; DenseMap, DIExpression *>> VariableMap; + DenseSet SeenDefForAggregate; + bool IsEntryBlock = BB->isEntryBlock(); for (auto &I : *BB) { if (DbgValueInst *DVI = dyn_cast(&I)) { DebugVariable Key(DVI->getVariable(), NoneType(), DVI->getDebugLoc()->getInlinedAt()); auto VMI = VariableMap.find(Key); + + auto *DAI = dyn_cast(DVI); + bool IsDbgValueKind = (!DAI || at::getAssignmentInsts(DAI).empty()); + bool RemovedFromEntry = false; + + // Remove undef dbg.assign intrinsics that are encountered before + // any non-undef intrinsics from the entry block. + if (IsEntryBlock) { + // Assert to check that SeenDefForAggregate is keyed by entire + // variables (not by fragments). Otherwise we'd incorrectly delete + // undefs sometimes, e.g. The first undef for a fragment after a def + // for the entire variable. + assert(Key.getFragment().hasValue() == false); + if (!SeenDefForAggregate.contains(Key)) { + bool IsUndef = DVI->isUndef() && IsDbgValueKind; + if (!IsUndef) { + SeenDefForAggregate.insert(Key); + } else if (DAI) { + ToBeRemoved.push_back(DVI); + RemovedFromEntry = true; + } + } + } + // Update the map if we found a new value/expression describing the // variable, or if the variable wasn't mapped already. SmallVector Values(DVI->getValues()); if (VMI == VariableMap.end() || VMI->second.first != Values || VMI->second.second != DVI->getExpression()) { - VariableMap[Key] = {Values, DVI->getExpression()}; + // Use a sentinal value (nullptr) for the DIExpression when we see a + // linked dbg.assign so that the next debug intrinsic will never match + // it. + if (IsDbgValueKind) + VariableMap[Key] = {Values, DVI->getExpression()}; + else + VariableMap[Key] = {Values, nullptr}; continue; } + + // Don't delete dbg.assign intrinsics that are linked to instructions or + // ones that have already been marked for removal. + if (!IsDbgValueKind || RemovedFromEntry) + continue; + // Found an identical mapping. Remember the instruction for later removal. ToBeRemoved.push_back(DVI); } Index: llvm/test/DebugInfo/Generic/assignment-tracking/remove-redundant.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/Generic/assignment-tracking/remove-redundant.ll @@ -0,0 +1,119 @@ +; RUN: opt -passes=redundant-dbg-inst-elim -S %s -o - \ +; RUN: | FileCheck %s --implicit-check-not="call void @llvm.dbg" + +;; Hand-written. Test how RemoveRedundantDbgInstrs interacts with dbg.assign +;; intrinsics. FileCehck directives are inline. + +define dso_local void @_Z1fv() !dbg !7 { +entry: + %test = alloca i32, align 4, !DIAssignID !20 +; CHECK: alloca +;; Forward scan: This dbg.assign for Local2 contains an undef value component +;; in the entry block and is the first debug intrinsic for the variable, but is +;; linked to an instruction so should not be deleted. +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i1 undef, metadata ![[Local2:[0-9]+]] + call void @llvm.dbg.assign(metadata i1 undef, metadata !19, metadata !DIExpression(), metadata !20, metadata i32* %test, metadata !DIExpression()), !dbg !14 + +;; Forward scan: dbg.assign for Local unlinked with undef value component, seen +;; before any non-undefs; delete it. +; CHECK-NEXT: @step() + call void @llvm.dbg.assign(metadata i32 undef, metadata !11, metadata !DIExpression(), metadata !15, metadata i32* undef, metadata !DIExpression()), !dbg !14 + call void @step() + +;; Forward scan: Repeat the previous test to check it works more than once. +; CHECK-NEXT: @step() + call void @llvm.dbg.assign(metadata i32 undef, metadata !11, metadata !DIExpression(), metadata !15, metadata i32* undef, metadata !DIExpression()), !dbg !14 + call void @step() + +;; Backward scan: Check that a dbg.value made redundant by a dbg.assign is +;; removed. +;; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 1, metadata ![[Local:[0-9]+]] +;; CHECK-NEXT: @step() + call void @llvm.dbg.value(metadata i32 0, metadata !11, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.assign(metadata i32 1, metadata !11, metadata !DIExpression(), metadata !15, metadata i32* undef, metadata !DIExpression()), !dbg !14 + call void @step() + +;; Backward scan: Check that a dbg.assign made redundant by a dbg.value is +;; removed. +;; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 3, metadata ![[Local:[0-9]+]] +;; CHECK-NEXT: @step() + call void @llvm.dbg.assign(metadata i32 2, metadata !11, metadata !DIExpression(), metadata !15, metadata i32* undef, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.value(metadata i32 3, metadata !11, metadata !DIExpression()), !dbg !14 + call void @step() + +;; Forward scan: Check that dbg.assign that looks like a dbg.value that is made +;; redundant by the previous dbg.value is removed. +;; CHECK-NEXT: @step() + call void @llvm.dbg.assign(metadata i32 3, metadata !11, metadata !DIExpression(), metadata !15, metadata i32* undef, metadata !DIExpression()), !dbg !14 + call void @step() + +;; Forward scan: Check that dbg.assign that looks like a dbg.value that is made +;; redundant by the previous dbg.assign is removed. +;; CHECK-NEXT: @step() + call void @llvm.dbg.assign(metadata i32 3, metadata !11, metadata !DIExpression(), metadata !15, metadata i32* undef, metadata !DIExpression()), !dbg !14 + call void @step() + +;; Forward scan: Now that we've seen non-undef dbg intrinsics for Local we +;; shouldn't delete undefs. +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 undef, metadata ![[Local]] + call void @llvm.dbg.assign(metadata i32 undef, metadata !11, metadata !DIExpression(), metadata !15, metadata i32* undef, metadata !DIExpression()), !dbg !14 + br label %next + +next: +;; Forward scan: Do not delete undef dbg.assigns from non-entry blocks. +; CHECK: call void @llvm.dbg.assign(metadata i32 undef, metadata ![[Local2]] +; CHECK-NEXT: @step() + call void @llvm.dbg.assign(metadata i32 undef, metadata !19, metadata !DIExpression(), metadata !21, metadata i32* %test, metadata !DIExpression()), !dbg !14 + call void @step() + +;; Forward scan: The next dbg.assign would be made redundant by this dbg.value +;; if it were not for the fact that it is linked to an instruction. Ensure it +;; isn't removed. +;; Backward scan: It (the next dbg.assign) is also followed by another for the +;; same variable - check it isn't remove (because it's linked). +; CHECK-NEXT: call void @llvm.dbg.value(metadata i32 0, metadata ![[Local2]] +; CHECK-NEXT: store +; CHECK-NEXT: store +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 0, metadata ![[Local2]] +; CHECK-NEXT: call void @llvm.dbg.assign(metadata i32 1, metadata ![[Local2]] + call void @llvm.dbg.value(metadata i32 0, metadata !19, metadata !DIExpression()), !dbg !14 + store i32 0, i32* %test, !DIAssignID !17 + store i32 1, i32* %test, !DIAssignID !16 + call void @llvm.dbg.assign(metadata i32 0, metadata !19, metadata !DIExpression(), metadata !17, metadata i32* %test, metadata !DIExpression()), !dbg !14 + call void @llvm.dbg.assign(metadata i32 1, metadata !19, metadata !DIExpression(), metadata !16, metadata i32* %test, metadata !DIExpression()), !dbg !14 + ret void, !dbg !18 +} + +; CHECK-DAG: ![[Local2]] = !DILocalVariable(name: "Local2", +; CHECK-DAG: ![[Local]] = !DILocalVariable(name: "Local", + +declare dso_local void @step() +declare void @llvm.dbg.assign(metadata, metadata, metadata, metadata, metadata, metadata) +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!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: "f", linkageName: "_Z1fv", scope: !1, file: !1, line: 1, type: !8, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11, !19} +!11 = !DILocalVariable(name: "Local", scope: !7, file: !1, line: 2, type: !12) +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = distinct !DIAssignID() +!14 = !DILocation(line: 0, scope: !7) +!15 = distinct !DIAssignID() +!16 = distinct !DIAssignID() +!17 = distinct !DIAssignID() +!18 = !DILocation(line: 6, column: 1, scope: !7) +!19 = !DILocalVariable(name: "Local2", scope: !7, file: !1, line: 2, type: !12) +!20 = distinct !DIAssignID() +!21 = distinct !DIAssignID()