2 * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "DFGCFGSimplificationPhase.h"
31 #include "DFGAbstractState.h"
32 #include "DFGBasicBlockInlines.h"
34 #include "DFGInsertionSet.h"
36 #include "DFGValidate.h"
37 #include "Operations.h"
39 namespace JSC
{ namespace DFG
{
41 class CFGSimplificationPhase
: public Phase
{
43 CFGSimplificationPhase(Graph
& graph
)
44 : Phase(graph
, "CFG simplification")
50 const bool extremeLogging
= false;
52 bool outerChanged
= false;
57 for (BlockIndex blockIndex
= 0; blockIndex
< m_graph
.m_blocks
.size(); ++blockIndex
) {
58 BasicBlock
* block
= m_graph
.m_blocks
[blockIndex
].get();
61 ASSERT(block
->isReachable
);
63 switch (block
->last()->op()) {
65 // Successor with one predecessor -> merge.
66 if (m_graph
.m_blocks
[m_graph
.successor(block
, 0)]->m_predecessors
.size() == 1) {
67 ASSERT(m_graph
.m_blocks
[m_graph
.successor(block
, 0)]->m_predecessors
[0]
69 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
70 dataLogF("CFGSimplify: Jump merge on Block #%u to Block #%u.\n",
71 blockIndex
, m_graph
.successor(block
, 0));
76 mergeBlocks(blockIndex
, m_graph
.successor(block
, 0), NoBlock
);
77 innerChanged
= outerChanged
= true;
80 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
81 dataLogF("Not jump merging on Block #%u to Block #%u because predecessors = ",
82 blockIndex
, m_graph
.successor(block
, 0));
83 for (unsigned i
= 0; i
< m_graph
.m_blocks
[m_graph
.successor(block
, 0)]->m_predecessors
.size(); ++i
) {
86 dataLogF("#%u", m_graph
.m_blocks
[m_graph
.successor(block
, 0)]->m_predecessors
[i
]);
92 // FIXME: Block only has a jump -> remove. This is tricky though because of
93 // liveness. What we really want is to slam in a phantom at the end of the
94 // block, after the terminal. But we can't right now. :-(
95 // Idea: what if I slam the ghosties into my successor? Nope, that's
96 // suboptimal, because if my successor has multiple predecessors then we'll
97 // be keeping alive things on other predecessor edges unnecessarily.
98 // What we really need is the notion of end-of-block ghosties!
103 // Branch on constant -> jettison the not-taken block and merge.
104 if (isKnownDirection(block
->cfaBranchDirection
)) {
105 bool condition
= branchCondition(block
->cfaBranchDirection
);
106 BasicBlock
* targetBlock
= m_graph
.m_blocks
[
107 m_graph
.successorForCondition(block
, condition
)].get();
108 if (targetBlock
->m_predecessors
.size() == 1) {
109 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
110 dataLogF("CFGSimplify: Known condition (%s) branch merge on Block #%u to Block #%u, jettisoning Block #%u.\n",
111 condition
? "true" : "false",
112 blockIndex
, m_graph
.successorForCondition(block
, condition
),
113 m_graph
.successorForCondition(block
, !condition
));
120 m_graph
.successorForCondition(block
, condition
),
121 m_graph
.successorForCondition(block
, !condition
));
123 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
124 dataLogF("CFGSimplify: Known condition (%s) branch->jump conversion on Block #%u to Block #%u, jettisoning Block #%u.\n",
125 condition
? "true" : "false",
126 blockIndex
, m_graph
.successorForCondition(block
, condition
),
127 m_graph
.successorForCondition(block
, !condition
));
132 BlockIndex takenBlockIndex
= m_graph
.successorForCondition(block
, condition
);
133 BlockIndex notTakenBlockIndex
= m_graph
.successorForCondition(block
, !condition
);
135 ASSERT(block
->last()->isTerminal());
136 CodeOrigin boundaryCodeOrigin
= block
->last()->codeOrigin
;
137 block
->last()->convertToPhantom();
138 ASSERT(block
->last()->refCount() == 1);
140 jettisonBlock(blockIndex
, notTakenBlockIndex
, boundaryCodeOrigin
);
143 m_graph
, SpecNone
, Jump
, boundaryCodeOrigin
,
144 OpInfo(takenBlockIndex
));
146 innerChanged
= outerChanged
= true;
150 if (m_graph
.successor(block
, 0) == m_graph
.successor(block
, 1)) {
151 BlockIndex targetBlockIndex
= m_graph
.successor(block
, 0);
152 BasicBlock
* targetBlock
= m_graph
.m_blocks
[targetBlockIndex
].get();
154 ASSERT(targetBlock
->isReachable
);
155 if (targetBlock
->m_predecessors
.size() == 1) {
156 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
157 dataLogF("CFGSimplify: Branch to same successor merge on Block #%u to Block #%u.\n",
158 blockIndex
, targetBlockIndex
);
161 mergeBlocks(blockIndex
, targetBlockIndex
, NoBlock
);
163 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
164 dataLogF("CFGSimplify: Branch->jump conversion to same successor on Block #%u to Block #%u.\n",
165 blockIndex
, targetBlockIndex
);
167 Node
* branch
= block
->last();
168 ASSERT(branch
->isTerminal());
169 ASSERT(branch
->op() == Branch
);
170 branch
->convertToPhantom();
171 ASSERT(branch
->refCount() == 1);
174 m_graph
, SpecNone
, Jump
, branch
->codeOrigin
,
175 OpInfo(targetBlockIndex
));
177 innerChanged
= outerChanged
= true;
181 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
182 dataLogF("Not branch simplifying on Block #%u because the successors differ and the condition is not known.\n",
186 // Branch to same destination -> jump.
187 // FIXME: this will currently not be hit because of the lack of jump-only
188 // block simplification.
199 // Here's the reason for this pass:
200 // Blocks: A, B, C, D, E, F
207 // Assume that A's branch is determined to go to B. Then the rest of this phase
208 // is smart enough to simplify down to:
215 // We will also merge A and B. But then we don't have any other mechanism to
216 // remove D, E as predecessors for F. Worse, the rest of this phase does not
217 // know how to fix the Phi functions of F to ensure that they no longer refer
218 // to variables in D, E. In general, we need a way to handle Phi simplification
220 // 1) Removal of a predecessor due to branch simplification. The branch
221 // simplifier already does that.
222 // 2) Invalidation of a predecessor because said predecessor was rendered
223 // unreachable. We do this here.
225 // This implies that when a block is unreachable, we must inspect its
226 // successors' Phi functions to remove any references from them into the
229 m_graph
.resetReachability();
231 for (BlockIndex blockIndex
= 0; blockIndex
< m_graph
.m_blocks
.size(); ++blockIndex
) {
232 BasicBlock
* block
= m_graph
.m_blocks
[blockIndex
].get();
235 if (block
->isReachable
)
238 killUnreachable(blockIndex
);
242 if (Options::validateGraphAtEachPhase())
244 } while (innerChanged
);
250 void killUnreachable(BlockIndex blockIndex
)
252 BasicBlock
* block
= m_graph
.m_blocks
[blockIndex
].get();
255 ASSERT(!block
->isReachable
);
257 for (unsigned phiIndex
= block
->phis
.size(); phiIndex
--;)
258 m_graph
.m_allocator
.free(block
->phis
[phiIndex
]);
259 for (unsigned nodeIndex
= block
->size(); nodeIndex
--;)
260 m_graph
.m_allocator
.free(block
->at(nodeIndex
));
262 m_graph
.m_blocks
[blockIndex
].clear();
265 void keepOperandAlive(BasicBlock
* block
, BasicBlock
* jettisonedBlock
, CodeOrigin codeOrigin
, int operand
)
267 Node
* livenessNode
= jettisonedBlock
->variablesAtHead
.operand(operand
);
270 if (livenessNode
->variableAccessData()->isCaptured())
273 m_graph
, SpecNone
, PhantomLocal
, codeOrigin
,
274 OpInfo(livenessNode
->variableAccessData()));
277 void jettisonBlock(BlockIndex blockIndex
, BlockIndex jettisonedBlockIndex
, CodeOrigin boundaryCodeOrigin
)
279 BasicBlock
* block
= m_graph
.m_blocks
[blockIndex
].get();
280 BasicBlock
* jettisonedBlock
= m_graph
.m_blocks
[jettisonedBlockIndex
].get();
282 for (size_t i
= 0; i
< jettisonedBlock
->variablesAtHead
.numberOfArguments(); ++i
)
283 keepOperandAlive(block
, jettisonedBlock
, boundaryCodeOrigin
, argumentToOperand(i
));
284 for (size_t i
= 0; i
< jettisonedBlock
->variablesAtHead
.numberOfLocals(); ++i
)
285 keepOperandAlive(block
, jettisonedBlock
, boundaryCodeOrigin
, i
);
287 fixJettisonedPredecessors(blockIndex
, jettisonedBlockIndex
);
290 void fixJettisonedPredecessors(BlockIndex blockIndex
, BlockIndex jettisonedBlockIndex
)
292 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
293 dataLogF("Fixing predecessors and phis due to jettison of Block #%u from Block #%u.\n",
294 jettisonedBlockIndex
, blockIndex
);
296 BasicBlock
* jettisonedBlock
= m_graph
.m_blocks
[jettisonedBlockIndex
].get();
297 for (unsigned i
= 0; i
< jettisonedBlock
->m_predecessors
.size(); ++i
) {
298 if (jettisonedBlock
->m_predecessors
[i
] != blockIndex
)
300 jettisonedBlock
->m_predecessors
[i
] = jettisonedBlock
->m_predecessors
.last();
301 jettisonedBlock
->m_predecessors
.removeLast();
307 BlockIndex firstBlockIndex
, BlockIndex secondBlockIndex
, BlockIndex jettisonedBlockIndex
)
309 // This will add all of the nodes in secondBlock to firstBlock, but in so doing
310 // it will also ensure that any GetLocals from the second block that refer to
311 // SetLocals in the first block are relinked. If jettisonedBlock is not NoBlock,
312 // then Phantoms are inserted for anything that the jettisonedBlock would have
315 BasicBlock
* firstBlock
= m_graph
.m_blocks
[firstBlockIndex
].get();
316 BasicBlock
* secondBlock
= m_graph
.m_blocks
[secondBlockIndex
].get();
318 // Remove the terminal of firstBlock since we don't need it anymore. Well, we don't
319 // really remove it; we actually turn it into a Phantom.
320 ASSERT(firstBlock
->last()->isTerminal());
321 CodeOrigin boundaryCodeOrigin
= firstBlock
->last()->codeOrigin
;
322 firstBlock
->last()->convertToPhantom();
323 ASSERT(firstBlock
->last()->refCount() == 1);
325 if (jettisonedBlockIndex
!= NoBlock
) {
326 BasicBlock
* jettisonedBlock
= m_graph
.m_blocks
[jettisonedBlockIndex
].get();
328 // Time to insert ghosties for things that need to be kept alive in case we OSR
329 // exit prior to hitting the firstBlock's terminal, and end up going down a
330 // different path than secondBlock.
332 for (size_t i
= 0; i
< jettisonedBlock
->variablesAtHead
.numberOfArguments(); ++i
)
333 keepOperandAlive(firstBlock
, jettisonedBlock
, boundaryCodeOrigin
, argumentToOperand(i
));
334 for (size_t i
= 0; i
< jettisonedBlock
->variablesAtHead
.numberOfLocals(); ++i
)
335 keepOperandAlive(firstBlock
, jettisonedBlock
, boundaryCodeOrigin
, i
);
338 for (size_t i
= 0; i
< secondBlock
->phis
.size(); ++i
)
339 firstBlock
->phis
.append(secondBlock
->phis
[i
]);
341 for (size_t i
= 0; i
< secondBlock
->size(); ++i
)
342 firstBlock
->append(secondBlock
->at(i
));
344 ASSERT(firstBlock
->last()->isTerminal());
346 // Fix the predecessors of my new successors. This is tricky, since we are going to reset
347 // all predecessors anyway due to reachability analysis. But we need to fix the
348 // predecessors eagerly to ensure that we know what they are in case the next block we
349 // consider in this phase wishes to query the predecessors of one of the blocks we
351 for (unsigned i
= m_graph
.numSuccessors(firstBlock
); i
--;) {
352 BasicBlock
* successor
= m_graph
.m_blocks
[m_graph
.successor(firstBlock
, i
)].get();
353 for (unsigned j
= 0; j
< successor
->m_predecessors
.size(); ++j
) {
354 if (successor
->m_predecessors
[j
] == secondBlockIndex
)
355 successor
->m_predecessors
[j
] = firstBlockIndex
;
359 // Fix the predecessors of my former successors. Again, we'd rather not do this, but it's
360 // an unfortunate necessity. See above comment.
361 if (jettisonedBlockIndex
!= NoBlock
)
362 fixJettisonedPredecessors(firstBlockIndex
, jettisonedBlockIndex
);
364 firstBlock
->valuesAtTail
= secondBlock
->valuesAtTail
;
365 firstBlock
->cfaBranchDirection
= secondBlock
->cfaBranchDirection
;
367 m_graph
.m_blocks
[secondBlockIndex
].clear();
371 bool performCFGSimplification(Graph
& graph
)
373 SamplingRegion
samplingRegion("DFG CFG Simplification Phase");
374 return runPhase
<CFGSimplificationPhase
>(graph
);
377 } } // namespace JSC::DFG
379 #endif // ENABLE(DFG_JIT)