2 * Copyright (C) 2011, 2012, 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 "DFGCSEPhase.h"
31 #include "DFGAbstractHeap.h"
32 #include "DFGClobberize.h"
33 #include "DFGEdgeUsesStructure.h"
36 #include "JSCInlines.h"
38 #include <wtf/FastBitVector.h>
40 namespace JSC
{ namespace DFG
{
42 enum CSEMode
{ NormalCSE
, StoreElimination
};
44 template<CSEMode cseMode
>
45 class CSEPhase
: public Phase
{
47 CSEPhase(Graph
& graph
)
48 : Phase(graph
, cseMode
== NormalCSE
? "common subexpression elimination" : "store elimination")
54 ASSERT(m_graph
.m_fixpointState
!= BeforeFixpoint
);
58 m_graph
.clearReplacements();
60 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
61 BasicBlock
* block
= m_graph
.block(blockIndex
);
65 // All Phis need to already be marked as relevant to OSR.
66 if (!ASSERT_DISABLED
) {
67 for (unsigned i
= 0; i
< block
->phis
.size(); ++i
)
68 ASSERT(block
->phis
[i
]->flags() & NodeRelevantToOSR
);
71 for (unsigned i
= block
->size(); i
--;) {
72 Node
* node
= block
->at(i
);
76 case GetLocal
: // FIXME: The GetLocal case is only necessary until we do https://bugs.webkit.org/show_bug.cgi?id=106707.
77 node
->mergeFlags(NodeRelevantToOSR
);
80 node
->clearFlags(NodeRelevantToOSR
);
86 for (BlockIndex blockIndex
= m_graph
.numBlocks(); blockIndex
--;) {
87 BasicBlock
* block
= m_graph
.block(blockIndex
);
91 for (unsigned i
= block
->size(); i
--;) {
92 Node
* node
= block
->at(i
);
93 if (!node
->containsMovHint())
96 ASSERT(node
->op() != ZombieHint
);
97 node
->child1()->mergeFlags(NodeRelevantToOSR
);
101 if (m_graph
.m_form
== SSA
) {
102 Vector
<BasicBlock
*> depthFirst
;
103 m_graph
.getBlocksInDepthFirstOrder(depthFirst
);
104 for (unsigned i
= 0; i
< depthFirst
.size(); ++i
)
105 performBlockCSE(depthFirst
[i
]);
107 for (unsigned blockIndex
= 0; blockIndex
< m_graph
.numBlocks(); ++blockIndex
)
108 performBlockCSE(m_graph
.block(blockIndex
));
116 unsigned endIndexForPureCSE()
118 unsigned result
= m_lastSeen
[m_currentNode
->op()];
119 if (result
== UINT_MAX
)
123 ASSERT(result
<= m_indexInBlock
);
127 Node
* pureCSE(Node
* node
)
129 Edge child1
= node
->child1().sanitized();
130 Edge child2
= node
->child2().sanitized();
131 Edge child3
= node
->child3().sanitized();
133 for (unsigned i
= endIndexForPureCSE(); i
--;) {
134 Node
* otherNode
= m_currentBlock
->at(i
);
135 if (otherNode
== child1
|| otherNode
== child2
|| otherNode
== child3
)
138 if (node
->op() != otherNode
->op())
141 Edge otherChild
= otherNode
->child1().sanitized();
144 if (otherChild
!= child1
)
147 otherChild
= otherNode
->child2().sanitized();
150 if (otherChild
!= child2
)
153 otherChild
= otherNode
->child3().sanitized();
156 if (otherChild
!= child3
)
164 Node
* constantCSE(Node
* node
)
166 for (unsigned i
= endIndexForPureCSE(); i
--;) {
167 Node
* otherNode
= m_currentBlock
->at(i
);
168 if (otherNode
->op() != node
->op())
171 if (otherNode
->constantNumber() != node
->constantNumber())
179 Node
* weakConstantCSE(Node
* node
)
181 for (unsigned i
= endIndexForPureCSE(); i
--;) {
182 Node
* otherNode
= m_currentBlock
->at(i
);
183 if (otherNode
->op() != WeakJSConstant
)
186 if (otherNode
->weakConstant() != node
->weakConstant())
194 Node
* constantStoragePointerCSE(Node
* node
)
196 for (unsigned i
= endIndexForPureCSE(); i
--;) {
197 Node
* otherNode
= m_currentBlock
->at(i
);
198 if (otherNode
->op() != ConstantStoragePointer
)
201 if (otherNode
->storagePointer() != node
->storagePointer())
209 Node
* getCalleeLoadElimination()
211 for (unsigned i
= m_indexInBlock
; i
--;) {
212 Node
* node
= m_currentBlock
->at(i
);
213 switch (node
->op()) {
223 Node
* getArrayLengthElimination(Node
* array
)
225 for (unsigned i
= m_indexInBlock
; i
--;) {
226 Node
* node
= m_currentBlock
->at(i
);
227 switch (node
->op()) {
229 if (node
->child1() == array
)
235 if (!m_graph
.byValIsPure(node
))
237 if (node
->arrayMode().mayStoreToHole())
242 if (m_graph
.clobbersWorld(node
))
249 Node
* globalVarLoadElimination(WriteBarrier
<Unknown
>* registerPointer
)
251 for (unsigned i
= m_indexInBlock
; i
--;) {
252 Node
* node
= m_currentBlock
->at(i
);
253 switch (node
->op()) {
255 if (node
->registerPointer() == registerPointer
)
259 if (node
->registerPointer() == registerPointer
)
260 return node
->child1().node();
265 if (m_graph
.clobbersWorld(node
))
271 Node
* scopedVarLoadElimination(Node
* registers
, int varNumber
)
273 for (unsigned i
= m_indexInBlock
; i
--;) {
274 Node
* node
= m_currentBlock
->at(i
);
275 switch (node
->op()) {
276 case GetClosureVar
: {
277 if (node
->child1() == registers
&& node
->varNumber() == varNumber
)
281 case PutClosureVar
: {
282 if (node
->varNumber() != varNumber
)
284 if (node
->child2() == registers
)
285 return node
->child3().node();
289 VariableAccessData
* variableAccessData
= node
->variableAccessData();
290 if (variableAccessData
->isCaptured()
291 && variableAccessData
->local() == static_cast<VirtualRegister
>(varNumber
))
298 if (m_graph
.clobbersWorld(node
))
304 bool varInjectionWatchpointElimination()
306 for (unsigned i
= m_indexInBlock
; i
--;) {
307 Node
* node
= m_currentBlock
->at(i
);
308 if (node
->op() == VarInjectionWatchpoint
)
310 if (m_graph
.clobbersWorld(node
))
316 Node
* globalVarStoreElimination(WriteBarrier
<Unknown
>* registerPointer
)
318 for (unsigned i
= m_indexInBlock
; i
--;) {
319 Node
* node
= m_currentBlock
->at(i
);
320 switch (node
->op()) {
322 if (node
->registerPointer() == registerPointer
)
327 if (node
->registerPointer() == registerPointer
)
334 if (m_graph
.clobbersWorld(node
) || node
->canExit())
340 Node
* scopedVarStoreElimination(Node
* scope
, Node
* registers
, int varNumber
)
342 for (unsigned i
= m_indexInBlock
; i
--;) {
343 Node
* node
= m_currentBlock
->at(i
);
344 switch (node
->op()) {
345 case PutClosureVar
: {
346 if (node
->varNumber() != varNumber
)
348 if (node
->child1() == scope
&& node
->child2() == registers
)
353 case GetClosureVar
: {
354 // Let's be conservative.
355 if (node
->varNumber() == varNumber
)
362 VariableAccessData
* variableAccessData
= node
->variableAccessData();
363 if (variableAccessData
->isCaptured()
364 && variableAccessData
->local() == static_cast<VirtualRegister
>(varNumber
))
372 if (m_graph
.clobbersWorld(node
) || node
->canExit())
378 Node
* getByValLoadElimination(Node
* child1
, Node
* child2
, ArrayMode arrayMode
)
380 for (unsigned i
= m_indexInBlock
; i
--;) {
381 Node
* node
= m_currentBlock
->at(i
);
382 if (node
== child1
|| node
== child2
)
385 switch (node
->op()) {
387 if (!m_graph
.byValIsPure(node
))
389 if (node
->child1() == child1
390 && node
->child2() == child2
391 && node
->arrayMode().type() == arrayMode
.type())
397 case PutByValAlias
: {
398 if (!m_graph
.byValIsPure(node
))
401 if (arrayMode
.typedArrayType() != NotTypedArray
)
403 if (m_graph
.varArgChild(node
, 0) == child1
404 && m_graph
.varArgChild(node
, 1) == child2
405 && node
->arrayMode().type() == arrayMode
.type())
406 return m_graph
.varArgChild(node
, 2).node();
407 // We must assume that the PutByVal will clobber the location we're getting from.
408 // FIXME: We can do better; if we know that the PutByVal is accessing an array of a
409 // different type than the GetByVal, then we know that they won't clobber each other.
410 // ... except of course for typed arrays, where all typed arrays clobber all other
411 // typed arrays! An Int32Array can alias a Float64Array for example, and so on.
415 if (m_graph
.clobbersWorld(node
))
423 bool checkFunctionElimination(JSCell
* function
, Node
* child1
)
425 for (unsigned i
= endIndexForPureCSE(); i
--;) {
426 Node
* node
= m_currentBlock
->at(i
);
430 if (node
->op() == CheckFunction
&& node
->child1() == child1
&& node
->function() == function
)
436 bool checkExecutableElimination(ExecutableBase
* executable
, Node
* child1
)
438 for (unsigned i
= endIndexForPureCSE(); i
--;) {
439 Node
* node
= m_currentBlock
->at(i
);
443 if (node
->op() == CheckExecutable
&& node
->child1() == child1
&& node
->executable() == executable
)
449 bool checkStructureElimination(const StructureSet
& structureSet
, Node
* child1
)
451 for (unsigned i
= m_indexInBlock
; i
--;) {
452 Node
* node
= m_currentBlock
->at(i
);
456 switch (node
->op()) {
458 if (node
->child1() == child1
459 && structureSet
.isSupersetOf(node
->structureSet()))
463 case StructureTransitionWatchpoint
:
464 if (node
->child1() == child1
465 && structureSet
.contains(node
->structure()))
470 if (node
->child1() == child1
471 && structureSet
.contains(node
->structureTransitionData().newStructure
))
473 if (structureSet
.contains(node
->structureTransitionData().previousStructure
))
478 // Setting a property cannot change the structure.
481 case MultiPutByOffset
:
482 if (node
->multiPutByOffsetData().writesStructures())
489 if (m_graph
.byValIsPure(node
)) {
490 // If PutByVal speculates that it's accessing an array with an
491 // integer index, then it's impossible for it to cause a structure
498 case ArrayifyToStructure
:
499 // We could check if the arrayification could affect our structures.
500 // But that seems like it would take Effort.
504 if (m_graph
.clobbersWorld(node
))
512 bool structureTransitionWatchpointElimination(Structure
* structure
, Node
* child1
)
514 for (unsigned i
= m_indexInBlock
; i
--;) {
515 Node
* node
= m_currentBlock
->at(i
);
519 switch (node
->op()) {
521 if (node
->child1() == child1
522 && node
->structureSet().containsOnly(structure
))
527 ASSERT(node
->structureTransitionData().previousStructure
!= structure
);
531 // Setting a property cannot change the structure.
534 case MultiPutByOffset
:
535 if (node
->multiPutByOffsetData().writesStructures())
542 if (m_graph
.byValIsPure(node
)) {
543 // If PutByVal speculates that it's accessing an array with an
544 // integer index, then it's impossible for it to cause a structure
550 case StructureTransitionWatchpoint
:
551 if (node
->structure() == structure
&& node
->child1() == child1
)
556 case ArrayifyToStructure
:
557 // We could check if the arrayification could affect our structures.
558 // But that seems like it would take Effort.
562 if (m_graph
.clobbersWorld(node
))
570 Node
* putStructureStoreElimination(Node
* child1
)
572 for (unsigned i
= m_indexInBlock
; i
--;) {
573 Node
* node
= m_currentBlock
->at(i
);
576 switch (node
->op()) {
580 case PhantomPutStructure
:
581 if (node
->child1() == child1
) // No need to retrace our steps.
586 if (node
->child1() == child1
)
590 // PutStructure needs to execute if we GC. Hence this needs to
591 // be careful with respect to nodes that GC.
592 case CreateArguments
:
593 case TearOffArguments
:
594 case NewFunctionNoCheck
:
596 case NewFunctionExpression
:
597 case CreateActivation
:
598 case TearOffActivation
:
605 case AllocatePropertyStorage
:
606 case ReallocatePropertyStorage
:
609 case NewStringObject
:
612 case MultiPutByOffset
:
615 // This either exits, causes a GC (lazy string allocation), or clobbers
616 // the world. The chances of it not doing any of those things are so
617 // slim that we might as well not even try to reason about it.
621 case GetIndexedPropertyStorage
:
622 if (node
->arrayMode().getIndexedPropertyStorageMayTriggerGC())
629 if (m_graph
.clobbersWorld(node
) || node
->canExit())
631 if (edgesUseStructure(m_graph
, node
))
637 Node
* getByOffsetLoadElimination(unsigned identifierNumber
, Node
* base
)
639 for (unsigned i
= m_indexInBlock
; i
--;) {
640 Node
* node
= m_currentBlock
->at(i
);
644 switch (node
->op()) {
646 if (node
->child2() == base
647 && m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
)
651 case MultiGetByOffset
:
652 if (node
->child1() == base
653 && node
->multiGetByOffsetData().identifierNumber
== identifierNumber
)
658 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
) {
659 if (node
->child2() == base
) // Must be same property storage.
660 return node
->child3().node();
665 case MultiPutByOffset
:
666 if (node
->multiPutByOffsetData().identifierNumber
== identifierNumber
) {
667 if (node
->child1() == base
)
668 return node
->child2().node();
676 if (m_graph
.byValIsPure(node
)) {
677 // If PutByVal speculates that it's accessing an array with an
678 // integer index, then it's impossible for it to cause a structure
685 if (m_graph
.clobbersWorld(node
))
693 Node
* putByOffsetStoreElimination(unsigned identifierNumber
, Node
* child1
)
695 for (unsigned i
= m_indexInBlock
; i
--;) {
696 Node
* node
= m_currentBlock
->at(i
);
700 switch (node
->op()) {
702 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
)
707 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
) {
708 if (node
->child1() == child1
) // Must be same property storage.
714 case MultiPutByOffset
:
715 if (node
->multiPutByOffsetData().identifierNumber
== identifierNumber
)
723 if (m_graph
.byValIsPure(node
)) {
724 // If PutByVal speculates that it's accessing an array with an
725 // integer index, then it's impossible for it to cause a structure
732 if (m_graph
.clobbersWorld(node
))
742 Node
* getPropertyStorageLoadElimination(Node
* child1
)
744 for (unsigned i
= m_indexInBlock
; i
--;) {
745 Node
* node
= m_currentBlock
->at(i
);
749 switch (node
->op()) {
751 if (node
->child1() == child1
)
755 case AllocatePropertyStorage
:
756 case ReallocatePropertyStorage
:
757 // If we can cheaply prove this is a change to our object's storage, we
758 // can optimize and use its result.
759 if (node
->child1() == child1
)
761 // Otherwise, we currently can't prove that this doesn't change our object's
762 // storage, so we conservatively assume that it may change the storage
763 // pointer of any object, including ours.
769 if (m_graph
.byValIsPure(node
)) {
770 // If PutByVal speculates that it's accessing an array with an
771 // integer index, then it's impossible for it to cause a structure
778 case ArrayifyToStructure
:
779 // We could check if the arrayification could affect our butterfly.
780 // But that seems like it would take Effort.
783 case MultiPutByOffset
:
784 if (node
->multiPutByOffsetData().reallocatesStorage())
789 if (m_graph
.clobbersWorld(node
))
797 bool checkArrayElimination(Node
* child1
, ArrayMode arrayMode
)
799 for (unsigned i
= m_indexInBlock
; i
--;) {
800 Node
* node
= m_currentBlock
->at(i
);
804 switch (node
->op()) {
806 if (node
->child1() == child1
&& node
->arrayMode() == arrayMode
)
811 case ArrayifyToStructure
:
812 // We could check if the arrayification could affect our array.
813 // But that seems like it would take Effort.
817 if (m_graph
.clobbersWorld(node
))
825 Node
* getIndexedPropertyStorageLoadElimination(Node
* child1
, ArrayMode arrayMode
)
827 for (unsigned i
= m_indexInBlock
; i
--;) {
828 Node
* node
= m_currentBlock
->at(i
);
832 switch (node
->op()) {
833 case GetIndexedPropertyStorage
: {
834 if (node
->child1() == child1
&& node
->arrayMode() == arrayMode
)
840 if (m_graph
.clobbersWorld(node
))
848 Node
* getTypedArrayByteOffsetLoadElimination(Node
* child1
)
850 for (unsigned i
= m_indexInBlock
; i
--;) {
851 Node
* node
= m_currentBlock
->at(i
);
855 switch (node
->op()) {
856 case GetTypedArrayByteOffset
: {
857 if (node
->child1() == child1
)
863 if (m_graph
.clobbersWorld(node
))
871 Node
* getMyScopeLoadElimination()
873 for (unsigned i
= m_indexInBlock
; i
--;) {
874 Node
* node
= m_currentBlock
->at(i
);
875 switch (node
->op()) {
876 case CreateActivation
:
877 // This may cause us to return a different scope.
888 Node
* getLocalLoadElimination(VirtualRegister local
, Node
*& relevantLocalOp
, bool careAboutClobbering
)
892 for (unsigned i
= m_indexInBlock
; i
--;) {
893 Node
* node
= m_currentBlock
->at(i
);
894 switch (node
->op()) {
896 if (node
->local() == local
) {
897 relevantLocalOp
= node
;
902 case GetLocalUnlinked
:
903 if (node
->unlinkedLocal() == local
) {
904 relevantLocalOp
= node
;
910 if (node
->local() == local
) {
911 relevantLocalOp
= node
;
912 return node
->child1().node();
918 if (static_cast<VirtualRegister
>(node
->varNumber()) == local
)
923 if (careAboutClobbering
&& m_graph
.clobbersWorld(node
))
931 bool uncapturedSetLocalStoreElimination(VirtualRegister local
, Node
* expectedNode
)
933 for (unsigned i
= m_indexInBlock
; i
--;) {
934 Node
* node
= m_currentBlock
->at(i
);
935 switch (node
->op()) {
938 if (node
->local() == local
)
942 case GetLocalUnlinked
:
943 if (node
->unlinkedLocal() == local
)
948 if (node
->local() != local
)
950 if (node
!= expectedNode
)
957 if (static_cast<VirtualRegister
>(node
->varNumber()) == local
)
963 if (node
->origin
.semantic
.inlineCallFrame
)
965 if (m_graph
.uncheckedActivationRegister() == local
)
969 case CheckArgumentsNotCreated
:
970 case GetMyArgumentsLength
:
971 case GetMyArgumentsLengthSafe
:
972 if (m_graph
.uncheckedArgumentsRegisterFor(node
->origin
.semantic
) == local
)
976 case GetMyArgumentByVal
:
977 case GetMyArgumentByValSafe
:
981 // If this is accessing arguments then it's potentially accessing locals.
982 if (node
->arrayMode().type() == Array::Arguments
)
986 case CreateArguments
:
987 case TearOffActivation
:
988 case TearOffArguments
:
989 // If an activation is being torn off then it means that captured variables
990 // are live. We could be clever here and check if the local qualifies as an
991 // argument register. But that seems like it would buy us very little since
992 // any kind of tear offs are rare to begin with.
998 if (m_graph
.clobbersWorld(node
))
1001 RELEASE_ASSERT_NOT_REACHED();
1005 bool capturedSetLocalStoreElimination(VirtualRegister local
, Node
* expectedNode
)
1007 for (unsigned i
= m_indexInBlock
; i
--;) {
1008 Node
* node
= m_currentBlock
->at(i
);
1009 switch (node
->op()) {
1012 if (node
->local() == local
)
1016 case GetLocalUnlinked
:
1017 if (node
->unlinkedLocal() == local
)
1022 if (node
->local() != local
)
1024 if (node
!= expectedNode
)
1034 case DoubleConstant
:
1042 RELEASE_ASSERT_NOT_REACHED();
1046 bool setLocalStoreElimination(VariableAccessData
* variableAccessData
, Node
* expectedNode
)
1048 if (variableAccessData
->isCaptured())
1049 return capturedSetLocalStoreElimination(variableAccessData
->local(), expectedNode
);
1050 return uncapturedSetLocalStoreElimination(variableAccessData
->local(), expectedNode
);
1053 bool invalidationPointElimination()
1055 for (unsigned i
= m_indexInBlock
; i
--;) {
1056 Node
* node
= m_currentBlock
->at(i
);
1057 if (node
->op() == InvalidationPoint
)
1059 if (writesOverlap(m_graph
, node
, Watchpoint_fire
))
1065 void eliminateIrrelevantPhantomChildren(Node
* node
)
1067 ASSERT(node
->op() == Phantom
);
1068 for (unsigned i
= 0; i
< AdjacencyList::Size
; ++i
) {
1069 Edge edge
= node
->children
.child(i
);
1072 if (edge
.useKind() != UntypedUse
)
1073 continue; // Keep the type check.
1074 if (edge
->flags() & NodeRelevantToOSR
)
1077 node
->children
.removeEdge(i
--);
1082 bool setReplacement(Node
* replacement
)
1087 if (!ASSERT_DISABLED
1088 && canonicalResultRepresentation(m_currentNode
->result()) != canonicalResultRepresentation(replacement
->result())) {
1090 dataLog("CSE attempting to replace a node with a mismatched result: ", m_currentNode
, " with ", replacement
, "\n");
1093 RELEASE_ASSERT_NOT_REACHED();
1096 m_currentNode
->convertToPhantom();
1097 eliminateIrrelevantPhantomChildren(m_currentNode
);
1099 // At this point we will eliminate all references to this node.
1100 m_currentNode
->misc
.replacement
= replacement
;
1109 ASSERT(m_currentNode
->mustGenerate());
1110 m_currentNode
->convertToPhantom();
1111 eliminateIrrelevantPhantomChildren(m_currentNode
);
1116 void eliminate(Node
* node
, NodeType phantomType
= Phantom
)
1120 ASSERT(node
->mustGenerate());
1121 node
->setOpAndDefaultFlags(phantomType
);
1122 if (phantomType
== Phantom
)
1123 eliminateIrrelevantPhantomChildren(node
);
1128 void performNodeCSE(Node
* node
)
1130 if (cseMode
== NormalCSE
)
1131 m_graph
.performSubstitution(node
);
1133 switch (node
->op()) {
1136 if (cseMode
== StoreElimination
)
1138 setReplacement(node
->child1().node());
1141 // Handle the pure nodes. These nodes never have any side-effects.
1156 case StringCharCodeAt
:
1166 case GetClosureRegisters
:
1169 case CompareEqConstant
:
1175 case BooleanToNumber
:
1176 if (cseMode
== StoreElimination
)
1178 setReplacement(pureCSE(node
));
1187 case UInt32ToNumber
:
1188 case DoubleAsInt32
: {
1189 if (cseMode
== StoreElimination
)
1191 Node
* candidate
= pureCSE(node
);
1194 if (!subsumes(candidate
->arithMode(), node
->arithMode())) {
1195 if (!subsumes(node
->arithMode(), candidate
->arithMode()))
1197 candidate
->setArithMode(node
->arithMode());
1199 setReplacement(candidate
);
1204 if (cseMode
== StoreElimination
)
1206 setReplacement(getCalleeLoadElimination());
1210 if (cseMode
== StoreElimination
)
1212 VariableAccessData
* variableAccessData
= node
->variableAccessData();
1213 if (!variableAccessData
->isCaptured())
1215 Node
* relevantLocalOp
;
1216 Node
* possibleReplacement
= getLocalLoadElimination(variableAccessData
->local(), relevantLocalOp
, variableAccessData
->isCaptured());
1217 if (!relevantLocalOp
)
1219 if (relevantLocalOp
->op() != GetLocalUnlinked
1220 && relevantLocalOp
->variableAccessData() != variableAccessData
)
1222 Node
* phi
= node
->child1().node();
1223 if (!setReplacement(possibleReplacement
))
1228 // If we replace a GetLocal with a GetLocalUnlinked, then turn the GetLocalUnlinked
1230 if (relevantLocalOp
->op() == GetLocalUnlinked
)
1231 relevantLocalOp
->convertToGetLocal(variableAccessData
, phi
);
1237 case GetLocalUnlinked
: {
1238 if (cseMode
== StoreElimination
)
1240 Node
* relevantLocalOpIgnored
;
1241 setReplacement(getLocalLoadElimination(node
->unlinkedLocal(), relevantLocalOpIgnored
, true));
1246 ASSERT(m_graph
.m_form
!= SSA
);
1247 VariableAccessData
* variableAccessData
= node
->variableAccessData();
1248 if (!node
->child1()) {
1249 // FIXME: It's silly that we punt on flush-eliminating here. We don't really
1250 // need child1 to figure out what's going on.
1251 // https://bugs.webkit.org/show_bug.cgi?id=130521
1254 Node
* replacement
= node
->child1().node();
1255 if (replacement
->op() != SetLocal
)
1257 ASSERT(replacement
->variableAccessData() == variableAccessData
);
1258 // FIXME: We should be able to remove SetLocals that can exit; we just need
1259 // to replace them with appropriate type checks.
1260 if (cseMode
== NormalCSE
) {
1261 // Need to be conservative at this time; if the SetLocal has any chance of performing
1262 // any speculations then we cannot do anything.
1263 FlushFormat format
= variableAccessData
->flushFormat();
1264 ASSERT(format
!= DeadFlush
);
1265 if (format
!= FlushedJSValue
)
1268 if (replacement
->canExit())
1271 if (!setLocalStoreElimination(variableAccessData
, replacement
))
1273 ASSERT(replacement
->op() == SetLocal
);
1274 node
->convertToPhantom();
1275 Node
* dataNode
= replacement
->child1().node();
1276 ASSERT(dataNode
->hasResult());
1277 node
->child1() = dataNode
->defaultEdge();
1284 case DoubleConstant
:
1286 if (cseMode
== StoreElimination
)
1288 // This is strange, but necessary. Some phases will convert nodes to constants,
1289 // which may result in duplicated constants. We use CSE to clean this up.
1290 setReplacement(constantCSE(node
));
1293 case WeakJSConstant
:
1294 if (cseMode
== StoreElimination
)
1296 // FIXME: have CSE for weak constants against strong constants and vice-versa.
1297 setReplacement(weakConstantCSE(node
));
1300 case ConstantStoragePointer
:
1301 if (cseMode
== StoreElimination
)
1303 setReplacement(constantStoragePointerCSE(node
));
1306 case GetArrayLength
:
1307 if (cseMode
== StoreElimination
)
1309 setReplacement(getArrayLengthElimination(node
->child1().node()));
1313 if (cseMode
== StoreElimination
)
1315 setReplacement(getMyScopeLoadElimination());
1318 // Handle nodes that are conditionally pure: these are pure, and can
1319 // be CSE'd, so long as the prediction is the one we want.
1322 case CompareGreater
:
1323 case CompareGreaterEq
:
1325 if (cseMode
== StoreElimination
)
1327 if (m_graph
.isPredictedNumerical(node
)) {
1328 Node
* replacement
= pureCSE(node
);
1329 if (replacement
&& m_graph
.isPredictedNumerical(replacement
))
1330 setReplacement(replacement
);
1335 // Finally handle heap accesses. These are not quite pure, but we can still
1336 // optimize them provided that some subtle conditions are met.
1338 if (cseMode
== StoreElimination
)
1340 setReplacement(globalVarLoadElimination(node
->registerPointer()));
1343 case GetClosureVar
: {
1344 if (cseMode
== StoreElimination
)
1346 setReplacement(scopedVarLoadElimination(node
->child1().node(), node
->varNumber()));
1350 case VarInjectionWatchpoint
:
1351 if (cseMode
== StoreElimination
)
1353 if (varInjectionWatchpointElimination())
1358 if (cseMode
== NormalCSE
)
1360 eliminate(globalVarStoreElimination(node
->registerPointer()));
1363 case PutClosureVar
: {
1364 if (cseMode
== NormalCSE
)
1366 eliminate(scopedVarStoreElimination(node
->child1().node(), node
->child2().node(), node
->varNumber()));
1371 if (cseMode
== StoreElimination
)
1373 if (m_graph
.byValIsPure(node
))
1374 setReplacement(getByValLoadElimination(node
->child1().node(), node
->child2().node(), node
->arrayMode()));
1377 case PutByValDirect
:
1379 if (cseMode
== StoreElimination
)
1381 Edge child1
= m_graph
.varArgChild(node
, 0);
1382 Edge child2
= m_graph
.varArgChild(node
, 1);
1383 if (node
->arrayMode().canCSEStorage()) {
1384 Node
* replacement
= getByValLoadElimination(child1
.node(), child2
.node(), node
->arrayMode());
1387 node
->setOp(PutByValAlias
);
1392 case CheckStructure
:
1393 if (cseMode
== StoreElimination
)
1395 if (checkStructureElimination(node
->structureSet(), node
->child1().node()))
1399 case StructureTransitionWatchpoint
:
1400 if (cseMode
== StoreElimination
)
1402 if (structureTransitionWatchpointElimination(node
->structure(), node
->child1().node()))
1407 if (cseMode
== NormalCSE
)
1409 eliminate(putStructureStoreElimination(node
->child1().node()), PhantomPutStructure
);
1413 if (cseMode
== StoreElimination
)
1415 if (checkFunctionElimination(node
->function(), node
->child1().node()))
1419 case CheckExecutable
:
1420 if (cseMode
== StoreElimination
)
1422 if (checkExecutableElimination(node
->executable(), node
->child1().node()))
1427 if (cseMode
== StoreElimination
)
1429 if (checkArrayElimination(node
->child1().node(), node
->arrayMode()))
1433 case GetIndexedPropertyStorage
: {
1434 if (cseMode
== StoreElimination
)
1436 setReplacement(getIndexedPropertyStorageLoadElimination(node
->child1().node(), node
->arrayMode()));
1440 case GetTypedArrayByteOffset
: {
1441 if (cseMode
== StoreElimination
)
1443 setReplacement(getTypedArrayByteOffsetLoadElimination(node
->child1().node()));
1448 if (cseMode
== StoreElimination
)
1450 setReplacement(getPropertyStorageLoadElimination(node
->child1().node()));
1454 if (cseMode
== StoreElimination
)
1456 setReplacement(getByOffsetLoadElimination(m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
, node
->child2().node()));
1459 case MultiGetByOffset
:
1460 if (cseMode
== StoreElimination
)
1462 setReplacement(getByOffsetLoadElimination(node
->multiGetByOffsetData().identifierNumber
, node
->child1().node()));
1466 if (cseMode
== NormalCSE
)
1468 eliminate(putByOffsetStoreElimination(m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
, node
->child1().node()));
1471 case InvalidationPoint
:
1472 if (invalidationPointElimination())
1477 // FIXME: we ought to remove Phantom's that have no children.
1478 // NB. It would be incorrect to do this for HardPhantom. In fact, the whole point
1479 // of HardPhantom is that we *don't* do this for HardPhantoms, since they signify
1480 // a more strict kind of liveness than the Phantom bytecode liveness.
1481 eliminateIrrelevantPhantomChildren(node
);
1489 m_lastSeen
[node
->op()] = m_indexInBlock
;
1492 void performBlockCSE(BasicBlock
* block
)
1496 if (!block
->isReachable
)
1499 m_currentBlock
= block
;
1500 for (unsigned i
= 0; i
< LastNodeType
; ++i
)
1501 m_lastSeen
[i
] = UINT_MAX
;
1503 for (m_indexInBlock
= 0; m_indexInBlock
< block
->size(); ++m_indexInBlock
) {
1504 m_currentNode
= block
->at(m_indexInBlock
);
1505 performNodeCSE(m_currentNode
);
1508 if (!ASSERT_DISABLED
&& cseMode
== StoreElimination
) {
1509 // Nobody should have replacements set.
1510 for (unsigned i
= 0; i
< block
->size(); ++i
)
1511 ASSERT(!block
->at(i
)->misc
.replacement
);
1515 BasicBlock
* m_currentBlock
;
1516 Node
* m_currentNode
;
1517 unsigned m_indexInBlock
;
1518 std::array
<unsigned, LastNodeType
> m_lastSeen
;
1519 bool m_changed
; // Only tracks changes that have a substantive effect on other optimizations.
1522 bool performCSE(Graph
& graph
)
1524 SamplingRegion
samplingRegion("DFG CSE Phase");
1525 return runPhase
<CSEPhase
<NormalCSE
>>(graph
);
1528 bool performStoreElimination(Graph
& graph
)
1530 SamplingRegion
samplingRegion("DFG Store Elimination Phase");
1531 return runPhase
<CSEPhase
<StoreElimination
>>(graph
);
1534 } } // namespace JSC::DFG
1536 #endif // ENABLE(DFG_JIT)