Index: include/llvm/IR/DebugInfo.h =================================================================== --- include/llvm/IR/DebugInfo.h +++ include/llvm/IR/DebugInfo.h @@ -66,6 +66,8 @@ public: /// \brief Process entire module and collect debug info anchors. void processModule(const Module &M); + /// \brief Process a single instruction and collect debug info anchors. + void processInstruction(const Module &M, const Instruction &I); /// \brief Process DbgDeclareInst. void processDeclare(const Module &M, const DbgDeclareInst *DDI); @@ -80,14 +82,15 @@ private: void InitializeTypeMap(const Module &M); - void processType(DIType *DT); - void processSubprogram(DISubprogram *SP); + void processCompileUnit(DICompileUnit *CU); void processScope(DIScope *Scope); + void processSubprogram(DISubprogram *SP); + void processType(DIType *DT); bool addCompileUnit(DICompileUnit *CU); bool addGlobalVariable(DIGlobalVariableExpression *DIG); + bool addScope(DIScope *Scope); bool addSubprogram(DISubprogram *SP); bool addType(DIType *DT); - bool addScope(DIScope *Scope); public: using compile_unit_iterator = Index: lib/IR/DebugInfo.cpp =================================================================== --- lib/IR/DebugInfo.cpp +++ lib/IR/DebugInfo.cpp @@ -61,49 +61,60 @@ } void DebugInfoFinder::processModule(const Module &M) { - for (auto *CU : M.debug_compile_units()) { - addCompileUnit(CU); - for (auto DIG : CU->getGlobalVariables()) { - if (!addGlobalVariable(DIG)) - continue; - auto *GV = DIG->getVariable(); - processScope(GV->getScope()); - processType(GV->getType().resolve()); - } - for (auto *ET : CU->getEnumTypes()) - processType(ET); - for (auto *RT : CU->getRetainedTypes()) - if (auto *T = dyn_cast(RT)) - processType(T); - else - processSubprogram(cast(RT)); - for (auto *Import : CU->getImportedEntities()) { - auto *Entity = Import->getEntity().resolve(); - if (auto *T = dyn_cast(Entity)) - processType(T); - else if (auto *SP = dyn_cast(Entity)) - processSubprogram(SP); - else if (auto *NS = dyn_cast(Entity)) - processScope(NS->getScope()); - else if (auto *M = dyn_cast(Entity)) - processScope(M->getScope()); - } - } + for (auto *CU : M.debug_compile_units()) + processCompileUnit(CU); for (auto &F : M.functions()) { if (auto *SP = cast_or_null(F.getSubprogram())) processSubprogram(SP); // There could be subprograms from inlined functions referenced from // instructions only. Walk the function to find them. - for (const BasicBlock &BB : F) { - for (const Instruction &I : BB) { - if (!I.getDebugLoc()) - continue; - processLocation(M, I.getDebugLoc().get()); - } - } + for (const BasicBlock &BB : F) + for (const Instruction &I : BB) + processInstruction(M, I); } } +void DebugInfoFinder::processCompileUnit(DICompileUnit *CU) { + if (!addCompileUnit(CU)) + return; + for (auto DIG : CU->getGlobalVariables()) { + if (!addGlobalVariable(DIG)) + continue; + auto *GV = DIG->getVariable(); + processScope(GV->getScope()); + processType(GV->getType().resolve()); + } + for (auto *ET : CU->getEnumTypes()) + processType(ET); + for (auto *RT : CU->getRetainedTypes()) + if (auto *T = dyn_cast(RT)) + processType(T); + else + processSubprogram(cast(RT)); + for (auto *Import : CU->getImportedEntities()) { + auto *Entity = Import->getEntity().resolve(); + if (auto *T = dyn_cast(Entity)) + processType(T); + else if (auto *SP = dyn_cast(Entity)) + processSubprogram(SP); + else if (auto *NS = dyn_cast(Entity)) + processScope(NS->getScope()); + else if (auto *M = dyn_cast(Entity)) + processScope(M->getScope()); + } +} + +void DebugInfoFinder::processInstruction(const Module &M, + const Instruction &I) { + if (auto *DDI = dyn_cast(&I)) + processDeclare(M, DDI); + else if (auto *DVI = dyn_cast(&I)) + processValue(M, DVI); + + if (auto DbgLoc = I.getDebugLoc()) + processLocation(M, DbgLoc.get()); +} + void DebugInfoFinder::processLocation(const Module &M, const DILocation *Loc) { if (!Loc) return; @@ -165,6 +176,7 @@ if (!addSubprogram(SP)) return; processScope(SP->getScope().resolve()); + processCompileUnit(SP->getUnit()); processType(SP->getType()); for (auto *Element : SP->getTemplateParams()) { if (auto *TType = dyn_cast(Element)) { Index: lib/Transforms/Utils/CloneFunction.cpp =================================================================== --- lib/Transforms/Utils/CloneFunction.cpp +++ lib/Transforms/Utils/CloneFunction.cpp @@ -49,22 +49,15 @@ Module *TheModule = F ? F->getParent() : nullptr; // Loop over all instructions, and copy them over. - for (BasicBlock::const_iterator II = BB->begin(), IE = BB->end(); - II != IE; ++II) { - - if (DIFinder && TheModule) { - if (auto *DDI = dyn_cast(II)) - DIFinder->processDeclare(*TheModule, DDI); - else if (auto *DVI = dyn_cast(II)) - DIFinder->processValue(*TheModule, DVI); + for (BasicBlock::const_iterator II = BB->begin(), IE = BB->end(); II != IE; + ++II) { - if (auto DbgLoc = II->getDebugLoc()) - DIFinder->processLocation(*TheModule, DbgLoc.get()); - } + if (DIFinder && TheModule) + DIFinder->processInstruction(*TheModule, *II); Instruction *NewInst = II->clone(); if (II->hasName()) - NewInst->setName(II->getName()+NameSuffix); + NewInst->setName(II->getName() + NameSuffix); NewBB->getInstList().push_back(NewInst); VMap[&*II] = NewInst; // Add instruction map to value. @@ -76,7 +69,7 @@ hasDynamicAllocas = true; } } - + if (CodeInfo) { CodeInfo->ContainsCalls |= hasCalls; CodeInfo->ContainsDynamicAllocas |= hasDynamicAllocas; @@ -175,7 +168,7 @@ // Create a new basic block and copy instructions into it! BasicBlock *CBB = CloneBasicBlock(&BB, VMap, NameSuffix, NewFunc, CodeInfo, - SP ? &DIFinder : nullptr); + ModuleLevelChanges ? &DIFinder : nullptr); // Add basic block mapping. VMap[&BB] = CBB; @@ -197,15 +190,15 @@ Returns.push_back(RI); } - for (DISubprogram *ISP : DIFinder.subprograms()) { - if (ISP != SP) { + for (DISubprogram *ISP : DIFinder.subprograms()) + if (ISP != SP) VMap.MD()[ISP].reset(ISP); - } - } - for (auto *Type : DIFinder.types()) { + for (DICompileUnit *CU : DIFinder.compile_units()) + VMap.MD()[CU].reset(CU); + + for (DIType *Type : DIFinder.types()) VMap.MD()[Type].reset(Type); - } // Loop over all of the instructions in the function, fixing up operand // references as we go. This uses VMap to do all the hard work. Index: lib/Transforms/Utils/CloneModule.cpp =================================================================== --- lib/Transforms/Utils/CloneModule.cpp +++ lib/Transforms/Utils/CloneModule.cpp @@ -50,6 +50,7 @@ // First off, we need to create the new module. std::unique_ptr New = llvm::make_unique(M.getModuleIdentifier(), M.getContext()); + New->setSourceFileName(M.getSourceFileName()); New->setDataLayout(M.getDataLayout()); New->setTargetTriple(M.getTargetTriple()); New->setModuleInlineAsm(M.getModuleInlineAsm()); Index: test/DebugInfo/X86/clone-module-2.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/clone-module-2.ll @@ -0,0 +1,41 @@ +; RUN: opt -run-twice -verify -S -o - %s | FileCheck %s + +; Derived from the following C-snippet +; +; static int eliminated(int j); +; __attribute__((nodebug)) int nodebug(int k) { return eliminated(k); } +; __attribute__((always_inline)) static int eliminated(int j) { return j * 2; } +; +; compiled with `clang -O1 -g1 -emit-llvm -S` + +; CHECK: DICompileUnit +; CHECK-NOT: DICompileUnit + +source_filename = "clone-module.c" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx" + +; Function Attrs: norecurse nounwind readnone ssp uwtable +define i32 @nodebug(i32 %k) local_unnamed_addr #0 { +entry: + %mul.i = shl nsw i32 %k, 1, !dbg !8 + ret i32 %mul.i +} + +attributes #0 = { norecurse nounwind readnone ssp uwtable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 (https://git.llvm.org/git/clang.git/ 195459d046e795f5952f7d2121e505eeb59c5574) (https://git.llvm.org/git/llvm.git/ e9dc5b5ade57869d1a443c568c6cf556ccf3b7af)", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, enums: !2) +!1 = !DIFile(filename: "test.c", directory: "/Volumes/Data/llvm/build/obj") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 7.0.0 (https://git.llvm.org/git/clang.git/ 195459d046e795f5952f7d2121e505eeb59c5574) (https://git.llvm.org/git/llvm.git/ e9dc5b5ade57869d1a443c568c6cf556ccf3b7af)"} +!8 = !DILocation(line: 3, column: 72, scope: !9) +!9 = distinct !DISubprogram(name: "eliminated", scope: !1, file: !1, line: 3, type: !10, isLocal: true, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2) +!10 = !DISubroutineType(types: !2) Index: test/DebugInfo/X86/clone-module.ll =================================================================== --- /dev/null +++ test/DebugInfo/X86/clone-module.ll @@ -0,0 +1,64 @@ +; RUN: opt -run-twice -verify -S -o - %s | FileCheck %s + +; Derived from the following C-snippet +; +; int inlined(int j); +; __attribute__((nodebug)) int nodebug(int k) { return inlined(k); } +; __attribute__((always_inline)) int inlined(int j) { return j * 2; } +; +; compiled with `clang -O1 -g3 -emit-llvm -S` by removing +; +; call void @llvm.dbg.value(metadata i32 %k, metadata !8, metadata !DIExpression()), !dbg !14 +; +; line from @nodebug function. + +; CHECK: DISubprogram +; CHECK-NOT: DISubprogram + +source_filename = "clone-module.c" +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx" + +; Function Attrs: nounwind readnone ssp uwtable +define i32 @nodebug(i32 %k) local_unnamed_addr #0 { +entry: + %mul.i = shl nsw i32 %k, 1, !dbg !15 + ret i32 %mul.i +} + +; Function Attrs: alwaysinline nounwind readnone ssp uwtable +define i32 @inlined(i32 %j) local_unnamed_addr #1 !dbg !9 { +entry: + call void @llvm.dbg.value(metadata i32 %j, metadata !8, metadata !DIExpression()), !dbg !14 + %mul = shl nsw i32 %j, 1, !dbg !15 + ret i32 %mul, !dbg !16 +} + +; Function Attrs: nounwind readnone speculatable +declare void @llvm.dbg.value(metadata, metadata, metadata) #2 + +attributes #0 = { nounwind readnone ssp uwtable } +attributes #1 = { alwaysinline nounwind readnone ssp uwtable } +attributes #2 = { nounwind readnone speculatable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 7.0.0 (https://git.llvm.org/git/clang.git/ 195459d046e795f5952f7d2121e505eeb59c5574) (https://git.llvm.org/git/llvm.git/ 69ec7d5667e9928db8435bfbee0da151c85a91c9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "clone-module.c", directory: "/somewhere") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 7.0.0 (https://git.llvm.org/git/clang.git/ 195459d046e795f5952f7d2121e505eeb59c5574) (https://git.llvm.org/git/llvm.git/ 69ec7d5667e9928db8435bfbee0da151c85a91c9)"} +!8 = !DILocalVariable(name: "j", arg: 1, scope: !9, file: !1, line: 3, type: !12) +!9 = distinct !DISubprogram(name: "inlined", scope: !1, file: !1, line: 3, type: !10, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !13) +!10 = !DISubroutineType(types: !11) +!11 = !{!12, !12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{!8} +!14 = !DILocation(line: 3, column: 48, scope: !9) +!15 = !DILocation(line: 3, column: 62, scope: !9) +!16 = !DILocation(line: 3, column: 53, scope: !9) Index: tools/opt/opt.cpp =================================================================== --- tools/opt/opt.cpp +++ tools/opt/opt.cpp @@ -730,7 +730,7 @@ // a stream to write to them. Note that llc does something similar and it // may be worth to abstract this out in the future. SmallVector Buffer; - SmallVector CompileTwiceBuffer; + SmallVector FirstRunBuffer; std::unique_ptr BOS; raw_ostream *OS = nullptr; @@ -759,28 +759,30 @@ // Before executing passes, print the final values of the LLVM options. cl::PrintOptionValues(); - // If requested, run all passes again with the same pass manager to catch - // bugs caused by persistent state in the passes - if (RunTwice) { + if (!RunTwice) { + // Now that we have all of the passes ready, run them. + Passes.run(*M); + } else { + // If requested, run all passes twice with the same pass manager to catch + // bugs caused by persistent state in the passes. std::unique_ptr M2(CloneModule(*M)); - Passes.run(*M2); - CompileTwiceBuffer = Buffer; + // Run all passes on the original module first, so the second run processes + // the clone to catch CloneModule bugs. + Passes.run(*M); + FirstRunBuffer = Buffer; Buffer.clear(); - } - // Now that we have all of the passes ready, run them. - Passes.run(*M); + Passes.run(*M2); - // Compare the two outputs and make sure they're the same - if (RunTwice) { + // Compare the two outputs and make sure they're the same assert(Out); - if (Buffer.size() != CompileTwiceBuffer.size() || - (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) != - 0)) { - errs() << "Running the pass manager twice changed the output.\n" - "Writing the result of the second run to the specified output.\n" - "To generate the one-run comparison binary, just run without\n" - "the compile-twice option\n"; + if (Buffer.size() != FirstRunBuffer.size() || + (memcmp(Buffer.data(), FirstRunBuffer.data(), Buffer.size()) != 0)) { + errs() + << "Running the pass manager twice changed the output.\n" + "Writing the result of the second run to the specified output.\n" + "To generate the one-run comparison binary, just run without\n" + "the compile-twice option\n"; Out->os() << BOS->str(); Out->keep(); if (OptRemarkFile)