diff options
Diffstat (limited to 'llvm/tools')
| -rw-r--r-- | llvm/tools/opt/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | llvm/tools/opt/Debugify.cpp | 463 | ||||
| -rw-r--r-- | llvm/tools/opt/Debugify.h | 74 | ||||
| -rw-r--r-- | llvm/tools/opt/NewPMDriver.cpp | 2 | ||||
| -rw-r--r-- | llvm/tools/opt/opt.cpp | 23 |
5 files changed, 23 insertions, 540 deletions
diff --git a/llvm/tools/opt/CMakeLists.txt b/llvm/tools/opt/CMakeLists.txt index 25a4ebba699..4ea9baf447a 100644 --- a/llvm/tools/opt/CMakeLists.txt +++ b/llvm/tools/opt/CMakeLists.txt @@ -27,7 +27,6 @@ set(LLVM_LINK_COMPONENTS add_llvm_tool(opt AnalysisWrappers.cpp BreakpointPrinter.cpp - Debugify.cpp GraphPrinters.cpp NewPMDriver.cpp PassPrinters.cpp diff --git a/llvm/tools/opt/Debugify.cpp b/llvm/tools/opt/Debugify.cpp deleted file mode 100644 index 222cc702bc1..00000000000 --- a/llvm/tools/opt/Debugify.cpp +++ /dev/null @@ -1,463 +0,0 @@ -//===- Debugify.cpp - Attach synthetic debug info to everything -----------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file This pass attaches synthetic debug info to everything. It can be used -/// to create targeted tests for debug info preservation. -/// -//===----------------------------------------------------------------------===// - -#include "Debugify.h" -#include "llvm/ADT/BitVector.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Type.h" -#include "llvm/Pass.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Transforms/IPO.h" - -using namespace llvm; - -namespace { - -cl::opt<bool> Quiet("debugify-quiet", - cl::desc("Suppress verbose debugify output")); - -raw_ostream &dbg() { return Quiet ? nulls() : errs(); } - -uint64_t getAllocSizeInBits(Module &M, Type *Ty) { - return Ty->isSized() ? M.getDataLayout().getTypeAllocSizeInBits(Ty) : 0; -} - -bool isFunctionSkipped(Function &F) { - return F.isDeclaration() || !F.hasExactDefinition(); -} - -/// Find the basic block's terminating instruction. -/// -/// Special care is needed to handle musttail and deopt calls, as these behave -/// like (but are in fact not) terminators. -Instruction *findTerminatingInstruction(BasicBlock &BB) { - if (auto *I = BB.getTerminatingMustTailCall()) - return I; - if (auto *I = BB.getTerminatingDeoptimizeCall()) - return I; - return BB.getTerminator(); -} - -bool applyDebugifyMetadata(Module &M, - iterator_range<Module::iterator> Functions, - StringRef Banner) { - // Skip modules with debug info. - if (M.getNamedMetadata("llvm.dbg.cu")) { - dbg() << Banner << "Skipping module with debug info\n"; - return false; - } - - DIBuilder DIB(M); - LLVMContext &Ctx = M.getContext(); - - // Get a DIType which corresponds to Ty. - DenseMap<uint64_t, DIType *> TypeCache; - auto getCachedDIType = [&](Type *Ty) -> DIType * { - uint64_t Size = getAllocSizeInBits(M, Ty); - DIType *&DTy = TypeCache[Size]; - if (!DTy) { - std::string Name = "ty" + utostr(Size); - DTy = DIB.createBasicType(Name, Size, dwarf::DW_ATE_unsigned); - } - return DTy; - }; - - unsigned NextLine = 1; - unsigned NextVar = 1; - auto File = DIB.createFile(M.getName(), "/"); - auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C, File, "debugify", - /*isOptimized=*/true, "", 0); - - // Visit each instruction. - for (Function &F : Functions) { - if (isFunctionSkipped(F)) - continue; - - auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); - DISubprogram::DISPFlags SPFlags = - DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized; - if (F.hasPrivateLinkage() || F.hasInternalLinkage()) - SPFlags |= DISubprogram::SPFlagLocalToUnit; - auto SP = DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, - SPType, NextLine, DINode::FlagZero, SPFlags); - F.setSubprogram(SP); - for (BasicBlock &BB : F) { - // Attach debug locations. - for (Instruction &I : BB) - I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP)); - - // Inserting debug values into EH pads can break IR invariants. - if (BB.isEHPad()) - continue; - - // Find the terminating instruction, after which no debug values are - // attached. - Instruction *LastInst = findTerminatingInstruction(BB); - assert(LastInst && "Expected basic block with a terminator"); - - // Maintain an insertion point which can't be invalidated when updates - // are made. - BasicBlock::iterator InsertPt = BB.getFirstInsertionPt(); - assert(InsertPt != BB.end() && "Expected to find an insertion point"); - Instruction *InsertBefore = &*InsertPt; - - // Attach debug values. - for (Instruction *I = &*BB.begin(); I != LastInst; I = I->getNextNode()) { - // Skip void-valued instructions. - if (I->getType()->isVoidTy()) - continue; - - // Phis and EH pads must be grouped at the beginning of the block. - // Only advance the insertion point when we finish visiting these. - if (!isa<PHINode>(I) && !I->isEHPad()) - InsertBefore = I->getNextNode(); - - std::string Name = utostr(NextVar++); - const DILocation *Loc = I->getDebugLoc().get(); - auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(), - getCachedDIType(I->getType()), - /*AlwaysPreserve=*/true); - DIB.insertDbgValueIntrinsic(I, LocalVar, DIB.createExpression(), Loc, - InsertBefore); - } - } - DIB.finalizeSubprogram(SP); - } - DIB.finalize(); - - // Track the number of distinct lines and variables. - NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.debugify"); - auto *IntTy = Type::getInt32Ty(Ctx); - auto addDebugifyOperand = [&](unsigned N) { - NMD->addOperand(MDNode::get( - Ctx, ValueAsMetadata::getConstant(ConstantInt::get(IntTy, N)))); - }; - 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!"); - - // Claim that this synthetic debug info is valid. - StringRef DIVersionKey = "Debug Info Version"; - if (!M.getModuleFlag(DIVersionKey)) - M.addModuleFlag(Module::Warning, DIVersionKey, DEBUG_METADATA_VERSION); - - return true; -} - -/// Return true if a mis-sized diagnostic is issued for \p DVI. -bool diagnoseMisSizedDbgValue(Module &M, DbgValueInst *DVI) { - // The size of a dbg.value's value operand should match the size of the - // variable it corresponds to. - // - // TODO: This, along with a check for non-null value operands, should be - // promoted to verifier failures. - Value *V = DVI->getValue(); - if (!V) - return false; - - // For now, don't try to interpret anything more complicated than an empty - // DIExpression. Eventually we should try to handle OP_deref and fragments. - if (DVI->getExpression()->getNumElements()) - return false; - - Type *Ty = V->getType(); - uint64_t ValueOperandSize = getAllocSizeInBits(M, Ty); - Optional<uint64_t> DbgVarSize = DVI->getFragmentSizeInBits(); - if (!ValueOperandSize || !DbgVarSize) - return false; - - bool HasBadSize = false; - if (Ty->isIntegerTy()) { - auto Signedness = DVI->getVariable()->getSignedness(); - if (Signedness && *Signedness == DIBasicType::Signedness::Signed) - HasBadSize = ValueOperandSize < *DbgVarSize; - } else { - HasBadSize = ValueOperandSize != *DbgVarSize; - } - - if (HasBadSize) { - dbg() << "ERROR: dbg.value operand has size " << ValueOperandSize - << ", but its variable has size " << *DbgVarSize << ": "; - DVI->print(dbg()); - dbg() << "\n"; - } - return HasBadSize; -} - -bool checkDebugifyMetadata(Module &M, - iterator_range<Module::iterator> Functions, - StringRef NameOfWrappedPass, StringRef Banner, - bool Strip, DebugifyStatsMap *StatsMap) { - // Skip modules without debugify metadata. - NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify"); - if (!NMD) { - dbg() << Banner << "Skipping module without debugify metadata\n"; - return false; - } - - auto getDebugifyOperand = [&](unsigned Idx) -> unsigned { - return mdconst::extract<ConstantInt>(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; - - // Track debug info loss statistics if able. - DebugifyStatistics *Stats = nullptr; - if (StatsMap && !NameOfWrappedPass.empty()) - Stats = &StatsMap->operator[](NameOfWrappedPass); - - BitVector MissingLines{OriginalNumLines, true}; - BitVector MissingVars{OriginalNumVars, true}; - for (Function &F : Functions) { - if (isFunctionSkipped(F)) - continue; - - // Find missing lines. - for (Instruction &I : instructions(F)) { - if (isa<DbgValueInst>(&I)) - continue; - - auto DL = I.getDebugLoc(); - if (DL && DL.getLine() != 0) { - MissingLines.reset(DL.getLine() - 1); - continue; - } - - if (!DL) { - dbg() << "ERROR: Instruction with empty DebugLoc in function "; - dbg() << F.getName() << " --"; - I.print(dbg()); - dbg() << "\n"; - HasErrors = true; - } - } - - // Find missing variables and mis-sized debug values. - for (Instruction &I : instructions(F)) { - auto *DVI = dyn_cast<DbgValueInst>(&I); - if (!DVI) - continue; - - unsigned Var = ~0U; - (void)to_integer(DVI->getVariable()->getName(), Var, 10); - assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable"); - bool HasBadSize = diagnoseMisSizedDbgValue(M, DVI); - if (!HasBadSize) - MissingVars.reset(Var - 1); - HasErrors |= HasBadSize; - } - } - - // Print the results. - for (unsigned Idx : MissingLines.set_bits()) - dbg() << "WARNING: Missing line " << Idx + 1 << "\n"; - - for (unsigned Idx : MissingVars.set_bits()) - dbg() << "WARNING: Missing variable " << Idx + 1 << "\n"; - - // Update DI loss statistics. - if (Stats) { - Stats->NumDbgLocsExpected += OriginalNumLines; - Stats->NumDbgLocsMissing += MissingLines.count(); - Stats->NumDbgValuesExpected += OriginalNumVars; - Stats->NumDbgValuesMissing += MissingVars.count(); - } - - dbg() << Banner; - if (!NameOfWrappedPass.empty()) - dbg() << " [" << NameOfWrappedPass << "]"; - dbg() << ": " << (HasErrors ? "FAIL" : "PASS") << '\n'; - - // Strip the Debugify Metadata if required. - if (Strip) { - StripDebugInfo(M); - M.eraseNamedMetadata(NMD); - return true; - } - - return false; -} - -/// 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: "); - } - - DebugifyFunctionPass() : FunctionPass(ID) {} - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - } - - static char ID; // Pass identification. -}; - -/// ModulePass for checking debug info inserted by -debugify, used with the -/// legacy module pass manager. -struct CheckDebugifyModulePass : public ModulePass { - bool runOnModule(Module &M) override { - return checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass, - "CheckModuleDebugify", Strip, StatsMap); - } - - CheckDebugifyModulePass(bool Strip = false, StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr) - : ModulePass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass), - StatsMap(StatsMap) {} - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - } - - static char ID; // Pass identification. - -private: - bool Strip; - StringRef NameOfWrappedPass; - DebugifyStatsMap *StatsMap; -}; - -/// 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)), - NameOfWrappedPass, "CheckFunctionDebugify", - Strip, StatsMap); - } - - CheckDebugifyFunctionPass(bool Strip = false, - StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr) - : FunctionPass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass), - StatsMap(StatsMap) {} - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - } - - static char ID; // Pass identification. - -private: - bool Strip; - StringRef NameOfWrappedPass; - DebugifyStatsMap *StatsMap; -}; - -} // end anonymous namespace - -void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map) { - std::error_code EC; - raw_fd_ostream OS{Path, EC}; - if (EC) { - errs() << "Could not open file: " << EC.message() << ", " << Path << '\n'; - return; - } - - OS << "Pass Name" << ',' << "# of missing debug values" << ',' - << "# of missing locations" << ',' << "Missing/Expected value ratio" << ',' - << "Missing/Expected location ratio" << '\n'; - for (const auto &Entry : Map) { - StringRef Pass = Entry.first; - DebugifyStatistics Stats = Entry.second; - - OS << Pass << ',' << Stats.NumDbgValuesMissing << ',' - << Stats.NumDbgLocsMissing << ',' << Stats.getMissingValueRatio() << ',' - << Stats.getEmptyLocationRatio() << '\n'; - } -} - -ModulePass *createDebugifyModulePass() { return new DebugifyModulePass(); } - -FunctionPass *createDebugifyFunctionPass() { - return new DebugifyFunctionPass(); -} - -PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) { - applyDebugifyMetadata(M, M.functions(), "ModuleDebugify: "); - return PreservedAnalyses::all(); -} - -ModulePass *createCheckDebugifyModulePass(bool Strip, - StringRef NameOfWrappedPass, - DebugifyStatsMap *StatsMap) { - return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap); -} - -FunctionPass *createCheckDebugifyFunctionPass(bool Strip, - StringRef NameOfWrappedPass, - DebugifyStatsMap *StatsMap) { - return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap); -} - -PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M, - ModuleAnalysisManager &) { - checkDebugifyMetadata(M, M.functions(), "", "CheckModuleDebugify", false, - nullptr); - return PreservedAnalyses::all(); -} - -char DebugifyModulePass::ID = 0; -static RegisterPass<DebugifyModulePass> DM("debugify", - "Attach debug info to everything"); - -char CheckDebugifyModulePass::ID = 0; -static RegisterPass<CheckDebugifyModulePass> - CDM("check-debugify", "Check debug info from -debugify"); - -char DebugifyFunctionPass::ID = 0; -static RegisterPass<DebugifyFunctionPass> DF("debugify-function", - "Attach debug info to a function"); - -char CheckDebugifyFunctionPass::ID = 0; -static RegisterPass<CheckDebugifyFunctionPass> - CDF("check-debugify-function", "Check debug info from -debugify-function"); diff --git a/llvm/tools/opt/Debugify.h b/llvm/tools/opt/Debugify.h deleted file mode 100644 index 266f577951a..00000000000 --- a/llvm/tools/opt/Debugify.h +++ /dev/null @@ -1,74 +0,0 @@ -//===- Debugify.h - Attach synthetic debug info to everything -------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file Interface to the `debugify` synthetic debug info testing utility. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_TOOLS_OPT_DEBUGIFY_H -#define LLVM_TOOLS_OPT_DEBUGIFY_H - -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/MapVector.h" -#include "llvm/IR/PassManager.h" -#include "llvm/Support/raw_ostream.h" - -llvm::ModulePass *createDebugifyModulePass(); -llvm::FunctionPass *createDebugifyFunctionPass(); - -struct NewPMDebugifyPass : public llvm::PassInfoMixin<NewPMDebugifyPass> { - llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); -}; - -/// Track how much `debugify` information has been lost. -struct DebugifyStatistics { - /// Number of missing dbg.values. - unsigned NumDbgValuesMissing = 0; - - /// Number of dbg.values expected. - unsigned NumDbgValuesExpected = 0; - - /// Number of instructions with empty debug locations. - unsigned NumDbgLocsMissing = 0; - - /// Number of instructions expected to have debug locations. - unsigned NumDbgLocsExpected = 0; - - /// Get the ratio of missing/expected dbg.values. - float getMissingValueRatio() const { - return float(NumDbgValuesMissing) / float(NumDbgLocsExpected); - } - - /// Get the ratio of missing/expected instructions with locations. - float getEmptyLocationRatio() const { - return float(NumDbgLocsMissing) / float(NumDbgLocsExpected); - } -}; - -/// Map pass names to a per-pass DebugifyStatistics instance. -using DebugifyStatsMap = llvm::MapVector<llvm::StringRef, DebugifyStatistics>; - -/// Export per-pass debugify statistics to the file specified by \p Path. -void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map); - -llvm::ModulePass * -createCheckDebugifyModulePass(bool Strip = false, - llvm::StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr); - -llvm::FunctionPass * -createCheckDebugifyFunctionPass(bool Strip = false, - llvm::StringRef NameOfWrappedPass = "", - DebugifyStatsMap *StatsMap = nullptr); - -struct NewPMCheckDebugifyPass - : public llvm::PassInfoMixin<NewPMCheckDebugifyPass> { - llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM); -}; - -#endif // LLVM_TOOLS_OPT_DEBUGIFY_H diff --git a/llvm/tools/opt/NewPMDriver.cpp b/llvm/tools/opt/NewPMDriver.cpp index efe0bec35d7..c3b0db57e86 100644 --- a/llvm/tools/opt/NewPMDriver.cpp +++ b/llvm/tools/opt/NewPMDriver.cpp @@ -13,7 +13,6 @@ //===----------------------------------------------------------------------===// #include "NewPMDriver.h" -#include "Debugify.h" #include "PassPrinters.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/AliasAnalysis.h" @@ -35,6 +34,7 @@ #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" #include "llvm/Transforms/Scalar/LoopPassManager.h" +#include "llvm/Transforms/Utils/Debugify.h" using namespace llvm; using namespace opt_tool; diff --git a/llvm/tools/opt/opt.cpp b/llvm/tools/opt/opt.cpp index 433f5f22d8d..1dc5dd448f3 100644 --- a/llvm/tools/opt/opt.cpp +++ b/llvm/tools/opt/opt.cpp @@ -12,7 +12,6 @@ //===----------------------------------------------------------------------===// #include "BreakpointPrinter.h" -#include "Debugify.h" #include "NewPMDriver.h" #include "PassPrinters.h" #include "llvm/ADT/Triple.h" @@ -56,6 +55,7 @@ #include "llvm/Transforms/IPO/AlwaysInliner.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/Debugify.h" #include <algorithm> #include <memory> using namespace llvm; @@ -482,6 +482,27 @@ void initializePollyPasses(llvm::PassRegistry &Registry); } #endif +void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map) { + std::error_code EC; + raw_fd_ostream OS{Path, EC}; + if (EC) { + errs() << "Could not open file: " << EC.message() << ", " << Path << '\n'; + return; + } + + OS << "Pass Name" << ',' << "# of missing debug values" << ',' + << "# of missing locations" << ',' << "Missing/Expected value ratio" << ',' + << "Missing/Expected location ratio" << '\n'; + for (const auto &Entry : Map) { + StringRef Pass = Entry.first; + DebugifyStatistics Stats = Entry.second; + + OS << Pass << ',' << Stats.NumDbgValuesMissing << ',' + << Stats.NumDbgLocsMissing << ',' << Stats.getMissingValueRatio() << ',' + << Stats.getEmptyLocationRatio() << '\n'; + } +} + //===----------------------------------------------------------------------===// // main for opt // |

