//===- MachineCheckDebugify.cpp - Check debug info ------------------------===// // // 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 checks debug info after mir-debugify (+ pass-to-test). Currently /// it simply checks the integrity of line info in DILocation and /// DILocalVariable which mir-debugifiy generated before. //===----------------------------------------------------------------------===// #include "llvm/ADT/StringExtras.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #define DEBUG_TYPE "mir-check-debugify" using namespace llvm; namespace { struct CheckDebugMachineModule : public ModulePass { bool runOnModule(Module &M) override { NamedMDNode *NMD = M.getNamedMetadata("llvm.mir.debugify"); if (!NMD) { errs() << "WARNING: Please run mir-debugify to generate " "llvm.mir.debugify metadata first.\n"; return false; } MachineModuleInfo &MMI = getAnalysis().getMMI(); auto getDebugifyOperand = [&](unsigned Idx) -> unsigned { return mdconst::extract(NMD->getOperand(Idx)->getOperand(0)) ->getZExtValue(); }; assert(NMD->getNumOperands() == 2 && "llvm.mir.debugify should have exactly 2 operands!"); unsigned NumLines = getDebugifyOperand(0); unsigned NumVars = getDebugifyOperand(1); BitVector MissingLines{NumLines, true}; BitVector MissingVars{NumVars, true}; for (Function &F : M.functions()) { MachineFunction *MF = MMI.getMachineFunction(F); if (!MF) continue; for (MachineBasicBlock &MBB : *MF) { // Find missing lines. // TODO: Avoid meta instructions other than dbg_val. for (MachineInstr &MI : MBB) { if (MI.isDebugValue()) continue; const DebugLoc DL = MI.getDebugLoc(); if (DL && DL.getLine() != 0) { MissingLines.reset(DL.getLine() - 1); continue; } if (!DL) { errs() << "WARNING: Instruction with empty DebugLoc in function "; errs() << F.getName() << " --"; MI.print(errs()); } } // Find missing variables. // TODO: Handle DBG_INSTR_REF which is under an experimental option now. for (MachineInstr &MI : MBB) { if (!MI.isDebugValue()) continue; const DILocalVariable *LocalVar = MI.getDebugVariable(); unsigned Var = ~0U; (void)to_integer(LocalVar->getName(), Var, 10); assert(Var <= NumVars && "Unexpected name for DILocalVariable"); MissingVars.reset(Var - 1); } } } bool Fail = false; for (unsigned Idx : MissingLines.set_bits()) { errs() << "WARNING: Missing line " << Idx + 1 << "\n"; Fail = true; } for (unsigned Idx : MissingVars.set_bits()) { errs() << "WARNING: Missing variable " << Idx + 1 << "\n"; Fail = true; } errs() << "Machine IR debug info check: "; errs() << (Fail ? "FAIL" : "PASS") << "\n"; return false; } CheckDebugMachineModule() : ModulePass(ID) {} void getAnalysisUsage(AnalysisUsage &AU) const override { AU.addRequired(); AU.setPreservesAll(); } static char ID; // Pass identification. }; char CheckDebugMachineModule::ID = 0; } // end anonymous namespace INITIALIZE_PASS_BEGIN(CheckDebugMachineModule, DEBUG_TYPE, "Machine Check Debug Module", false, false) INITIALIZE_PASS_END(CheckDebugMachineModule, DEBUG_TYPE, "Machine Check Debug Module", false, false) ModulePass *llvm::createCheckDebugMachineModulePass() { return new CheckDebugMachineModule(); }