/src/hermes/lib/Optimizer/Scalar/DCE.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) Meta Platforms, Inc. and affiliates. |
3 | | * |
4 | | * This source code is licensed under the MIT license found in the |
5 | | * LICENSE file in the root directory of this source tree. |
6 | | */ |
7 | | |
8 | | #define DEBUG_TYPE "dce" |
9 | | |
10 | | #include "hermes/Optimizer/Scalar/DCE.h" |
11 | | |
12 | | #include "hermes/IR/Analysis.h" |
13 | | #include "hermes/IR/CFG.h" |
14 | | #include "hermes/IR/IRBuilder.h" |
15 | | #include "hermes/IR/Instrs.h" |
16 | | #include "hermes/Support/Statistic.h" |
17 | | |
18 | | #include "llvh/ADT/DenseMap.h" |
19 | | #include "llvh/ADT/DenseSet.h" |
20 | | #include "llvh/Support/Debug.h" |
21 | | |
22 | | using namespace hermes; |
23 | | using llvh::dbgs; |
24 | | |
25 | | STATISTIC(NumDCE, "Number of instructions DCE'd"); |
26 | | STATISTIC(NumFuncDCE, "Number of functions DCE'd"); |
27 | | |
28 | 0 | static bool performFunctionDCE(Function *F) { |
29 | 0 | bool changed = false; |
30 | 0 | PostOrderAnalysis PO(F); |
31 | | |
32 | | // Scan the function in post order (from end to start). We want to visit the |
33 | | // uses of the instruction before we visit the instruction itself in order to |
34 | | // allow the optimization to delete long chains of dead code. |
35 | 0 | for (auto *BB : PO) { |
36 | | // Scan the instructions in the block from end to start. |
37 | 0 | for (auto it = BB->rbegin(), e = BB->rend(); it != e; /* nothing */) { |
38 | 0 | Instruction *I = &*it; |
39 | | // Move the iterator to the next instruction in the block. This will |
40 | | // allow us to delete the current instruction. |
41 | 0 | ++it; |
42 | | |
43 | | // If the instruction writes to memory then we can't remove it. Notice |
44 | | // that it is okay to delete instructions that only read memory and are |
45 | | // unused. |
46 | | // |
47 | | // Terminators don't have any uses but are never supposed to be removed |
48 | | // as dead code. |
49 | | // |
50 | | // CreateScopeInst may not have any users, but it is lowered to |
51 | | // HBCCreateEnvironmentInst which should always be emitted and DCE'd if |
52 | | // appropriate. |
53 | | // |
54 | | // HasRestrictedGlobalPropertyInst doesn't have a result but should never |
55 | | // be removed as they perform runtime validation. |
56 | 0 | if (I->mayWriteMemory() || llvh::isa<TerminatorInst>(I) || |
57 | 0 | llvh::isa<CreateScopeInst>(I) || |
58 | 0 | llvh::isa<ThrowIfHasRestrictedGlobalPropertyInst>(I)) { |
59 | 0 | continue; |
60 | 0 | } |
61 | | |
62 | | // If some other instruction is using the result of this instruction then |
63 | | // we can't delete it. |
64 | 0 | if (I->getNumUsers()) |
65 | 0 | continue; |
66 | | |
67 | 0 | LLVM_DEBUG( |
68 | 0 | dbgs() << "\t\tDCE: Erasing instruction \"" << I->getName() |
69 | 0 | << "\"\n"); |
70 | 0 | I->eraseFromParent(); |
71 | 0 | ++NumDCE; |
72 | 0 | changed = true; |
73 | 0 | } |
74 | 0 | } |
75 | 0 | return changed; |
76 | 0 | } |
77 | | |
78 | 0 | bool DCE::runOnModule(Module *M) { |
79 | 0 | bool changed = false; |
80 | | |
81 | | // A list of unused functions to deallocate from memory. |
82 | | // We need to destroy the memory at the very end of this function because |
83 | | // a dead function may have a variable that is referrenced by an inner |
84 | | // function (which will also become dead once the outer function is removed). |
85 | | // However we cannot destroy the outer function right away until we destroy |
86 | | // the inner function. |
87 | 0 | llvh::SmallVector<Function *, 16> toDestroy; |
88 | | |
89 | | // Perform per-function DCE. |
90 | 0 | for (auto &F : *M) { |
91 | 0 | LLVM_DEBUG( |
92 | 0 | dbgs() << "\tDCE: running on function \"" << F.getInternalName() |
93 | 0 | << "\"\n"); |
94 | 0 | changed |= performFunctionDCE(&F); |
95 | 0 | } |
96 | |
|
97 | 0 | bool localChanged = false; |
98 | 0 | do { |
99 | | // A list of unused functions to remove from the module without being |
100 | | // destroyed. |
101 | 0 | llvh::SmallVector<Function *, 16> toRemove; |
102 | 0 | localChanged = false; |
103 | 0 | for (auto &F : *M) { |
104 | | // Try to remove unused functions. Notice that the top-level-function has |
105 | | // no external users so we must check for it explicitly. |
106 | 0 | if (M->findCJSModule(&F)) { |
107 | | // If the function is a top-level module. |
108 | 0 | continue; |
109 | 0 | } |
110 | | // Don't delete the function if it is at global scope, or if it is the |
111 | | // entry point of a module. |
112 | 0 | if (!F.isGlobalScope() && &F != M->getTopLevelFunction() && |
113 | 0 | !F.hasUsers()) { |
114 | 0 | toRemove.push_back(&F); |
115 | 0 | toDestroy.push_back(&F); |
116 | 0 | changed = true; |
117 | 0 | localChanged = true; |
118 | 0 | NumFuncDCE++; |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | | // We erase the basic blocks and instructions from each function in |
123 | | // toRemove, and also remove the function from the module. However |
124 | | // the memory of the function remain alive. |
125 | 0 | for (auto *F : toRemove) { |
126 | 0 | LLVM_DEBUG( |
127 | 0 | dbgs() << "\tDCE: Erasing function \"" << F->getInternalName() |
128 | 0 | << "\"\n"); |
129 | 0 | F->eraseFromParentNoDestroy(); |
130 | 0 | } |
131 | 0 | } while (localChanged); |
132 | | |
133 | | // Now that all instructions have been destroyed from each dead function, |
134 | | // it's now safe to destroy them including the variables in them. |
135 | 0 | for (auto *F : toDestroy) { |
136 | 0 | assert(F->empty() && "All basic blocks should have been deleted."); |
137 | 0 | Value::destroy(F); |
138 | 0 | } |
139 | | |
140 | 0 | return changed | localChanged; |
141 | 0 | } |
142 | | |
143 | 0 | std::unique_ptr<Pass> hermes::createDCE() { |
144 | 0 | return std::make_unique<DCE>(); |
145 | 0 | } |
146 | | |
147 | | #undef DEBUG_TYPE |