Path: blob/main/contrib/llvm-project/llvm/lib/Transforms/Utils/Debugify.cpp
35271 views
//===- Debugify.cpp - Check debug info preservation in optimizations ------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file In the `synthetic` mode, the `-debugify` attaches synthetic debug info9/// to everything. It can be used to create targeted tests for debug info10/// preservation. In addition, when using the `original` mode, it can check11/// original debug info preservation. The `synthetic` mode is default one.12///13//===----------------------------------------------------------------------===//1415#include "llvm/Transforms/Utils/Debugify.h"16#include "llvm/ADT/BitVector.h"17#include "llvm/ADT/StringExtras.h"18#include "llvm/IR/DIBuilder.h"19#include "llvm/IR/DebugInfo.h"20#include "llvm/IR/InstIterator.h"21#include "llvm/IR/Instructions.h"22#include "llvm/IR/IntrinsicInst.h"23#include "llvm/IR/Module.h"24#include "llvm/IR/PassInstrumentation.h"25#include "llvm/Pass.h"26#include "llvm/Support/CommandLine.h"27#include "llvm/Support/FileSystem.h"28#include "llvm/Support/JSON.h"29#include <optional>3031#define DEBUG_TYPE "debugify"3233using namespace llvm;3435namespace {3637cl::opt<bool> Quiet("debugify-quiet",38cl::desc("Suppress verbose debugify output"));3940cl::opt<uint64_t> DebugifyFunctionsLimit(41"debugify-func-limit",42cl::desc("Set max number of processed functions per pass."),43cl::init(UINT_MAX));4445enum class Level {46Locations,47LocationsAndVariables48};4950cl::opt<Level> DebugifyLevel(51"debugify-level", cl::desc("Kind of debug info to add"),52cl::values(clEnumValN(Level::Locations, "locations", "Locations only"),53clEnumValN(Level::LocationsAndVariables, "location+variables",54"Locations and Variables")),55cl::init(Level::LocationsAndVariables));5657raw_ostream &dbg() { return Quiet ? nulls() : errs(); }5859uint64_t getAllocSizeInBits(Module &M, Type *Ty) {60return Ty->isSized() ? M.getDataLayout().getTypeAllocSizeInBits(Ty) : 0;61}6263bool isFunctionSkipped(Function &F) {64return F.isDeclaration() || !F.hasExactDefinition();65}6667/// Find the basic block's terminating instruction.68///69/// Special care is needed to handle musttail and deopt calls, as these behave70/// like (but are in fact not) terminators.71Instruction *findTerminatingInstruction(BasicBlock &BB) {72if (auto *I = BB.getTerminatingMustTailCall())73return I;74if (auto *I = BB.getTerminatingDeoptimizeCall())75return I;76return BB.getTerminator();77}78} // end anonymous namespace7980bool llvm::applyDebugifyMetadata(81Module &M, iterator_range<Module::iterator> Functions, StringRef Banner,82std::function<bool(DIBuilder &DIB, Function &F)> ApplyToMF) {83// Skip modules with debug info.84if (M.getNamedMetadata("llvm.dbg.cu")) {85dbg() << Banner << "Skipping module with debug info\n";86return false;87}8889DIBuilder DIB(M);90LLVMContext &Ctx = M.getContext();91auto *Int32Ty = Type::getInt32Ty(Ctx);9293// Get a DIType which corresponds to Ty.94DenseMap<uint64_t, DIType *> TypeCache;95auto getCachedDIType = [&](Type *Ty) -> DIType * {96uint64_t Size = getAllocSizeInBits(M, Ty);97DIType *&DTy = TypeCache[Size];98if (!DTy) {99std::string Name = "ty" + utostr(Size);100DTy = DIB.createBasicType(Name, Size, dwarf::DW_ATE_unsigned);101}102return DTy;103};104105unsigned NextLine = 1;106unsigned NextVar = 1;107auto File = DIB.createFile(M.getName(), "/");108auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C, File, "debugify",109/*isOptimized=*/true, "", 0);110111// Visit each instruction.112for (Function &F : Functions) {113if (isFunctionSkipped(F))114continue;115116bool InsertedDbgVal = false;117auto SPType =118DIB.createSubroutineType(DIB.getOrCreateTypeArray(std::nullopt));119DISubprogram::DISPFlags SPFlags =120DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized;121if (F.hasPrivateLinkage() || F.hasInternalLinkage())122SPFlags |= DISubprogram::SPFlagLocalToUnit;123auto SP = DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine,124SPType, NextLine, DINode::FlagZero, SPFlags);125F.setSubprogram(SP);126127// Helper that inserts a dbg.value before \p InsertBefore, copying the128// location (and possibly the type, if it's non-void) from \p TemplateInst.129auto insertDbgVal = [&](Instruction &TemplateInst,130Instruction *InsertBefore) {131std::string Name = utostr(NextVar++);132Value *V = &TemplateInst;133if (TemplateInst.getType()->isVoidTy())134V = ConstantInt::get(Int32Ty, 0);135const DILocation *Loc = TemplateInst.getDebugLoc().get();136auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(),137getCachedDIType(V->getType()),138/*AlwaysPreserve=*/true);139DIB.insertDbgValueIntrinsic(V, LocalVar, DIB.createExpression(), Loc,140InsertBefore);141};142143for (BasicBlock &BB : F) {144// Attach debug locations.145for (Instruction &I : BB)146I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP));147148if (DebugifyLevel < Level::LocationsAndVariables)149continue;150151// Inserting debug values into EH pads can break IR invariants.152if (BB.isEHPad())153continue;154155// Find the terminating instruction, after which no debug values are156// attached.157Instruction *LastInst = findTerminatingInstruction(BB);158assert(LastInst && "Expected basic block with a terminator");159160// Maintain an insertion point which can't be invalidated when updates161// are made.162BasicBlock::iterator InsertPt = BB.getFirstInsertionPt();163assert(InsertPt != BB.end() && "Expected to find an insertion point");164Instruction *InsertBefore = &*InsertPt;165166// Attach debug values.167for (Instruction *I = &*BB.begin(); I != LastInst; I = I->getNextNode()) {168// Skip void-valued instructions.169if (I->getType()->isVoidTy())170continue;171172// Phis and EH pads must be grouped at the beginning of the block.173// Only advance the insertion point when we finish visiting these.174if (!isa<PHINode>(I) && !I->isEHPad())175InsertBefore = I->getNextNode();176177insertDbgVal(*I, InsertBefore);178InsertedDbgVal = true;179}180}181// Make sure we emit at least one dbg.value, otherwise MachineDebugify may182// not have anything to work with as it goes about inserting DBG_VALUEs.183// (It's common for MIR tests to be written containing skeletal IR with184// empty functions -- we're still interested in debugifying the MIR within185// those tests, and this helps with that.)186if (DebugifyLevel == Level::LocationsAndVariables && !InsertedDbgVal) {187auto *Term = findTerminatingInstruction(F.getEntryBlock());188insertDbgVal(*Term, Term);189}190if (ApplyToMF)191ApplyToMF(DIB, F);192DIB.finalizeSubprogram(SP);193}194DIB.finalize();195196// Track the number of distinct lines and variables.197NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.debugify");198auto addDebugifyOperand = [&](unsigned N) {199NMD->addOperand(MDNode::get(200Ctx, ValueAsMetadata::getConstant(ConstantInt::get(Int32Ty, N))));201};202addDebugifyOperand(NextLine - 1); // Original number of lines.203addDebugifyOperand(NextVar - 1); // Original number of variables.204assert(NMD->getNumOperands() == 2 &&205"llvm.debugify should have exactly 2 operands!");206207// Claim that this synthetic debug info is valid.208StringRef DIVersionKey = "Debug Info Version";209if (!M.getModuleFlag(DIVersionKey))210M.addModuleFlag(Module::Warning, DIVersionKey, DEBUG_METADATA_VERSION);211212return true;213}214215static bool216applyDebugify(Function &F,217enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,218DebugInfoPerPass *DebugInfoBeforePass = nullptr,219StringRef NameOfWrappedPass = "") {220Module &M = *F.getParent();221auto FuncIt = F.getIterator();222if (Mode == DebugifyMode::SyntheticDebugInfo)223return applyDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),224"FunctionDebugify: ", /*ApplyToMF*/ nullptr);225assert(DebugInfoBeforePass);226return collectDebugInfoMetadata(M, M.functions(), *DebugInfoBeforePass,227"FunctionDebugify (original debuginfo)",228NameOfWrappedPass);229}230231static bool232applyDebugify(Module &M,233enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,234DebugInfoPerPass *DebugInfoBeforePass = nullptr,235StringRef NameOfWrappedPass = "") {236if (Mode == DebugifyMode::SyntheticDebugInfo)237return applyDebugifyMetadata(M, M.functions(),238"ModuleDebugify: ", /*ApplyToMF*/ nullptr);239return collectDebugInfoMetadata(M, M.functions(), *DebugInfoBeforePass,240"ModuleDebugify (original debuginfo)",241NameOfWrappedPass);242}243244bool llvm::stripDebugifyMetadata(Module &M) {245bool Changed = false;246247// Remove the llvm.debugify and llvm.mir.debugify module-level named metadata.248NamedMDNode *DebugifyMD = M.getNamedMetadata("llvm.debugify");249if (DebugifyMD) {250M.eraseNamedMetadata(DebugifyMD);251Changed = true;252}253254if (auto *MIRDebugifyMD = M.getNamedMetadata("llvm.mir.debugify")) {255M.eraseNamedMetadata(MIRDebugifyMD);256Changed = true;257}258259// Strip out all debug intrinsics and supporting metadata (subprograms, types,260// variables, etc).261Changed |= StripDebugInfo(M);262263// Strip out the dead dbg.value prototype.264Function *DbgValF = M.getFunction("llvm.dbg.value");265if (DbgValF) {266assert(DbgValF->isDeclaration() && DbgValF->use_empty() &&267"Not all debug info stripped?");268DbgValF->eraseFromParent();269Changed = true;270}271272// Strip out the module-level Debug Info Version metadata.273// FIXME: There must be an easier way to remove an operand from a NamedMDNode.274NamedMDNode *NMD = M.getModuleFlagsMetadata();275if (!NMD)276return Changed;277SmallVector<MDNode *, 4> Flags(NMD->operands());278NMD->clearOperands();279for (MDNode *Flag : Flags) {280auto *Key = cast<MDString>(Flag->getOperand(1));281if (Key->getString() == "Debug Info Version") {282Changed = true;283continue;284}285NMD->addOperand(Flag);286}287// If we left it empty we might as well remove it.288if (NMD->getNumOperands() == 0)289NMD->eraseFromParent();290291return Changed;292}293294bool llvm::collectDebugInfoMetadata(Module &M,295iterator_range<Module::iterator> Functions,296DebugInfoPerPass &DebugInfoBeforePass,297StringRef Banner,298StringRef NameOfWrappedPass) {299LLVM_DEBUG(dbgs() << Banner << ": (before) " << NameOfWrappedPass << '\n');300301if (!M.getNamedMetadata("llvm.dbg.cu")) {302dbg() << Banner << ": Skipping module without debug info\n";303return false;304}305306uint64_t FunctionsCnt = DebugInfoBeforePass.DIFunctions.size();307// Visit each instruction.308for (Function &F : Functions) {309// Use DI collected after previous Pass (when -debugify-each is used).310if (DebugInfoBeforePass.DIFunctions.count(&F))311continue;312313if (isFunctionSkipped(F))314continue;315316// Stop collecting DI if the Functions number reached the limit.317if (++FunctionsCnt >= DebugifyFunctionsLimit)318break;319// Collect the DISubprogram.320auto *SP = F.getSubprogram();321DebugInfoBeforePass.DIFunctions.insert({&F, SP});322if (SP) {323LLVM_DEBUG(dbgs() << " Collecting subprogram: " << *SP << '\n');324for (const DINode *DN : SP->getRetainedNodes()) {325if (const auto *DV = dyn_cast<DILocalVariable>(DN)) {326DebugInfoBeforePass.DIVariables[DV] = 0;327}328}329}330331for (BasicBlock &BB : F) {332// Collect debug locations (!dbg) and debug variable intrinsics.333for (Instruction &I : BB) {334// Skip PHIs.335if (isa<PHINode>(I))336continue;337338// Cllect dbg.values and dbg.declare.339if (DebugifyLevel > Level::Locations) {340auto HandleDbgVariable = [&](auto *DbgVar) {341if (!SP)342return;343// Skip inlined variables.344if (DbgVar->getDebugLoc().getInlinedAt())345return;346// Skip undef values.347if (DbgVar->isKillLocation())348return;349350auto *Var = DbgVar->getVariable();351DebugInfoBeforePass.DIVariables[Var]++;352};353for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange()))354HandleDbgVariable(&DVR);355if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I))356HandleDbgVariable(DVI);357}358359// Skip debug instructions other than dbg.value and dbg.declare.360if (isa<DbgInfoIntrinsic>(&I))361continue;362363LLVM_DEBUG(dbgs() << " Collecting info for inst: " << I << '\n');364DebugInfoBeforePass.InstToDelete.insert({&I, &I});365366const DILocation *Loc = I.getDebugLoc().get();367bool HasLoc = Loc != nullptr;368DebugInfoBeforePass.DILocations.insert({&I, HasLoc});369}370}371}372373return true;374}375376// This checks the preservation of original debug info attached to functions.377static bool checkFunctions(const DebugFnMap &DIFunctionsBefore,378const DebugFnMap &DIFunctionsAfter,379StringRef NameOfWrappedPass,380StringRef FileNameFromCU, bool ShouldWriteIntoJSON,381llvm::json::Array &Bugs) {382bool Preserved = true;383for (const auto &F : DIFunctionsAfter) {384if (F.second)385continue;386auto SPIt = DIFunctionsBefore.find(F.first);387if (SPIt == DIFunctionsBefore.end()) {388if (ShouldWriteIntoJSON)389Bugs.push_back(llvm::json::Object({{"metadata", "DISubprogram"},390{"name", F.first->getName()},391{"action", "not-generate"}}));392else393dbg() << "ERROR: " << NameOfWrappedPass394<< " did not generate DISubprogram for " << F.first->getName()395<< " from " << FileNameFromCU << '\n';396Preserved = false;397} else {398auto SP = SPIt->second;399if (!SP)400continue;401// If the function had the SP attached before the pass, consider it as402// a debug info bug.403if (ShouldWriteIntoJSON)404Bugs.push_back(llvm::json::Object({{"metadata", "DISubprogram"},405{"name", F.first->getName()},406{"action", "drop"}}));407else408dbg() << "ERROR: " << NameOfWrappedPass << " dropped DISubprogram of "409<< F.first->getName() << " from " << FileNameFromCU << '\n';410Preserved = false;411}412}413414return Preserved;415}416417// This checks the preservation of the original debug info attached to418// instructions.419static bool checkInstructions(const DebugInstMap &DILocsBefore,420const DebugInstMap &DILocsAfter,421const WeakInstValueMap &InstToDelete,422StringRef NameOfWrappedPass,423StringRef FileNameFromCU,424bool ShouldWriteIntoJSON,425llvm::json::Array &Bugs) {426bool Preserved = true;427for (const auto &L : DILocsAfter) {428if (L.second)429continue;430auto Instr = L.first;431432// In order to avoid pointer reuse/recycling, skip the values that might433// have been deleted during a pass.434auto WeakInstrPtr = InstToDelete.find(Instr);435if (WeakInstrPtr != InstToDelete.end() && !WeakInstrPtr->second)436continue;437438auto FnName = Instr->getFunction()->getName();439auto BB = Instr->getParent();440auto BBName = BB->hasName() ? BB->getName() : "no-name";441auto InstName = Instruction::getOpcodeName(Instr->getOpcode());442443auto InstrIt = DILocsBefore.find(Instr);444if (InstrIt == DILocsBefore.end()) {445if (ShouldWriteIntoJSON)446Bugs.push_back(llvm::json::Object({{"metadata", "DILocation"},447{"fn-name", FnName.str()},448{"bb-name", BBName.str()},449{"instr", InstName},450{"action", "not-generate"}}));451else452dbg() << "WARNING: " << NameOfWrappedPass453<< " did not generate DILocation for " << *Instr454<< " (BB: " << BBName << ", Fn: " << FnName455<< ", File: " << FileNameFromCU << ")\n";456Preserved = false;457} else {458if (!InstrIt->second)459continue;460// If the instr had the !dbg attached before the pass, consider it as461// a debug info issue.462if (ShouldWriteIntoJSON)463Bugs.push_back(llvm::json::Object({{"metadata", "DILocation"},464{"fn-name", FnName.str()},465{"bb-name", BBName.str()},466{"instr", InstName},467{"action", "drop"}}));468else469dbg() << "WARNING: " << NameOfWrappedPass << " dropped DILocation of "470<< *Instr << " (BB: " << BBName << ", Fn: " << FnName471<< ", File: " << FileNameFromCU << ")\n";472Preserved = false;473}474}475476return Preserved;477}478479// This checks the preservation of original debug variable intrinsics.480static bool checkVars(const DebugVarMap &DIVarsBefore,481const DebugVarMap &DIVarsAfter,482StringRef NameOfWrappedPass, StringRef FileNameFromCU,483bool ShouldWriteIntoJSON, llvm::json::Array &Bugs) {484bool Preserved = true;485for (const auto &V : DIVarsBefore) {486auto VarIt = DIVarsAfter.find(V.first);487if (VarIt == DIVarsAfter.end())488continue;489490unsigned NumOfDbgValsAfter = VarIt->second;491492if (V.second > NumOfDbgValsAfter) {493if (ShouldWriteIntoJSON)494Bugs.push_back(llvm::json::Object(495{{"metadata", "dbg-var-intrinsic"},496{"name", V.first->getName()},497{"fn-name", V.first->getScope()->getSubprogram()->getName()},498{"action", "drop"}}));499else500dbg() << "WARNING: " << NameOfWrappedPass501<< " drops dbg.value()/dbg.declare() for " << V.first->getName()502<< " from "503<< "function " << V.first->getScope()->getSubprogram()->getName()504<< " (file " << FileNameFromCU << ")\n";505Preserved = false;506}507}508509return Preserved;510}511512// Write the json data into the specifed file.513static void writeJSON(StringRef OrigDIVerifyBugsReportFilePath,514StringRef FileNameFromCU, StringRef NameOfWrappedPass,515llvm::json::Array &Bugs) {516std::error_code EC;517raw_fd_ostream OS_FILE{OrigDIVerifyBugsReportFilePath, EC,518sys::fs::OF_Append | sys::fs::OF_TextWithCRLF};519if (EC) {520errs() << "Could not open file: " << EC.message() << ", "521<< OrigDIVerifyBugsReportFilePath << '\n';522return;523}524525if (auto L = OS_FILE.lock()) {526OS_FILE << "{\"file\":\"" << FileNameFromCU << "\", ";527528StringRef PassName =529NameOfWrappedPass != "" ? NameOfWrappedPass : "no-name";530OS_FILE << "\"pass\":\"" << PassName << "\", ";531532llvm::json::Value BugsToPrint{std::move(Bugs)};533OS_FILE << "\"bugs\": " << BugsToPrint;534535OS_FILE << "}\n";536}537OS_FILE.close();538}539540bool llvm::checkDebugInfoMetadata(Module &M,541iterator_range<Module::iterator> Functions,542DebugInfoPerPass &DebugInfoBeforePass,543StringRef Banner, StringRef NameOfWrappedPass,544StringRef OrigDIVerifyBugsReportFilePath) {545LLVM_DEBUG(dbgs() << Banner << ": (after) " << NameOfWrappedPass << '\n');546547if (!M.getNamedMetadata("llvm.dbg.cu")) {548dbg() << Banner << ": Skipping module without debug info\n";549return false;550}551552// Map the debug info holding DIs after a pass.553DebugInfoPerPass DebugInfoAfterPass;554555// Visit each instruction.556for (Function &F : Functions) {557if (isFunctionSkipped(F))558continue;559560// Don't process functions without DI collected before the Pass.561if (!DebugInfoBeforePass.DIFunctions.count(&F))562continue;563// TODO: Collect metadata other than DISubprograms.564// Collect the DISubprogram.565auto *SP = F.getSubprogram();566DebugInfoAfterPass.DIFunctions.insert({&F, SP});567568if (SP) {569LLVM_DEBUG(dbgs() << " Collecting subprogram: " << *SP << '\n');570for (const DINode *DN : SP->getRetainedNodes()) {571if (const auto *DV = dyn_cast<DILocalVariable>(DN)) {572DebugInfoAfterPass.DIVariables[DV] = 0;573}574}575}576577for (BasicBlock &BB : F) {578// Collect debug locations (!dbg) and debug variable intrinsics.579for (Instruction &I : BB) {580// Skip PHIs.581if (isa<PHINode>(I))582continue;583584// Collect dbg.values and dbg.declares.585if (DebugifyLevel > Level::Locations) {586auto HandleDbgVariable = [&](auto *DbgVar) {587if (!SP)588return;589// Skip inlined variables.590if (DbgVar->getDebugLoc().getInlinedAt())591return;592// Skip undef values.593if (DbgVar->isKillLocation())594return;595596auto *Var = DbgVar->getVariable();597DebugInfoAfterPass.DIVariables[Var]++;598};599for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange()))600HandleDbgVariable(&DVR);601if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I))602HandleDbgVariable(DVI);603}604605// Skip debug instructions other than dbg.value and dbg.declare.606if (isa<DbgInfoIntrinsic>(&I))607continue;608609LLVM_DEBUG(dbgs() << " Collecting info for inst: " << I << '\n');610611const DILocation *Loc = I.getDebugLoc().get();612bool HasLoc = Loc != nullptr;613614DebugInfoAfterPass.DILocations.insert({&I, HasLoc});615}616}617}618619// TODO: The name of the module could be read better?620StringRef FileNameFromCU =621(cast<DICompileUnit>(M.getNamedMetadata("llvm.dbg.cu")->getOperand(0)))622->getFilename();623624auto DIFunctionsBefore = DebugInfoBeforePass.DIFunctions;625auto DIFunctionsAfter = DebugInfoAfterPass.DIFunctions;626627auto DILocsBefore = DebugInfoBeforePass.DILocations;628auto DILocsAfter = DebugInfoAfterPass.DILocations;629630auto InstToDelete = DebugInfoBeforePass.InstToDelete;631632auto DIVarsBefore = DebugInfoBeforePass.DIVariables;633auto DIVarsAfter = DebugInfoAfterPass.DIVariables;634635bool ShouldWriteIntoJSON = !OrigDIVerifyBugsReportFilePath.empty();636llvm::json::Array Bugs;637638bool ResultForFunc =639checkFunctions(DIFunctionsBefore, DIFunctionsAfter, NameOfWrappedPass,640FileNameFromCU, ShouldWriteIntoJSON, Bugs);641bool ResultForInsts = checkInstructions(642DILocsBefore, DILocsAfter, InstToDelete, NameOfWrappedPass,643FileNameFromCU, ShouldWriteIntoJSON, Bugs);644645bool ResultForVars = checkVars(DIVarsBefore, DIVarsAfter, NameOfWrappedPass,646FileNameFromCU, ShouldWriteIntoJSON, Bugs);647648bool Result = ResultForFunc && ResultForInsts && ResultForVars;649650StringRef ResultBanner = NameOfWrappedPass != "" ? NameOfWrappedPass : Banner;651if (ShouldWriteIntoJSON && !Bugs.empty())652writeJSON(OrigDIVerifyBugsReportFilePath, FileNameFromCU, NameOfWrappedPass,653Bugs);654655if (Result)656dbg() << ResultBanner << ": PASS\n";657else658dbg() << ResultBanner << ": FAIL\n";659660// In the case of the `debugify-each`, no need to go over all the instructions661// again in the collectDebugInfoMetadata(), since as an input we can use662// the debugging information from the previous pass.663DebugInfoBeforePass = DebugInfoAfterPass;664665LLVM_DEBUG(dbgs() << "\n\n");666return Result;667}668669namespace {670/// Return true if a mis-sized diagnostic is issued for \p DbgVal.671template <typename DbgValTy>672bool diagnoseMisSizedDbgValue(Module &M, DbgValTy *DbgVal) {673// The size of a dbg.value's value operand should match the size of the674// variable it corresponds to.675//676// TODO: This, along with a check for non-null value operands, should be677// promoted to verifier failures.678679// For now, don't try to interpret anything more complicated than an empty680// DIExpression. Eventually we should try to handle OP_deref and fragments.681if (DbgVal->getExpression()->getNumElements())682return false;683684Value *V = DbgVal->getVariableLocationOp(0);685if (!V)686return false;687688Type *Ty = V->getType();689uint64_t ValueOperandSize = getAllocSizeInBits(M, Ty);690std::optional<uint64_t> DbgVarSize = DbgVal->getFragmentSizeInBits();691if (!ValueOperandSize || !DbgVarSize)692return false;693694bool HasBadSize = false;695if (Ty->isIntegerTy()) {696auto Signedness = DbgVal->getVariable()->getSignedness();697if (Signedness && *Signedness == DIBasicType::Signedness::Signed)698HasBadSize = ValueOperandSize < *DbgVarSize;699} else {700HasBadSize = ValueOperandSize != *DbgVarSize;701}702703if (HasBadSize) {704dbg() << "ERROR: dbg.value operand has size " << ValueOperandSize705<< ", but its variable has size " << *DbgVarSize << ": ";706DbgVal->print(dbg());707dbg() << "\n";708}709return HasBadSize;710}711712bool checkDebugifyMetadata(Module &M,713iterator_range<Module::iterator> Functions,714StringRef NameOfWrappedPass, StringRef Banner,715bool Strip, DebugifyStatsMap *StatsMap) {716// Skip modules without debugify metadata.717NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify");718if (!NMD) {719dbg() << Banner << ": Skipping module without debugify metadata\n";720return false;721}722723auto getDebugifyOperand = [&](unsigned Idx) -> unsigned {724return mdconst::extract<ConstantInt>(NMD->getOperand(Idx)->getOperand(0))725->getZExtValue();726};727assert(NMD->getNumOperands() == 2 &&728"llvm.debugify should have exactly 2 operands!");729unsigned OriginalNumLines = getDebugifyOperand(0);730unsigned OriginalNumVars = getDebugifyOperand(1);731bool HasErrors = false;732733// Track debug info loss statistics if able.734DebugifyStatistics *Stats = nullptr;735if (StatsMap && !NameOfWrappedPass.empty())736Stats = &StatsMap->operator[](NameOfWrappedPass);737738BitVector MissingLines{OriginalNumLines, true};739BitVector MissingVars{OriginalNumVars, true};740for (Function &F : Functions) {741if (isFunctionSkipped(F))742continue;743744// Find missing lines.745for (Instruction &I : instructions(F)) {746if (isa<DbgValueInst>(&I))747continue;748749auto DL = I.getDebugLoc();750if (DL && DL.getLine() != 0) {751MissingLines.reset(DL.getLine() - 1);752continue;753}754755if (!isa<PHINode>(&I) && !DL) {756dbg() << "WARNING: Instruction with empty DebugLoc in function ";757dbg() << F.getName() << " --";758I.print(dbg());759dbg() << "\n";760}761}762763// Find missing variables and mis-sized debug values.764auto CheckForMisSized = [&](auto *DbgVal) {765unsigned Var = ~0U;766(void)to_integer(DbgVal->getVariable()->getName(), Var, 10);767assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable");768bool HasBadSize = diagnoseMisSizedDbgValue(M, DbgVal);769if (!HasBadSize)770MissingVars.reset(Var - 1);771HasErrors |= HasBadSize;772};773for (Instruction &I : instructions(F)) {774for (DbgVariableRecord &DVR : filterDbgVars(I.getDbgRecordRange()))775if (DVR.isDbgValue() || DVR.isDbgAssign())776CheckForMisSized(&DVR);777auto *DVI = dyn_cast<DbgValueInst>(&I);778if (!DVI)779continue;780CheckForMisSized(DVI);781}782}783784// Print the results.785for (unsigned Idx : MissingLines.set_bits())786dbg() << "WARNING: Missing line " << Idx + 1 << "\n";787788for (unsigned Idx : MissingVars.set_bits())789dbg() << "WARNING: Missing variable " << Idx + 1 << "\n";790791// Update DI loss statistics.792if (Stats) {793Stats->NumDbgLocsExpected += OriginalNumLines;794Stats->NumDbgLocsMissing += MissingLines.count();795Stats->NumDbgValuesExpected += OriginalNumVars;796Stats->NumDbgValuesMissing += MissingVars.count();797}798799dbg() << Banner;800if (!NameOfWrappedPass.empty())801dbg() << " [" << NameOfWrappedPass << "]";802dbg() << ": " << (HasErrors ? "FAIL" : "PASS") << '\n';803804// Strip debugify metadata if required.805bool Ret = false;806if (Strip)807Ret = stripDebugifyMetadata(M);808809return Ret;810}811812/// ModulePass for attaching synthetic debug info to everything, used with the813/// legacy module pass manager.814struct DebugifyModulePass : public ModulePass {815bool runOnModule(Module &M) override {816bool Result =817applyDebugify(M, Mode, DebugInfoBeforePass, NameOfWrappedPass);818return Result;819}820821DebugifyModulePass(enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,822StringRef NameOfWrappedPass = "",823DebugInfoPerPass *DebugInfoBeforePass = nullptr)824: ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass),825DebugInfoBeforePass(DebugInfoBeforePass), Mode(Mode) {}826827void getAnalysisUsage(AnalysisUsage &AU) const override {828AU.setPreservesAll();829}830831static char ID; // Pass identification.832833private:834StringRef NameOfWrappedPass;835DebugInfoPerPass *DebugInfoBeforePass;836enum DebugifyMode Mode;837};838839/// FunctionPass for attaching synthetic debug info to instructions within a840/// single function, used with the legacy module pass manager.841struct DebugifyFunctionPass : public FunctionPass {842bool runOnFunction(Function &F) override {843bool Result =844applyDebugify(F, Mode, DebugInfoBeforePass, NameOfWrappedPass);845return Result;846}847848DebugifyFunctionPass(849enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,850StringRef NameOfWrappedPass = "",851DebugInfoPerPass *DebugInfoBeforePass = nullptr)852: FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass),853DebugInfoBeforePass(DebugInfoBeforePass), Mode(Mode) {}854855void getAnalysisUsage(AnalysisUsage &AU) const override {856AU.setPreservesAll();857}858859static char ID; // Pass identification.860861private:862StringRef NameOfWrappedPass;863DebugInfoPerPass *DebugInfoBeforePass;864enum DebugifyMode Mode;865};866867/// ModulePass for checking debug info inserted by -debugify, used with the868/// legacy module pass manager.869struct CheckDebugifyModulePass : public ModulePass {870bool runOnModule(Module &M) override {871bool Result;872if (Mode == DebugifyMode::SyntheticDebugInfo)873Result = checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass,874"CheckModuleDebugify", Strip, StatsMap);875else876Result = checkDebugInfoMetadata(877M, M.functions(), *DebugInfoBeforePass,878"CheckModuleDebugify (original debuginfo)", NameOfWrappedPass,879OrigDIVerifyBugsReportFilePath);880881return Result;882}883884CheckDebugifyModulePass(885bool Strip = false, StringRef NameOfWrappedPass = "",886DebugifyStatsMap *StatsMap = nullptr,887enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,888DebugInfoPerPass *DebugInfoBeforePass = nullptr,889StringRef OrigDIVerifyBugsReportFilePath = "")890: ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass),891OrigDIVerifyBugsReportFilePath(OrigDIVerifyBugsReportFilePath),892StatsMap(StatsMap), DebugInfoBeforePass(DebugInfoBeforePass), Mode(Mode),893Strip(Strip) {}894895void getAnalysisUsage(AnalysisUsage &AU) const override {896AU.setPreservesAll();897}898899static char ID; // Pass identification.900901private:902StringRef NameOfWrappedPass;903StringRef OrigDIVerifyBugsReportFilePath;904DebugifyStatsMap *StatsMap;905DebugInfoPerPass *DebugInfoBeforePass;906enum DebugifyMode Mode;907bool Strip;908};909910/// FunctionPass for checking debug info inserted by -debugify-function, used911/// with the legacy module pass manager.912struct CheckDebugifyFunctionPass : public FunctionPass {913bool runOnFunction(Function &F) override {914Module &M = *F.getParent();915auto FuncIt = F.getIterator();916bool Result;917if (Mode == DebugifyMode::SyntheticDebugInfo)918Result = checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),919NameOfWrappedPass, "CheckFunctionDebugify",920Strip, StatsMap);921else922Result = checkDebugInfoMetadata(923M, make_range(FuncIt, std::next(FuncIt)), *DebugInfoBeforePass,924"CheckFunctionDebugify (original debuginfo)", NameOfWrappedPass,925OrigDIVerifyBugsReportFilePath);926927return Result;928}929930CheckDebugifyFunctionPass(931bool Strip = false, StringRef NameOfWrappedPass = "",932DebugifyStatsMap *StatsMap = nullptr,933enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,934DebugInfoPerPass *DebugInfoBeforePass = nullptr,935StringRef OrigDIVerifyBugsReportFilePath = "")936: FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass),937OrigDIVerifyBugsReportFilePath(OrigDIVerifyBugsReportFilePath),938StatsMap(StatsMap), DebugInfoBeforePass(DebugInfoBeforePass), Mode(Mode),939Strip(Strip) {}940941void getAnalysisUsage(AnalysisUsage &AU) const override {942AU.setPreservesAll();943}944945static char ID; // Pass identification.946947private:948StringRef NameOfWrappedPass;949StringRef OrigDIVerifyBugsReportFilePath;950DebugifyStatsMap *StatsMap;951DebugInfoPerPass *DebugInfoBeforePass;952enum DebugifyMode Mode;953bool Strip;954};955956} // end anonymous namespace957958void llvm::exportDebugifyStats(StringRef Path, const DebugifyStatsMap &Map) {959std::error_code EC;960raw_fd_ostream OS{Path, EC};961if (EC) {962errs() << "Could not open file: " << EC.message() << ", " << Path << '\n';963return;964}965966OS << "Pass Name" << ',' << "# of missing debug values" << ','967<< "# of missing locations" << ',' << "Missing/Expected value ratio" << ','968<< "Missing/Expected location ratio" << '\n';969for (const auto &Entry : Map) {970StringRef Pass = Entry.first;971DebugifyStatistics Stats = Entry.second;972973OS << Pass << ',' << Stats.NumDbgValuesMissing << ','974<< Stats.NumDbgLocsMissing << ',' << Stats.getMissingValueRatio() << ','975<< Stats.getEmptyLocationRatio() << '\n';976}977}978979ModulePass *createDebugifyModulePass(enum DebugifyMode Mode,980llvm::StringRef NameOfWrappedPass,981DebugInfoPerPass *DebugInfoBeforePass) {982if (Mode == DebugifyMode::SyntheticDebugInfo)983return new DebugifyModulePass();984assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode");985return new DebugifyModulePass(Mode, NameOfWrappedPass, DebugInfoBeforePass);986}987988FunctionPass *989createDebugifyFunctionPass(enum DebugifyMode Mode,990llvm::StringRef NameOfWrappedPass,991DebugInfoPerPass *DebugInfoBeforePass) {992if (Mode == DebugifyMode::SyntheticDebugInfo)993return new DebugifyFunctionPass();994assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode");995return new DebugifyFunctionPass(Mode, NameOfWrappedPass, DebugInfoBeforePass);996}997998PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) {999if (Mode == DebugifyMode::SyntheticDebugInfo)1000applyDebugifyMetadata(M, M.functions(),1001"ModuleDebugify: ", /*ApplyToMF*/ nullptr);1002else1003collectDebugInfoMetadata(M, M.functions(), *DebugInfoBeforePass,1004"ModuleDebugify (original debuginfo)",1005NameOfWrappedPass);10061007PreservedAnalyses PA;1008PA.preserveSet<CFGAnalyses>();1009return PA;1010}10111012ModulePass *createCheckDebugifyModulePass(1013bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap,1014enum DebugifyMode Mode, DebugInfoPerPass *DebugInfoBeforePass,1015StringRef OrigDIVerifyBugsReportFilePath) {1016if (Mode == DebugifyMode::SyntheticDebugInfo)1017return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap);1018assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode");1019return new CheckDebugifyModulePass(false, NameOfWrappedPass, nullptr, Mode,1020DebugInfoBeforePass,1021OrigDIVerifyBugsReportFilePath);1022}10231024FunctionPass *createCheckDebugifyFunctionPass(1025bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap,1026enum DebugifyMode Mode, DebugInfoPerPass *DebugInfoBeforePass,1027StringRef OrigDIVerifyBugsReportFilePath) {1028if (Mode == DebugifyMode::SyntheticDebugInfo)1029return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap);1030assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode");1031return new CheckDebugifyFunctionPass(false, NameOfWrappedPass, nullptr, Mode,1032DebugInfoBeforePass,1033OrigDIVerifyBugsReportFilePath);1034}10351036PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M,1037ModuleAnalysisManager &) {1038if (Mode == DebugifyMode::SyntheticDebugInfo)1039checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass,1040"CheckModuleDebugify", Strip, StatsMap);1041else1042checkDebugInfoMetadata(1043M, M.functions(), *DebugInfoBeforePass,1044"CheckModuleDebugify (original debuginfo)", NameOfWrappedPass,1045OrigDIVerifyBugsReportFilePath);10461047return PreservedAnalyses::all();1048}10491050static bool isIgnoredPass(StringRef PassID) {1051return isSpecialPass(PassID, {"PassManager", "PassAdaptor",1052"AnalysisManagerProxy", "PrintFunctionPass",1053"PrintModulePass", "BitcodeWriterPass",1054"ThinLTOBitcodeWriterPass", "VerifierPass"});1055}10561057void DebugifyEachInstrumentation::registerCallbacks(1058PassInstrumentationCallbacks &PIC, ModuleAnalysisManager &MAM) {1059PIC.registerBeforeNonSkippedPassCallback([this, &MAM](StringRef P, Any IR) {1060if (isIgnoredPass(P))1061return;1062PreservedAnalyses PA;1063PA.preserveSet<CFGAnalyses>();1064if (const auto **CF = llvm::any_cast<const Function *>(&IR)) {1065Function &F = *const_cast<Function *>(*CF);1066applyDebugify(F, Mode, DebugInfoBeforePass, P);1067MAM.getResult<FunctionAnalysisManagerModuleProxy>(*F.getParent())1068.getManager()1069.invalidate(F, PA);1070} else if (const auto **CM = llvm::any_cast<const Module *>(&IR)) {1071Module &M = *const_cast<Module *>(*CM);1072applyDebugify(M, Mode, DebugInfoBeforePass, P);1073MAM.invalidate(M, PA);1074}1075});1076PIC.registerAfterPassCallback(1077[this, &MAM](StringRef P, Any IR, const PreservedAnalyses &PassPA) {1078if (isIgnoredPass(P))1079return;1080PreservedAnalyses PA;1081PA.preserveSet<CFGAnalyses>();1082if (const auto **CF = llvm::any_cast<const Function *>(&IR)) {1083auto &F = *const_cast<Function *>(*CF);1084Module &M = *F.getParent();1085auto It = F.getIterator();1086if (Mode == DebugifyMode::SyntheticDebugInfo)1087checkDebugifyMetadata(M, make_range(It, std::next(It)), P,1088"CheckFunctionDebugify", /*Strip=*/true,1089DIStatsMap);1090else1091checkDebugInfoMetadata(M, make_range(It, std::next(It)),1092*DebugInfoBeforePass,1093"CheckModuleDebugify (original debuginfo)",1094P, OrigDIVerifyBugsReportFilePath);1095MAM.getResult<FunctionAnalysisManagerModuleProxy>(*F.getParent())1096.getManager()1097.invalidate(F, PA);1098} else if (const auto **CM = llvm::any_cast<const Module *>(&IR)) {1099Module &M = *const_cast<Module *>(*CM);1100if (Mode == DebugifyMode::SyntheticDebugInfo)1101checkDebugifyMetadata(M, M.functions(), P, "CheckModuleDebugify",1102/*Strip=*/true, DIStatsMap);1103else1104checkDebugInfoMetadata(M, M.functions(), *DebugInfoBeforePass,1105"CheckModuleDebugify (original debuginfo)",1106P, OrigDIVerifyBugsReportFilePath);1107MAM.invalidate(M, PA);1108}1109});1110}11111112char DebugifyModulePass::ID = 0;1113static RegisterPass<DebugifyModulePass> DM("debugify",1114"Attach debug info to everything");11151116char CheckDebugifyModulePass::ID = 0;1117static RegisterPass<CheckDebugifyModulePass>1118CDM("check-debugify", "Check debug info from -debugify");11191120char DebugifyFunctionPass::ID = 0;1121static RegisterPass<DebugifyFunctionPass> DF("debugify-function",1122"Attach debug info to a function");11231124char CheckDebugifyFunctionPass::ID = 0;1125static RegisterPass<CheckDebugifyFunctionPass>1126CDF("check-debugify-function", "Check debug info from -debugify-function");112711281129