1fe6060f1SDimitry Andric //===- RemoveRedundantDebugValues.cpp - Remove Redundant Debug Value MIs --===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric
9fe6060f1SDimitry Andric #include "llvm/ADT/DenseMap.h"
10fe6060f1SDimitry Andric #include "llvm/ADT/DenseSet.h"
11fe6060f1SDimitry Andric #include "llvm/ADT/SmallVector.h"
12fe6060f1SDimitry Andric #include "llvm/ADT/Statistic.h"
13fe6060f1SDimitry Andric #include "llvm/CodeGen/MachineBasicBlock.h"
14fe6060f1SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
15fe6060f1SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
16fe6060f1SDimitry Andric #include "llvm/IR/DebugInfoMetadata.h"
17fe6060f1SDimitry Andric #include "llvm/IR/Function.h"
18fe6060f1SDimitry Andric #include "llvm/InitializePasses.h"
19fe6060f1SDimitry Andric #include "llvm/Pass.h"
2081ad6265SDimitry Andric #include "llvm/PassRegistry.h"
21fe6060f1SDimitry Andric
22fe6060f1SDimitry Andric /// \file RemoveRedundantDebugValues.cpp
23fe6060f1SDimitry Andric ///
24fe6060f1SDimitry Andric /// The RemoveRedundantDebugValues pass removes redundant DBG_VALUEs that
25fe6060f1SDimitry Andric /// appear in MIR after the register allocator.
26fe6060f1SDimitry Andric
27fe6060f1SDimitry Andric #define DEBUG_TYPE "removeredundantdebugvalues"
28fe6060f1SDimitry Andric
29fe6060f1SDimitry Andric using namespace llvm;
30fe6060f1SDimitry Andric
31fe6060f1SDimitry Andric STATISTIC(NumRemovedBackward, "Number of DBG_VALUEs removed (backward scan)");
32fe6060f1SDimitry Andric STATISTIC(NumRemovedForward, "Number of DBG_VALUEs removed (forward scan)");
33fe6060f1SDimitry Andric
34fe6060f1SDimitry Andric namespace {
35fe6060f1SDimitry Andric
36fe6060f1SDimitry Andric class RemoveRedundantDebugValues : public MachineFunctionPass {
37fe6060f1SDimitry Andric public:
38fe6060f1SDimitry Andric static char ID;
39fe6060f1SDimitry Andric
40fe6060f1SDimitry Andric RemoveRedundantDebugValues();
41fe6060f1SDimitry Andric
42fe6060f1SDimitry Andric bool reduceDbgValues(MachineFunction &MF);
43fe6060f1SDimitry Andric
44fe6060f1SDimitry Andric /// Remove redundant debug value MIs for the given machine function.
45fe6060f1SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override;
46fe6060f1SDimitry Andric
getAnalysisUsage(AnalysisUsage & AU) const47fe6060f1SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {
48fe6060f1SDimitry Andric AU.setPreservesCFG();
49fe6060f1SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU);
50fe6060f1SDimitry Andric }
51fe6060f1SDimitry Andric };
52fe6060f1SDimitry Andric
53fe6060f1SDimitry Andric } // namespace
54fe6060f1SDimitry Andric
55fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
56fe6060f1SDimitry Andric // Implementation
57fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
58fe6060f1SDimitry Andric
59fe6060f1SDimitry Andric char RemoveRedundantDebugValues::ID = 0;
60fe6060f1SDimitry Andric
61fe6060f1SDimitry Andric char &llvm::RemoveRedundantDebugValuesID = RemoveRedundantDebugValues::ID;
62fe6060f1SDimitry Andric
63fe6060f1SDimitry Andric INITIALIZE_PASS(RemoveRedundantDebugValues, DEBUG_TYPE,
64fe6060f1SDimitry Andric "Remove Redundant DEBUG_VALUE analysis", false, false)
65fe6060f1SDimitry Andric
66fe6060f1SDimitry Andric /// Default construct and initialize the pass.
RemoveRedundantDebugValues()67fe6060f1SDimitry Andric RemoveRedundantDebugValues::RemoveRedundantDebugValues()
68fe6060f1SDimitry Andric : MachineFunctionPass(ID) {
69fe6060f1SDimitry Andric initializeRemoveRedundantDebugValuesPass(*PassRegistry::getPassRegistry());
70fe6060f1SDimitry Andric }
71fe6060f1SDimitry Andric
72fe6060f1SDimitry Andric // This analysis aims to remove redundant DBG_VALUEs by going forward
73fe6060f1SDimitry Andric // in the basic block by considering the first DBG_VALUE as a valid
74fe6060f1SDimitry Andric // until its first (location) operand is not clobbered/modified.
75fe6060f1SDimitry Andric // For example:
76fe6060f1SDimitry Andric // (1) DBG_VALUE $edi, !"var1", ...
77fe6060f1SDimitry Andric // (2) <block of code that does affect $edi>
78fe6060f1SDimitry Andric // (3) DBG_VALUE $edi, !"var1", ...
79fe6060f1SDimitry Andric // ...
80fe6060f1SDimitry Andric // in this case, we can remove (3).
81fe6060f1SDimitry Andric // TODO: Support DBG_VALUE_LIST and other debug instructions.
reduceDbgValsForwardScan(MachineBasicBlock & MBB)82fe6060f1SDimitry Andric static bool reduceDbgValsForwardScan(MachineBasicBlock &MBB) {
83fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "\n == Forward Scan == \n");
84fe6060f1SDimitry Andric
85fe6060f1SDimitry Andric SmallVector<MachineInstr *, 8> DbgValsToBeRemoved;
86fe6060f1SDimitry Andric DenseMap<DebugVariable, std::pair<MachineOperand *, const DIExpression *>>
87fe6060f1SDimitry Andric VariableMap;
88fe6060f1SDimitry Andric const auto *TRI = MBB.getParent()->getSubtarget().getRegisterInfo();
89fe6060f1SDimitry Andric
90fe6060f1SDimitry Andric for (auto &MI : MBB) {
91fe6060f1SDimitry Andric if (MI.isDebugValue()) {
92bdd1243dSDimitry Andric DebugVariable Var(MI.getDebugVariable(), std::nullopt,
93fe6060f1SDimitry Andric MI.getDebugLoc()->getInlinedAt());
94fe6060f1SDimitry Andric auto VMI = VariableMap.find(Var);
95fe6060f1SDimitry Andric // Just stop tracking this variable, until we cover DBG_VALUE_LIST.
96fe6060f1SDimitry Andric // 1 DBG_VALUE $rax, "x", DIExpression()
97fe6060f1SDimitry Andric // ...
98fe6060f1SDimitry Andric // 2 DBG_VALUE_LIST "x", DIExpression(...), $rax, $rbx
99fe6060f1SDimitry Andric // ...
100fe6060f1SDimitry Andric // 3 DBG_VALUE $rax, "x", DIExpression()
101fe6060f1SDimitry Andric if (MI.isDebugValueList() && VMI != VariableMap.end()) {
102fe6060f1SDimitry Andric VariableMap.erase(VMI);
103fe6060f1SDimitry Andric continue;
104fe6060f1SDimitry Andric }
105fe6060f1SDimitry Andric
106fe6060f1SDimitry Andric MachineOperand &Loc = MI.getDebugOperand(0);
107fe6060f1SDimitry Andric if (!Loc.isReg()) {
108*5f757f3fSDimitry Andric // If it's not a register, just stop tracking such variable.
109fe6060f1SDimitry Andric if (VMI != VariableMap.end())
110fe6060f1SDimitry Andric VariableMap.erase(VMI);
111fe6060f1SDimitry Andric continue;
112fe6060f1SDimitry Andric }
113fe6060f1SDimitry Andric
114fe6060f1SDimitry Andric // We have found a new value for a variable.
115fe6060f1SDimitry Andric if (VMI == VariableMap.end() ||
116fe6060f1SDimitry Andric VMI->second.first->getReg() != Loc.getReg() ||
117fe6060f1SDimitry Andric VMI->second.second != MI.getDebugExpression()) {
118fe6060f1SDimitry Andric VariableMap[Var] = {&Loc, MI.getDebugExpression()};
119fe6060f1SDimitry Andric continue;
120fe6060f1SDimitry Andric }
121fe6060f1SDimitry Andric
122fe6060f1SDimitry Andric // Found an identical DBG_VALUE, so it can be considered
123fe6060f1SDimitry Andric // for later removal.
124fe6060f1SDimitry Andric DbgValsToBeRemoved.push_back(&MI);
125fe6060f1SDimitry Andric }
126fe6060f1SDimitry Andric
127fe6060f1SDimitry Andric if (MI.isMetaInstruction())
128fe6060f1SDimitry Andric continue;
129fe6060f1SDimitry Andric
130fe6060f1SDimitry Andric // Stop tracking any location that is clobbered by this instruction.
131fe6060f1SDimitry Andric for (auto &Var : VariableMap) {
132fe6060f1SDimitry Andric auto &LocOp = Var.second.first;
133fe6060f1SDimitry Andric if (MI.modifiesRegister(LocOp->getReg(), TRI))
134fe6060f1SDimitry Andric VariableMap.erase(Var.first);
135fe6060f1SDimitry Andric }
136fe6060f1SDimitry Andric }
137fe6060f1SDimitry Andric
138fe6060f1SDimitry Andric for (auto &Instr : DbgValsToBeRemoved) {
139fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "removing "; Instr->dump());
140fe6060f1SDimitry Andric Instr->eraseFromParent();
141fe6060f1SDimitry Andric ++NumRemovedForward;
142fe6060f1SDimitry Andric }
143fe6060f1SDimitry Andric
144fe6060f1SDimitry Andric return !DbgValsToBeRemoved.empty();
145fe6060f1SDimitry Andric }
146fe6060f1SDimitry Andric
147fe6060f1SDimitry Andric // This analysis aims to remove redundant DBG_VALUEs by going backward
148fe6060f1SDimitry Andric // in the basic block and removing all but the last DBG_VALUE for any
149fe6060f1SDimitry Andric // given variable in a set of consecutive DBG_VALUE instructions.
150fe6060f1SDimitry Andric // For example:
151fe6060f1SDimitry Andric // (1) DBG_VALUE $edi, !"var1", ...
152fe6060f1SDimitry Andric // (2) DBG_VALUE $esi, !"var2", ...
153fe6060f1SDimitry Andric // (3) DBG_VALUE $edi, !"var1", ...
154fe6060f1SDimitry Andric // ...
155fe6060f1SDimitry Andric // in this case, we can remove (1).
reduceDbgValsBackwardScan(MachineBasicBlock & MBB)156fe6060f1SDimitry Andric static bool reduceDbgValsBackwardScan(MachineBasicBlock &MBB) {
157fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "\n == Backward Scan == \n");
158fe6060f1SDimitry Andric SmallVector<MachineInstr *, 8> DbgValsToBeRemoved;
159fe6060f1SDimitry Andric SmallDenseSet<DebugVariable> VariableSet;
160fe6060f1SDimitry Andric
1610eae32dcSDimitry Andric for (MachineInstr &MI : llvm::reverse(MBB)) {
1620eae32dcSDimitry Andric if (MI.isDebugValue()) {
1630eae32dcSDimitry Andric DebugVariable Var(MI.getDebugVariable(), MI.getDebugExpression(),
1640eae32dcSDimitry Andric MI.getDebugLoc()->getInlinedAt());
165fe6060f1SDimitry Andric auto R = VariableSet.insert(Var);
166fe6060f1SDimitry Andric // If it is a DBG_VALUE describing a constant as:
167fe6060f1SDimitry Andric // DBG_VALUE 0, ...
168fe6060f1SDimitry Andric // we just don't consider such instructions as candidates
169fe6060f1SDimitry Andric // for redundant removal.
1700eae32dcSDimitry Andric if (MI.isNonListDebugValue()) {
1710eae32dcSDimitry Andric MachineOperand &Loc = MI.getDebugOperand(0);
172fe6060f1SDimitry Andric if (!Loc.isReg()) {
173fe6060f1SDimitry Andric // If we have already encountered this variable, just stop
174fe6060f1SDimitry Andric // tracking it.
175fe6060f1SDimitry Andric if (!R.second)
176fe6060f1SDimitry Andric VariableSet.erase(Var);
177fe6060f1SDimitry Andric continue;
178fe6060f1SDimitry Andric }
179fe6060f1SDimitry Andric }
180fe6060f1SDimitry Andric
181fe6060f1SDimitry Andric // We have already encountered the value for this variable,
182fe6060f1SDimitry Andric // so this one can be deleted.
183fe6060f1SDimitry Andric if (!R.second)
1840eae32dcSDimitry Andric DbgValsToBeRemoved.push_back(&MI);
185fe6060f1SDimitry Andric continue;
186fe6060f1SDimitry Andric }
187fe6060f1SDimitry Andric
188fe6060f1SDimitry Andric // If we encountered a non-DBG_VALUE, try to find the next
189fe6060f1SDimitry Andric // sequence with consecutive DBG_VALUE instructions.
190fe6060f1SDimitry Andric VariableSet.clear();
191fe6060f1SDimitry Andric }
192fe6060f1SDimitry Andric
193fe6060f1SDimitry Andric for (auto &Instr : DbgValsToBeRemoved) {
194fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "removing "; Instr->dump());
195fe6060f1SDimitry Andric Instr->eraseFromParent();
196fe6060f1SDimitry Andric ++NumRemovedBackward;
197fe6060f1SDimitry Andric }
198fe6060f1SDimitry Andric
199fe6060f1SDimitry Andric return !DbgValsToBeRemoved.empty();
200fe6060f1SDimitry Andric }
201fe6060f1SDimitry Andric
reduceDbgValues(MachineFunction & MF)202fe6060f1SDimitry Andric bool RemoveRedundantDebugValues::reduceDbgValues(MachineFunction &MF) {
203fe6060f1SDimitry Andric LLVM_DEBUG(dbgs() << "\nDebug Value Reduction\n");
204fe6060f1SDimitry Andric
205fe6060f1SDimitry Andric bool Changed = false;
206fe6060f1SDimitry Andric
207fe6060f1SDimitry Andric for (auto &MBB : MF) {
208fe6060f1SDimitry Andric Changed |= reduceDbgValsBackwardScan(MBB);
209fe6060f1SDimitry Andric Changed |= reduceDbgValsForwardScan(MBB);
210fe6060f1SDimitry Andric }
211fe6060f1SDimitry Andric
212fe6060f1SDimitry Andric return Changed;
213fe6060f1SDimitry Andric }
214fe6060f1SDimitry Andric
runOnMachineFunction(MachineFunction & MF)215fe6060f1SDimitry Andric bool RemoveRedundantDebugValues::runOnMachineFunction(MachineFunction &MF) {
216fe6060f1SDimitry Andric // Skip functions without debugging information.
217fe6060f1SDimitry Andric if (!MF.getFunction().getSubprogram())
218fe6060f1SDimitry Andric return false;
219fe6060f1SDimitry Andric
220fe6060f1SDimitry Andric // Skip functions from NoDebug compilation units.
221fe6060f1SDimitry Andric if (MF.getFunction().getSubprogram()->getUnit()->getEmissionKind() ==
222fe6060f1SDimitry Andric DICompileUnit::NoDebug)
223fe6060f1SDimitry Andric return false;
224fe6060f1SDimitry Andric
225fe6060f1SDimitry Andric bool Changed = reduceDbgValues(MF);
226fe6060f1SDimitry Andric return Changed;
227fe6060f1SDimitry Andric }
228