2 * Copyright (C) 2011, 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 "DFGCSEPhase.h"
33 #include "JSCellInlines.h"
34 #include <wtf/FastBitVector.h>
36 namespace JSC
{ namespace DFG
{
38 enum CSEMode
{ NormalCSE
, StoreElimination
};
40 template<CSEMode cseMode
>
41 class CSEPhase
: public Phase
{
43 CSEPhase(Graph
& graph
)
44 : Phase(graph
, cseMode
== NormalCSE
? "common subexpression elimination" : "store elimination")
50 ASSERT((cseMode
== NormalCSE
) == (m_graph
.m_fixpointState
== FixpointNotConverged
));
51 ASSERT(m_graph
.m_fixpointState
!= BeforeFixpoint
);
55 for (unsigned block
= 0; block
< m_graph
.m_blocks
.size(); ++block
)
56 performBlockCSE(m_graph
.m_blocks
[block
].get());
63 Node
* canonicalize(Node
* node
)
68 if (node
->op() == ValueToInt32
)
69 node
= node
->child1().node();
73 Node
* canonicalize(Edge edge
)
75 return canonicalize(edge
.node());
78 unsigned endIndexForPureCSE()
80 unsigned result
= m_lastSeen
[m_currentNode
->op()];
81 if (result
== UINT_MAX
)
85 ASSERT(result
<= m_indexInBlock
);
86 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
87 dataLogF(" limit %u: ", result
);
92 Node
* pureCSE(Node
* node
)
94 Node
* child1
= canonicalize(node
->child1());
95 Node
* child2
= canonicalize(node
->child2());
96 Node
* child3
= canonicalize(node
->child3());
98 for (unsigned i
= endIndexForPureCSE(); i
--;) {
99 Node
* otherNode
= m_currentBlock
->at(i
);
100 if (otherNode
== child1
|| otherNode
== child2
|| otherNode
== child3
)
103 if (node
->op() != otherNode
->op())
106 if (node
->arithNodeFlags() != otherNode
->arithNodeFlags())
109 Node
* otherChild
= canonicalize(otherNode
->child1());
112 if (otherChild
!= child1
)
115 otherChild
= canonicalize(otherNode
->child2());
118 if (otherChild
!= child2
)
121 otherChild
= canonicalize(otherNode
->child3());
124 if (otherChild
!= child3
)
132 Node
* int32ToDoubleCSE(Node
* node
)
134 for (unsigned i
= m_indexInBlock
; i
--;) {
135 Node
* otherNode
= m_currentBlock
->at(i
);
136 if (otherNode
== node
->child1())
138 switch (otherNode
->op()) {
140 case ForwardInt32ToDouble
:
141 if (otherNode
->child1() == node
->child1())
151 Node
* constantCSE(Node
* node
)
153 for (unsigned i
= endIndexForPureCSE(); i
--;) {
154 Node
* otherNode
= m_currentBlock
->at(i
);
155 if (otherNode
->op() != JSConstant
)
158 if (otherNode
->constantNumber() != node
->constantNumber())
166 Node
* weakConstantCSE(Node
* node
)
168 for (unsigned i
= endIndexForPureCSE(); i
--;) {
169 Node
* otherNode
= m_currentBlock
->at(i
);
170 if (otherNode
->op() != WeakJSConstant
)
173 if (otherNode
->weakConstant() != node
->weakConstant())
181 Node
* getCalleeLoadElimination(InlineCallFrame
* inlineCallFrame
)
183 for (unsigned i
= m_indexInBlock
; i
--;) {
184 Node
* node
= m_currentBlock
->at(i
);
185 if (node
->codeOrigin
.inlineCallFrame
!= inlineCallFrame
)
187 switch (node
->op()) {
191 return node
->child1().node();
199 Node
* getArrayLengthElimination(Node
* array
)
201 for (unsigned i
= m_indexInBlock
; i
--;) {
202 Node
* node
= m_currentBlock
->at(i
);
203 switch (node
->op()) {
205 if (node
->child1() == array
)
210 if (!m_graph
.byValIsPure(node
))
212 if (node
->arrayMode().mayStoreToHole())
217 if (m_graph
.clobbersWorld(node
))
224 Node
* globalVarLoadElimination(WriteBarrier
<Unknown
>* registerPointer
)
226 for (unsigned i
= m_indexInBlock
; i
--;) {
227 Node
* node
= m_currentBlock
->at(i
);
228 switch (node
->op()) {
230 if (node
->registerPointer() == registerPointer
)
234 if (node
->registerPointer() == registerPointer
)
235 return node
->child1().node();
240 if (m_graph
.clobbersWorld(node
))
246 Node
* scopedVarLoadElimination(Node
* registers
, unsigned varNumber
)
248 for (unsigned i
= m_indexInBlock
; i
--;) {
249 Node
* node
= m_currentBlock
->at(i
);
250 switch (node
->op()) {
252 if (node
->child1() == registers
&& node
->varNumber() == varNumber
)
257 if (node
->varNumber() != varNumber
)
259 if (node
->child2() == registers
)
260 return node
->child3().node();
264 VariableAccessData
* variableAccessData
= node
->variableAccessData();
265 if (variableAccessData
->isCaptured()
266 && variableAccessData
->local() == static_cast<VirtualRegister
>(varNumber
))
273 if (m_graph
.clobbersWorld(node
))
279 bool globalVarWatchpointElimination(WriteBarrier
<Unknown
>* registerPointer
)
281 for (unsigned i
= m_indexInBlock
; i
--;) {
282 Node
* node
= m_currentBlock
->at(i
);
283 switch (node
->op()) {
284 case GlobalVarWatchpoint
:
285 if (node
->registerPointer() == registerPointer
)
289 if (node
->registerPointer() == registerPointer
)
295 if (m_graph
.clobbersWorld(node
))
301 Node
* globalVarStoreElimination(WriteBarrier
<Unknown
>* registerPointer
)
303 for (unsigned i
= m_indexInBlock
; i
--;) {
304 Node
* node
= m_currentBlock
->at(i
);
305 switch (node
->op()) {
307 case PutGlobalVarCheck
:
308 if (node
->registerPointer() == registerPointer
)
313 if (node
->registerPointer() == registerPointer
)
320 if (m_graph
.clobbersWorld(node
) || node
->canExit())
326 Node
* scopedVarStoreElimination(Node
* scope
, Node
* registers
, unsigned varNumber
)
328 for (unsigned i
= m_indexInBlock
; i
--;) {
329 Node
* node
= m_currentBlock
->at(i
);
330 switch (node
->op()) {
332 if (node
->varNumber() != varNumber
)
334 if (node
->child1() == scope
&& node
->child2() == registers
)
340 // Let's be conservative.
341 if (node
->varNumber() == varNumber
)
347 VariableAccessData
* variableAccessData
= node
->variableAccessData();
348 if (variableAccessData
->isCaptured()
349 && variableAccessData
->local() == static_cast<VirtualRegister
>(varNumber
))
357 if (m_graph
.clobbersWorld(node
) || node
->canExit())
363 Node
* getByValLoadElimination(Node
* child1
, Node
* child2
)
365 for (unsigned i
= m_indexInBlock
; i
--;) {
366 Node
* node
= m_currentBlock
->at(i
);
367 if (node
== child1
|| node
== canonicalize(child2
))
370 switch (node
->op()) {
372 if (!m_graph
.byValIsPure(node
))
374 if (node
->child1() == child1
&& canonicalize(node
->child2()) == canonicalize(child2
))
378 case PutByValAlias
: {
379 if (!m_graph
.byValIsPure(node
))
381 if (m_graph
.varArgChild(node
, 0) == child1
&& canonicalize(m_graph
.varArgChild(node
, 1)) == canonicalize(child2
))
382 return m_graph
.varArgChild(node
, 2).node();
383 // We must assume that the PutByVal will clobber the location we're getting from.
384 // FIXME: We can do better; if we know that the PutByVal is accessing an array of a
385 // different type than the GetByVal, then we know that they won't clobber each other.
386 // ... except of course for typed arrays, where all typed arrays clobber all other
387 // typed arrays! An Int32Array can alias a Float64Array for example, and so on.
392 // GetByVal currently always speculates that it's accessing an
393 // array with an integer index, which means that it's impossible
394 // for a structure change or a put to property storage to affect
398 if (m_graph
.clobbersWorld(node
))
406 bool checkFunctionElimination(JSCell
* function
, Node
* child1
)
408 for (unsigned i
= endIndexForPureCSE(); i
--;) {
409 Node
* node
= m_currentBlock
->at(i
);
413 if (node
->op() == CheckFunction
&& node
->child1() == child1
&& node
->function() == function
)
419 bool checkExecutableElimination(ExecutableBase
* executable
, Node
* child1
)
421 for (unsigned i
= endIndexForPureCSE(); i
--;) {
422 Node
* node
= m_currentBlock
->at(i
);
426 if (node
->op() == CheckExecutable
&& node
->child1() == child1
&& node
->executable() == executable
)
432 bool checkStructureElimination(const StructureSet
& structureSet
, Node
* child1
)
434 for (unsigned i
= m_indexInBlock
; i
--;) {
435 Node
* node
= m_currentBlock
->at(i
);
439 switch (node
->op()) {
441 case ForwardCheckStructure
:
442 if (node
->child1() == child1
443 && structureSet
.isSupersetOf(node
->structureSet()))
447 case StructureTransitionWatchpoint
:
448 case ForwardStructureTransitionWatchpoint
:
449 if (node
->child1() == child1
450 && structureSet
.contains(node
->structure()))
455 if (node
->child1() == child1
456 && structureSet
.contains(node
->structureTransitionData().newStructure
))
458 if (structureSet
.contains(node
->structureTransitionData().previousStructure
))
463 // Setting a property cannot change the structure.
468 if (m_graph
.byValIsPure(node
)) {
469 // If PutByVal speculates that it's accessing an array with an
470 // integer index, then it's impossible for it to cause a structure
477 case ArrayifyToStructure
:
478 // We could check if the arrayification could affect our structures.
479 // But that seems like it would take Effort.
483 if (m_graph
.clobbersWorld(node
))
491 bool structureTransitionWatchpointElimination(Structure
* structure
, Node
* child1
)
493 for (unsigned i
= m_indexInBlock
; i
--;) {
494 Node
* node
= m_currentBlock
->at(i
);
498 switch (node
->op()) {
500 case ForwardCheckStructure
:
501 if (node
->child1() == child1
502 && node
->structureSet().containsOnly(structure
))
507 ASSERT(node
->structureTransitionData().previousStructure
!= structure
);
511 // Setting a property cannot change the structure.
516 if (m_graph
.byValIsPure(node
)) {
517 // If PutByVal speculates that it's accessing an array with an
518 // integer index, then it's impossible for it to cause a structure
524 case StructureTransitionWatchpoint
:
525 case ForwardStructureTransitionWatchpoint
:
526 if (node
->structure() == structure
&& node
->child1() == child1
)
531 case ArrayifyToStructure
:
532 // We could check if the arrayification could affect our structures.
533 // But that seems like it would take Effort.
537 if (m_graph
.clobbersWorld(node
))
545 Node
* putStructureStoreElimination(Node
* child1
)
547 for (unsigned i
= m_indexInBlock
; i
--;) {
548 Node
* node
= m_currentBlock
->at(i
);
551 switch (node
->op()) {
553 case ForwardCheckStructure
:
556 case PhantomPutStructure
:
557 if (node
->child1() == child1
) // No need to retrace our steps.
562 if (node
->child1() == child1
)
566 // PutStructure needs to execute if we GC. Hence this needs to
567 // be careful with respect to nodes that GC.
568 case CreateArguments
:
569 case TearOffArguments
:
570 case NewFunctionNoCheck
:
572 case NewFunctionExpression
:
573 case CreateActivation
:
574 case TearOffActivation
:
581 case AllocatePropertyStorage
:
582 case ReallocatePropertyStorage
:
585 case NewStringObject
:
589 case GetIndexedPropertyStorage
:
590 if (node
->arrayMode().getIndexedPropertyStorageMayTriggerGC())
597 if (m_graph
.clobbersWorld(node
) || node
->canExit())
603 Node
* getByOffsetLoadElimination(unsigned identifierNumber
, Node
* child1
)
605 for (unsigned i
= m_indexInBlock
; i
--;) {
606 Node
* node
= m_currentBlock
->at(i
);
610 switch (node
->op()) {
612 if (node
->child1() == child1
613 && m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
)
618 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
) {
619 if (node
->child1() == child1
) // Must be same property storage.
620 return node
->child3().node();
626 // Changing the structure cannot change the outcome of a property get.
631 if (m_graph
.byValIsPure(node
)) {
632 // If PutByVal speculates that it's accessing an array with an
633 // integer index, then it's impossible for it to cause a structure
640 if (m_graph
.clobbersWorld(node
))
648 Node
* putByOffsetStoreElimination(unsigned identifierNumber
, Node
* child1
)
650 for (unsigned i
= m_indexInBlock
; i
--;) {
651 Node
* node
= m_currentBlock
->at(i
);
655 switch (node
->op()) {
657 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
)
662 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
) {
663 if (node
->child1() == child1
) // Must be same property storage.
672 if (m_graph
.byValIsPure(node
)) {
673 // If PutByVal speculates that it's accessing an array with an
674 // integer index, then it's impossible for it to cause a structure
681 if (m_graph
.clobbersWorld(node
))
691 Node
* getPropertyStorageLoadElimination(Node
* child1
)
693 for (unsigned i
= m_indexInBlock
; i
--;) {
694 Node
* node
= m_currentBlock
->at(i
);
698 switch (node
->op()) {
700 if (node
->child1() == child1
)
704 case AllocatePropertyStorage
:
705 case ReallocatePropertyStorage
:
706 // If we can cheaply prove this is a change to our object's storage, we
707 // can optimize and use its result.
708 if (node
->child1() == child1
)
710 // Otherwise, we currently can't prove that this doesn't change our object's
711 // storage, so we conservatively assume that it may change the storage
712 // pointer of any object, including ours.
717 // Changing the structure or putting to the storage cannot
718 // change the property storage pointer.
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 case ArrayifyToStructure
:
733 // We could check if the arrayification could affect our butterfly.
734 // But that seems like it would take Effort.
738 if (m_graph
.clobbersWorld(node
))
746 bool checkArrayElimination(Node
* child1
, ArrayMode arrayMode
)
748 for (unsigned i
= m_indexInBlock
; i
--;) {
749 Node
* node
= m_currentBlock
->at(i
);
753 switch (node
->op()) {
756 // Changing the structure or putting to the storage cannot
757 // change the property storage pointer.
761 if (node
->child1() == child1
&& node
->arrayMode() == arrayMode
)
766 case ArrayifyToStructure
:
767 // We could check if the arrayification could affect our array.
768 // But that seems like it would take Effort.
772 if (m_graph
.clobbersWorld(node
))
780 Node
* getIndexedPropertyStorageLoadElimination(Node
* child1
, ArrayMode arrayMode
)
782 for (unsigned i
= m_indexInBlock
; i
--;) {
783 Node
* node
= m_currentBlock
->at(i
);
787 switch (node
->op()) {
788 case GetIndexedPropertyStorage
: {
789 if (node
->child1() == child1
&& node
->arrayMode() == arrayMode
)
796 // Changing the structure or putting to the storage cannot
797 // change the property storage pointer.
801 if (m_graph
.clobbersWorld(node
))
809 Node
* getMyScopeLoadElimination(InlineCallFrame
* inlineCallFrame
)
811 for (unsigned i
= m_indexInBlock
; i
--;) {
812 Node
* node
= m_currentBlock
->at(i
);
813 if (node
->codeOrigin
.inlineCallFrame
!= inlineCallFrame
)
815 switch (node
->op()) {
816 case CreateActivation
:
817 // This may cause us to return a different scope.
822 return node
->child1().node();
830 Node
* getLocalLoadElimination(VirtualRegister local
, Node
*& relevantLocalOp
, bool careAboutClobbering
)
834 for (unsigned i
= m_indexInBlock
; i
--;) {
835 Node
* node
= m_currentBlock
->at(i
);
836 switch (node
->op()) {
838 if (node
->local() == local
) {
839 relevantLocalOp
= node
;
844 case GetLocalUnlinked
:
845 if (node
->unlinkedLocal() == local
) {
846 relevantLocalOp
= node
;
852 if (node
->local() == local
) {
853 relevantLocalOp
= node
;
854 return node
->child1().node();
859 if (static_cast<VirtualRegister
>(node
->varNumber()) == local
)
864 if (careAboutClobbering
&& m_graph
.clobbersWorld(node
))
872 struct SetLocalStoreEliminationResult
{
873 SetLocalStoreEliminationResult()
874 : mayBeAccessed(false)
876 , mayClobberWorld(false)
882 bool mayClobberWorld
;
884 SetLocalStoreEliminationResult
setLocalStoreElimination(
885 VirtualRegister local
, Node
* expectedNode
)
887 SetLocalStoreEliminationResult result
;
888 for (unsigned i
= m_indexInBlock
; i
--;) {
889 Node
* node
= m_currentBlock
->at(i
);
890 switch (node
->op()) {
893 if (node
->local() == local
)
894 result
.mayBeAccessed
= true;
897 case GetLocalUnlinked
:
898 if (node
->unlinkedLocal() == local
)
899 result
.mayBeAccessed
= true;
903 if (node
->local() != local
)
905 if (node
!= expectedNode
)
906 result
.mayBeAccessed
= true;
911 if (static_cast<VirtualRegister
>(node
->varNumber()) == local
)
912 result
.mayBeAccessed
= true;
917 if (m_graph
.uncheckedActivationRegisterFor(node
->codeOrigin
) == local
)
918 result
.mayBeAccessed
= true;
921 case CheckArgumentsNotCreated
:
922 case GetMyArgumentsLength
:
923 case GetMyArgumentsLengthSafe
:
924 if (m_graph
.uncheckedArgumentsRegisterFor(node
->codeOrigin
) == local
)
925 result
.mayBeAccessed
= true;
928 case GetMyArgumentByVal
:
929 case GetMyArgumentByValSafe
:
930 result
.mayBeAccessed
= true;
934 // If this is accessing arguments then it's potentially accessing locals.
935 if (node
->arrayMode().type() == Array::Arguments
)
936 result
.mayBeAccessed
= true;
939 case CreateArguments
:
940 case TearOffActivation
:
941 case TearOffArguments
:
942 // If an activation is being torn off then it means that captured variables
943 // are live. We could be clever here and check if the local qualifies as an
944 // argument register. But that seems like it would buy us very little since
945 // any kind of tear offs are rare to begin with.
946 result
.mayBeAccessed
= true;
952 result
.mayExit
|= node
->canExit();
953 result
.mayClobberWorld
|= m_graph
.clobbersWorld(node
);
955 RELEASE_ASSERT_NOT_REACHED();
956 // Be safe in release mode.
957 result
.mayBeAccessed
= true;
961 void eliminateIrrelevantPhantomChildren(Node
* node
)
963 ASSERT(node
->op() == Phantom
);
964 for (unsigned i
= 0; i
< AdjacencyList::Size
; ++i
) {
965 Edge edge
= node
->children
.child(i
);
968 if (edge
.useKind() != UntypedUse
)
969 continue; // Keep the type check.
970 if (edge
->flags() & NodeRelevantToOSR
)
973 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
974 dataLog(" Eliminating edge @", m_currentNode
->index(), " -> @", edge
->index());
976 node
->children
.removeEdge(i
--);
981 bool setReplacement(Node
* replacement
)
986 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
987 dataLogF(" Replacing @%u -> @%u", m_currentNode
->index(), replacement
->index());
990 m_currentNode
->convertToPhantom();
991 eliminateIrrelevantPhantomChildren(m_currentNode
);
993 // At this point we will eliminate all references to this node.
994 m_currentNode
->replacement
= replacement
;
1003 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
1004 dataLogF(" Eliminating @%u", m_currentNode
->index());
1007 ASSERT(m_currentNode
->mustGenerate());
1008 m_currentNode
->convertToPhantom();
1009 eliminateIrrelevantPhantomChildren(m_currentNode
);
1014 void eliminate(Node
* node
, NodeType phantomType
= Phantom
)
1018 ASSERT(node
->mustGenerate());
1019 node
->setOpAndDefaultNonExitFlags(phantomType
);
1020 if (phantomType
== Phantom
)
1021 eliminateIrrelevantPhantomChildren(node
);
1026 void performNodeCSE(Node
* node
)
1028 if (cseMode
== NormalCSE
)
1029 m_graph
.performSubstitution(node
);
1031 if (node
->op() == SetLocal
)
1032 node
->child1()->mergeFlags(NodeRelevantToOSR
);
1034 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
1035 dataLogF(" %s @%u: ", Graph::opName(node
->op()), node
->index());
1038 // NOTE: there are some nodes that we deliberately don't CSE even though we
1039 // probably could, like MakeRope and ToPrimitive. That's because there is no
1040 // evidence that doing CSE on these nodes would result in a performance
1041 // progression. Hence considering these nodes in CSE would just mean that this
1042 // code does more work with no win. Of course, we may want to reconsider this,
1043 // since MakeRope is trivially CSE-able. It's not trivially doable for
1044 // ToPrimitive, but we could change that with some speculations if we really
1047 switch (node
->op()) {
1050 if (cseMode
== StoreElimination
)
1052 setReplacement(node
->child1().node());
1055 // Handle the pure nodes. These nodes never have any side-effects.
1074 case StringCharCodeAt
:
1085 case GetScopeRegisters
:
1088 case CompareEqConstant
:
1090 if (cseMode
== StoreElimination
)
1092 setReplacement(pureCSE(node
));
1096 case ForwardInt32ToDouble
:
1097 if (cseMode
== StoreElimination
)
1099 setReplacement(int32ToDoubleCSE(node
));
1103 if (cseMode
== StoreElimination
)
1105 setReplacement(getCalleeLoadElimination(node
->codeOrigin
.inlineCallFrame
));
1109 if (cseMode
== StoreElimination
)
1111 VariableAccessData
* variableAccessData
= node
->variableAccessData();
1112 if (!variableAccessData
->isCaptured())
1114 Node
* relevantLocalOp
;
1115 Node
* possibleReplacement
= getLocalLoadElimination(variableAccessData
->local(), relevantLocalOp
, variableAccessData
->isCaptured());
1116 if (!relevantLocalOp
)
1118 if (relevantLocalOp
->op() != GetLocalUnlinked
1119 && relevantLocalOp
->variableAccessData() != variableAccessData
)
1121 Node
* phi
= node
->child1().node();
1122 if (!setReplacement(possibleReplacement
))
1127 // If we replace a GetLocal with a GetLocalUnlinked, then turn the GetLocalUnlinked
1129 if (relevantLocalOp
->op() == GetLocalUnlinked
)
1130 relevantLocalOp
->convertToGetLocal(variableAccessData
, phi
);
1136 case GetLocalUnlinked
: {
1137 if (cseMode
== StoreElimination
)
1139 Node
* relevantLocalOpIgnored
;
1140 setReplacement(getLocalLoadElimination(node
->unlinkedLocal(), relevantLocalOpIgnored
, true));
1145 VariableAccessData
* variableAccessData
= node
->variableAccessData();
1146 VirtualRegister local
= variableAccessData
->local();
1147 Node
* replacement
= node
->child1().node();
1148 if (replacement
->op() != SetLocal
)
1150 ASSERT(replacement
->variableAccessData() == variableAccessData
);
1151 // FIXME: We should be able to remove SetLocals that can exit; we just need
1152 // to replace them with appropriate type checks.
1153 if (cseMode
== NormalCSE
) {
1154 // Need to be conservative at this time; if the SetLocal has any chance of performing
1155 // any speculations then we cannot do anything.
1156 if (variableAccessData
->isCaptured()) {
1157 // Captured SetLocals never speculate and hence never exit.
1159 if (variableAccessData
->shouldUseDoubleFormat())
1161 SpeculatedType prediction
= variableAccessData
->argumentAwarePrediction();
1162 if (isInt32Speculation(prediction
))
1164 if (isArraySpeculation(prediction
))
1166 if (isBooleanSpeculation(prediction
))
1170 if (replacement
->canExit())
1173 SetLocalStoreEliminationResult result
=
1174 setLocalStoreElimination(local
, replacement
);
1175 if (result
.mayBeAccessed
|| result
.mayClobberWorld
)
1177 ASSERT(replacement
->op() == SetLocal
);
1178 // FIXME: Investigate using mayExit as a further optimization.
1179 node
->convertToPhantom();
1180 Node
* dataNode
= replacement
->child1().node();
1181 ASSERT(dataNode
->hasResult());
1182 m_graph
.clearAndDerefChild1(node
);
1183 node
->children
.child1() = Edge(dataNode
);
1190 if (cseMode
== StoreElimination
)
1192 // This is strange, but necessary. Some phases will convert nodes to constants,
1193 // which may result in duplicated constants. We use CSE to clean this up.
1194 setReplacement(constantCSE(node
));
1197 case WeakJSConstant
:
1198 if (cseMode
== StoreElimination
)
1200 // FIXME: have CSE for weak constants against strong constants and vice-versa.
1201 setReplacement(weakConstantCSE(node
));
1204 case GetArrayLength
:
1205 if (cseMode
== StoreElimination
)
1207 setReplacement(getArrayLengthElimination(node
->child1().node()));
1211 if (cseMode
== StoreElimination
)
1213 setReplacement(getMyScopeLoadElimination(node
->codeOrigin
.inlineCallFrame
));
1216 // Handle nodes that are conditionally pure: these are pure, and can
1217 // be CSE'd, so long as the prediction is the one we want.
1221 case CompareGreater
:
1222 case CompareGreaterEq
:
1224 if (cseMode
== StoreElimination
)
1226 if (m_graph
.isPredictedNumerical(node
)) {
1227 Node
* replacement
= pureCSE(node
);
1228 if (replacement
&& m_graph
.isPredictedNumerical(replacement
))
1229 setReplacement(replacement
);
1234 // Finally handle heap accesses. These are not quite pure, but we can still
1235 // optimize them provided that some subtle conditions are met.
1237 if (cseMode
== StoreElimination
)
1239 setReplacement(globalVarLoadElimination(node
->registerPointer()));
1242 case GetScopedVar
: {
1243 if (cseMode
== StoreElimination
)
1245 setReplacement(scopedVarLoadElimination(node
->child1().node(), node
->varNumber()));
1249 case GlobalVarWatchpoint
:
1250 if (cseMode
== StoreElimination
)
1252 if (globalVarWatchpointElimination(node
->registerPointer()))
1257 case PutGlobalVarCheck
:
1258 if (cseMode
== NormalCSE
)
1260 eliminate(globalVarStoreElimination(node
->registerPointer()));
1263 case PutScopedVar
: {
1264 if (cseMode
== NormalCSE
)
1266 eliminate(scopedVarStoreElimination(node
->child1().node(), node
->child2().node(), node
->varNumber()));
1271 if (cseMode
== StoreElimination
)
1273 if (m_graph
.byValIsPure(node
))
1274 setReplacement(getByValLoadElimination(node
->child1().node(), node
->child2().node()));
1278 if (cseMode
== StoreElimination
)
1280 Edge child1
= m_graph
.varArgChild(node
, 0);
1281 Edge child2
= m_graph
.varArgChild(node
, 1);
1282 if (node
->arrayMode().canCSEStorage()) {
1283 Node
* replacement
= getByValLoadElimination(child1
.node(), child2
.node());
1286 node
->setOp(PutByValAlias
);
1291 case CheckStructure
:
1292 case ForwardCheckStructure
:
1293 if (cseMode
== StoreElimination
)
1295 if (checkStructureElimination(node
->structureSet(), node
->child1().node()))
1299 case StructureTransitionWatchpoint
:
1300 case ForwardStructureTransitionWatchpoint
:
1301 if (cseMode
== StoreElimination
)
1303 if (structureTransitionWatchpointElimination(node
->structure(), node
->child1().node()))
1308 if (cseMode
== NormalCSE
)
1310 eliminate(putStructureStoreElimination(node
->child1().node()), PhantomPutStructure
);
1314 if (cseMode
== StoreElimination
)
1316 if (checkFunctionElimination(node
->function(), node
->child1().node()))
1320 case CheckExecutable
:
1321 if (cseMode
== StoreElimination
)
1323 if (checkExecutableElimination(node
->executable(), node
->child1().node()))
1328 if (cseMode
== StoreElimination
)
1330 if (checkArrayElimination(node
->child1().node(), node
->arrayMode()))
1334 case GetIndexedPropertyStorage
: {
1335 if (cseMode
== StoreElimination
)
1337 setReplacement(getIndexedPropertyStorageLoadElimination(node
->child1().node(), node
->arrayMode()));
1342 if (cseMode
== StoreElimination
)
1344 setReplacement(getPropertyStorageLoadElimination(node
->child1().node()));
1348 if (cseMode
== StoreElimination
)
1350 setReplacement(getByOffsetLoadElimination(m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
, node
->child1().node()));
1354 if (cseMode
== NormalCSE
)
1356 eliminate(putByOffsetStoreElimination(m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
, node
->child1().node()));
1360 // FIXME: we ought to remove Phantom's that have no children.
1362 eliminateIrrelevantPhantomChildren(node
);
1370 m_lastSeen
[node
->op()] = m_indexInBlock
;
1371 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
1376 void performBlockCSE(BasicBlock
* block
)
1380 if (!block
->isReachable
)
1383 m_currentBlock
= block
;
1384 for (unsigned i
= 0; i
< LastNodeType
; ++i
)
1385 m_lastSeen
[i
] = UINT_MAX
;
1387 // All Phis need to already be marked as relevant to OSR, and have their
1388 // replacements cleared, so we don't get confused while doing substitutions on
1390 for (unsigned i
= 0; i
< block
->phis
.size(); ++i
) {
1391 ASSERT(block
->phis
[i
]->flags() & NodeRelevantToOSR
);
1392 block
->phis
[i
]->replacement
= 0;
1395 // Make all of my SetLocal and GetLocal nodes relevant to OSR, and do some other
1396 // necessary bookkeeping.
1397 for (unsigned i
= 0; i
< block
->size(); ++i
) {
1398 Node
* node
= block
->at(i
);
1400 node
->replacement
= 0;
1402 switch (node
->op()) {
1404 case GetLocal
: // FIXME: The GetLocal case is only necessary until we do https://bugs.webkit.org/show_bug.cgi?id=106707.
1405 node
->mergeFlags(NodeRelevantToOSR
);
1408 node
->clearFlags(NodeRelevantToOSR
);
1413 for (m_indexInBlock
= 0; m_indexInBlock
< block
->size(); ++m_indexInBlock
) {
1414 m_currentNode
= block
->at(m_indexInBlock
);
1415 performNodeCSE(m_currentNode
);
1418 if (!ASSERT_DISABLED
&& cseMode
== StoreElimination
) {
1419 // Nobody should have replacements set.
1420 for (unsigned i
= 0; i
< block
->size(); ++i
)
1421 ASSERT(!block
->at(i
)->replacement
);
1425 BasicBlock
* m_currentBlock
;
1426 Node
* m_currentNode
;
1427 unsigned m_indexInBlock
;
1428 FixedArray
<unsigned, LastNodeType
> m_lastSeen
;
1429 bool m_changed
; // Only tracks changes that have a substantive effect on other optimizations.
1432 bool performCSE(Graph
& graph
)
1434 SamplingRegion
samplingRegion("DFG CSE Phase");
1435 return runPhase
<CSEPhase
<NormalCSE
> >(graph
);
1438 bool performStoreElimination(Graph
& graph
)
1440 SamplingRegion
samplingRegion("DFG Store Elimination Phase");
1441 return runPhase
<CSEPhase
<StoreElimination
> >(graph
);
1444 } } // namespace JSC::DFG
1446 #endif // ENABLE(DFG_JIT)