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
->child2() == registers
&& node
->varNumber() == varNumber
)
258 return node
->child3().node();
262 VariableAccessData
* variableAccessData
= node
->variableAccessData();
263 if (variableAccessData
->isCaptured()
264 && variableAccessData
->local() == static_cast<VirtualRegister
>(varNumber
))
271 if (m_graph
.clobbersWorld(node
))
277 bool globalVarWatchpointElimination(WriteBarrier
<Unknown
>* registerPointer
)
279 for (unsigned i
= m_indexInBlock
; i
--;) {
280 Node
* node
= m_currentBlock
->at(i
);
281 switch (node
->op()) {
282 case GlobalVarWatchpoint
:
283 if (node
->registerPointer() == registerPointer
)
287 if (node
->registerPointer() == registerPointer
)
293 if (m_graph
.clobbersWorld(node
))
299 Node
* globalVarStoreElimination(WriteBarrier
<Unknown
>* registerPointer
)
301 for (unsigned i
= m_indexInBlock
; i
--;) {
302 Node
* node
= m_currentBlock
->at(i
);
303 switch (node
->op()) {
305 case PutGlobalVarCheck
:
306 if (node
->registerPointer() == registerPointer
)
311 if (node
->registerPointer() == registerPointer
)
318 if (m_graph
.clobbersWorld(node
) || node
->canExit())
324 Node
* scopedVarStoreElimination(Node
* scope
, Node
* registers
, unsigned varNumber
)
326 for (unsigned i
= m_indexInBlock
; i
--;) {
327 Node
* node
= m_currentBlock
->at(i
);
328 switch (node
->op()) {
330 if (node
->child1() == scope
&& node
->child2() == registers
&& node
->varNumber() == varNumber
)
336 // Let's be conservative.
337 if (node
->varNumber() == varNumber
)
343 VariableAccessData
* variableAccessData
= node
->variableAccessData();
344 if (variableAccessData
->isCaptured()
345 && variableAccessData
->local() == static_cast<VirtualRegister
>(varNumber
))
353 if (m_graph
.clobbersWorld(node
) || node
->canExit())
359 Node
* getByValLoadElimination(Node
* child1
, Node
* child2
)
361 for (unsigned i
= m_indexInBlock
; i
--;) {
362 Node
* node
= m_currentBlock
->at(i
);
363 if (node
== child1
|| node
== canonicalize(child2
))
366 switch (node
->op()) {
368 if (!m_graph
.byValIsPure(node
))
370 if (node
->child1() == child1
&& canonicalize(node
->child2()) == canonicalize(child2
))
374 case PutByValAlias
: {
375 if (!m_graph
.byValIsPure(node
))
377 if (m_graph
.varArgChild(node
, 0) == child1
&& canonicalize(m_graph
.varArgChild(node
, 1)) == canonicalize(child2
))
378 return m_graph
.varArgChild(node
, 2).node();
379 // We must assume that the PutByVal will clobber the location we're getting from.
380 // FIXME: We can do better; if we know that the PutByVal is accessing an array of a
381 // different type than the GetByVal, then we know that they won't clobber each other.
382 // ... except of course for typed arrays, where all typed arrays clobber all other
383 // typed arrays! An Int32Array can alias a Float64Array for example, and so on.
388 // GetByVal currently always speculates that it's accessing an
389 // array with an integer index, which means that it's impossible
390 // for a structure change or a put to property storage to affect
394 if (m_graph
.clobbersWorld(node
))
402 bool checkFunctionElimination(JSCell
* function
, Node
* child1
)
404 for (unsigned i
= endIndexForPureCSE(); i
--;) {
405 Node
* node
= m_currentBlock
->at(i
);
409 if (node
->op() == CheckFunction
&& node
->child1() == child1
&& node
->function() == function
)
415 bool checkExecutableElimination(ExecutableBase
* executable
, Node
* child1
)
417 for (unsigned i
= endIndexForPureCSE(); i
--;) {
418 Node
* node
= m_currentBlock
->at(i
);
422 if (node
->op() == CheckExecutable
&& node
->child1() == child1
&& node
->executable() == executable
)
428 bool checkStructureElimination(const StructureSet
& structureSet
, Node
* child1
)
430 for (unsigned i
= m_indexInBlock
; i
--;) {
431 Node
* node
= m_currentBlock
->at(i
);
435 switch (node
->op()) {
437 case ForwardCheckStructure
:
438 if (node
->child1() == child1
439 && structureSet
.isSupersetOf(node
->structureSet()))
443 case StructureTransitionWatchpoint
:
444 case ForwardStructureTransitionWatchpoint
:
445 if (node
->child1() == child1
446 && structureSet
.contains(node
->structure()))
451 if (node
->child1() == child1
452 && structureSet
.contains(node
->structureTransitionData().newStructure
))
454 if (structureSet
.contains(node
->structureTransitionData().previousStructure
))
459 // Setting a property cannot change the structure.
464 if (m_graph
.byValIsPure(node
)) {
465 // If PutByVal speculates that it's accessing an array with an
466 // integer index, then it's impossible for it to cause a structure
473 case ArrayifyToStructure
:
474 // We could check if the arrayification could affect our structures.
475 // But that seems like it would take Effort.
479 if (m_graph
.clobbersWorld(node
))
487 bool structureTransitionWatchpointElimination(Structure
* structure
, Node
* child1
)
489 for (unsigned i
= m_indexInBlock
; i
--;) {
490 Node
* node
= m_currentBlock
->at(i
);
494 switch (node
->op()) {
496 case ForwardCheckStructure
:
497 if (node
->child1() == child1
498 && node
->structureSet().containsOnly(structure
))
503 ASSERT(node
->structureTransitionData().previousStructure
!= structure
);
507 // Setting a property cannot change the structure.
512 if (m_graph
.byValIsPure(node
)) {
513 // If PutByVal speculates that it's accessing an array with an
514 // integer index, then it's impossible for it to cause a structure
520 case StructureTransitionWatchpoint
:
521 case ForwardStructureTransitionWatchpoint
:
522 if (node
->structure() == structure
&& node
->child1() == child1
)
527 case ArrayifyToStructure
:
528 // We could check if the arrayification could affect our structures.
529 // But that seems like it would take Effort.
533 if (m_graph
.clobbersWorld(node
))
541 Node
* putStructureStoreElimination(Node
* child1
)
543 for (unsigned i
= m_indexInBlock
; i
--;) {
544 Node
* node
= m_currentBlock
->at(i
);
547 switch (node
->op()) {
549 case ForwardCheckStructure
:
552 case PhantomPutStructure
:
553 if (node
->child1() == child1
) // No need to retrace our steps.
558 if (node
->child1() == child1
)
562 // PutStructure needs to execute if we GC. Hence this needs to
563 // be careful with respect to nodes that GC.
564 case CreateArguments
:
565 case TearOffArguments
:
566 case NewFunctionNoCheck
:
568 case NewFunctionExpression
:
569 case CreateActivation
:
570 case TearOffActivation
:
577 case AllocatePropertyStorage
:
578 case ReallocatePropertyStorage
:
581 case NewStringObject
:
585 case GetIndexedPropertyStorage
:
586 if (node
->arrayMode().getIndexedPropertyStorageMayTriggerGC())
593 if (m_graph
.clobbersWorld(node
) || node
->canExit())
599 Node
* getByOffsetLoadElimination(unsigned identifierNumber
, Node
* child1
)
601 for (unsigned i
= m_indexInBlock
; i
--;) {
602 Node
* node
= m_currentBlock
->at(i
);
606 switch (node
->op()) {
608 if (node
->child1() == child1
609 && m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
)
614 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
) {
615 if (node
->child1() == child1
) // Must be same property storage.
616 return node
->child3().node();
622 // Changing the structure cannot change the outcome of a property get.
627 if (m_graph
.byValIsPure(node
)) {
628 // If PutByVal speculates that it's accessing an array with an
629 // integer index, then it's impossible for it to cause a structure
636 if (m_graph
.clobbersWorld(node
))
644 Node
* putByOffsetStoreElimination(unsigned identifierNumber
, Node
* child1
)
646 for (unsigned i
= m_indexInBlock
; i
--;) {
647 Node
* node
= m_currentBlock
->at(i
);
651 switch (node
->op()) {
653 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
)
658 if (m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
== identifierNumber
) {
659 if (node
->child1() == child1
) // Must be same property storage.
668 if (m_graph
.byValIsPure(node
)) {
669 // If PutByVal speculates that it's accessing an array with an
670 // integer index, then it's impossible for it to cause a structure
677 if (m_graph
.clobbersWorld(node
))
687 Node
* getPropertyStorageLoadElimination(Node
* child1
)
689 for (unsigned i
= m_indexInBlock
; i
--;) {
690 Node
* node
= m_currentBlock
->at(i
);
694 switch (node
->op()) {
696 if (node
->child1() == child1
)
700 case AllocatePropertyStorage
:
701 case ReallocatePropertyStorage
:
702 // If we can cheaply prove this is a change to our object's storage, we
703 // can optimize and use its result.
704 if (node
->child1() == child1
)
706 // Otherwise, we currently can't prove that this doesn't change our object's
707 // storage, so we conservatively assume that it may change the storage
708 // pointer of any object, including ours.
713 // Changing the structure or putting to the storage cannot
714 // change the property storage pointer.
719 if (m_graph
.byValIsPure(node
)) {
720 // If PutByVal speculates that it's accessing an array with an
721 // integer index, then it's impossible for it to cause a structure
728 case ArrayifyToStructure
:
729 // We could check if the arrayification could affect our butterfly.
730 // But that seems like it would take Effort.
734 if (m_graph
.clobbersWorld(node
))
742 bool checkArrayElimination(Node
* child1
, ArrayMode arrayMode
)
744 for (unsigned i
= m_indexInBlock
; i
--;) {
745 Node
* node
= m_currentBlock
->at(i
);
749 switch (node
->op()) {
752 // Changing the structure or putting to the storage cannot
753 // change the property storage pointer.
757 if (node
->child1() == child1
&& node
->arrayMode() == arrayMode
)
762 case ArrayifyToStructure
:
763 // We could check if the arrayification could affect our array.
764 // But that seems like it would take Effort.
768 if (m_graph
.clobbersWorld(node
))
776 Node
* getIndexedPropertyStorageLoadElimination(Node
* child1
, ArrayMode arrayMode
)
778 for (unsigned i
= m_indexInBlock
; i
--;) {
779 Node
* node
= m_currentBlock
->at(i
);
783 switch (node
->op()) {
784 case GetIndexedPropertyStorage
: {
785 if (node
->child1() == child1
&& node
->arrayMode() == arrayMode
)
792 // Changing the structure or putting to the storage cannot
793 // change the property storage pointer.
797 if (m_graph
.clobbersWorld(node
))
805 Node
* getMyScopeLoadElimination(InlineCallFrame
* inlineCallFrame
)
807 for (unsigned i
= m_indexInBlock
; i
--;) {
808 Node
* node
= m_currentBlock
->at(i
);
809 if (node
->codeOrigin
.inlineCallFrame
!= inlineCallFrame
)
811 switch (node
->op()) {
812 case CreateActivation
:
813 // This may cause us to return a different scope.
818 return node
->child1().node();
826 Node
* getLocalLoadElimination(VirtualRegister local
, Node
*& relevantLocalOp
, bool careAboutClobbering
)
830 for (unsigned i
= m_indexInBlock
; i
--;) {
831 Node
* node
= m_currentBlock
->at(i
);
832 switch (node
->op()) {
834 if (node
->local() == local
) {
835 relevantLocalOp
= node
;
840 case GetLocalUnlinked
:
841 if (node
->unlinkedLocal() == local
) {
842 relevantLocalOp
= node
;
848 if (node
->local() == local
) {
849 relevantLocalOp
= node
;
850 return node
->child1().node();
855 if (static_cast<VirtualRegister
>(node
->varNumber()) == local
)
860 if (careAboutClobbering
&& m_graph
.clobbersWorld(node
))
868 struct SetLocalStoreEliminationResult
{
869 SetLocalStoreEliminationResult()
870 : mayBeAccessed(false)
872 , mayClobberWorld(false)
878 bool mayClobberWorld
;
880 SetLocalStoreEliminationResult
setLocalStoreElimination(
881 VirtualRegister local
, Node
* expectedNode
)
883 SetLocalStoreEliminationResult result
;
884 for (unsigned i
= m_indexInBlock
; i
--;) {
885 Node
* node
= m_currentBlock
->at(i
);
886 switch (node
->op()) {
889 if (node
->local() == local
)
890 result
.mayBeAccessed
= true;
893 case GetLocalUnlinked
:
894 if (node
->unlinkedLocal() == local
)
895 result
.mayBeAccessed
= true;
899 if (node
->local() != local
)
901 if (node
!= expectedNode
)
902 result
.mayBeAccessed
= true;
907 if (static_cast<VirtualRegister
>(node
->varNumber()) == local
)
908 result
.mayBeAccessed
= true;
913 if (m_graph
.uncheckedActivationRegisterFor(node
->codeOrigin
) == local
)
914 result
.mayBeAccessed
= true;
917 case CheckArgumentsNotCreated
:
918 case GetMyArgumentsLength
:
919 case GetMyArgumentsLengthSafe
:
920 if (m_graph
.uncheckedArgumentsRegisterFor(node
->codeOrigin
) == local
)
921 result
.mayBeAccessed
= true;
924 case GetMyArgumentByVal
:
925 case GetMyArgumentByValSafe
:
926 result
.mayBeAccessed
= true;
930 // If this is accessing arguments then it's potentially accessing locals.
931 if (node
->arrayMode().type() == Array::Arguments
)
932 result
.mayBeAccessed
= true;
935 case CreateArguments
:
936 case TearOffActivation
:
937 case TearOffArguments
:
938 // If an activation is being torn off then it means that captured variables
939 // are live. We could be clever here and check if the local qualifies as an
940 // argument register. But that seems like it would buy us very little since
941 // any kind of tear offs are rare to begin with.
942 result
.mayBeAccessed
= true;
948 result
.mayExit
|= node
->canExit();
949 result
.mayClobberWorld
|= m_graph
.clobbersWorld(node
);
951 RELEASE_ASSERT_NOT_REACHED();
952 // Be safe in release mode.
953 result
.mayBeAccessed
= true;
957 void eliminateIrrelevantPhantomChildren(Node
* node
)
959 ASSERT(node
->op() == Phantom
);
960 for (unsigned i
= 0; i
< AdjacencyList::Size
; ++i
) {
961 Edge edge
= node
->children
.child(i
);
964 if (edge
.useKind() != UntypedUse
)
965 continue; // Keep the type check.
966 if (edge
->flags() & NodeRelevantToOSR
)
969 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
970 dataLog(" Eliminating edge @", m_currentNode
->index(), " -> @", edge
->index());
972 node
->children
.removeEdge(i
--);
977 bool setReplacement(Node
* replacement
)
982 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
983 dataLogF(" Replacing @%u -> @%u", m_currentNode
->index(), replacement
->index());
986 m_currentNode
->convertToPhantom();
987 eliminateIrrelevantPhantomChildren(m_currentNode
);
989 // At this point we will eliminate all references to this node.
990 m_currentNode
->replacement
= replacement
;
999 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
1000 dataLogF(" Eliminating @%u", m_currentNode
->index());
1003 ASSERT(m_currentNode
->mustGenerate());
1004 m_currentNode
->convertToPhantom();
1005 eliminateIrrelevantPhantomChildren(m_currentNode
);
1010 void eliminate(Node
* node
, NodeType phantomType
= Phantom
)
1014 ASSERT(node
->mustGenerate());
1015 node
->setOpAndDefaultNonExitFlags(phantomType
);
1016 if (phantomType
== Phantom
)
1017 eliminateIrrelevantPhantomChildren(node
);
1022 void performNodeCSE(Node
* node
)
1024 if (cseMode
== NormalCSE
)
1025 m_graph
.performSubstitution(node
);
1027 if (node
->op() == SetLocal
)
1028 node
->child1()->mergeFlags(NodeRelevantToOSR
);
1030 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
1031 dataLogF(" %s @%u: ", Graph::opName(node
->op()), node
->index());
1034 // NOTE: there are some nodes that we deliberately don't CSE even though we
1035 // probably could, like MakeRope and ToPrimitive. That's because there is no
1036 // evidence that doing CSE on these nodes would result in a performance
1037 // progression. Hence considering these nodes in CSE would just mean that this
1038 // code does more work with no win. Of course, we may want to reconsider this,
1039 // since MakeRope is trivially CSE-able. It's not trivially doable for
1040 // ToPrimitive, but we could change that with some speculations if we really
1043 switch (node
->op()) {
1046 if (cseMode
== StoreElimination
)
1048 setReplacement(node
->child1().node());
1051 // Handle the pure nodes. These nodes never have any side-effects.
1070 case StringCharCodeAt
:
1081 case GetScopeRegisters
:
1084 case CompareEqConstant
:
1086 if (cseMode
== StoreElimination
)
1088 setReplacement(pureCSE(node
));
1092 case ForwardInt32ToDouble
:
1093 if (cseMode
== StoreElimination
)
1095 setReplacement(int32ToDoubleCSE(node
));
1099 if (cseMode
== StoreElimination
)
1101 setReplacement(getCalleeLoadElimination(node
->codeOrigin
.inlineCallFrame
));
1105 if (cseMode
== StoreElimination
)
1107 VariableAccessData
* variableAccessData
= node
->variableAccessData();
1108 if (!variableAccessData
->isCaptured())
1110 Node
* relevantLocalOp
;
1111 Node
* possibleReplacement
= getLocalLoadElimination(variableAccessData
->local(), relevantLocalOp
, variableAccessData
->isCaptured());
1112 if (!relevantLocalOp
)
1114 if (relevantLocalOp
->op() != GetLocalUnlinked
1115 && relevantLocalOp
->variableAccessData() != variableAccessData
)
1117 Node
* phi
= node
->child1().node();
1118 if (!setReplacement(possibleReplacement
))
1123 // If we replace a GetLocal with a GetLocalUnlinked, then turn the GetLocalUnlinked
1125 if (relevantLocalOp
->op() == GetLocalUnlinked
)
1126 relevantLocalOp
->convertToGetLocal(variableAccessData
, phi
);
1132 case GetLocalUnlinked
: {
1133 if (cseMode
== StoreElimination
)
1135 Node
* relevantLocalOpIgnored
;
1136 setReplacement(getLocalLoadElimination(node
->unlinkedLocal(), relevantLocalOpIgnored
, true));
1141 VariableAccessData
* variableAccessData
= node
->variableAccessData();
1142 VirtualRegister local
= variableAccessData
->local();
1143 Node
* replacement
= node
->child1().node();
1144 if (replacement
->op() != SetLocal
)
1146 ASSERT(replacement
->variableAccessData() == variableAccessData
);
1147 // FIXME: We should be able to remove SetLocals that can exit; we just need
1148 // to replace them with appropriate type checks.
1149 if (cseMode
== NormalCSE
) {
1150 // Need to be conservative at this time; if the SetLocal has any chance of performing
1151 // any speculations then we cannot do anything.
1152 if (variableAccessData
->isCaptured()) {
1153 // Captured SetLocals never speculate and hence never exit.
1155 if (variableAccessData
->shouldUseDoubleFormat())
1157 SpeculatedType prediction
= variableAccessData
->argumentAwarePrediction();
1158 if (isInt32Speculation(prediction
))
1160 if (isArraySpeculation(prediction
))
1162 if (isBooleanSpeculation(prediction
))
1166 if (replacement
->canExit())
1169 SetLocalStoreEliminationResult result
=
1170 setLocalStoreElimination(local
, replacement
);
1171 if (result
.mayBeAccessed
|| result
.mayClobberWorld
)
1173 ASSERT(replacement
->op() == SetLocal
);
1174 // FIXME: Investigate using mayExit as a further optimization.
1175 node
->convertToPhantom();
1176 Node
* dataNode
= replacement
->child1().node();
1177 ASSERT(dataNode
->hasResult());
1178 m_graph
.clearAndDerefChild1(node
);
1179 node
->children
.child1() = Edge(dataNode
);
1186 if (cseMode
== StoreElimination
)
1188 // This is strange, but necessary. Some phases will convert nodes to constants,
1189 // which may result in duplicated constants. We use CSE to clean this up.
1190 setReplacement(constantCSE(node
));
1193 case WeakJSConstant
:
1194 if (cseMode
== StoreElimination
)
1196 // FIXME: have CSE for weak constants against strong constants and vice-versa.
1197 setReplacement(weakConstantCSE(node
));
1200 case GetArrayLength
:
1201 if (cseMode
== StoreElimination
)
1203 setReplacement(getArrayLengthElimination(node
->child1().node()));
1207 if (cseMode
== StoreElimination
)
1209 setReplacement(getMyScopeLoadElimination(node
->codeOrigin
.inlineCallFrame
));
1212 // Handle nodes that are conditionally pure: these are pure, and can
1213 // be CSE'd, so long as the prediction is the one we want.
1217 case CompareGreater
:
1218 case CompareGreaterEq
:
1220 if (cseMode
== StoreElimination
)
1222 if (m_graph
.isPredictedNumerical(node
)) {
1223 Node
* replacement
= pureCSE(node
);
1224 if (replacement
&& m_graph
.isPredictedNumerical(replacement
))
1225 setReplacement(replacement
);
1230 // Finally handle heap accesses. These are not quite pure, but we can still
1231 // optimize them provided that some subtle conditions are met.
1233 if (cseMode
== StoreElimination
)
1235 setReplacement(globalVarLoadElimination(node
->registerPointer()));
1238 case GetScopedVar
: {
1239 if (cseMode
== StoreElimination
)
1241 setReplacement(scopedVarLoadElimination(node
->child1().node(), node
->varNumber()));
1245 case GlobalVarWatchpoint
:
1246 if (cseMode
== StoreElimination
)
1248 if (globalVarWatchpointElimination(node
->registerPointer()))
1253 case PutGlobalVarCheck
:
1254 if (cseMode
== NormalCSE
)
1256 eliminate(globalVarStoreElimination(node
->registerPointer()));
1259 case PutScopedVar
: {
1260 if (cseMode
== NormalCSE
)
1262 eliminate(scopedVarStoreElimination(node
->child1().node(), node
->child2().node(), node
->varNumber()));
1267 if (cseMode
== StoreElimination
)
1269 if (m_graph
.byValIsPure(node
))
1270 setReplacement(getByValLoadElimination(node
->child1().node(), node
->child2().node()));
1274 if (cseMode
== StoreElimination
)
1276 Edge child1
= m_graph
.varArgChild(node
, 0);
1277 Edge child2
= m_graph
.varArgChild(node
, 1);
1278 if (node
->arrayMode().canCSEStorage()) {
1279 Node
* replacement
= getByValLoadElimination(child1
.node(), child2
.node());
1282 node
->setOp(PutByValAlias
);
1287 case CheckStructure
:
1288 case ForwardCheckStructure
:
1289 if (cseMode
== StoreElimination
)
1291 if (checkStructureElimination(node
->structureSet(), node
->child1().node()))
1295 case StructureTransitionWatchpoint
:
1296 case ForwardStructureTransitionWatchpoint
:
1297 if (cseMode
== StoreElimination
)
1299 if (structureTransitionWatchpointElimination(node
->structure(), node
->child1().node()))
1304 if (cseMode
== NormalCSE
)
1306 eliminate(putStructureStoreElimination(node
->child1().node()), PhantomPutStructure
);
1310 if (cseMode
== StoreElimination
)
1312 if (checkFunctionElimination(node
->function(), node
->child1().node()))
1316 case CheckExecutable
:
1317 if (cseMode
== StoreElimination
)
1319 if (checkExecutableElimination(node
->executable(), node
->child1().node()))
1324 if (cseMode
== StoreElimination
)
1326 if (checkArrayElimination(node
->child1().node(), node
->arrayMode()))
1330 case GetIndexedPropertyStorage
: {
1331 if (cseMode
== StoreElimination
)
1333 setReplacement(getIndexedPropertyStorageLoadElimination(node
->child1().node(), node
->arrayMode()));
1338 if (cseMode
== StoreElimination
)
1340 setReplacement(getPropertyStorageLoadElimination(node
->child1().node()));
1344 if (cseMode
== StoreElimination
)
1346 setReplacement(getByOffsetLoadElimination(m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
, node
->child1().node()));
1350 if (cseMode
== NormalCSE
)
1352 eliminate(putByOffsetStoreElimination(m_graph
.m_storageAccessData
[node
->storageAccessDataIndex()].identifierNumber
, node
->child1().node()));
1356 // FIXME: we ought to remove Phantom's that have no children.
1358 eliminateIrrelevantPhantomChildren(node
);
1366 m_lastSeen
[node
->op()] = m_indexInBlock
;
1367 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
1372 void performBlockCSE(BasicBlock
* block
)
1376 if (!block
->isReachable
)
1379 m_currentBlock
= block
;
1380 for (unsigned i
= 0; i
< LastNodeType
; ++i
)
1381 m_lastSeen
[i
] = UINT_MAX
;
1383 // All Phis need to already be marked as relevant to OSR, and have their
1384 // replacements cleared, so we don't get confused while doing substitutions on
1386 for (unsigned i
= 0; i
< block
->phis
.size(); ++i
) {
1387 ASSERT(block
->phis
[i
]->flags() & NodeRelevantToOSR
);
1388 block
->phis
[i
]->replacement
= 0;
1391 // Make all of my SetLocal and GetLocal nodes relevant to OSR, and do some other
1392 // necessary bookkeeping.
1393 for (unsigned i
= 0; i
< block
->size(); ++i
) {
1394 Node
* node
= block
->at(i
);
1396 node
->replacement
= 0;
1398 switch (node
->op()) {
1400 case GetLocal
: // FIXME: The GetLocal case is only necessary until we do https://bugs.webkit.org/show_bug.cgi?id=106707.
1401 node
->mergeFlags(NodeRelevantToOSR
);
1404 node
->clearFlags(NodeRelevantToOSR
);
1409 for (m_indexInBlock
= 0; m_indexInBlock
< block
->size(); ++m_indexInBlock
) {
1410 m_currentNode
= block
->at(m_indexInBlock
);
1411 performNodeCSE(m_currentNode
);
1414 if (!ASSERT_DISABLED
&& cseMode
== StoreElimination
) {
1415 // Nobody should have replacements set.
1416 for (unsigned i
= 0; i
< block
->size(); ++i
)
1417 ASSERT(!block
->at(i
)->replacement
);
1421 BasicBlock
* m_currentBlock
;
1422 Node
* m_currentNode
;
1423 unsigned m_indexInBlock
;
1424 FixedArray
<unsigned, LastNodeType
> m_lastSeen
;
1425 bool m_changed
; // Only tracks changes that have a substantive effect on other optimizations.
1428 bool performCSE(Graph
& graph
)
1430 SamplingRegion
samplingRegion("DFG CSE Phase");
1431 return runPhase
<CSEPhase
<NormalCSE
> >(graph
);
1434 bool performStoreElimination(Graph
& graph
)
1436 SamplingRegion
samplingRegion("DFG Store Elimination Phase");
1437 return runPhase
<CSEPhase
<StoreElimination
> >(graph
);
1440 } } // namespace JSC::DFG
1442 #endif // ENABLE(DFG_JIT)