| //===- RemoveRedundantDebugValues.cpp - Remove Redundant Debug Value MIs --===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/CodeGen/RemoveRedundantDebugValues.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/DenseSet.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/CodeGen/MachineBasicBlock.h" | |
| #include "llvm/CodeGen/MachineFunctionPass.h" | |
| #include "llvm/CodeGen/TargetSubtargetInfo.h" | |
| #include "llvm/IR/DebugInfoMetadata.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/Pass.h" | |
| #include "llvm/PassRegistry.h" | |
| /// \file RemoveRedundantDebugValues.cpp | |
| /// | |
| /// The RemoveRedundantDebugValues pass removes redundant DBG_VALUEs that | |
| /// appear in MIR after the register allocator. | |
| #define DEBUG_TYPE "removeredundantdebugvalues" | |
| using namespace llvm; | |
| STATISTIC(NumRemovedBackward, "Number of DBG_VALUEs removed (backward scan)"); | |
| STATISTIC(NumRemovedForward, "Number of DBG_VALUEs removed (forward scan)"); | |
| namespace { | |
| struct RemoveRedundantDebugValuesImpl { | |
| bool reduceDbgValues(MachineFunction &MF); | |
| }; | |
| class RemoveRedundantDebugValuesLegacy : public MachineFunctionPass { | |
| public: | |
| static char ID; | |
| RemoveRedundantDebugValuesLegacy(); | |
| /// Remove redundant debug value MIs for the given machine function. | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.setPreservesCFG(); | |
| MachineFunctionPass::getAnalysisUsage(AU); | |
| } | |
| }; | |
| } // namespace | |
| //===----------------------------------------------------------------------===// | |
| // Implementation | |
| //===----------------------------------------------------------------------===// | |
| char RemoveRedundantDebugValuesLegacy::ID = 0; | |
| char &llvm::RemoveRedundantDebugValuesID = RemoveRedundantDebugValuesLegacy::ID; | |
| INITIALIZE_PASS(RemoveRedundantDebugValuesLegacy, DEBUG_TYPE, | |
| "Remove Redundant DEBUG_VALUE analysis", false, false) | |
| /// Default construct and initialize the pass. | |
| RemoveRedundantDebugValuesLegacy::RemoveRedundantDebugValuesLegacy() | |
| : MachineFunctionPass(ID) {} | |
| // This analysis aims to remove redundant DBG_VALUEs by going forward | |
| // in the basic block by considering the first DBG_VALUE as a valid | |
| // until its first (location) operand is not clobbered/modified. | |
| // For example: | |
| // (1) DBG_VALUE $edi, !"var1", ... | |
| // (2) <block of code that does affect $edi> | |
| // (3) DBG_VALUE $edi, !"var1", ... | |
| // ... | |
| // in this case, we can remove (3). | |
| // TODO: Support DBG_VALUE_LIST and other debug instructions. | |
| static bool reduceDbgValsForwardScan(MachineBasicBlock &MBB) { | |
| LLVM_DEBUG(dbgs() << "\n == Forward Scan == \n"); | |
| SmallVector<MachineInstr *, 8> DbgValsToBeRemoved; | |
| DenseMap<DebugVariable, std::pair<MachineOperand *, const DIExpression *>> | |
| VariableMap; | |
| const auto *TRI = MBB.getParent()->getSubtarget().getRegisterInfo(); | |
| for (auto &MI : MBB) { | |
| if (MI.isDebugValue()) { | |
| DebugVariable Var(MI.getDebugVariable(), std::nullopt, | |
| MI.getDebugLoc()->getInlinedAt()); | |
| auto VMI = VariableMap.find(Var); | |
| // Just stop tracking this variable, until we cover DBG_VALUE_LIST. | |
| // 1 DBG_VALUE $rax, "x", DIExpression() | |
| // ... | |
| // 2 DBG_VALUE_LIST "x", DIExpression(...), $rax, $rbx | |
| // ... | |
| // 3 DBG_VALUE $rax, "x", DIExpression() | |
| if (MI.isDebugValueList() && VMI != VariableMap.end()) { | |
| VariableMap.erase(VMI); | |
| continue; | |
| } | |
| MachineOperand &Loc = MI.getDebugOperand(0); | |
| if (!Loc.isReg()) { | |
| // If it's not a register, just stop tracking such variable. | |
| if (VMI != VariableMap.end()) | |
| VariableMap.erase(VMI); | |
| continue; | |
| } | |
| // We have found a new value for a variable. | |
| if (VMI == VariableMap.end() || | |
| VMI->second.first->getReg() != Loc.getReg() || | |
| VMI->second.second != MI.getDebugExpression()) { | |
| VariableMap[Var] = {&Loc, MI.getDebugExpression()}; | |
| continue; | |
| } | |
| // Found an identical DBG_VALUE, so it can be considered | |
| // for later removal. | |
| DbgValsToBeRemoved.push_back(&MI); | |
| } | |
| if (MI.isMetaInstruction()) | |
| continue; | |
| // Stop tracking any location that is clobbered by this instruction. | |
| for (auto &Var : VariableMap) { | |
| auto &LocOp = Var.second.first; | |
| if (MI.modifiesRegister(LocOp->getReg(), TRI)) | |
| VariableMap.erase(Var.first); | |
| } | |
| } | |
| for (auto &Instr : DbgValsToBeRemoved) { | |
| LLVM_DEBUG(dbgs() << "removing "; Instr->dump()); | |
| Instr->eraseFromParent(); | |
| ++NumRemovedForward; | |
| } | |
| return !DbgValsToBeRemoved.empty(); | |
| } | |
| // This analysis aims to remove redundant DBG_VALUEs by going backward | |
| // in the basic block and removing all but the last DBG_VALUE for any | |
| // given variable in a set of consecutive DBG_VALUE instructions. | |
| // For example: | |
| // (1) DBG_VALUE $edi, !"var1", ... | |
| // (2) DBG_VALUE $esi, !"var2", ... | |
| // (3) DBG_VALUE $edi, !"var1", ... | |
| // ... | |
| // in this case, we can remove (1). | |
| static bool reduceDbgValsBackwardScan(MachineBasicBlock &MBB) { | |
| LLVM_DEBUG(dbgs() << "\n == Backward Scan == \n"); | |
| SmallVector<MachineInstr *, 8> DbgValsToBeRemoved; | |
| SmallDenseSet<DebugVariable> VariableSet; | |
| for (MachineInstr &MI : llvm::reverse(MBB)) { | |
| if (MI.isDebugValue()) { | |
| DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(), | |
| MI.getDebugLoc()->getInlinedAt()); | |
| auto R = VariableSet.insert(Var); | |
| // If it is a DBG_VALUE describing a constant as: | |
| // DBG_VALUE 0, ... | |
| // we just don't consider such instructions as candidates | |
| // for redundant removal. | |
| if (MI.isNonListDebugValue()) { | |
| MachineOperand &Loc = MI.getDebugOperand(0); | |
| if (!Loc.isReg()) { | |
| // If we have already encountered this variable, just stop | |
| // tracking it. | |
| if (!R.second) | |
| VariableSet.erase(Var); | |
| continue; | |
| } | |
| } | |
| // We have already encountered the value for this variable, | |
| // so this one can be deleted. | |
| if (!R.second) | |
| DbgValsToBeRemoved.push_back(&MI); | |
| continue; | |
| } | |
| // If we encountered a non-DBG_VALUE, try to find the next | |
| // sequence with consecutive DBG_VALUE instructions. | |
| VariableSet.clear(); | |
| } | |
| for (auto &Instr : DbgValsToBeRemoved) { | |
| LLVM_DEBUG(dbgs() << "removing "; Instr->dump()); | |
| Instr->eraseFromParent(); | |
| ++NumRemovedBackward; | |
| } | |
| return !DbgValsToBeRemoved.empty(); | |
| } | |
| bool RemoveRedundantDebugValuesImpl::reduceDbgValues(MachineFunction &MF) { | |
| LLVM_DEBUG(dbgs() << "\nDebug Value Reduction\n"); | |
| bool Changed = false; | |
| for (auto &MBB : MF) { | |
| Changed |= reduceDbgValsBackwardScan(MBB); | |
| Changed |= reduceDbgValsForwardScan(MBB); | |
| } | |
| return Changed; | |
| } | |
| bool RemoveRedundantDebugValuesLegacy::runOnMachineFunction( | |
| MachineFunction &MF) { | |
| // Skip functions without debugging information or functions from NoDebug | |
| // compilation units. | |
| if (!MF.getFunction().getSubprogram() || | |
| (MF.getFunction().getSubprogram()->getUnit()->getEmissionKind() == | |
| DICompileUnit::NoDebug)) | |
| return false; | |
| return RemoveRedundantDebugValuesImpl().reduceDbgValues(MF); | |
| } | |
| PreservedAnalyses | |
| RemoveRedundantDebugValuesPass::run(MachineFunction &MF, | |
| MachineFunctionAnalysisManager &MFAM) { | |
| // Skip functions without debugging information or functions from NoDebug | |
| // compilation units. | |
| if (!MF.getFunction().getSubprogram() || | |
| (MF.getFunction().getSubprogram()->getUnit()->getEmissionKind() == | |
| DICompileUnit::NoDebug)) | |
| return PreservedAnalyses::all(); | |
| if (!RemoveRedundantDebugValuesImpl().reduceDbgValues(MF)) | |
| return PreservedAnalyses::all(); | |
| auto PA = getMachineFunctionPassPreservedAnalyses(); | |
| PA.preserveSet<CFGAnalyses>(); | |
| return PA; | |
| } |