2 * Copyright (C) 2013-2015 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 "DFGCPSRethreadingPhase.h"
31 #include "DFGBasicBlockInlines.h"
34 #include "JSCInlines.h"
36 namespace JSC
{ namespace DFG
{
38 class CPSRethreadingPhase
: public Phase
{
40 CPSRethreadingPhase(Graph
& graph
)
41 : Phase(graph
, "CPS rethreading")
47 RELEASE_ASSERT(m_graph
.m_refCountState
== EverythingIsLive
);
49 if (m_graph
.m_form
== ThreadedCPS
)
53 freeUnnecessaryNodes();
54 m_graph
.clearReplacements();
55 canonicalizeLocalsInBlocks();
56 specialCaseArguments();
57 propagatePhis
<LocalOperand
>();
58 propagatePhis
<ArgumentOperand
>();
61 m_graph
.m_form
= ThreadedCPS
;
67 void clearIsLoadedFrom()
69 for (unsigned i
= 0; i
< m_graph
.m_variableAccessData
.size(); ++i
)
70 m_graph
.m_variableAccessData
[i
].setIsLoadedFrom(false);
73 void freeUnnecessaryNodes()
75 SamplingRegion
samplingRegion("DFG CPS Rethreading: freeUnnecessaryNodes");
77 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
78 BasicBlock
* block
= m_graph
.block(blockIndex
);
81 ASSERT(block
->isReachable
);
83 unsigned fromIndex
= 0;
85 while (fromIndex
< block
->size()) {
86 Node
* node
= block
->at(fromIndex
++);
91 node
->children
.setChild1(Edge());
94 if (!node
->child1()) {
95 m_graph
.m_allocator
.free(node
);
98 switch (node
->child1()->op()) {
102 node
->convertPhantomToPhantomLocal();
105 ASSERT(node
->child1()->hasResult());
112 block
->at(toIndex
++) = node
;
114 block
->resize(toIndex
);
116 for (unsigned phiIndex
= block
->phis
.size(); phiIndex
--;)
117 m_graph
.m_allocator
.free(block
->phis
[phiIndex
]);
118 block
->phis
.resize(0);
122 template<OperandKind operandKind
>
123 void clearVariables()
126 m_block
->variablesAtHead
.sizeFor
<operandKind
>()
127 == m_block
->variablesAtTail
.sizeFor
<operandKind
>());
129 for (unsigned i
= m_block
->variablesAtHead
.sizeFor
<operandKind
>(); i
--;) {
130 m_block
->variablesAtHead
.atFor
<operandKind
>(i
) = nullptr;
131 m_block
->variablesAtTail
.atFor
<operandKind
>(i
) = nullptr;
135 ALWAYS_INLINE Node
* addPhiSilently(BasicBlock
* block
, const NodeOrigin
& origin
, VariableAccessData
* variable
)
137 Node
* result
= m_graph
.addNode(SpecNone
, Phi
, origin
, OpInfo(variable
));
138 block
->phis
.append(result
);
142 template<OperandKind operandKind
>
143 ALWAYS_INLINE Node
* addPhi(BasicBlock
* block
, const NodeOrigin
& origin
, VariableAccessData
* variable
, size_t index
)
145 Node
* result
= addPhiSilently(block
, origin
, variable
);
146 phiStackFor
<operandKind
>().append(PhiStackEntry(block
, index
, result
));
150 template<OperandKind operandKind
>
151 ALWAYS_INLINE Node
* addPhi(const NodeOrigin
& origin
, VariableAccessData
* variable
, size_t index
)
153 return addPhi
<operandKind
>(m_block
, origin
, variable
, index
);
156 template<OperandKind operandKind
>
157 void canonicalizeGetLocalFor(Node
* node
, VariableAccessData
* variable
, size_t idx
)
159 ASSERT(!node
->child1());
161 if (Node
* otherNode
= m_block
->variablesAtTail
.atFor
<operandKind
>(idx
)) {
162 ASSERT(otherNode
->variableAccessData() == variable
);
164 switch (otherNode
->op()) {
167 otherNode
= otherNode
->child1().node();
168 if (otherNode
->op() == Phi
) {
169 // We need to have a GetLocal, so this might as well be the one.
170 node
->children
.setChild1(Edge(otherNode
));
171 m_block
->variablesAtTail
.atFor
<operandKind
>(idx
) = node
;
174 ASSERT(otherNode
->op() == SetLocal
|| otherNode
->op() == SetArgument
);
180 ASSERT(otherNode
->op() == SetLocal
|| otherNode
->op() == SetArgument
|| otherNode
->op() == GetLocal
);
181 ASSERT(otherNode
->variableAccessData() == variable
);
183 if (otherNode
->op() == SetArgument
) {
184 variable
->setIsLoadedFrom(true);
185 node
->children
.setChild1(Edge(otherNode
));
186 m_block
->variablesAtTail
.atFor
<operandKind
>(idx
) = node
;
190 if (otherNode
->op() == GetLocal
) {
191 // Replace all references to this GetLocal with otherNode.
192 node
->replaceWith(otherNode
);
196 ASSERT(otherNode
->op() == SetLocal
);
197 node
->replaceWith(otherNode
->child1().node());
201 variable
->setIsLoadedFrom(true);
202 Node
* phi
= addPhi
<operandKind
>(node
->origin
, variable
, idx
);
203 node
->children
.setChild1(Edge(phi
));
204 m_block
->variablesAtHead
.atFor
<operandKind
>(idx
) = phi
;
205 m_block
->variablesAtTail
.atFor
<operandKind
>(idx
) = node
;
208 void canonicalizeGetLocal(Node
* node
)
210 VariableAccessData
* variable
= node
->variableAccessData();
211 if (variable
->local().isArgument())
212 canonicalizeGetLocalFor
<ArgumentOperand
>(node
, variable
, variable
->local().toArgument());
214 canonicalizeGetLocalFor
<LocalOperand
>(node
, variable
, variable
->local().toLocal());
217 template<NodeType nodeType
, OperandKind operandKind
>
218 void canonicalizeFlushOrPhantomLocalFor(Node
* node
, VariableAccessData
* variable
, size_t idx
)
220 ASSERT(!node
->child1());
222 if (Node
* otherNode
= m_block
->variablesAtTail
.atFor
<operandKind
>(idx
)) {
223 ASSERT(otherNode
->variableAccessData() == variable
);
225 switch (otherNode
->op()) {
229 otherNode
= otherNode
->child1().node();
235 ASSERT(otherNode
->op() == Phi
|| otherNode
->op() == SetLocal
|| otherNode
->op() == SetArgument
);
237 if (nodeType
== PhantomLocal
&& otherNode
->op() == SetLocal
) {
238 // PhantomLocal(SetLocal) doesn't make sense. PhantomLocal means: at this
239 // point I know I would have been interested in the value of this variable
240 // for the purpose of OSR. PhantomLocal(SetLocal) means: at this point I
241 // know that I would have read the value written by that SetLocal. This is
242 // redundant and inefficient, since really it just means that we want to
243 // keep the last MovHinted value of that local alive.
249 variable
->setIsLoadedFrom(true);
250 // There is nothing wrong with having redundant Flush's. It just needs to
251 // be linked appropriately. Note that if there had already been a previous
252 // use at tail then we don't override it. It's fine for variablesAtTail to
253 // omit Flushes and PhantomLocals. On the other hand, having it refer to a
254 // Flush or a PhantomLocal if just before it the last use was a GetLocal would
255 // seriously confuse the CFA.
256 node
->children
.setChild1(Edge(otherNode
));
260 variable
->setIsLoadedFrom(true);
261 node
->children
.setChild1(Edge(addPhi
<operandKind
>(node
->origin
, variable
, idx
)));
262 m_block
->variablesAtHead
.atFor
<operandKind
>(idx
) = node
;
263 m_block
->variablesAtTail
.atFor
<operandKind
>(idx
) = node
;
266 template<NodeType nodeType
>
267 void canonicalizeFlushOrPhantomLocal(Node
* node
)
269 VariableAccessData
* variable
= node
->variableAccessData();
270 if (variable
->local().isArgument())
271 canonicalizeFlushOrPhantomLocalFor
<nodeType
, ArgumentOperand
>(node
, variable
, variable
->local().toArgument());
273 canonicalizeFlushOrPhantomLocalFor
<nodeType
, LocalOperand
>(node
, variable
, variable
->local().toLocal());
276 void canonicalizeSet(Node
* node
)
278 m_block
->variablesAtTail
.setOperand(node
->local(), node
);
281 void canonicalizeLocalsInBlock()
285 ASSERT(m_block
->isReachable
);
287 clearVariables
<ArgumentOperand
>();
288 clearVariables
<LocalOperand
>();
290 // Assumes that all phi references have been removed. Assumes that things that
291 // should be live have a non-zero ref count, but doesn't assume that the ref
292 // counts are correct beyond that (more formally !!logicalRefCount == !!actualRefCount
293 // but not logicalRefCount == actualRefCount). Assumes that it can break ref
296 for (unsigned nodeIndex
= 0; nodeIndex
< m_block
->size(); ++nodeIndex
) {
297 Node
* node
= m_block
->at(nodeIndex
);
299 m_graph
.performSubstitution(node
);
301 // The rules for threaded CPS form:
303 // Head variable: describes what is live at the head of the basic block.
304 // Head variable links may refer to Flush, PhantomLocal, Phi, or SetArgument.
305 // SetArgument may only appear in the root block.
307 // Tail variable: the last thing that happened to the variable in the block.
308 // It may be a Flush, PhantomLocal, GetLocal, SetLocal, SetArgument, or Phi.
309 // SetArgument may only appear in the root block. Note that if there ever
310 // was a GetLocal to the variable, and it was followed by PhantomLocals and
311 // Flushes but not SetLocals, then the tail variable will be the GetLocal.
312 // This reflects the fact that you only care that the tail variable is a
313 // Flush or PhantomLocal if nothing else interesting happened. Likewise, if
314 // there ever was a SetLocal and it was followed by Flushes, then the tail
315 // variable will be a SetLocal and not those subsequent Flushes.
317 // Child of GetLocal: the operation that the GetLocal keeps alive. It may be
318 // a Phi from the current block. For arguments, it may be a SetArgument.
320 // Child of SetLocal: must be a value producing node.
322 // Child of Flush: it may be a Phi from the current block or a SetLocal. For
323 // arguments it may also be a SetArgument.
325 // Child of PhantomLocal: it may be a Phi from the current block. For
326 // arguments it may also be a SetArgument.
328 // Children of Phi: other Phis in the same basic block, or any of the
329 // following from predecessor blocks: SetLocal, Phi, or SetArgument. These
330 // are computed by looking at the tail variables of the predecessor blocks
331 // and either using it directly (if it's a SetLocal, Phi, or SetArgument) or
332 // loading that nodes child (if it's a GetLocal, PhanomLocal, or Flush - all
333 // of these will have children that are SetLocal, Phi, or SetArgument).
335 switch (node
->op()) {
337 canonicalizeGetLocal(node
);
341 canonicalizeSet(node
);
345 canonicalizeFlushOrPhantomLocal
<Flush
>(node
);
349 canonicalizeFlushOrPhantomLocal
<PhantomLocal
>(node
);
353 canonicalizeSet(node
);
362 void canonicalizeLocalsInBlocks()
364 SamplingRegion
samplingRegion("DFG CPS Rethreading: canonicalizeLocalsInBlocks");
366 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
367 m_block
= m_graph
.block(blockIndex
);
368 canonicalizeLocalsInBlock();
372 void specialCaseArguments()
374 // Normally, a SetArgument denotes the start of a live range for a local's value on the stack.
375 // But those SetArguments used for the actual arguments to the machine CodeBlock get
376 // special-cased. We could have instead used two different node types - one for the arguments
377 // at the prologue case, and another for the other uses. But this seemed like IR overkill.
378 for (unsigned i
= m_graph
.m_arguments
.size(); i
--;)
379 m_graph
.block(0)->variablesAtHead
.setArgumentFirstTime(i
, m_graph
.m_arguments
[i
]);
382 template<OperandKind operandKind
>
385 Vector
<PhiStackEntry
, 128>& phiStack
= operandKind
== ArgumentOperand
? m_argumentPhiStack
: m_localPhiStack
;
387 SamplingRegion
samplingRegion("DFG CPS Rethreading: propagatePhis");
389 // Ensure that attempts to use this fail instantly.
392 while (!phiStack
.isEmpty()) {
393 PhiStackEntry entry
= phiStack
.last();
394 phiStack
.removeLast();
396 BasicBlock
* block
= entry
.m_block
;
397 PredecessorList
& predecessors
= block
->predecessors
;
398 Node
* currentPhi
= entry
.m_phi
;
399 VariableAccessData
* variable
= currentPhi
->variableAccessData();
400 size_t index
= entry
.m_index
;
402 for (size_t i
= predecessors
.size(); i
--;) {
403 BasicBlock
* predecessorBlock
= predecessors
[i
];
405 Node
* variableInPrevious
= predecessorBlock
->variablesAtTail
.atFor
<operandKind
>(index
);
406 if (!variableInPrevious
) {
407 variableInPrevious
= addPhi
<operandKind
>(predecessorBlock
, currentPhi
->origin
, variable
, index
);
408 predecessorBlock
->variablesAtTail
.atFor
<operandKind
>(index
) = variableInPrevious
;
409 predecessorBlock
->variablesAtHead
.atFor
<operandKind
>(index
) = variableInPrevious
;
411 switch (variableInPrevious
->op()) {
415 ASSERT(variableInPrevious
->variableAccessData() == variableInPrevious
->child1()->variableAccessData());
416 variableInPrevious
= variableInPrevious
->child1().node();
424 variableInPrevious
->op() == SetLocal
425 || variableInPrevious
->op() == Phi
426 || variableInPrevious
->op() == SetArgument
);
428 if (!currentPhi
->child1()) {
429 currentPhi
->children
.setChild1(Edge(variableInPrevious
));
432 if (!currentPhi
->child2()) {
433 currentPhi
->children
.setChild2(Edge(variableInPrevious
));
436 if (!currentPhi
->child3()) {
437 currentPhi
->children
.setChild3(Edge(variableInPrevious
));
441 Node
* newPhi
= addPhiSilently(block
, currentPhi
->origin
, variable
);
442 newPhi
->children
= currentPhi
->children
;
443 currentPhi
->children
.initialize(newPhi
, variableInPrevious
, 0);
448 struct PhiStackEntry
{
449 PhiStackEntry(BasicBlock
* block
, size_t index
, Node
* phi
)
461 template<OperandKind operandKind
>
462 Vector
<PhiStackEntry
, 128>& phiStackFor()
464 if (operandKind
== ArgumentOperand
)
465 return m_argumentPhiStack
;
466 return m_localPhiStack
;
469 void computeIsFlushed()
471 m_graph
.clearFlagsOnAllNodes(NodeIsFlushed
);
473 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
474 BasicBlock
* block
= m_graph
.block(blockIndex
);
477 for (unsigned nodeIndex
= block
->size(); nodeIndex
--;) {
478 Node
* node
= block
->at(nodeIndex
);
479 if (node
->op() != Flush
)
481 addFlushedLocalOp(node
);
484 while (!m_flushedLocalOpWorklist
.isEmpty()) {
485 Node
* node
= m_flushedLocalOpWorklist
.takeLast();
486 switch (node
->op()) {
493 ASSERT(node
->flags() & NodeIsFlushed
);
494 DFG_NODE_DO_TO_CHILDREN(m_graph
, node
, addFlushedLocalEdge
);
498 DFG_CRASH(m_graph
, node
, "Invalid node in flush graph");
504 void addFlushedLocalOp(Node
* node
)
506 if (node
->mergeFlags(NodeIsFlushed
))
507 m_flushedLocalOpWorklist
.append(node
);
510 void addFlushedLocalEdge(Node
*, Edge edge
)
512 addFlushedLocalOp(edge
.node());
516 Vector
<PhiStackEntry
, 128> m_argumentPhiStack
;
517 Vector
<PhiStackEntry
, 128> m_localPhiStack
;
518 Vector
<Node
*, 128> m_flushedLocalOpWorklist
;
521 bool performCPSRethreading(Graph
& graph
)
523 SamplingRegion
samplingRegion("DFG CPS Rethreading Phase");
524 return runPhase
<CPSRethreadingPhase
>(graph
);
527 } } // namespace JSC::DFG
529 #endif // ENABLE(DFG_JIT)