Index: include/llvm/Transforms/Utils/Local.h =================================================================== --- include/llvm/Transforms/Utils/Local.h +++ include/llvm/Transforms/Utils/Local.h @@ -310,6 +310,11 @@ void replaceDbgValueForAlloca(AllocaInst *AI, Value *NewAllocaAddress, DIBuilder &Builder, int Offset = 0); +/// Assuming the instruction \p I is going to be deleted, attempt to salvage any +/// dbg.value intrinsics referring to \p I by rewriting its effect into a +/// DIExpression. +void salvageDebugInfo(Instruction &I); + /// Remove all instructions from a basic block other than it's terminator /// and any present EH pad instructions. unsigned removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB); Index: lib/Transforms/InstCombine/InstCombineInternal.h =================================================================== --- lib/Transforms/InstCombine/InstCombineInternal.h +++ lib/Transforms/InstCombine/InstCombineInternal.h @@ -28,6 +28,9 @@ #include "llvm/IR/PatternMatch.h" #include "llvm/Pass.h" #include "llvm/Transforms/InstCombine/InstCombineWorklist.h" +#include "llvm/Transforms/Utils/Local.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/IR/DIBuilder.h" #define DEBUG_TYPE "instcombine" @@ -470,8 +473,9 @@ /// methods should return the value returned by this function. Instruction *eraseInstFromFunction(Instruction &I) { DEBUG(dbgs() << "IC: ERASE " << I << '\n'); - assert(I.use_empty() && "Cannot erase instruction that is used!"); + salvageDebugInfo(I); + // Make sure that we reprocess all operands now that we reduced their // use counts. if (I.getNumOperands() < 8) { Index: lib/Transforms/Utils/Local.cpp =================================================================== --- lib/Transforms/Utils/Local.cpp +++ lib/Transforms/Utils/Local.cpp @@ -1357,6 +1357,53 @@ } } +void llvm::salvageDebugInfo(Instruction &I) { + auto MDWrap = [&](Value *V) { + return MetadataAsValue::get(I.getContext(), ValueAsMetadata::get(V)); + }; + auto &M = *I.getModule(); + SmallVector DbgValues; + if (auto *BitCast = dyn_cast(&I)) { + // Bitcasts are entirely irrelevant for debug info. Rewrite the dbg.value to + // use the cast's source. + findDbgValues(DbgValues, &I); + for (auto *DVI : DbgValues) { + DVI->setOperand(0, MDWrap(I.getOperand(0))); + DEBUG(dbgs() << "SALVAGE: " << *DVI << '\n'); + } + } else if (auto *GEP = dyn_cast(&I)) { + findDbgValues(DbgValues, &I); + for (auto *DVI : DbgValues) { + unsigned BitWidth = + M.getDataLayout().getPointerSizeInBits(GEP->getPointerAddressSpace()); + APInt Offset(BitWidth, 0); + + if (GEP->accumulateConstantOffset(M.getDataLayout(), Offset)) { + if (!Offset.isIntN(64)) + return; + auto *DIExpr = DVI->getExpression(); + DIBuilder DIB(M, /*AllowUnresolved*/ false); + DIExpr = BuildReplacementDIExpr(DIB, DIExpr, /*Deref*/ false, + Offset.getLimitedValue()); + DVI->setOperand(0, MDWrap(I.getOperand(0))); + DVI->setOperand(3, MetadataAsValue::get(I.getContext(), DIExpr)); + DEBUG(dbgs() << "SALVAGE: " << *DVI << '\n'); + } + } + } else if (auto *Load = dyn_cast(&I)) { + SmallVector DbgValues; + findDbgValues(DbgValues, &I); + for (auto *DVI : DbgValues) { + auto *DIExpr = DVI->getExpression(); + DIBuilder DIB(M, /*AllowUnresolved*/ false); + DIExpr = BuildReplacementDIExpr(DIB, DIExpr, /*Deref*/ true, 0); + DVI->setOperand(0, MDWrap(I.getOperand(0))); + DVI->setOperand(3, MetadataAsValue::get(I.getContext(), DIExpr)); + DEBUG(dbgs() << "SALVAGE: " << *DVI << '\n'); + } + } +} + unsigned llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) { unsigned NumDeadInst = 0; // Delete the instructions backwards, as it has a reduced likelihood of Index: test/Transforms/InstCombine/debuginfo-dce.ll =================================================================== --- /dev/null +++ test/Transforms/InstCombine/debuginfo-dce.ll @@ -0,0 +1,114 @@ +; RUN: opt -instcombine %s -S -o - | FileCheck %s +; struct entry { +; struct entry *next; +; }; +; void scan(struct entry *queue, struct entry *end) +; { +; struct entry *entry; +; for (entry = (struct entry *)((char *)(queue->next) - 8); +; &entry->next == end; +; entry = (struct entry *)((char *)(entry->next) - 8)) { +; } +; } + +source_filename = "test.c" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.12.0" + +%struct.entry = type { %struct.entry* } + +; Function Attrs: nounwind ssp uwtable +define void @scan(%struct.entry* %queue, %struct.entry* %end) local_unnamed_addr #0 !dbg !14 { +entry: + call void @llvm.dbg.value(metadata %struct.entry* %queue, i64 0, metadata !18, metadata !21), !dbg !22 + call void @llvm.dbg.value(metadata %struct.entry* %end, i64 0, metadata !19, metadata !21), !dbg !23 + %next = getelementptr inbounds %struct.entry, %struct.entry* %queue, i32 0, i32 0, !dbg !24 + %0 = load %struct.entry*, %struct.entry** %next, align 8, !dbg !24 + %1 = bitcast %struct.entry* %0 to i8*, !dbg !31 + %add.ptr = getelementptr inbounds i8, i8* %1, i64 -8, !dbg !32 + %2 = bitcast i8* %add.ptr to %struct.entry*, !dbg !33 + call void @llvm.dbg.value(metadata %struct.entry* %2, i64 0, metadata !20, metadata !21), !dbg !34 + br label %for.cond, !dbg !35 + +for.cond: ; preds = %for.inc, %entry + %entry1.0 = phi %struct.entry* [ %2, %entry ], [ %6, %for.inc ] + call void @llvm.dbg.value(metadata %struct.entry* %entry1.0, i64 0, metadata !20, metadata !21), !dbg !34 + %next2 = getelementptr inbounds %struct.entry, %struct.entry* %entry1.0, i32 0, i32 0, !dbg !36 + %3 = bitcast %struct.entry* %end to %struct.entry**, !dbg !38 + %cmp = icmp eq %struct.entry** %next2, %3, !dbg !39 + br i1 %cmp, label %for.inc, label %for.end, !dbg !40 + +for.inc: ; preds = %for.cond + %4 = load %struct.entry*, %struct.entry** %next2, align 8, !dbg !41 + %5 = bitcast %struct.entry* %4 to i8*, !dbg !42 + %add.ptr4 = getelementptr inbounds i8, i8* %5, i64 -8, !dbg !43 + %6 = bitcast i8* %add.ptr4 to %struct.entry*, !dbg !44 + call void @llvm.dbg.value(metadata %struct.entry* %6, i64 0, metadata !20, metadata !21), !dbg !34 +; Verify that the eliminated instructions (bitcast, gep, load) are salvaged into +; a DIExpression. +; +; CHECK: for.inc: +; CHECK-NEXT: %2 = bitcast i8* %entry1.0.in to i8** +; CHECK-NEXT: call void @llvm.dbg.value(metadata i8** %2, i64 0, +; CHECK-SAME: metadata ![[EXPR:[0-9]+]]) +; CHECK: ![[EXPR]] = !DIExpression(DW_OP_deref, DW_OP_minus, 8) + br label %for.cond, !dbg !45, !llvm.loop !46 + +for.end: ; preds = %for.cond + ret void, !dbg !48 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1 + +attributes #0 = { nounwind ssp uwtable } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!10, !11, !12} +!llvm.ident = !{!13} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 5.0.0 (trunk 297628) (llvm/trunk 297643)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{} +!3 = !{!4, !8} +!4 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !5, size: 64) +!5 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "entry", file: !1, line: 1, size: 64, elements: !6) +!6 = !{!7} +!7 = !DIDerivedType(tag: DW_TAG_member, name: "next", scope: !5, file: !1, line: 2, baseType: !4, size: 64) +!8 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64) +!9 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!10 = !{i32 2, !"Dwarf Version", i32 4} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"PIC Level", i32 2} +!13 = !{!"clang version 5.0.0 (trunk 297628) (llvm/trunk 297643)"} +!14 = distinct !DISubprogram(name: "scan", scope: !1, file: !1, line: 4, type: !15, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !17) +!15 = !DISubroutineType(types: !16) +!16 = !{null, !4, !4} +!17 = !{!18, !19, !20} +!18 = !DILocalVariable(name: "queue", arg: 1, scope: !14, file: !1, line: 4, type: !4) +!19 = !DILocalVariable(name: "end", arg: 2, scope: !14, file: !1, line: 4, type: !4) +!20 = !DILocalVariable(name: "entry", scope: !14, file: !1, line: 6, type: !4) +!21 = !DIExpression() +!22 = !DILocation(line: 4, column: 25, scope: !14) +!23 = !DILocation(line: 4, column: 46, scope: !14) +!24 = !DILocation(line: 7, column: 49, scope: !25) +!25 = distinct !DILexicalBlock(scope: !14, file: !1, line: 7, column: 3) +!31 = !DILocation(line: 7, column: 33, scope: !25) +!32 = !DILocation(line: 7, column: 55, scope: !25) +!33 = !DILocation(line: 7, column: 16, scope: !25) +!34 = !DILocation(line: 6, column: 17, scope: !14) +!35 = !DILocation(line: 7, column: 8, scope: !25) +!36 = !DILocation(line: 8, column: 16, scope: !37) +!37 = distinct !DILexicalBlock(scope: !25, file: !1, line: 7, column: 3) +!38 = !DILocation(line: 8, column: 24, scope: !37) +!39 = !DILocation(line: 8, column: 21, scope: !37) +!40 = !DILocation(line: 7, column: 3, scope: !25) +!41 = !DILocation(line: 9, column: 49, scope: !37) +!42 = !DILocation(line: 9, column: 33, scope: !37) +!43 = !DILocation(line: 9, column: 55, scope: !37) +!44 = !DILocation(line: 9, column: 16, scope: !37) +!45 = !DILocation(line: 7, column: 3, scope: !37) +!46 = distinct !{!46, !40, !47} +!47 = !DILocation(line: 10, column: 3, scope: !25) +!48 = !DILocation(line: 11, column: 1, scope: !14)