2 * Copyright (C) 2013, 2014 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 "DFGSSAConversionPhase.h"
31 #include "DFGBasicBlockInlines.h"
33 #include "DFGInsertionSet.h"
35 #include "JSCInlines.h"
37 namespace JSC
{ namespace DFG
{
39 class SSAConversionPhase
: public Phase
{
40 static const bool verbose
= false;
41 static const bool dumpGraph
= false;
44 SSAConversionPhase(Graph
& graph
)
45 : Phase(graph
, "SSA conversion")
46 , m_insertionSet(graph
)
53 RELEASE_ASSERT(m_graph
.m_form
== ThreadedCPS
);
56 dataLog("Graph dump at top of SSA conversion:\n");
60 // Eliminate all duplicate or self-pointing Phi edges. This means that
63 // p: Phi(@n1, @n2, @n3)
69 // if each @ni in {@n1, @n2, @n3} is either equal to @p to is equal
70 // to @x, for exactly one other @x. Additionally, trivial Phis (i.e.
71 // p: Phi(@x)) are forwarded, so that if have an edge to such @p, we
72 // replace it with @x. This loop does this for Phis only; later we do
73 // such forwarding for Phi references found in other nodes.
75 // See Aycock and Horspool in CC'00 for a better description of what
79 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
80 BasicBlock
* block
= m_graph
.block(blockIndex
);
83 for (unsigned phiIndex
= block
->phis
.size(); phiIndex
--;) {
84 Node
* phi
= block
->phis
[phiIndex
];
85 if (phi
->variableAccessData()->isCaptured())
87 forwardPhiChildren(phi
);
88 deduplicateChildren(phi
);
93 // For each basic block, for each local live at the head of that block,
94 // figure out what node we should be referring to instead of that local.
95 // If it turns out to be a non-trivial Phi, make sure that we create an
96 // SSA Phi and Upsilons in predecessor blocks. We reuse
97 // BasicBlock::variablesAtHead for tracking which nodes to refer to.
98 Operands
<bool> nonTrivialPhis(OperandsLike
, m_graph
.block(0)->variablesAtHead
);
99 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
100 BasicBlock
* block
= m_graph
.block(blockIndex
);
104 nonTrivialPhis
.fill(false);
105 for (unsigned i
= block
->phis
.size(); i
--;) {
106 Node
* phi
= block
->phis
[i
];
107 if (!phi
->children
.justOneChild())
108 nonTrivialPhis
.operand(phi
->local()) = true;
111 for (unsigned i
= block
->variablesAtHead
.size(); i
--;) {
112 Node
* node
= block
->variablesAtHead
[i
];
117 dataLog("At block #", blockIndex
, " for operand r", block
->variablesAtHead
.operandForIndex(i
), " have node ", node
, "\n");
119 VariableAccessData
* variable
= node
->variableAccessData();
120 if (variable
->isCaptured()) {
121 // Poison this entry in variablesAtHead because we don't
122 // want anyone to try to refer to it, if the variable is
124 block
->variablesAtHead
[i
] = 0;
128 switch (node
->op()) {
135 node
= node
->child1().node();
138 RELEASE_ASSERT_NOT_REACHED();
140 RELEASE_ASSERT(node
->op() == Phi
|| node
->op() == SetArgument
);
142 bool isFlushed
= !!(node
->flags() & NodeIsFlushed
);
144 if (node
->op() == Phi
) {
145 if (!nonTrivialPhis
.operand(node
->local())) {
146 Edge edge
= node
->children
.justOneChild();
149 dataLog(" One child: ", edge
, ", ", RawPointer(edge
.node()), "\n");
150 node
= edge
.node(); // It's something from a different basic block.
153 dataLog(" Non-trivial.\n");
154 // It's a non-trivial Phi.
155 FlushFormat format
= variable
->flushFormat();
156 NodeFlags result
= resultFor(format
);
157 UseKind useKind
= useKindFor(format
);
159 node
= m_insertionSet
.insertNode(0, SpecNone
, Phi
, NodeOrigin());
161 dataLog(" Inserted new node: ", node
, "\n");
162 node
->mergeFlags(result
);
163 RELEASE_ASSERT((node
->flags() & NodeResultMask
) == result
);
165 for (unsigned j
= block
->predecessors
.size(); j
--;) {
166 BasicBlock
* predecessor
= block
->predecessors
[j
];
167 predecessor
->appendNonTerminal(
168 m_graph
, SpecNone
, Upsilon
, predecessor
->last()->origin
,
169 OpInfo(node
), Edge(predecessor
->variablesAtTail
[i
], useKind
));
173 // Do nothing. For multiple reasons.
175 // Reason #1: If the local is flushed then we don't need to bother
176 // with a MovHint since every path to this point in the code will
177 // have flushed the bytecode variable using a SetLocal and hence
178 // the Availability::flushedAt() will agree, and that will be
179 // sufficient for figuring out how to recover the variable's value.
181 // Reason #2: If we had inserted a MovHint and the Phi function had
182 // died (because the only user of the value was the "flush" - i.e.
183 // some asynchronous runtime thingy) then the MovHint would turn
184 // into a ZombieHint, which would fool us into thinking that the
187 // Reason #3: If we had inserted a MovHint then even if the Phi
188 // stayed alive, we would still end up generating inefficient code
189 // since we would be telling the OSR exit compiler to use some SSA
190 // value for the bytecode variable rather than just telling it that
191 // the value was already on the stack.
193 m_insertionSet
.insertNode(
194 0, SpecNone
, MovHint
, NodeOrigin(),
195 OpInfo(variable
->local().offset()), node
->defaultEdge());
200 block
->variablesAtHead
[i
] = node
;
203 m_insertionSet
.execute(block
);
207 dataLog("Variables at head after SSA Phi insertion:\n");
208 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
209 BasicBlock
* block
= m_graph
.block(blockIndex
);
212 dataLog(" ", *block
, ": ", block
->variablesAtHead
, "\n");
216 // At this point variablesAtHead in each block refers to either:
218 // 1) A new SSA phi in the current block.
219 // 2) A SetArgument, which will soon get converted into a GetArgument.
220 // 3) An old CPS phi in a different block.
222 // We don't have to do anything for (1) and (2), but we do need to
223 // do a replacement for (3).
225 // Clear all replacements, since other phases may have used them.
226 m_graph
.clearReplacements();
229 dataLog("Graph just before identifying replacements:\n");
233 // For all of the old CPS Phis, figure out what they correspond to in SSA.
234 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
235 BasicBlock
* block
= m_graph
.block(blockIndex
);
239 dataLog("Dealing with block #", blockIndex
, "\n");
240 for (unsigned phiIndex
= block
->phis
.size(); phiIndex
--;) {
241 Node
* phi
= block
->phis
[phiIndex
];
244 "Considering ", phi
, " (", RawPointer(phi
), "), for r",
245 phi
->local(), ", and its replacement in ", *block
, ", ",
246 block
->variablesAtHead
.operand(phi
->local()), "\n");
248 ASSERT(phi
!= block
->variablesAtHead
.operand(phi
->local()));
249 phi
->misc
.replacement
= block
->variablesAtHead
.operand(phi
->local());
253 // Now make sure that all variablesAtHead in each block points to the
254 // canonical SSA value. Prior to this, variablesAtHead[local] may point to
255 // an old CPS Phi in a different block.
256 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
257 BasicBlock
* block
= m_graph
.block(blockIndex
);
260 for (size_t i
= block
->variablesAtHead
.size(); i
--;) {
261 Node
* node
= block
->variablesAtHead
[i
];
264 while (node
->misc
.replacement
) {
265 ASSERT(node
!= node
->misc
.replacement
);
266 node
= node
->misc
.replacement
;
268 block
->variablesAtHead
[i
] = node
;
273 dataLog("Variables at head after convergence:\n");
274 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
275 BasicBlock
* block
= m_graph
.block(blockIndex
);
278 dataLog(" ", *block
, ": ", block
->variablesAtHead
, "\n");
282 // Convert operations over locals into operations over SSA nodes.
283 // - GetLocal over captured variables lose their phis.
284 // - GetLocal over uncaptured variables die and get replaced with references
285 // to the node specified by variablesAtHead.
286 // - SetLocal gets NodeMustGenerate if it's flushed, or turns into a
288 // - Flush loses its children and turns into a Phantom.
289 // - PhantomLocal becomes Phantom, and its child is whatever is specified
290 // by variablesAtHead.
291 // - SetArgument turns into GetArgument unless it's a captured variable.
292 // - Upsilons get their children fixed to refer to the true value of that local
293 // at the end of the block. Prior to this loop, Upsilons will refer to
294 // variableAtTail[operand], which may be any of Flush, PhantomLocal, GetLocal,
295 // SetLocal, SetArgument, or Phi. We accomplish this by setting the
296 // replacement pointers of all of those nodes to refer to either
297 // variablesAtHead[operand], or the child of the SetLocal.
298 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
299 BasicBlock
* block
= m_graph
.block(blockIndex
);
303 for (unsigned phiIndex
= block
->phis
.size(); phiIndex
--;) {
304 block
->phis
[phiIndex
]->misc
.replacement
=
305 block
->variablesAtHead
.operand(block
->phis
[phiIndex
]->local());
307 for (unsigned nodeIndex
= block
->size(); nodeIndex
--;)
308 ASSERT(!block
->at(nodeIndex
)->misc
.replacement
);
310 for (unsigned nodeIndex
= 0; nodeIndex
< block
->size(); ++nodeIndex
) {
311 Node
* node
= block
->at(nodeIndex
);
313 m_graph
.performSubstitution(node
);
315 switch (node
->op()) {
317 VariableAccessData
* variable
= node
->variableAccessData();
318 if (variable
->isCaptured() || !!(node
->flags() & NodeIsFlushed
))
319 node
->mergeFlags(NodeMustGenerate
);
321 node
->setOpAndDefaultFlags(Check
);
322 node
->misc
.replacement
= node
->child1().node(); // Only for Upsilons.
327 // It seems tempting to just do forwardPhi(GetLocal), except that we
328 // could have created a new (SSA) Phi, and the GetLocal could still be
329 // referring to an old (CPS) Phi. Uses variablesAtHead to tell us what
331 node
->children
.reset();
332 VariableAccessData
* variable
= node
->variableAccessData();
333 if (variable
->isCaptured())
335 node
->convertToPhantom();
336 node
->misc
.replacement
= block
->variablesAtHead
.operand(variable
->local());
341 node
->children
.reset();
342 node
->convertToPhantom();
343 // This is only for Upsilons. An Upsilon will only refer to a Flush if
344 // there were no SetLocals or GetLocals in the block.
345 node
->misc
.replacement
= block
->variablesAtHead
.operand(node
->local());
350 ASSERT(node
->child1().useKind() == UntypedUse
);
351 VariableAccessData
* variable
= node
->variableAccessData();
352 if (variable
->isCaptured()) {
353 // This is a fun case. We could have a captured variable that had some
354 // or all of its uses strength reduced to phantoms rather than flushes.
355 // SSA conversion will currently still treat it as flushed, in the sense
356 // that it will just keep the SetLocal. Therefore, there is nothing that
357 // needs to be done here: we don't need to also keep the source value
358 // alive. And even if we did want to keep the source value alive, we
359 // wouldn't be able to, because the variablesAtHead value for a captured
360 // local wouldn't have been computed by the Phi reduction algorithm
362 node
->children
.reset();
365 block
->variablesAtHead
.operand(variable
->local())->defaultEdge();
367 node
->convertToPhantom();
368 // This is only for Upsilons. An Upsilon will only refer to a
369 // PhantomLocal if there were no SetLocals or GetLocals in the block.
370 node
->misc
.replacement
= block
->variablesAtHead
.operand(variable
->local());
375 VariableAccessData
* variable
= node
->variableAccessData();
376 if (variable
->isCaptured())
378 node
->setOpAndDefaultFlags(GetArgument
);
379 node
->setResult(resultFor(node
->variableAccessData()->flushFormat()));
389 // Free all CPS phis and reset variables vectors.
390 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
391 BasicBlock
* block
= m_graph
.block(blockIndex
);
394 for (unsigned phiIndex
= block
->phis
.size(); phiIndex
--;)
395 m_graph
.m_allocator
.free(block
->phis
[phiIndex
]);
397 block
->variablesAtHead
.clear();
398 block
->variablesAtTail
.clear();
399 block
->valuesAtHead
.clear();
400 block
->valuesAtHead
.clear();
401 block
->ssa
= adoptPtr(new BasicBlock::SSAData(block
));
404 m_graph
.m_arguments
.clear();
406 m_graph
.m_form
= SSA
;
411 void forwardPhiChildren(Node
* node
)
413 for (unsigned i
= 0; i
< AdjacencyList::Size
; ++i
) {
414 Edge
& edge
= node
->children
.child(i
);
417 m_changed
|= forwardPhiEdge(edge
);
421 Node
* forwardPhi(Node
* node
)
424 switch (node
->op()) {
426 Edge edge
= node
->children
.justOneChild();
434 if (node
->variableAccessData()->isCaptured())
436 node
= node
->child1().node();
444 bool forwardPhiEdge(Edge
& edge
)
446 Node
* newNode
= forwardPhi(edge
.node());
447 if (newNode
== edge
.node())
449 edge
.setNode(newNode
);
453 void deduplicateChildren(Node
* node
)
455 for (unsigned i
= 0; i
< AdjacencyList::Size
; ++i
) {
456 Edge edge
= node
->children
.child(i
);
460 node
->children
.removeEdge(i
--);
464 for (unsigned j
= i
+ 1; j
< AdjacencyList::Size
; ++j
) {
465 if (node
->children
.child(j
) == edge
) {
466 node
->children
.removeEdge(j
--);
473 InsertionSet m_insertionSet
;
477 bool performSSAConversion(Graph
& graph
)
479 SamplingRegion
samplingRegion("DFG SSA Conversion Phase");
480 return runPhase
<SSAConversionPhase
>(graph
);
483 } } // namespace JSC::DFG
485 #endif // ENABLE(DFG_JIT)