//===- 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/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 { class RemoveRedundantDebugValues : public MachineFunctionPass { public: static char ID; RemoveRedundantDebugValues(); bool reduceDbgValues(MachineFunction &MF); /// 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 RemoveRedundantDebugValues::ID = 0; char &llvm::RemoveRedundantDebugValuesID = RemoveRedundantDebugValues::ID; INITIALIZE_PASS(RemoveRedundantDebugValues, DEBUG_TYPE, "Remove Redundant DEBUG_VALUE analysis", false, false) /// Default construct and initialize the pass. RemoveRedundantDebugValues::RemoveRedundantDebugValues() : MachineFunctionPass(ID) { initializeRemoveRedundantDebugValuesPass(*PassRegistry::getPassRegistry()); } // 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) // (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 DbgValsToBeRemoved; DenseMap> 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 DbgValsToBeRemoved; SmallDenseSet 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 RemoveRedundantDebugValues::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 RemoveRedundantDebugValues::runOnMachineFunction(MachineFunction &MF) { // Skip functions without debugging information. if (!MF.getFunction().getSubprogram()) return false; // Skip functions from NoDebug compilation units. if (MF.getFunction().getSubprogram()->getUnit()->getEmissionKind() == DICompileUnit::NoDebug) return false; bool Changed = reduceDbgValues(MF); return Changed; }