2 * Copyright (C) 2011 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 "DFGCSEPhase.h"
34 namespace JSC
{ namespace DFG
{
36 class CSEPhase
: public Phase
{
38 CSEPhase(Graph
& graph
)
39 : Phase(graph
, "common subexpression elimination")
41 // Replacements are used to implement local common subexpression elimination.
42 m_replacements
.resize(m_graph
.size());
44 for (unsigned i
= 0; i
< m_graph
.size(); ++i
)
45 m_replacements
[i
] = NoNode
;
50 for (unsigned block
= 0; block
< m_graph
.m_blocks
.size(); ++block
)
51 performBlockCSE(*m_graph
.m_blocks
[block
]);
56 NodeIndex
canonicalize(NodeIndex nodeIndex
)
58 if (nodeIndex
== NoNode
)
61 if (m_graph
[nodeIndex
].op() == ValueToInt32
)
62 nodeIndex
= m_graph
[nodeIndex
].child1().index();
66 NodeIndex
canonicalize(Edge nodeUse
)
68 return canonicalize(nodeUse
.indexUnchecked());
71 unsigned endIndexForPureCSE()
73 unsigned result
= m_lastSeen
[m_graph
[m_compileIndex
].op()];
74 if (result
== UINT_MAX
)
78 ASSERT(result
<= m_indexInBlock
);
79 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
80 dataLog(" limit %u: ", result
);
85 NodeIndex
pureCSE(Node
& node
)
87 NodeIndex child1
= canonicalize(node
.child1());
88 NodeIndex child2
= canonicalize(node
.child2());
89 NodeIndex child3
= canonicalize(node
.child3());
91 for (unsigned i
= endIndexForPureCSE(); i
--;) {
92 NodeIndex index
= m_currentBlock
->at(i
);
93 if (index
== child1
|| index
== child2
|| index
== child3
)
96 Node
& otherNode
= m_graph
[index
];
97 if (node
.op() != otherNode
.op())
100 if (node
.arithNodeFlags() != otherNode
.arithNodeFlags())
103 NodeIndex otherChild
= canonicalize(otherNode
.child1());
104 if (otherChild
== NoNode
)
106 if (otherChild
!= child1
)
109 otherChild
= canonicalize(otherNode
.child2());
110 if (otherChild
== NoNode
)
112 if (otherChild
!= child2
)
115 otherChild
= canonicalize(otherNode
.child3());
116 if (otherChild
== NoNode
)
118 if (otherChild
!= child3
)
126 bool isPredictedNumerical(Node
& node
)
128 PredictedType left
= m_graph
[node
.child1()].prediction();
129 PredictedType right
= m_graph
[node
.child2()].prediction();
130 return isNumberPrediction(left
) && isNumberPrediction(right
);
133 bool logicalNotIsPure(Node
& node
)
135 PredictedType prediction
= m_graph
[node
.child1()].prediction();
136 return isBooleanPrediction(prediction
) || !prediction
;
139 bool byValIsPure(Node
& node
)
141 return m_graph
[node
.child2()].shouldSpeculateInteger()
142 && ((node
.op() == PutByVal
|| node
.op() == PutByValAlias
)
143 ? isActionableMutableArrayPrediction(m_graph
[node
.child1()].prediction())
144 : isActionableArrayPrediction(m_graph
[node
.child1()].prediction()));
147 bool clobbersWorld(NodeIndex nodeIndex
)
149 Node
& node
= m_graph
[nodeIndex
];
150 if (node
.flags() & NodeClobbersWorld
)
152 if (!(node
.flags() & NodeMightClobber
))
159 case CompareGreaterEq
:
161 return !isPredictedNumerical(node
);
163 return !logicalNotIsPure(node
);
165 return !byValIsPure(node
);
167 ASSERT_NOT_REACHED();
168 return true; // If by some oddity we hit this case in release build it's safer to have CSE assume the worst.
172 NodeIndex
impureCSE(Node
& node
)
174 NodeIndex child1
= canonicalize(node
.child1());
175 NodeIndex child2
= canonicalize(node
.child2());
176 NodeIndex child3
= canonicalize(node
.child3());
178 for (unsigned i
= m_indexInBlock
; i
--;) {
179 NodeIndex index
= m_currentBlock
->at(i
);
180 if (index
== child1
|| index
== child2
|| index
== child3
)
183 Node
& otherNode
= m_graph
[index
];
184 if (node
.op() == otherNode
.op()
185 && node
.arithNodeFlags() == otherNode
.arithNodeFlags()) {
186 NodeIndex otherChild
= canonicalize(otherNode
.child1());
187 if (otherChild
== NoNode
)
189 if (otherChild
== child1
) {
190 otherChild
= canonicalize(otherNode
.child2());
191 if (otherChild
== NoNode
)
193 if (otherChild
== child2
) {
194 otherChild
= canonicalize(otherNode
.child3());
195 if (otherChild
== NoNode
)
197 if (otherChild
== child3
)
202 if (clobbersWorld(index
))
208 NodeIndex
globalVarLoadElimination(unsigned varNumber
, JSGlobalObject
* globalObject
)
210 for (unsigned i
= m_indexInBlock
; i
--;) {
211 NodeIndex index
= m_currentBlock
->at(i
);
212 Node
& node
= m_graph
[index
];
215 if (node
.varNumber() == varNumber
&& codeBlock()->globalObjectFor(node
.codeOrigin
) == globalObject
)
219 if (node
.varNumber() == varNumber
&& codeBlock()->globalObjectFor(node
.codeOrigin
) == globalObject
)
220 return node
.child1().index();
225 if (clobbersWorld(index
))
231 NodeIndex
getByValLoadElimination(NodeIndex child1
, NodeIndex child2
)
233 for (unsigned i
= m_indexInBlock
; i
--;) {
234 NodeIndex index
= m_currentBlock
->at(i
);
235 if (index
== child1
|| index
== canonicalize(child2
))
238 Node
& node
= m_graph
[index
];
241 if (!byValIsPure(node
))
243 if (node
.child1() == child1
&& canonicalize(node
.child2()) == canonicalize(child2
))
248 if (!byValIsPure(node
))
250 if (node
.child1() == child1
&& canonicalize(node
.child2()) == canonicalize(child2
))
251 return node
.child3().index();
252 // We must assume that the PutByVal will clobber the location we're getting from.
253 // FIXME: We can do better; if we know that the PutByVal is accessing an array of a
254 // different type than the GetByVal, then we know that they won't clobber each other.
258 // GetByVal currently always speculates that it's accessing an
259 // array with an integer index, which means that it's impossible
260 // for a structure change or a put to property storage to affect
264 // A push cannot affect previously existing elements in the array.
267 if (clobbersWorld(index
))
275 bool checkFunctionElimination(JSFunction
* function
, NodeIndex child1
)
277 for (unsigned i
= endIndexForPureCSE(); i
--;) {
278 NodeIndex index
= m_currentBlock
->at(i
);
282 Node
& node
= m_graph
[index
];
283 if (node
.op() == CheckFunction
&& node
.child1() == child1
&& node
.function() == function
)
289 bool checkStructureLoadElimination(const StructureSet
& structureSet
, NodeIndex child1
)
291 for (unsigned i
= m_indexInBlock
; i
--;) {
292 NodeIndex index
= m_currentBlock
->at(i
);
296 Node
& node
= m_graph
[index
];
299 if (node
.child1() == child1
300 && structureSet
.isSupersetOf(node
.structureSet()))
305 if (node
.child1() == child1
306 && structureSet
.contains(node
.structureTransitionData().newStructure
))
308 if (structureSet
.contains(node
.structureTransitionData().previousStructure
))
313 // Setting a property cannot change the structure.
318 if (byValIsPure(node
)) {
319 // If PutByVal speculates that it's accessing an array with an
320 // integer index, then it's impossible for it to cause a structure
327 if (clobbersWorld(index
))
335 NodeIndex
getByOffsetLoadElimination(unsigned identifierNumber
, NodeIndex child1
)
337 for (unsigned i
= m_indexInBlock
; i
--;) {
338 NodeIndex index
= m_currentBlock
->at(i
);
342 Node
& node
= m_graph
[index
];
345 if (node
.child1() == child1
346 && m_graph
.m_storageAccessData
[node
.storageAccessDataIndex()].identifierNumber
== identifierNumber
)
351 if (m_graph
.m_storageAccessData
[node
.storageAccessDataIndex()].identifierNumber
== identifierNumber
) {
352 if (node
.child2() == child1
)
353 return node
.child3().index();
359 // Changing the structure cannot change the outcome of a property get.
364 if (byValIsPure(node
)) {
365 // If PutByVal speculates that it's accessing an array with an
366 // integer index, then it's impossible for it to cause a structure
373 if (clobbersWorld(index
))
381 NodeIndex
getPropertyStorageLoadElimination(NodeIndex child1
)
383 for (unsigned i
= m_indexInBlock
; i
--;) {
384 NodeIndex index
= m_currentBlock
->at(i
);
388 Node
& node
= m_graph
[index
];
390 case GetPropertyStorage
:
391 if (node
.child1() == child1
)
397 // Changing the structure or putting to the storage cannot
398 // change the property storage pointer.
403 if (byValIsPure(node
)) {
404 // If PutByVal speculates that it's accessing an array with an
405 // integer index, then it's impossible for it to cause a structure
412 if (clobbersWorld(index
))
420 NodeIndex
getIndexedPropertyStorageLoadElimination(NodeIndex child1
, bool hasIntegerIndexPrediction
)
422 for (unsigned i
= m_indexInBlock
; i
--;) {
423 NodeIndex index
= m_currentBlock
->at(i
);
427 Node
& node
= m_graph
[index
];
429 case GetIndexedPropertyStorage
: {
430 PredictedType basePrediction
= m_graph
[node
.child2()].prediction();
431 bool nodeHasIntegerIndexPrediction
= !(!(basePrediction
& PredictInt32
) && basePrediction
);
432 if (node
.child1() == child1
&& hasIntegerIndexPrediction
== nodeHasIntegerIndexPrediction
)
439 // Changing the structure or putting to the storage cannot
440 // change the property storage pointer.
444 // PutByValAlias can't change the indexed storage pointer
448 if (isFixedIndexedStorageObjectPrediction(m_graph
[node
.child1()].prediction()) && byValIsPure(node
))
453 if (clobbersWorld(index
))
461 NodeIndex
getScopeChainLoadElimination(unsigned depth
)
463 for (unsigned i
= endIndexForPureCSE(); i
--;) {
464 NodeIndex index
= m_currentBlock
->at(i
);
465 Node
& node
= m_graph
[index
];
466 if (node
.op() == GetScopeChain
467 && node
.scopeChainDepth() == depth
)
473 void performSubstitution(Edge
& child
, bool addRef
= true)
475 // Check if this operand is actually unused.
479 // Check if there is any replacement.
480 NodeIndex replacement
= m_replacements
[child
.index()];
481 if (replacement
== NoNode
)
484 child
.setIndex(replacement
);
486 // There is definitely a replacement. Assert that the replacement does not
487 // have a replacement.
488 ASSERT(m_replacements
[child
.index()] == NoNode
);
491 m_graph
[child
].ref();
494 void setReplacement(NodeIndex replacement
)
496 if (replacement
== NoNode
)
499 // Be safe. Don't try to perform replacements if the predictions don't
501 if (m_graph
[m_compileIndex
].prediction() != m_graph
[replacement
].prediction())
504 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
505 dataLog(" Replacing @%u -> @%u", m_compileIndex
, replacement
);
508 Node
& node
= m_graph
[m_compileIndex
];
509 node
.setOpAndDefaultFlags(Phantom
);
512 // At this point we will eliminate all references to this node.
513 m_replacements
[m_compileIndex
] = replacement
;
518 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
519 dataLog(" Eliminating @%u", m_compileIndex
);
522 Node
& node
= m_graph
[m_compileIndex
];
523 ASSERT(node
.refCount() == 1);
524 ASSERT(node
.mustGenerate());
525 node
.setOpAndDefaultFlags(Phantom
);
528 void performNodeCSE(Node
& node
)
530 bool shouldGenerate
= node
.shouldGenerate();
532 if (node
.flags() & NodeHasVarArgs
) {
533 for (unsigned childIdx
= node
.firstChild(); childIdx
< node
.firstChild() + node
.numChildren(); childIdx
++)
534 performSubstitution(m_graph
.m_varArgChildren
[childIdx
], shouldGenerate
);
536 performSubstitution(node
.children
.child1(), shouldGenerate
);
537 performSubstitution(node
.children
.child2(), shouldGenerate
);
538 performSubstitution(node
.children
.child3(), shouldGenerate
);
544 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
545 dataLog(" %s @%u: ", Graph::opName(m_graph
[m_compileIndex
].op()), m_compileIndex
);
548 // NOTE: there are some nodes that we deliberately don't CSE even though we
549 // probably could, like StrCat and ToPrimitive. That's because there is no
550 // evidence that doing CSE on these nodes would result in a performance
551 // progression. Hence considering these nodes in CSE would just mean that this
552 // code does more work with no win. Of course, we may want to reconsider this,
553 // since StrCat is trivially CSE-able. It's not trivially doable for
554 // ToPrimitive, but we could change that with some speculations if we really
559 // Handle the pure nodes. These nodes never have any side-effects.
576 case GetInt8ArrayLength
:
577 case GetInt16ArrayLength
:
578 case GetInt32ArrayLength
:
579 case GetUint8ArrayLength
:
580 case GetUint8ClampedArrayLength
:
581 case GetUint16ArrayLength
:
582 case GetUint32ArrayLength
:
583 case GetFloat32ArrayLength
:
584 case GetFloat64ArrayLength
:
586 case GetStringLength
:
588 case StringCharCodeAt
:
597 setReplacement(pureCSE(node
));
601 setReplacement(impureCSE(node
));
605 setReplacement(getScopeChainLoadElimination(node
.scopeChainDepth()));
608 // Handle nodes that are conditionally pure: these are pure, and can
609 // be CSE'd, so long as the prediction is the one we want.
614 case CompareGreaterEq
:
616 if (isPredictedNumerical(node
)) {
617 NodeIndex replacementIndex
= pureCSE(node
);
618 if (replacementIndex
!= NoNode
&& isPredictedNumerical(m_graph
[replacementIndex
]))
619 setReplacement(replacementIndex
);
625 if (logicalNotIsPure(node
)) {
626 NodeIndex replacementIndex
= pureCSE(node
);
627 if (replacementIndex
!= NoNode
&& logicalNotIsPure(m_graph
[replacementIndex
]))
628 setReplacement(replacementIndex
);
633 // Finally handle heap accesses. These are not quite pure, but we can still
634 // optimize them provided that some subtle conditions are met.
636 setReplacement(globalVarLoadElimination(node
.varNumber(), codeBlock()->globalObjectFor(node
.codeOrigin
)));
640 if (byValIsPure(node
))
641 setReplacement(getByValLoadElimination(node
.child1().index(), node
.child2().index()));
645 if (byValIsPure(node
) && getByValLoadElimination(node
.child1().index(), node
.child2().index()) != NoNode
)
646 node
.setOp(PutByValAlias
);
650 if (checkStructureLoadElimination(node
.structureSet(), node
.child1().index()))
655 if (checkFunctionElimination(node
.function(), node
.child1().index()))
659 case GetIndexedPropertyStorage
: {
660 PredictedType basePrediction
= m_graph
[node
.child2()].prediction();
661 bool nodeHasIntegerIndexPrediction
= !(!(basePrediction
& PredictInt32
) && basePrediction
);
662 setReplacement(getIndexedPropertyStorageLoadElimination(node
.child1().index(), nodeHasIntegerIndexPrediction
));
666 case GetPropertyStorage
:
667 setReplacement(getPropertyStorageLoadElimination(node
.child1().index()));
671 setReplacement(getByOffsetLoadElimination(m_graph
.m_storageAccessData
[node
.storageAccessDataIndex()].identifierNumber
, node
.child1().index()));
679 m_lastSeen
[node
.op()] = m_indexInBlock
;
680 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
685 void performBlockCSE(BasicBlock
& block
)
687 m_currentBlock
= &block
;
688 for (unsigned i
= 0; i
< LastNodeType
; ++i
)
689 m_lastSeen
[i
] = UINT_MAX
;
691 for (m_indexInBlock
= 0; m_indexInBlock
< block
.size(); ++m_indexInBlock
) {
692 m_compileIndex
= block
[m_indexInBlock
];
693 performNodeCSE(m_graph
[m_compileIndex
]);
697 BasicBlock
* m_currentBlock
;
698 NodeIndex m_compileIndex
;
699 unsigned m_indexInBlock
;
700 Vector
<NodeIndex
, 16> m_replacements
;
701 FixedArray
<unsigned, LastNodeType
> m_lastSeen
;
704 void performCSE(Graph
& graph
)
706 runPhase
<CSEPhase
>(graph
);
709 } } // namespace JSC::DFG
711 #endif // ENABLE(DFG_JIT)