Index: include/llvm/IR/IRPrintingPasses.h =================================================================== --- include/llvm/IR/IRPrintingPasses.h +++ include/llvm/IR/IRPrintingPasses.h @@ -23,6 +23,7 @@ #include namespace llvm { +class Pass; class BasicBlockPass; class Function; class FunctionPass; @@ -54,6 +55,9 @@ /// non-printable characters in it. void printLLVMNameWithoutPrefix(raw_ostream &OS, StringRef Name); +/// Return true if a pass is for IR printing. +bool isIRPrintingPass(Pass *P); + /// Pass for printing a Module as LLVM's text IR assembly. /// /// Note: This pass is for use with the new pass manager. Use the create...Pass Index: lib/IR/IRPrintingPasses.cpp =================================================================== --- lib/IR/IRPrintingPasses.cpp +++ lib/IR/IRPrintingPasses.cpp @@ -150,3 +150,11 @@ const std::string &Banner) { return new PrintBasicBlockPass(OS, Banner); } + +bool llvm::isIRPrintingPass(Pass *P) { + const char *PID = (const char*)P->getPassID(); + + return (PID == &PrintModulePassWrapper::ID) + || (PID == &PrintFunctionPassWrapper::ID) + || (PID == &PrintBasicBlockPass::ID); +} Index: test/DebugInfo/debugify-each.ll =================================================================== --- test/DebugInfo/debugify-each.ll +++ test/DebugInfo/debugify-each.ll @@ -0,0 +1,87 @@ +; RUN: opt -debugify-each -O1 -S -print-after=called-value-propagation -o - < %s 2> %t +; FileCheck < %t %s + +; RUN: opt -debugify-each -O1 -S -print-after=mem2reg -o - < %s 2> %t +; FileCheck < %t %s -check-prefix=CHECK-FUNC + +; RUN: opt -debugify-each -debugify -S -o - < %s 2>&1 | \ +; RUN: FileCheck %s -check-prefix=CHECK-REPEAT + +; RUN: opt -debugify-each -check-debugify -S -o - < %s 2>&1 | \ +; RUN: FileCheck %s -check-prefix=CHECK-REPEAT-CHECK + +; RUN: opt -debugify-each -strip -S -o - < %s | \ +; RUN: FileCheck %s -check-prefix=CHECK-FAIL + +; CHECK-LABEL: define void @foo +define void @foo() { +; CHECK: ret void, !dbg ![[RET1:.*]] +; CHECK-FUNC: ret void, !dbg + ret void +} + +; CHECK-LABEL: define i32 @bar +define i32 @bar() { +; CHECK: call void @foo(), !dbg ![[CALL1:.*]] +; CHECK-FUNC: call void @foo(), !dbg + call void @foo() + +; CHECK-NOT: add + %sum = add i32 0, 1 + +; CHECK: ret i32 0, !dbg ![[RET2:.*]] +; CHECK-FUNC: ret i32 0, !dbg + ret i32 0 +} + +; CHECK-LABEL: define weak_odr zeroext i1 @baz +define weak_odr zeroext i1 @baz() { +; CHECK-NOT: !dbg + ret i1 false +} + +; CHECK-DAG: !llvm.dbg.cu = !{![[CU:.*]]} +; CHECK-DAG: !llvm.debugify = !{![[NUM_INSTS:.*]], ![[NUM_VARS:.*]]} + +; CHECK-DAG: ![[CU]] = distinct !DICompileUnit(language: DW_LANG_C, file: {{.*}}, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: {{.*}}) +; CHECK-DAG: !DIFile(filename: "", directory: "/") +; CHECK-DAG: distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: {{.*}}, line: 1, type: {{.*}}, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: {{.*}}, variables: {{.*}}) +; CHECK-DAG: distinct !DISubprogram(name: "bar", linkageName: "bar", scope: null, file: {{.*}}, line: 2, type: {{.*}}, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: true, unit: {{.*}}, variables: {{.*}}) + +; --- DILocations +; CHECK-DAG: ![[RET1]] = !DILocation(line: 1, column: 1 +; CHECK-DAG: ![[CALL1]] = !DILocation(line: 2, column: 1 +; CHECK-DAG: ![[RET2]] = !DILocation(line: 3, column: 1 + +; --- Metadata counts +; CHECK-DAG: ![[NUM_INSTS]] = !{i32 3} +; CHECK-DAG: ![[NUM_VARS]] = !{i32 0} + +; --- Debugify-each repeat case +; CHECK-REPEAT: ModuleDebugify: Skipping module with debug info +; CHECK-REPEAT: CheckModuleDebugify: PASS +; CHECK-REPEAT: CheckFunctionDebugify: PASS +; CHECK-REPEAT: CheckFunctionDebugify: PASS +; CHECK-REPEAT: CheckFunctionDebugify: PASS + +; --- Debugify-each repeat check case +; CHECK-REPEAT-CHECK: CheckModuleDebugify: PASS +; CHECK-REPEAT-CHECK: CheckModuleDebugify: PASS +; CHECK-REPEAT-CHECK: CheckFunctionDebugify: PASS +; CHECK-REPEAT-CHECK: CheckFunctionDebugify: PASS +; CHECK-REPEAT-CHECK: CheckFunctionDebugify: PASS + +; --- Debugify-each failure case +; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function foo -- ret void +; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar -- call void @foo() +; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar -- {{.*}} add i32 0, 1 +; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar -- ret i32 0 +; CHECK-FAIL: WARNING: Missing line 1 +; CHECK-FAIL: WARNING: Missing line 2 +; CHECK-FAIL: WARNING: Missing line 3 +; CHECK-FAIL: WARNING: Missing line 4 +; CHECK-FAIL: ERROR: Missing variable 1 +; CHECK-FAIL: CheckModuleDebugify: FAIL +; CHECK-FAIL: CheckFunctionDebugify: PASS +; CHECK-FAIL: CheckFunctionDebugify: PASS +; CHECK-FAIL: CheckFunctionDebugify: PASS Index: test/DebugInfo/debugify.ll =================================================================== --- test/DebugInfo/debugify.ll +++ test/DebugInfo/debugify.ll @@ -7,11 +7,11 @@ ; RUN: FileCheck %s -check-prefix=CHECK-REPEAT ; RUN: opt -debugify -check-debugify -S -o - < %s | \ -; RUN: FileCheck %s -implicit-check-not="CheckDebugify: FAIL" +; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" ; RUN: opt -passes=debugify,check-debugify -S -o - < %s | \ -; RUN: FileCheck %s -implicit-check-not="CheckDebugify: FAIL" +; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" ; RUN: opt -enable-debugify -passes=verify -S -o - < %s | \ -; RUN: FileCheck %s -implicit-check-not="CheckDebugify: FAIL" +; RUN: FileCheck %s -implicit-check-not="CheckModuleDebugify: FAIL" ; RUN: opt -debugify -strip -check-debugify -S -o - < %s | \ ; RUN: FileCheck %s -check-prefix=CHECK-FAIL @@ -68,18 +68,18 @@ ; CHECK-DAG: ![[NUM_VARS]] = !{i32 1} ; --- Repeat case -; CHECK-REPEAT: Debugify: Skipping module with debug info +; CHECK-REPEAT: ModuleDebugify: Skipping module with debug info ; --- Failure case -; CHECK-FAIL: ERROR: Instruction with empty DebugLoc -- ret void -; CHECK-FAIL: ERROR: Instruction with empty DebugLoc -- call void @foo() -; CHECK-FAIL: ERROR: Instruction with empty DebugLoc -- {{.*}} add i32 0, 1 -; CHECK-FAIL: ERROR: Instruction with empty DebugLoc -- ret i32 0 +; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function foo -- ret void +; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar -- call void @foo() +; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar -- {{.*}} add i32 0, 1 +; CHECK-FAIL: ERROR: Instruction with empty DebugLoc in function bar -- ret i32 0 ; CHECK-FAIL: WARNING: Missing line 1 ; CHECK-FAIL: WARNING: Missing line 2 ; CHECK-FAIL: WARNING: Missing line 3 ; CHECK-FAIL: WARNING: Missing line 4 ; CHECK-FAIL: ERROR: Missing variable 1 -; CHECK-FAIL: CheckDebugify: FAIL +; CHECK-FAIL: CheckModuleDebugify: FAIL -; PASS: CheckDebugify: PASS +; PASS: CheckModuleDebugify: PASS Index: test/Transforms/Mem2Reg/PromoteMemToRegister.ll =================================================================== --- test/Transforms/Mem2Reg/PromoteMemToRegister.ll +++ test/Transforms/Mem2Reg/PromoteMemToRegister.ll @@ -2,7 +2,7 @@ ; RUN: opt < %s -debugify -mem2reg -check-debugify -S | FileCheck %s ; CHECK-NOT: alloca -; CHECK: CheckDebugify: PASS +; CHECK: CheckModuleDebugify: PASS define double @testfunc(i32 %i, double %j) { %I = alloca i32 ; [#uses=4] Index: tools/opt/Debugify.cpp =================================================================== --- tools/opt/Debugify.cpp +++ tools/opt/Debugify.cpp @@ -39,10 +39,12 @@ return F.isDeclaration() || !F.hasExactDefinition(); } -bool applyDebugifyMetadata(Module &M) { +bool applyDebugifyMetadata(Module &M, + iterator_range Functions, + std::string Banner) { // Skip modules with debug info. if (M.getNamedMetadata("llvm.dbg.cu")) { - errs() << "Debugify: Skipping module with debug info\n"; + errs() << Banner << "Skipping module with debug info\n"; return false; } @@ -51,7 +53,7 @@ // Get a DIType which corresponds to Ty. DenseMap TypeCache; - auto getCachedDIType = [&](Type *Ty) -> DIType * { + auto getCachedDIType = [&] (Type *Ty) -> DIType * { uint64_t Size = Ty->isSized() ? M.getDataLayout().getTypeAllocSizeInBits(Ty) : 0; DIType *&DTy = TypeCache[Size]; @@ -65,12 +67,11 @@ unsigned NextLine = 1; unsigned NextVar = 1; auto File = DIB.createFile(M.getName(), "/"); - auto CU = - DIB.createCompileUnit(dwarf::DW_LANG_C, DIB.createFile(M.getName(), "/"), + auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C, File, "debugify", /*isOptimized=*/true, "", 0); // Visit each instruction. - for (Function &F : M) { + for (Function &F : Functions) { if (isFunctionSkipped(F)) continue; @@ -118,54 +119,60 @@ }; addDebugifyOperand(NextLine - 1); // Original number of lines. addDebugifyOperand(NextVar - 1); // Original number of variables. + assert(NMD->getNumOperands() == 2 && + "llvm.debugify should have exactly 2 operands!"); return true; } -void checkDebugifyMetadata(Module &M) { +bool checkDebugifyMetadata(Module &M, + iterator_range Functions, + std::string Banner, + bool Strip) { // Skip modules without debugify metadata. NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify"); - if (!NMD) - return; + if (!NMD) { + errs() << Banner << "Skipping module without debugify metadata\n"; + return false; + } - auto getDebugifyOperand = [&](unsigned Idx) -> unsigned { + auto getDebugifyOperand = [&] (unsigned Idx) -> unsigned { return mdconst::extract(NMD->getOperand(Idx)->getOperand(0)) ->getZExtValue(); }; + assert(NMD->getNumOperands() == 2 && + "llvm.debugify should have exactly 2 operands!"); unsigned OriginalNumLines = getDebugifyOperand(0); unsigned OriginalNumVars = getDebugifyOperand(1); bool HasErrors = false; - // Find missing lines. BitVector MissingLines{OriginalNumLines, true}; - for (Function &F : M) { + BitVector MissingVars{OriginalNumVars, true}; + for (Function &F : Functions) { if (isFunctionSkipped(F)) continue; + // Find missing lines. for (Instruction &I : instructions(F)) { if (isa(&I)) continue; auto DL = I.getDebugLoc(); - if (DL) { + if (DL && MissingLines.size() > (DL.getLine() - 1)) { MissingLines.reset(DL.getLine() - 1); continue; } - outs() << "ERROR: Instruction with empty DebugLoc -- "; + if (DL) + outs() << "ERROR: Instruction with invalid DebugLoc "; + else + outs() << "ERROR: Instruction with empty DebugLoc "; + outs() << " in function " << F.getName() << " -- "; I.print(outs()); outs() << "\n"; HasErrors = true; } - } - for (unsigned Idx : MissingLines.set_bits()) - outs() << "WARNING: Missing line " << Idx + 1 << "\n"; - - // Find missing variables. - BitVector MissingVars{OriginalNumVars, true}; - for (Function &F : M) { - if (isFunctionSkipped(F)) - continue; + // Find missing variables. for (Instruction &I : instructions(F)) { auto *DVI = dyn_cast(&I); if (!DVI) @@ -173,22 +180,63 @@ unsigned Var = ~0U; (void)to_integer(DVI->getVariable()->getName(), Var, 10); - assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable"); + assert(Var <= MissingVars.size() && "Unexpected name for DILocalVariable"); MissingVars.reset(Var - 1); } } + + // Print the results. + for (unsigned Idx : MissingLines.set_bits()) + outs() << "WARNING: Missing line " << Idx + 1 << "\n"; + for (unsigned Idx : MissingVars.set_bits()) outs() << "ERROR: Missing variable " << Idx + 1 << "\n"; HasErrors |= MissingVars.count() > 0; - outs() << "CheckDebugify: " << (HasErrors ? "FAIL" : "PASS") << "\n"; + outs() << Banner << (HasErrors ? "FAIL" : "PASS") << '\n'; + if (HasErrors) { + outs() << "Module IR Dump\n"; + M.print(outs(), nullptr, false); + } + + // Strip the Debugify Metadata if required. + if (Strip) { + StripDebugInfo(M); + M.eraseNamedMetadata(NMD); + if (Function *F = M.getFunction("llvm.dbg.value")) + F->eraseFromParent(); + return true; + } + + return false; } -/// Attach synthetic debug info to everything. -struct DebugifyPass : public ModulePass { - bool runOnModule(Module &M) override { return applyDebugifyMetadata(M); } +/// ModulePass for attaching synthetic debug info to everything, used with the +/// legacy module pass manager. +struct DebugifyModulePass : public ModulePass { + bool runOnModule(Module &M) override { + return applyDebugifyMetadata(M, M.functions(), "ModuleDebugify: "); + } + + DebugifyModulePass() : ModulePass(ID) {} + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + static char ID; // Pass identification. +}; + +/// FunctionPass for attaching synthetic debug info to everything, used with the +/// legacy module pass manager. +struct DebugifyFunctionPass : public FunctionPass { + bool runOnFunction(Function &F) override { + return applyDebugifyMetadata(*(F.getParent()), + make_range(F.getIterator(), std::next(F.getIterator())), + "FunctionDebugify: "); + } - DebugifyPass() : ModulePass(ID) {} + DebugifyFunctionPass() : FunctionPass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); @@ -197,43 +245,87 @@ static char ID; // Pass identification. }; -/// Check debug info inserted by -debugify for completeness. -struct CheckDebugifyPass : public ModulePass { +/// ModulePass for checking debug info inserted by -debugify, used with the +/// legacy module pass manager. +struct CheckDebugifyModulePass : public ModulePass { bool runOnModule(Module &M) override { - checkDebugifyMetadata(M); - return false; + return checkDebugifyMetadata(M, + M.functions(), + "CheckModuleDebugify: ", + Strip); } - CheckDebugifyPass() : ModulePass(ID) {} + CheckDebugifyModulePass(bool Strip = false) : ModulePass(ID), Strip(Strip) {} + + static char ID; // Pass identification. + +private: + bool Strip; +}; + +/// FunctionPass for checking debug info inserted by -debugify-function, used +/// with the legacy module pass manager. +struct CheckDebugifyFunctionPass : public FunctionPass { + bool runOnFunction(Function &F) override { + return checkDebugifyMetadata(*(F.getParent()), + make_range(F.getIterator(), std::next(F.getIterator())), + "CheckFunctionDebugify: ", + Strip); + } + + CheckDebugifyFunctionPass(bool Strip = false) : FunctionPass(ID), Strip(Strip) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); } static char ID; // Pass identification. + +private: + bool Strip; }; } // end anonymous namespace -ModulePass *createDebugifyPass() { return new DebugifyPass(); } +ModulePass *createDebugifyModulePass() { + return new DebugifyModulePass(); +} + +FunctionPass *createDebugifyFunctionPass() { + return new DebugifyFunctionPass(); +} PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) { - applyDebugifyMetadata(M); + applyDebugifyMetadata(M, M.functions(), "ModuleDebugify: "); return PreservedAnalyses::all(); } -ModulePass *createCheckDebugifyPass() { return new CheckDebugifyPass(); } +ModulePass *createCheckDebugifyModulePass(bool Strip) { + return new CheckDebugifyModulePass(Strip); +} + +FunctionPass *createCheckDebugifyFunctionPass(bool Strip) { + return new CheckDebugifyFunctionPass(Strip); +} PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M, ModuleAnalysisManager &) { - checkDebugifyMetadata(M); + checkDebugifyMetadata(M, M.functions(), "CheckModuleDebugify: ", false); return PreservedAnalyses::all(); } -char DebugifyPass::ID = 0; -static RegisterPass X("debugify", +char DebugifyModulePass::ID = 0; +static RegisterPass DM("debugify", "Attach debug info to everything"); -char CheckDebugifyPass::ID = 0; -static RegisterPass Y("check-debugify", +char CheckDebugifyModulePass::ID = 0; +static RegisterPass CDM("check-debugify", "Check debug info from -debugify"); + +char DebugifyFunctionPass::ID = 0; +static RegisterPass DF("debugify-function", + "Attach debug info to a function"); + +char CheckDebugifyFunctionPass::ID = 0; +static RegisterPass CDF("check-debugify-function", + "Check debug info from -debugify-function"); Index: tools/opt/PassPrinters.h =================================================================== --- tools/opt/PassPrinters.h +++ tools/opt/PassPrinters.h @@ -49,13 +49,15 @@ } // end namespace llvm -llvm::ModulePass *createDebugifyPass(); +llvm::ModulePass *createDebugifyModulePass(); +llvm::FunctionPass *createDebugifyFunctionPass(); struct NewPMDebugifyPass : public llvm::PassInfoMixin { llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); }; -llvm::ModulePass *createCheckDebugifyPass(); +llvm::ModulePass *createCheckDebugifyModulePass(bool Strip = false); +llvm::FunctionPass *createCheckDebugifyFunctionPass(bool Strip = false); struct NewPMCheckDebugifyPass : public llvm::PassInfoMixin { Index: tools/opt/opt.cpp =================================================================== --- tools/opt/opt.cpp +++ tools/opt/opt.cpp @@ -207,6 +207,11 @@ cl::desc( "Start the pipeline with debugify and end it with check-debugify")); +static cl::opt DebugifyEach( + "debugify-each", + cl::desc( + "Start each pass with debugify and end it with check-debugify")); + static cl::opt PrintBreakpoints("print-breakpoints-for-testing", cl::desc("Print select breakpoints location for testing")); @@ -256,6 +261,45 @@ cl::desc("YAML output filename for pass remarks"), cl::value_desc("filename")); +class OptCustomPassManager : public legacy::PassManager { +public: + using super = legacy::PassManager; + OptCustomPassManager() : super() {} + + void add(Pass *P) override { + bool WrapWithDebugify = DebugifyEach && !P->getAsImmutablePass() && !isIRPrintingPass(P); + if (!WrapWithDebugify) { + super::add(P); + return; + } + PassKind Kind = P->getPassKind(); + // TODO: Implement Debugify for BasicBlockPass, LoopPass. + switch (Kind) { + case PT_Function: + super::add(createDebugifyFunctionPass()); + super::add(P); + super::add(createCheckDebugifyFunctionPass(true)); + break; + case PT_Module: + super::add(createDebugifyModulePass()); + super::add(P); + super::add(createCheckDebugifyModulePass(true)); + break; + case PT_BasicBlock: + case PT_Region: + case PT_Loop: + case PT_CallGraphSCC: + case PT_PassManager: + super::add(P); + break; + } + } + + bool run(Module &M) { + return super::run(M); + } +}; + static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... PM.add(P); @@ -556,8 +600,8 @@ // Create a PassManager to hold and optimize the collection of passes we are // about to build. - // - legacy::PassManager Passes; + OptCustomPassManager Passes; + bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach; // Add an appropriate TargetLibraryInfo pass for the module's triple. TargetLibraryInfoImpl TLII(ModuleTriple); @@ -571,8 +615,8 @@ Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis() : TargetIRAnalysis())); - if (EnableDebugify) - Passes.add(createDebugifyPass()); + if (AddOneTimeDebugifyPasses) + Passes.add(createDebugifyModulePass()); std::unique_ptr FPasses; if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz || @@ -719,8 +763,8 @@ if (!NoVerify && !VerifyEach) Passes.add(createVerifierPass()); - if (EnableDebugify) - Passes.add(createCheckDebugifyPass()); + if (AddOneTimeDebugifyPasses) + Passes.add(createCheckDebugifyModulePass(false)); // In run twice mode, we want to make sure the output is bit-by-bit // equivalent if we run the pass manager again, so setup two buffers and