Index: llvm/docs/HowToUpdateDebugInfo.rst =================================================================== --- llvm/docs/HowToUpdateDebugInfo.rst +++ llvm/docs/HowToUpdateDebugInfo.rst @@ -342,8 +342,8 @@ :ref:`Mutation testing for IR-level transformations` can be used for MIR-level transformations as well: much like the IR-level pass, ``mir-debugify`` inserts sequentially increasing line locations to each -``MachineInstr`` in a ``Module`` (although there is no equivalent MIR-level -``check-debugify`` pass). +``MachineInstr`` in a ``Module``. And the MIR-level ``mir-check-debugify`` is +similar to IR-level ``check-debugify`` pass. For example, here is a snippet before: @@ -403,16 +403,32 @@ $ llc -debugify-and-strip-all-safe -run-pass=... $ llc -debugify-and-strip-all-safe -O1 +If you want to check it after each pass in a pipeline, use +``-debugify-check-and-strip-all-safe``. This can also be combined with +``-start-before`` and ``-start-after``. For example: + +.. code-block:: bash + + $ llc -debugify-check-and-strip-all-safe -run-pass=... + $ llc -debugify-check-and-strip-all-safe -O1 + +To check all debug info from a test, use ``mir-check-debugify``, like: + +.. code-block:: bash + + $ llc -run-pass=mir-debugify,other-pass,mir-check-debugify + To strip out all debug info from a test, use ``mir-strip-debug``, like: .. code-block:: bash $ llc -run-pass=mir-debugify,other-pass,mir-strip-debug -It can be useful to combine ``mir-debugify`` and ``mir-strip-debug`` to -identify backend transformations which break in the presence of debug info. -For example, to run the AArch64 backend tests with all normal passes -"sandwiched" in between MIRDebugify and MIRStripDebugify mutation passes, run: +It can be useful to combine ``mir-debugify``, ``mir-check-debugify`` and/or +``mir-strip-debug`` to identify backend transformations which break in +the presence of debug info. For example, to run the AArch64 backend tests +with all normal passes "sandwiched" in between MIRDebugify and +MIRStripDebugify mutation passes, run: .. code-block:: bash Index: llvm/include/llvm/CodeGen/Passes.h =================================================================== --- llvm/include/llvm/CodeGen/Passes.h +++ llvm/include/llvm/CodeGen/Passes.h @@ -482,6 +482,9 @@ /// info was generated by another source such as clang. ModulePass *createStripDebugMachineModulePass(bool OnlyDebugified); + /// Creates MIR Check Debug pass. \see MachineCheckDebugify.cpp + ModulePass *createCheckDebugMachineModulePass(); + /// The pass fixups statepoint machine instruction to replace usage of /// caller saved registers with stack slots. extern char &FixupStatepointCallerSavedID; Index: llvm/include/llvm/CodeGen/TargetPassConfig.h =================================================================== --- llvm/include/llvm/CodeGen/TargetPassConfig.h +++ llvm/include/llvm/CodeGen/TargetPassConfig.h @@ -313,6 +313,9 @@ /// Add a pass to remove debug info from the MIR. void addStripDebugPass(); + /// Add a pass to check synthesized debug info for MIR. + void addCheckDebugPass(); + /// Add standard passes before a pass that's about to be added. For example, /// the DebugifyMachineModulePass if it is enabled. void addMachinePrePasses(bool AllowDebugify = true); Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -112,6 +112,7 @@ void initializeCallGraphWrapperPassPass(PassRegistry&); void initializeCallSiteSplittingLegacyPassPass(PassRegistry&); void initializeCalledValuePropagationLegacyPassPass(PassRegistry &); +void initializeCheckDebugMachineModulePass(PassRegistry &); void initializeCodeGenPreparePass(PassRegistry&); void initializeConstantHoistingLegacyPassPass(PassRegistry&); void initializeConstantMergeLegacyPassPass(PassRegistry&); Index: llvm/lib/CodeGen/CMakeLists.txt =================================================================== --- llvm/lib/CodeGen/CMakeLists.txt +++ llvm/lib/CodeGen/CMakeLists.txt @@ -74,6 +74,7 @@ MachineCombiner.cpp MachineCopyPropagation.cpp MachineCSE.cpp + MachineCheckDebugify.cpp MachineDebugify.cpp MachineDominanceFrontier.cpp MachineDominators.cpp Index: llvm/lib/CodeGen/CodeGen.cpp =================================================================== --- llvm/lib/CodeGen/CodeGen.cpp +++ llvm/lib/CodeGen/CodeGen.cpp @@ -25,6 +25,7 @@ initializeBranchRelaxationPass(Registry); initializeCFGuardLongjmpPass(Registry); initializeCFIInstrInserterPass(Registry); + initializeCheckDebugMachineModulePass(Registry); initializeCodeGenPreparePass(Registry); initializeDeadMachineInstructionElimPass(Registry); initializeDebugifyMachineModulePass(Registry); Index: llvm/lib/CodeGen/MachineCheckDebugify.cpp =================================================================== --- /dev/null +++ llvm/lib/CodeGen/MachineCheckDebugify.cpp @@ -0,0 +1,126 @@ +//===- 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/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Transforms/Utils/Debugify.h" + +#define DEBUG_TYPE "mir-check-debugify" + +using namespace llvm; + +namespace { + +struct CheckDebugMachineModule : public ModulePass { + bool runOnModule(Module &M) override { + MachineModuleInfo &MMI = + getAnalysis().getMMI(); + + 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; + } + + 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.addPreserved(); + AU.setPreservesCFG(); + } + + 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(); +} Index: llvm/lib/CodeGen/MachineDebugify.cpp =================================================================== --- llvm/lib/CodeGen/MachineDebugify.cpp +++ llvm/lib/CodeGen/MachineDebugify.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" @@ -89,10 +90,11 @@ // Do this by introducing debug uses of each register definition. If that is // not possible (e.g. we have a phi or a meta instruction), emit a constant. uint64_t NextImm = 0; + SmallSet VarSet; const MCInstrDesc &DbgValDesc = TII.get(TargetOpcode::DBG_VALUE); for (MachineBasicBlock &MBB : MF) { MachineBasicBlock::iterator FirstNonPHIIt = MBB.getFirstNonPHI(); - for (auto I = MBB.begin(), E = MBB.end(); I != E; ) { + for (auto I = MBB.begin(), E = MBB.end(); I != E;) { MachineInstr &MI = *I; ++I; @@ -113,6 +115,7 @@ Line = EarliestDVI->getDebugLoc().getLine(); DILocalVariable *LocalVar = Line2Var[Line]; assert(LocalVar && "No variable for current line?"); + VarSet.insert(LocalVar); // Emit DBG_VALUEs for register definitions. SmallVector RegDefs; @@ -132,6 +135,33 @@ } } + // Here we save the number of lines and variables into "llvm.mir.debugify". + // It is useful for mir-check-debugify. + NamedMDNode *NMD = M.getNamedMetadata("llvm.mir.debugify"); + IntegerType *Int32Ty = Type::getInt32Ty(Ctx); + if (!NMD) { + NMD = M.getOrInsertNamedMetadata("llvm.mir.debugify"); + auto addDebugifyOperand = [&](unsigned N) { + NMD->addOperand(MDNode::get( + Ctx, ValueAsMetadata::getConstant(ConstantInt::get(Int32Ty, N)))); + }; + // Add number of lines. + addDebugifyOperand(NextLine - 1); + // Add number of variables. + addDebugifyOperand(VarSet.size()); + } else { + assert(NMD->getNumOperands() == 2 && + "llvm.mir.debugify should have exactly 2 operands!"); + auto setDebugifyOperand = [&](unsigned Idx, unsigned N) { + NMD->setOperand(Idx, MDNode::get(Ctx, ValueAsMetadata::getConstant( + ConstantInt::get(Int32Ty, N)))); + }; + // Set number of lines. + setDebugifyOperand(0, NextLine - 1); + // Set number of variables. + setDebugifyOperand(1, VarSet.size()); + } + return true; } Index: llvm/lib/CodeGen/TargetPassConfig.cpp =================================================================== --- llvm/lib/CodeGen/TargetPassConfig.cpp +++ llvm/lib/CodeGen/TargetPassConfig.cpp @@ -120,6 +120,12 @@ "Debugify MIR before and Strip debug after " "each pass except those known to be unsafe when debug info is present"), cl::ZeroOrMore); +static cl::opt DebugifyCheckAndStripAll( + "debugify-check-and-strip-all-safe", cl::Hidden, + cl::desc( + "Debugify MIR before, by checking and stripping the debug info after, " + "each pass except those known to be unsafe when debug info is present"), + cl::ZeroOrMore); enum RunOutliner { AlwaysOutline, NeverOutline, TargetDefault }; // Enable or disable the MachineOutliner. static cl::opt EnableMachineOutliner( @@ -625,15 +631,26 @@ PM->add(createStripDebugMachineModulePass(/*OnlyDebugified=*/true)); } +void TargetPassConfig::addCheckDebugPass() { + PM->add(createCheckDebugMachineModulePass()); +} + void TargetPassConfig::addMachinePrePasses(bool AllowDebugify) { - if (AllowDebugify && DebugifyAndStripAll == cl::BOU_TRUE && DebugifyIsSafe) + if (AllowDebugify && DebugifyIsSafe && + (DebugifyAndStripAll == cl::BOU_TRUE || + DebugifyCheckAndStripAll == cl::BOU_TRUE)) addDebugifyPass(); } void TargetPassConfig::addMachinePostPasses(const std::string &Banner, bool AllowVerify, bool AllowStrip) { - if (DebugifyAndStripAll == cl::BOU_TRUE && DebugifyIsSafe) - addStripDebugPass(); + if (DebugifyIsSafe) { + if (DebugifyCheckAndStripAll == cl::BOU_TRUE) { + addCheckDebugPass(); + addStripDebugPass(); + } else if (DebugifyAndStripAll == cl::BOU_TRUE) + addStripDebugPass(); + } if (AllowVerify) addVerifyPass(Banner); } Index: llvm/test/CodeGen/AArch64/GlobalISel/constant-mir-debugify.mir =================================================================== --- llvm/test/CodeGen/AArch64/GlobalISel/constant-mir-debugify.mir +++ llvm/test/CodeGen/AArch64/GlobalISel/constant-mir-debugify.mir @@ -13,17 +13,17 @@ liveins: $x0 ; CHECK-LABEL: name: fconstant_to_constant_s32 ; CHECK: liveins: $x0 - ; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x0, debug-location !10 - ; CHECK: DBG_VALUE [[COPY]](p0), $noreg, !8, !DIExpression(), debug-location !10 - ; CHECK: [[C:%[0-9]+]]:_(s32) = G_FCONSTANT float 0x3FA99999A0000000, debug-location !DILocation(line: 2, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[C]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 2, column: 1, scope: !5) - ; CHECK: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 524, debug-location !DILocation(line: 3, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[C1]](s64), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 3, column: 1, scope: !5) - ; CHECK: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[COPY]], [[C1]](s64), debug-location !DILocation(line: 4, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[PTR_ADD]](p0), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 4, column: 1, scope: !5) - ; CHECK: G_STORE [[C]](s32), [[PTR_ADD]](p0), debug-location !DILocation(line: 5, column: 1, scope: !5) :: (store 4) - ; CHECK: DBG_VALUE 0, $noreg, !8, !DIExpression(), debug-location !DILocation(line: 5, column: 1, scope: !5) - ; CHECK: RET_ReallyLR debug-location !DILocation(line: 6, column: 1, scope: !5) + ; CHECK: [[COPY:%[0-9]+]]:_(p0) = COPY $x0, debug-location !11 + ; CHECK: DBG_VALUE [[COPY]](p0), $noreg, !9, !DIExpression(), debug-location !11 + ; CHECK: [[C:%[0-9]+]]:_(s32) = G_FCONSTANT float 0x3FA99999A0000000, debug-location !DILocation(line: 2, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[C]](s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 2, column: 1, scope: !6) + ; CHECK: [[C1:%[0-9]+]]:_(s64) = G_CONSTANT i64 524, debug-location !DILocation(line: 3, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[C1]](s64), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 3, column: 1, scope: !6) + ; CHECK: [[PTR_ADD:%[0-9]+]]:_(p0) = G_PTR_ADD [[COPY]], [[C1]](s64), debug-location !DILocation(line: 4, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[PTR_ADD]](p0), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 4, column: 1, scope: !6) + ; CHECK: G_STORE [[C]](s32), [[PTR_ADD]](p0), debug-location !DILocation(line: 5, column: 1, scope: !6) :: (store 4) + ; CHECK: DBG_VALUE 0, $noreg, !9, !DIExpression(), debug-location !DILocation(line: 5, column: 1, scope: !6) + ; CHECK: RET_ReallyLR debug-location !DILocation(line: 6, column: 1, scope: !6) %0:_(p0) = COPY $x0 %3:_(s32) = G_FCONSTANT float 0x3FA99999A0000000 %1:_(s64) = G_CONSTANT i64 524 Index: llvm/test/CodeGen/AArch64/GlobalISel/phi-mir-debugify.mir =================================================================== --- llvm/test/CodeGen/AArch64/GlobalISel/phi-mir-debugify.mir +++ llvm/test/CodeGen/AArch64/GlobalISel/phi-mir-debugify.mir @@ -36,41 +36,41 @@ ; CHECK: bb.0: ; CHECK: successors: %bb.1(0x40000000), %bb.2(0x40000000) ; CHECK: liveins: $w0 - ; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $w0, debug-location !10 - ; CHECK: DBG_VALUE [[COPY]](s32), $noreg, !8, !DIExpression(), debug-location !10 - ; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0, debug-location !DILocation(line: 2, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[C]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 2, column: 1, scope: !5) - ; CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1, debug-location !DILocation(line: 3, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[C1]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 3, column: 1, scope: !5) - ; CHECK: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 2, debug-location !DILocation(line: 4, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[C2]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 4, column: 1, scope: !5) - ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(ugt), [[COPY]](s32), [[C]], debug-location !DILocation(line: 5, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[ICMP]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 5, column: 1, scope: !5) - ; CHECK: G_BRCOND [[ICMP]](s1), %bb.1, debug-location !DILocation(line: 6, column: 1, scope: !5) - ; CHECK: G_BR %bb.2, debug-location !DILocation(line: 7, column: 1, scope: !5) + ; CHECK: [[COPY:%[0-9]+]]:_(s32) = COPY $w0, debug-location !11 + ; CHECK: DBG_VALUE [[COPY]](s32), $noreg, !9, !DIExpression(), debug-location !11 + ; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0, debug-location !DILocation(line: 2, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[C]](s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 2, column: 1, scope: !6) + ; CHECK: [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 1, debug-location !DILocation(line: 3, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[C1]](s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 3, column: 1, scope: !6) + ; CHECK: [[C2:%[0-9]+]]:_(s32) = G_CONSTANT i32 2, debug-location !DILocation(line: 4, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[C2]](s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 4, column: 1, scope: !6) + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(ugt), [[COPY]](s32), [[C]], debug-location !DILocation(line: 5, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[ICMP]](s1), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 5, column: 1, scope: !6) + ; CHECK: G_BRCOND [[ICMP]](s1), %bb.1, debug-location !DILocation(line: 6, column: 1, scope: !6) + ; CHECK: G_BR %bb.2, debug-location !DILocation(line: 7, column: 1, scope: !6) ; CHECK: bb.1: ; CHECK: successors: %bb.3(0x80000000) - ; CHECK: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[C1]], debug-location !DILocation(line: 8, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[ADD]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 8, column: 1, scope: !5) - ; CHECK: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ADD]](s32), debug-location !DILocation(line: 9, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[TRUNC]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 9, column: 1, scope: !5) - ; CHECK: G_BR %bb.3, debug-location !DILocation(line: 10, column: 1, scope: !5) + ; CHECK: [[ADD:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[C1]], debug-location !DILocation(line: 8, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[ADD]](s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 8, column: 1, scope: !6) + ; CHECK: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ADD]](s32), debug-location !DILocation(line: 9, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[TRUNC]](s1), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 9, column: 1, scope: !6) + ; CHECK: G_BR %bb.3, debug-location !DILocation(line: 10, column: 1, scope: !6) ; CHECK: bb.2: ; CHECK: successors: %bb.3(0x80000000) - ; CHECK: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[C2]], debug-location !DILocation(line: 11, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[ADD1]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 11, column: 1, scope: !5) - ; CHECK: [[TRUNC1:%[0-9]+]]:_(s1) = G_TRUNC [[ADD1]](s32), debug-location !DILocation(line: 12, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[TRUNC1]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 12, column: 1, scope: !5) + ; CHECK: [[ADD1:%[0-9]+]]:_(s32) = G_ADD [[COPY]], [[C2]], debug-location !DILocation(line: 11, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[ADD1]](s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 11, column: 1, scope: !6) + ; CHECK: [[TRUNC1:%[0-9]+]]:_(s1) = G_TRUNC [[ADD1]](s32), debug-location !DILocation(line: 12, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[TRUNC1]](s1), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 12, column: 1, scope: !6) ; CHECK: bb.3: - ; CHECK: [[PHI:%[0-9]+]]:_(s1) = G_PHI [[TRUNC]](s1), %bb.1, [[TRUNC1]](s1), %bb.2, debug-location !DILocation(line: 13, column: 1, scope: !5) - ; CHECK: [[PHI1:%[0-9]+]]:_(s1) = G_PHI [[TRUNC]](s1), %bb.1, [[TRUNC1]](s1), %bb.2, debug-location !DILocation(line: 14, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[PHI]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 13, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[PHI1]](s1), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 14, column: 1, scope: !5) - ; CHECK: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[PHI]](s1), debug-location !DILocation(line: 15, column: 1, scope: !5) - ; CHECK: DBG_VALUE [[ZEXT]](s32), $noreg, !8, !DIExpression(), debug-location !DILocation(line: 15, column: 1, scope: !5) - ; CHECK: $w0 = COPY [[ZEXT]](s32), debug-location !DILocation(line: 16, column: 1, scope: !5) - ; CHECK: DBG_VALUE $w0, $noreg, !8, !DIExpression(), debug-location !DILocation(line: 16, column: 1, scope: !5) - ; CHECK: RET_ReallyLR implicit $w0, debug-location !DILocation(line: 17, column: 1, scope: !5) + ; CHECK: [[PHI:%[0-9]+]]:_(s1) = G_PHI [[TRUNC]](s1), %bb.1, [[TRUNC1]](s1), %bb.2, debug-location !DILocation(line: 13, column: 1, scope: !6) + ; CHECK: [[PHI1:%[0-9]+]]:_(s1) = G_PHI [[TRUNC]](s1), %bb.1, [[TRUNC1]](s1), %bb.2, debug-location !DILocation(line: 14, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[PHI]](s1), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 13, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[PHI1]](s1), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 14, column: 1, scope: !6) + ; CHECK: [[ZEXT:%[0-9]+]]:_(s32) = G_ZEXT [[PHI]](s1), debug-location !DILocation(line: 15, column: 1, scope: !6) + ; CHECK: DBG_VALUE [[ZEXT]](s32), $noreg, !9, !DIExpression(), debug-location !DILocation(line: 15, column: 1, scope: !6) + ; CHECK: $w0 = COPY [[ZEXT]](s32), debug-location !DILocation(line: 16, column: 1, scope: !6) + ; CHECK: DBG_VALUE $w0, $noreg, !9, !DIExpression(), debug-location !DILocation(line: 16, column: 1, scope: !6) + ; CHECK: RET_ReallyLR implicit $w0, debug-location !DILocation(line: 17, column: 1, scope: !6) bb.0: successors: %bb.1(0x40000000), %bb.2(0x40000000) liveins: $w0 Index: llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables-x.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables-x.mir @@ -0,0 +1,79 @@ +# RUN: llc -mtriple=x86_64-unknown-linux-gnu -run-pass=mir-check-debugify -o - %s 2>&1 | FileCheck %s +--- | + ; ModuleID = 'check-line-and-variables.mir' + source_filename = "check-line-and-variables.c" + + @ga = dso_local local_unnamed_addr global i32 2, align 4 + + ; Function Attrs: nofree norecurse nounwind uwtable writeonly + define dso_local i32 @foo(i32 %a, i32 %b) local_unnamed_addr #0 !dbg !9 { + entry: + %add = add nsw i32 %b, %a, !dbg !15 + call void @llvm.dbg.value(metadata i32 %add, metadata !12, metadata !DIExpression()), !dbg !15 + %mul = shl nsw i32 %add, 1, !dbg !16 + call void @llvm.dbg.value(metadata i32 %mul, metadata !14, metadata !DIExpression()), !dbg !16 + store i32 %mul, i32* @ga, align 4, !dbg !17 + ret i32 %add, !dbg !18 + } + + declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + + !llvm.module.flags = !{!0, !1} + !llvm.ident = !{!2} + !llvm.dbg.cu = !{!3} + !llvm.mir.debugify = !{!6, !7} + !llvm.debugify = !{!8, !7} + + !0 = !{i32 1, !"wchar_size", i32 4} + !1 = !{i32 2, !"Debug Info Version", i32 3} + !2 = !{!"clang version 12.0.0 (https://github.com/llvm/llvm-project.git c0a922b3db2d39f36c0c01776cce90cc160a7d62)"} + !3 = distinct !DICompileUnit(language: DW_LANG_C, file: !4, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !5) + !4 = !DIFile(filename: "main.mir", directory: "/") + !5 = !{} + !6 = !{i32 6} + !7 = !{i32 2} + !8 = !{i32 4} + !9 = distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: !4, line: 1, type: !10, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !11) + !10 = !DISubroutineType(types: !5) + !11 = !{!12, !14} + !12 = !DILocalVariable(name: "1", scope: !9, file: !4, line: 1, type: !13) + !13 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned) + !14 = !DILocalVariable(name: "2", scope: !9, file: !4, line: 2, type: !13) + !15 = !DILocation(line: 1, column: 1, scope: !9) + !16 = !DILocation(line: 2, column: 1, scope: !9) + !17 = !DILocation(line: 3, column: 1, scope: !9) + !18 = !DILocation(line: 4, column: 1, scope: !9) + +... +--- +name: foo +body: | + bb.0.entry: + liveins: $edi, $esi + + %1:gr32 = COPY $esi, debug-location !15 + DBG_VALUE %1, $noreg, !12, !DIExpression(), debug-location !15 + ; Let it missing !14 and debug-location !16 + ; %0:gr32 = COPY $edi, debug-location !16 + ; DBG_VALUE %0, $noreg, !14, !DIExpression(), debug-location !16 + %0:gr32 = COPY $edi + %2:gr32 = nsw ADD32rr %1, %0, implicit-def dead $eflags, debug-location !17 + DBG_VALUE %2, $noreg, !12, !DIExpression(), debug-location !17 + DBG_VALUE $eflags, $noreg, !12, !DIExpression(), debug-location !17 + %3:gr32 = nsw ADD32rr %2, %2, implicit-def dead $eflags, debug-location !18 + DBG_VALUE %3, $noreg, !12, !DIExpression(), debug-location !18 + DBG_VALUE $eflags, $noreg, !12, !DIExpression(), debug-location !18 + MOV32mr $rip, 1, $noreg, @ga, $noreg, killed %3, debug-location !DILocation(line: 5, column: 1, scope: !9) :: (store 4 into @ga, !tbaa !18) + DBG_VALUE 0, $noreg, !12, !DIExpression(), debug-location !DILocation(line: 5, column: 1, scope: !9) + ; Let it miss Line 6: Change "!DILocation(line: 6, ..." to "!DILocation(line: 5, ..." + $eax = COPY %2, debug-location !DILocation(line: 5, column: 1, scope: !9) + DBG_VALUE $eax, $noreg, !12, !DIExpression(), debug-location !DILocation(line: 6, column: 1, scope: !9) + RET 0, $eax, debug-location !DILocation(line: 7, column: 1, scope: !9) + + ;CHECK: WARNING: Instruction with empty DebugLoc in function foo --%1:gr32 = COPY $edi + ;CHECK-NEXT: WARNING: Missing line 2 + ;CHECK-NEXT: WARNING: Missing line 6 + ;CHECK-NEXT: WARNING: Missing variable 2 + ;CHECK-NEXT: Machine IR debug info check: FAIL + +... Index: llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.ll @@ -0,0 +1,29 @@ +; RUN: llc -debugify-check-and-strip-all-safe -o - %s 2>&1 | FileCheck %s + +; ModuleID = 'main.c' +source_filename = "main.c" + +@ga = dso_local global i32 2, align 4 + +define dso_local i32 @foo(i32 %a, i32 %b) { +entry: + %a.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + store i32 %b, i32* %b.addr, align 4 + %0 = load i32, i32* %a.addr, align 4 + %1 = load i32, i32* %b.addr, align 4 + %add = add nsw i32 %0, %1 + store i32 %add, i32* %c, align 4 + %2 = load i32, i32* %c, align 4 + %mul = mul nsw i32 %2, 2 + store i32 %mul, i32* @ga, align 4 + %3 = load i32, i32* %c, align 4 + ret i32 %3 +} + +; Different Back-Ends may have different number of passes, here we only +; check two of them to make sure -debugify-check-and-strip-all-safe works. +;CHECK: Machine IR debug info check: PASS +;CHECK: Machine IR debug info check: PASS Index: llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/Generic/MIRDebugify/check-line-and-variables.mir @@ -0,0 +1,69 @@ +# RUN: llc -mtriple=x86_64-unknown-linux-gnu -run-pass=mir-debugify,dead-mi-elimination,mir-check-debugify -o - %s 2>&1 | FileCheck %s +# RUN: llc -mtriple=x86_64-unknown-linux-gnu -run-pass=mir-debugify,mir-check-debugify -o - %s 2>&1 | FileCheck %s --check-prefix=CHECK-PASS +--- | + ; ModuleID = 'check-line-and-variables.mir' + source_filename = "check-line-and-variables.ll" + + @ga = dso_local global i32 2, align 4 + + ; Function Attrs: noinline nounwind optnone uwtable + define dso_local i32 @foo(i32 %a, i32 %b) { + entry: + %a.addr = alloca i32, align 4 + %b.addr = alloca i32, align 4 + %c = alloca i32, align 4 + store i32 %a, i32* %a.addr, align 4 + store i32 %b, i32* %b.addr, align 4 + %0 = load i32, i32* %a.addr, align 4 + %1 = load i32, i32* %b.addr, align 4 + %add = add nsw i32 %0, %1 + store i32 %add, i32* %c, align 4 + %2 = load i32, i32* %c, align 4 + %mul = mul nsw i32 %2, 2 + store i32 %mul, i32* @ga, align 4 + %3 = load i32, i32* %c, align 4 + ; dead-mi-elimination will remove %4 = ... + %4 = load i32, i32* %a.addr, align 4 + ret i32 %3 + } + +... +--- +name: foo +alignment: 16 +stack: + - { id: 0, name: a.addr, type: default, offset: 0, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: b.addr, type: default, offset: 0, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 2, name: c, type: default, offset: 0, size: 4, alignment: 4, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +body: | + bb.0.entry: + liveins: $edi, $esi + + %2:gr32 = COPY $esi + %0:gr32 = COPY $edi + %1:gr32 = COPY killed %0 + %3:gr32 = COPY killed %2 + MOV32mr %stack.0.a.addr, 1, $noreg, 0, $noreg, %1 :: (store 4 into %ir.a.addr) + MOV32mr %stack.1.b.addr, 1, $noreg, 0, $noreg, %3 :: (store 4 into %ir.b.addr) + %14:gr32 = MOV32rm %stack.0.a.addr, 1, $noreg, 0, $noreg :: (load 4 from %ir.a.addr) + %13:gr32 = ADD32rm killed %14, %stack.1.b.addr, 1, $noreg, 0, $noreg, implicit-def $eflags :: (load 4 from %ir.b.addr) + ; dead-mi-elimination will remove %15:gr32 = ... + %15:gr32 = MOV32rm %stack.0.a.addr, 1, $noreg, 0, $noreg :: (load 4 from %ir.a.addr) + MOV32mr %stack.2.c, 1, $noreg, 0, $noreg, killed %13 :: (store 4 into %ir.c) + %9:gr32 = MOV32rm %stack.2.c, 1, $noreg, 0, $noreg :: (load 4 from %ir.c) + %8:gr32 = SHL32ri killed %9, 1, implicit-def $eflags + MOV32mr $noreg, 1, $noreg, @ga, $noreg, killed %8 :: (store 4 into @ga) + %5:gr32 = MOV32rm %stack.2.c, 1, $noreg, 0, $noreg :: (load 4 from %ir.c) + $eax = COPY %5 + RETQ implicit $eax + + ;CHECK: WARNING: Missing line 9 + ;CHECK-NEXT: Machine IR debug info check: FAIL + ;CHECK-PASS: Machine IR debug info check: PASS +... Index: llvm/test/CodeGen/Generic/MIRDebugify/locations-and-values.mir =================================================================== --- llvm/test/CodeGen/Generic/MIRDebugify/locations-and-values.mir +++ llvm/test/CodeGen/Generic/MIRDebugify/locations-and-values.mir @@ -17,10 +17,10 @@ ret i32 %sub } - ; ALL: !llvm.dbg.cu = !{!0} + ; ALL: !llvm.dbg.cu = !{![[CU:[0-9]+]]} ; ALL: !llvm.debugify = ; ALL: !llvm.module.flags = !{![[VERSION:[0-9]+]]} - ; ALL: !0 = distinct !DICompileUnit( + ; ALL: ![[CU]] = distinct !DICompileUnit( ; ALL: ![[VERSION]] = !{i32 2, !"Debug Info Version", i32 3} ; VALUE: [[VAR1:![0-9]+]] = !DILocalVariable(name: "1" ; VALUE: [[VAR2:![0-9]+]] = !DILocalVariable(name: "2" @@ -44,8 +44,8 @@ ; VALUE: DBG_VALUE %1(s32), $noreg, [[VAR2]], !DIExpression(), debug-location [[L2]] ; ALL: %2:_(s32) = G_CONSTANT i32 2, debug-location [[L3]] ; VALUE: DBG_VALUE %2(s32), $noreg, [[VAR1]], !DIExpression(), debug-location [[L3]] - ; ALL: %3:_(s32) = G_ADD %0, %2, debug-location !DILocation(line: 4, column: 1, scope: !6) + ; ALL: %3:_(s32) = G_ADD %0, %2, debug-location !DILocation(line: 4, column: 1, scope: [[SP:![0-9]+]]) ; VALUE: DBG_VALUE %3(s32), $noreg, [[VAR1]], !DIExpression(), debug-location !DILocation(line: 4 - ; ALL: %4:_(s32) = G_SUB %3, %1, debug-location !DILocation(line: 5, column: 1, scope: !6) + ; ALL: %4:_(s32) = G_SUB %3, %1, debug-location !DILocation(line: 5, column: 1, scope: [[SP]]) ; VALUE: DBG_VALUE %4(s32), $noreg, [[VAR1]], !DIExpression(), debug-location !DILocation(line: 5 ... Index: llvm/utils/gn/secondary/llvm/lib/CodeGen/BUILD.gn =================================================================== --- llvm/utils/gn/secondary/llvm/lib/CodeGen/BUILD.gn +++ llvm/utils/gn/secondary/llvm/lib/CodeGen/BUILD.gn @@ -99,6 +99,7 @@ "MachineBlockPlacement.cpp", "MachineBranchProbabilityInfo.cpp", "MachineCSE.cpp", + "MachineCheckDebugify.cpp", "MachineCombiner.cpp", "MachineCopyPropagation.cpp", "MachineDebugify.cpp",