| //===- Debugify.cpp - Attach synthetic debug info to everything -----------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file This pass attaches synthetic debug info to everything. It can be used |
| /// to create targeted tests for debug info preservation. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #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 { |
| |
| bool applyDebugifyMetadata(Module &M) { |
| // Skip modules with debug info. |
| if (M.getNamedMetadata("llvm.dbg.cu")) { |
| errs() << "Debugify: 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 = M.getDataLayout().getTypeAllocSizeInBits(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, DIB.createFile(M.getName(), "/"), |
| "debugify", /*isOptimized=*/true, "", 0); |
| |
| // Visit each instruction. |
| for (Function &F : M) { |
| if (F.isDeclaration()) |
| continue; |
| |
| auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); |
| bool IsLocalToUnit = F.hasPrivateLinkage() || F.hasInternalLinkage(); |
| auto SP = |
| DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, SPType, |
| IsLocalToUnit, F.hasExactDefinition(), NextLine, |
| DINode::FlagZero, /*isOptimized=*/true); |
| F.setSubprogram(SP); |
| for (BasicBlock &BB : F) { |
| // Attach debug locations. |
| for (Instruction &I : BB) |
| I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP)); |
| |
| // Attach debug values. |
| for (Instruction &I : BB) { |
| // Skip void-valued instructions. |
| if (I.getType()->isVoidTy()) |
| continue; |
| |
| // Skip the terminator instruction and any just-inserted intrinsics. |
| if (isa<TerminatorInst>(&I) || isa<DbgValueInst>(&I)) |
| break; |
| |
| 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, |
| BB.getTerminator()); |
| } |
| } |
| 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. |
| return true; |
| } |
| |
| void checkDebugifyMetadata(Module &M) { |
| // Skip modules without debugify metadata. |
| NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify"); |
| if (!NMD) |
| return; |
| |
| auto getDebugifyOperand = [&](unsigned Idx) -> unsigned { |
| return mdconst::extract<ConstantInt>(NMD->getOperand(Idx)->getOperand(0)) |
| ->getZExtValue(); |
| }; |
| unsigned OriginalNumLines = getDebugifyOperand(0); |
| unsigned OriginalNumVars = getDebugifyOperand(1); |
| bool HasErrors = false; |
| |
| // Find missing lines. |
| BitVector MissingLines{OriginalNumLines, true}; |
| for (Function &F : M) { |
| for (Instruction &I : instructions(F)) { |
| if (isa<DbgValueInst>(&I)) |
| continue; |
| |
| auto DL = I.getDebugLoc(); |
| if (DL) { |
| MissingLines.reset(DL.getLine() - 1); |
| continue; |
| } |
| |
| outs() << "ERROR: Instruction with empty DebugLoc -- "; |
| 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) { |
| 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"); |
| MissingVars.reset(Var - 1); |
| } |
| } |
| for (unsigned Idx : MissingVars.set_bits()) |
| outs() << "ERROR: Missing variable " << Idx + 1 << "\n"; |
| HasErrors |= MissingVars.count() > 0; |
| |
| outs() << "CheckDebugify: " << (HasErrors ? "FAIL" : "PASS") << "\n"; |
| } |
| |
| /// Attach synthetic debug info to everything. |
| struct DebugifyPass : public ModulePass { |
| bool runOnModule(Module &M) override { return applyDebugifyMetadata(M); } |
| |
| DebugifyPass() : ModulePass(ID) {} |
| |
| void getAnalysisUsage(AnalysisUsage &AU) const override { |
| AU.setPreservesAll(); |
| } |
| |
| static char ID; // Pass identification. |
| }; |
| |
| /// Check debug info inserted by -debugify for completeness. |
| struct CheckDebugifyPass : public ModulePass { |
| bool runOnModule(Module &M) override { |
| checkDebugifyMetadata(M); |
| return false; |
| } |
| |
| CheckDebugifyPass() : ModulePass(ID) {} |
| |
| void getAnalysisUsage(AnalysisUsage &AU) const override { |
| AU.setPreservesAll(); |
| } |
| |
| static char ID; // Pass identification. |
| }; |
| |
| } // end anonymous namespace |
| |
| char DebugifyPass::ID = 0; |
| static RegisterPass<DebugifyPass> X("debugify", |
| "Attach debug info to everything"); |
| |
| char CheckDebugifyPass::ID = 0; |
| static RegisterPass<CheckDebugifyPass> Y("check-debugify", |
| "Check debug info from -debugify"); |