js/src/jit/UnreachableCodeElimination.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:91b2a7e153d0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jit/UnreachableCodeElimination.h"
8
9 #include "jit/AliasAnalysis.h"
10 #include "jit/IonAnalysis.h"
11 #include "jit/MIRGenerator.h"
12 #include "jit/ValueNumbering.h"
13
14 using namespace js;
15 using namespace jit;
16
17 bool
18 UnreachableCodeElimination::analyze()
19 {
20 // The goal of this routine is to eliminate code that is
21 // unreachable, either because there is no path from the entry
22 // block to the code, or because the path traverses a conditional
23 // branch where the condition is a constant (e.g., "if (false) {
24 // ... }"). The latter can either appear in the source form or
25 // arise due to optimizations.
26 //
27 // The stategy is straightforward. The pass begins with a
28 // depth-first search. We set a bit on each basic block that
29 // is visited. If a block terminates in a conditional branch
30 // predicated on a constant, we rewrite the block to an unconditional
31 // jump and do not visit the now irrelevant basic block.
32 //
33 // Once the initial DFS is complete, we do a second pass over the
34 // blocks to find those that were not reached. Those blocks are
35 // simply removed wholesale. We must also correct any phis that
36 // may be affected..
37
38 // Pass 1: Identify unreachable blocks (if any).
39 if (!prunePointlessBranchesAndMarkReachableBlocks())
40 return false;
41
42 return removeUnmarkedBlocksAndCleanup();
43 }
44
45 bool
46 UnreachableCodeElimination::removeUnmarkedBlocks(size_t marked)
47 {
48 marked_ = marked;
49 return removeUnmarkedBlocksAndCleanup();
50 }
51
52 bool
53 UnreachableCodeElimination::removeUnmarkedBlocksAndCleanup()
54 {
55 // Everything is reachable, no work required.
56 JS_ASSERT(marked_ <= graph_.numBlocks());
57 if (marked_ == graph_.numBlocks()) {
58 graph_.unmarkBlocks();
59 return true;
60 }
61
62 // Pass 2: Remove unmarked blocks (see analyze() above).
63 if (!removeUnmarkedBlocksAndClearDominators())
64 return false;
65 graph_.unmarkBlocks();
66
67 AssertGraphCoherency(graph_);
68
69 IonSpewPass("UCE-mid-point");
70
71 // Pass 3: Recompute dominators and tweak phis.
72 BuildDominatorTree(graph_);
73 if (redundantPhis_ && !EliminatePhis(mir_, graph_, ConservativeObservability))
74 return false;
75
76 // Pass 4: Rerun alias analysis
77 if (rerunAliasAnalysis_) {
78 AliasAnalysis analysis(mir_, graph_);
79 if (!analysis.analyze())
80 return false;
81 }
82
83 // Pass 5: It's important for optimizations to re-run GVN (and in
84 // turn alias analysis) after UCE if we eliminated branches.
85 if (rerunAliasAnalysis_ && mir_->optimizationInfo().gvnEnabled()) {
86 ValueNumberer gvn(mir_, graph_, mir_->optimizationInfo().gvnKind() == GVN_Optimistic);
87 if (!gvn.clear() || !gvn.analyze())
88 return false;
89 IonSpewPass("GVN-after-UCE");
90 AssertExtendedGraphCoherency(graph_);
91
92 if (mir_->shouldCancel("GVN-after-UCE"))
93 return false;
94 }
95
96 return true;
97 }
98
99 bool
100 UnreachableCodeElimination::enqueue(MBasicBlock *block, BlockList &list)
101 {
102 if (block->isMarked())
103 return true;
104
105 block->mark();
106 marked_++;
107 return list.append(block);
108 }
109
110 MBasicBlock *
111 UnreachableCodeElimination::optimizableSuccessor(MBasicBlock *block)
112 {
113 // If the last instruction in `block` is a test instruction of a
114 // constant value, returns the successor that the branch will
115 // always branch to at runtime. Otherwise, returns nullptr.
116
117 MControlInstruction *ins = block->lastIns();
118 if (!ins->isTest())
119 return nullptr;
120
121 MTest *testIns = ins->toTest();
122 MDefinition *v = testIns->getOperand(0);
123 if (!v->isConstant())
124 return nullptr;
125
126 BranchDirection bdir = v->toConstant()->valueToBoolean() ? TRUE_BRANCH : FALSE_BRANCH;
127 return testIns->branchSuccessor(bdir);
128 }
129
130 bool
131 UnreachableCodeElimination::prunePointlessBranchesAndMarkReachableBlocks()
132 {
133 BlockList worklist, optimizableBlocks;
134
135 // Process everything reachable from the start block, ignoring any
136 // OSR block.
137 if (!enqueue(graph_.entryBlock(), worklist))
138 return false;
139 while (!worklist.empty()) {
140 if (mir_->shouldCancel("Eliminate Unreachable Code"))
141 return false;
142
143 MBasicBlock *block = worklist.popCopy();
144
145 // If this block is a test on a constant operand, only enqueue
146 // the relevant successor. Also, remember the block for later.
147 if (MBasicBlock *succ = optimizableSuccessor(block)) {
148 if (!optimizableBlocks.append(block))
149 return false;
150 if (!enqueue(succ, worklist))
151 return false;
152 } else {
153 // Otherwise just visit all successors.
154 for (size_t i = 0; i < block->numSuccessors(); i++) {
155 MBasicBlock *succ = block->getSuccessor(i);
156 if (!enqueue(succ, worklist))
157 return false;
158 }
159 }
160 }
161
162 // Now, if there is an OSR block, check that all of its successors
163 // were reachable (bug 880377). If not, we are in danger of
164 // creating a CFG with two disjoint parts, so simply mark all
165 // blocks as reachable. This generally occurs when the TI info for
166 // stack types is incorrect or incomplete, due to operations that
167 // have not yet executed in baseline.
168 if (graph_.osrBlock()) {
169 MBasicBlock *osrBlock = graph_.osrBlock();
170 JS_ASSERT(!osrBlock->isMarked());
171 if (!enqueue(osrBlock, worklist))
172 return false;
173 for (size_t i = 0; i < osrBlock->numSuccessors(); i++) {
174 if (!osrBlock->getSuccessor(i)->isMarked()) {
175 // OSR block has an otherwise unreachable successor, abort.
176 for (MBasicBlockIterator iter(graph_.begin()); iter != graph_.end(); iter++)
177 iter->mark();
178 marked_ = graph_.numBlocks();
179 return true;
180 }
181 }
182 }
183
184 // Now that we know we will not abort due to OSR, go back and
185 // transform any tests on constant operands into gotos.
186 for (uint32_t i = 0; i < optimizableBlocks.length(); i++) {
187 MBasicBlock *block = optimizableBlocks[i];
188 MBasicBlock *succ = optimizableSuccessor(block);
189 JS_ASSERT(succ);
190
191 MGoto *gotoIns = MGoto::New(graph_.alloc(), succ);
192 block->discardLastIns();
193 block->end(gotoIns);
194 MBasicBlock *successorWithPhis = block->successorWithPhis();
195 if (successorWithPhis && successorWithPhis != succ)
196 block->setSuccessorWithPhis(nullptr, 0);
197 }
198
199 return true;
200 }
201
202 void
203 UnreachableCodeElimination::checkDependencyAndRemoveUsesFromUnmarkedBlocks(MDefinition *instr)
204 {
205 // When the instruction depends on removed block,
206 // alias analysis needs to get rerun to have the right dependency.
207 if (!disableAliasAnalysis_ && instr->dependency() && !instr->dependency()->block()->isMarked())
208 rerunAliasAnalysis_ = true;
209
210 for (MUseIterator iter(instr->usesBegin()); iter != instr->usesEnd(); ) {
211 if (!iter->consumer()->block()->isMarked()) {
212 instr->setUseRemovedUnchecked();
213 iter = instr->removeUse(iter);
214 } else {
215 iter++;
216 }
217 }
218 }
219
220 bool
221 UnreachableCodeElimination::removeUnmarkedBlocksAndClearDominators()
222 {
223 // Removes blocks that are not marked from the graph. For blocks
224 // that *are* marked, clears the mark and adjusts the id to its
225 // new value. Also adds blocks that are immediately reachable
226 // from an unmarked block to the frontier.
227
228 size_t id = marked_;
229 for (PostorderIterator iter(graph_.poBegin()); iter != graph_.poEnd();) {
230 if (mir_->shouldCancel("Eliminate Unreachable Code"))
231 return false;
232
233 MBasicBlock *block = *iter;
234 iter++;
235
236 // Unconditionally clear the dominators. It's somewhat complex to
237 // adjust the values and relatively fast to just recompute.
238 block->clearDominatorInfo();
239
240 if (block->isMarked()) {
241 block->setId(--id);
242 for (MPhiIterator iter(block->phisBegin()); iter != block->phisEnd(); iter++)
243 checkDependencyAndRemoveUsesFromUnmarkedBlocks(*iter);
244 for (MInstructionIterator iter(block->begin()); iter != block->end(); iter++)
245 checkDependencyAndRemoveUsesFromUnmarkedBlocks(*iter);
246 } else {
247 if (block->numPredecessors() > 1) {
248 // If this block had phis, then any reachable
249 // predecessors need to have the successorWithPhis
250 // flag cleared.
251 for (size_t i = 0; i < block->numPredecessors(); i++)
252 block->getPredecessor(i)->setSuccessorWithPhis(nullptr, 0);
253 }
254
255 if (block->isLoopBackedge()) {
256 // NB. We have to update the loop header if we
257 // eliminate the backedge. At first I thought this
258 // check would be insufficient, because it would be
259 // possible to have code like this:
260 //
261 // while (true) {
262 // ...;
263 // if (1 == 1) break;
264 // }
265 //
266 // in which the backedge is removed as part of
267 // rewriting the condition, but no actual blocks are
268 // removed. However, in all such cases, the backedge
269 // would be a critical edge and hence the critical
270 // edge block is being removed.
271 block->loopHeaderOfBackedge()->clearLoopHeader();
272 }
273
274 for (size_t i = 0, c = block->numSuccessors(); i < c; i++) {
275 MBasicBlock *succ = block->getSuccessor(i);
276 if (succ->isMarked()) {
277 // succ is on the frontier of blocks to be removed:
278 succ->removePredecessor(block);
279
280 if (!redundantPhis_) {
281 for (MPhiIterator iter(succ->phisBegin()); iter != succ->phisEnd(); iter++) {
282 if (iter->operandIfRedundant()) {
283 redundantPhis_ = true;
284 break;
285 }
286 }
287 }
288 }
289 }
290
291 graph_.removeBlock(block);
292 }
293 }
294
295 JS_ASSERT(id == 0);
296
297 return true;
298 }

mercurial