Index: llvm/trunk/include/llvm/IR/IRPrintingPasses.h =================================================================== --- llvm/trunk/include/llvm/IR/IRPrintingPasses.h +++ llvm/trunk/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: llvm/trunk/lib/IR/IRPrintingPasses.cpp =================================================================== --- llvm/trunk/lib/IR/IRPrintingPasses.cpp +++ llvm/trunk/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: llvm/trunk/test/DebugInfo/debugify-each.ll =================================================================== --- llvm/trunk/test/DebugInfo/debugify-each.ll +++ llvm/trunk/test/DebugInfo/debugify-each.ll @@ -0,0 +1,24 @@ +; RUN: opt -debugify-each -O3 -S -o - < %s | FileCheck %s +; RUN: opt -debugify-each -instrprof -sroa -sccp -S -o - < %s | FileCheck %s + +define void @foo() { + ret void +} + +define void @bar() { + ret void +} + +; Verify that the module & function (check-)debugify passes run at least twice. + +; CHECK-DAG: CheckModuleDebugify: PASS +; CHECK-DAG: CheckFunctionDebugify: PASS +; CHECK-DAG: CheckFunctionDebugify: PASS +; CHECK-DAG: CheckFunctionDebugify: PASS +; CHECK-DAG: CheckFunctionDebugify: PASS + +; CHECK-DAG: CheckModuleDebugify: PASS +; CHECK-DAG: CheckFunctionDebugify: PASS +; CHECK-DAG: CheckFunctionDebugify: PASS +; CHECK-DAG: CheckFunctionDebugify: PASS +; CHECK-DAG: CheckFunctionDebugify: PASS Index: llvm/trunk/test/DebugInfo/debugify.ll =================================================================== --- llvm/trunk/test/DebugInfo/debugify.ll +++ llvm/trunk/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: llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll =================================================================== --- llvm/trunk/test/Transforms/Mem2Reg/PromoteMemToRegister.ll +++ llvm/trunk/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: llvm/trunk/tools/opt/Debugify.cpp =================================================================== --- llvm/trunk/tools/opt/Debugify.cpp +++ llvm/trunk/tools/opt/Debugify.cpp @@ -39,10 +39,12 @@ return F.isDeclaration() || !F.hasExactDefinition(); } -bool applyDebugifyMetadata(Module &M) { +bool applyDebugifyMetadata(Module &M, + iterator_range Functions, + StringRef 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; } @@ -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,57 @@ }; 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, + StringRef 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 { 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 && DL.getLine() != 0) { MissingLines.reset(DL.getLine() - 1); continue; } - outs() << "ERROR: Instruction with empty DebugLoc -- "; + outs() << "ERROR: Instruction with empty DebugLoc in function "; + outs() << 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) @@ -177,18 +181,58 @@ 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); + 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 instructions within a +/// single function, used with the legacy module pass manager. +struct DebugifyFunctionPass : public FunctionPass { + bool runOnFunction(Function &F) override { + Module &M = *F.getParent(); + auto FuncIt = F.getIterator(); + return applyDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)), + "FunctionDebugify: "); + } - DebugifyPass() : ModulePass(ID) {} + DebugifyFunctionPass() : FunctionPass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesAll(); @@ -197,43 +241,85 @@ 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 { + Module &M = *F.getParent(); + auto FuncIt = F.getIterator(); + return checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)), + "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: llvm/trunk/tools/opt/PassPrinters.h =================================================================== --- llvm/trunk/tools/opt/PassPrinters.h +++ llvm/trunk/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: llvm/trunk/tools/opt/opt.cpp =================================================================== --- llvm/trunk/tools/opt/opt.cpp +++ llvm/trunk/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,37 @@ cl::desc("YAML output filename for pass remarks"), cl::value_desc("filename")); +class OptCustomPassManager : public legacy::PassManager { +public: + using super = legacy::PassManager; + + 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; + default: + super::add(P); + break; + } + } +}; + static inline void addPass(legacy::PassManagerBase &PM, Pass *P) { // Add the pass to the pass manager... PM.add(P); @@ -556,8 +592,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 +607,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 +755,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