2 * Copyright (C) 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 "DFGConstantFoldingPhase.h"
31 #include "DFGAbstractState.h"
32 #include "DFGBasicBlock.h"
34 #include "DFGInsertionSet.h"
36 #include "GetByIdStatus.h"
37 #include "Operations.h"
38 #include "PutByIdStatus.h"
40 namespace JSC
{ namespace DFG
{
42 class ConstantFoldingPhase
: public Phase
{
44 ConstantFoldingPhase(Graph
& graph
)
45 : Phase(graph
, "constant folding")
47 , m_insertionSet(graph
)
55 for (BlockIndex blockIndex
= 0; blockIndex
< m_graph
.m_blocks
.size(); ++blockIndex
) {
56 BasicBlock
* block
= m_graph
.m_blocks
[blockIndex
].get();
59 if (!block
->cfaDidFinish
)
60 changed
|= paintUnreachableCode(blockIndex
);
61 if (block
->cfaFoundConstants
)
62 changed
|= foldConstants(blockIndex
);
69 bool foldConstants(BlockIndex blockIndex
)
71 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
72 dataLogF("Constant folding considering Block #%u.\n", blockIndex
);
74 BasicBlock
* block
= m_graph
.m_blocks
[blockIndex
].get();
76 m_state
.beginBasicBlock(block
);
77 for (unsigned indexInBlock
= 0; indexInBlock
< block
->size(); ++indexInBlock
) {
78 if (!m_state
.isValid())
81 Node
* node
= block
->at(indexInBlock
);
83 bool eliminated
= false;
86 case CheckArgumentsNotCreated
: {
87 if (!isEmptySpeculation(
88 m_state
.variables().operand(
89 m_graph
.argumentsRegisterFor(node
->codeOrigin
)).m_type
))
91 node
->convertToPhantom();
97 case ForwardCheckStructure
:
98 case ArrayifyToStructure
: {
99 AbstractValue
& value
= m_state
.forNode(node
->child1());
101 if (node
->op() == ArrayifyToStructure
)
102 set
= node
->structure();
104 set
= node
->structureSet();
105 if (value
.m_currentKnownStructure
.isSubsetOf(set
)) {
106 m_state
.execute(indexInBlock
); // Catch the fact that we may filter on cell.
107 node
->convertToPhantom();
111 StructureAbstractValue
& structureValue
= value
.m_futurePossibleStructure
;
112 if (structureValue
.isSubsetOf(set
)
113 && structureValue
.hasSingleton()) {
114 Structure
* structure
= structureValue
.singleton();
115 m_state
.execute(indexInBlock
); // Catch the fact that we may filter on cell.
116 node
->convertToStructureTransitionWatchpoint(structure
);
125 if (!node
->arrayMode().alreadyChecked(m_graph
, node
, m_state
.forNode(node
->child1())))
127 node
->convertToPhantom();
132 case CheckFunction
: {
133 if (m_state
.forNode(node
->child1()).value() != node
->function())
135 node
->convertToPhantom();
142 CodeOrigin codeOrigin
= node
->codeOrigin
;
143 Edge childEdge
= node
->child1();
144 Node
* child
= childEdge
.node();
145 unsigned identifierNumber
= node
->identifierNumber();
147 if (childEdge
.useKind() != CellUse
)
150 Structure
* structure
= m_state
.forNode(child
).bestProvenStructure();
154 bool needsWatchpoint
= !m_state
.forNode(child
).m_currentKnownStructure
.hasSingleton();
155 bool needsCellCheck
= m_state
.forNode(child
).m_type
& ~SpecCell
;
157 GetByIdStatus status
= GetByIdStatus::computeFor(
158 vm(), structure
, codeBlock()->identifier(identifierNumber
));
160 if (!status
.isSimple()) {
161 // FIXME: We could handle prototype cases.
162 // https://bugs.webkit.org/show_bug.cgi?id=110386
166 ASSERT(status
.structureSet().size() == 1);
167 ASSERT(status
.chain().isEmpty());
168 ASSERT(status
.structureSet().singletonStructure() == structure
);
170 // Now before we do anything else, push the CFA forward over the GetById
171 // and make sure we signal to the loop that it should continue and not
172 // do any eliminations.
173 m_state
.execute(indexInBlock
);
176 if (needsWatchpoint
) {
177 ASSERT(m_state
.forNode(child
).m_futurePossibleStructure
.isSubsetOf(StructureSet(structure
)));
178 m_insertionSet
.insertNode(
179 indexInBlock
, SpecNone
, StructureTransitionWatchpoint
, codeOrigin
,
180 OpInfo(structure
), childEdge
);
181 } else if (needsCellCheck
) {
182 m_insertionSet
.insertNode(
183 indexInBlock
, SpecNone
, Phantom
, codeOrigin
, childEdge
);
186 childEdge
.setUseKind(KnownCellUse
);
188 Edge propertyStorage
;
190 if (isInlineOffset(status
.offset()))
191 propertyStorage
= childEdge
;
193 propertyStorage
= Edge(m_insertionSet
.insertNode(
194 indexInBlock
, SpecNone
, GetButterfly
, codeOrigin
, childEdge
));
197 node
->convertToGetByOffset(m_graph
.m_storageAccessData
.size(), propertyStorage
);
199 StorageAccessData storageAccessData
;
200 storageAccessData
.offset
= indexRelativeToBase(status
.offset());
201 storageAccessData
.identifierNumber
= identifierNumber
;
202 m_graph
.m_storageAccessData
.append(storageAccessData
);
207 case PutByIdDirect
: {
208 CodeOrigin codeOrigin
= node
->codeOrigin
;
209 Edge childEdge
= node
->child1();
210 Node
* child
= childEdge
.node();
211 unsigned identifierNumber
= node
->identifierNumber();
213 ASSERT(childEdge
.useKind() == CellUse
);
215 Structure
* structure
= m_state
.forNode(child
).bestProvenStructure();
219 bool needsWatchpoint
= !m_state
.forNode(child
).m_currentKnownStructure
.hasSingleton();
220 bool needsCellCheck
= m_state
.forNode(child
).m_type
& ~SpecCell
;
222 PutByIdStatus status
= PutByIdStatus::computeFor(
224 m_graph
.globalObjectFor(codeOrigin
),
226 codeBlock()->identifier(identifierNumber
),
227 node
->op() == PutByIdDirect
);
229 if (!status
.isSimpleReplace() && !status
.isSimpleTransition())
232 ASSERT(status
.oldStructure() == structure
);
234 // Now before we do anything else, push the CFA forward over the PutById
235 // and make sure we signal to the loop that it should continue and not
236 // do any eliminations.
237 m_state
.execute(indexInBlock
);
240 if (needsWatchpoint
) {
241 ASSERT(m_state
.forNode(child
).m_futurePossibleStructure
.isSubsetOf(StructureSet(structure
)));
242 m_insertionSet
.insertNode(
243 indexInBlock
, SpecNone
, StructureTransitionWatchpoint
, codeOrigin
,
244 OpInfo(structure
), childEdge
);
245 } else if (needsCellCheck
) {
246 m_insertionSet
.insertNode(
247 indexInBlock
, SpecNone
, Phantom
, codeOrigin
, childEdge
);
250 childEdge
.setUseKind(KnownCellUse
);
252 StructureTransitionData
* transitionData
= 0;
253 if (status
.isSimpleTransition()) {
254 transitionData
= m_graph
.addStructureTransitionData(
255 StructureTransitionData(structure
, status
.newStructure()));
257 if (node
->op() == PutById
) {
258 if (!structure
->storedPrototype().isNull()) {
259 addStructureTransitionCheck(
260 codeOrigin
, indexInBlock
,
261 structure
->storedPrototype().asCell());
264 for (WriteBarrier
<Structure
>* it
= status
.structureChain()->head(); *it
; ++it
) {
265 JSValue prototype
= (*it
)->storedPrototype();
266 if (prototype
.isNull())
268 ASSERT(prototype
.isCell());
269 addStructureTransitionCheck(
270 codeOrigin
, indexInBlock
, prototype
.asCell());
275 Edge propertyStorage
;
277 if (isInlineOffset(status
.offset()))
278 propertyStorage
= childEdge
;
279 else if (status
.isSimpleReplace() || structure
->outOfLineCapacity() == status
.newStructure()->outOfLineCapacity()) {
280 propertyStorage
= Edge(m_insertionSet
.insertNode(
281 indexInBlock
, SpecNone
, GetButterfly
, codeOrigin
, childEdge
));
282 } else if (!structure
->outOfLineCapacity()) {
283 ASSERT(status
.newStructure()->outOfLineCapacity());
284 ASSERT(!isInlineOffset(status
.offset()));
285 propertyStorage
= Edge(m_insertionSet
.insertNode(
286 indexInBlock
, SpecNone
, AllocatePropertyStorage
,
287 codeOrigin
, OpInfo(transitionData
), childEdge
));
289 ASSERT(structure
->outOfLineCapacity());
290 ASSERT(status
.newStructure()->outOfLineCapacity() > structure
->outOfLineCapacity());
291 ASSERT(!isInlineOffset(status
.offset()));
293 propertyStorage
= Edge(m_insertionSet
.insertNode(
294 indexInBlock
, SpecNone
, ReallocatePropertyStorage
, codeOrigin
,
295 OpInfo(transitionData
), childEdge
,
296 Edge(m_insertionSet
.insertNode(
297 indexInBlock
, SpecNone
, GetButterfly
, codeOrigin
, childEdge
))));
300 if (status
.isSimpleTransition()) {
301 m_insertionSet
.insertNode(
302 indexInBlock
, SpecNone
, PutStructure
, codeOrigin
,
303 OpInfo(transitionData
), childEdge
);
306 node
->convertToPutByOffset(m_graph
.m_storageAccessData
.size(), propertyStorage
);
308 StorageAccessData storageAccessData
;
309 storageAccessData
.offset
= indexRelativeToBase(status
.offset());
310 storageAccessData
.identifierNumber
= identifierNumber
;
311 m_graph
.m_storageAccessData
.append(storageAccessData
);
324 m_state
.execute(indexInBlock
);
325 if (!node
->shouldGenerate() || m_state
.didClobber() || node
->hasConstant())
327 JSValue value
= m_state
.forNode(node
).value();
331 CodeOrigin codeOrigin
= node
->codeOrigin
;
332 AdjacencyList children
= node
->children
;
334 if (node
->op() == GetLocal
) {
335 // GetLocals without a Phi child are guaranteed dead. We don't have to
336 // do anything about them.
340 if (m_graph
.m_form
!= LoadStore
) {
341 VariableAccessData
* variable
= node
->variableAccessData();
342 Node
* phi
= node
->child1().node();
344 && block
->variablesAtHead
.operand(variable
->local()) == phi
345 && block
->variablesAtTail
.operand(variable
->local()) == node
) {
347 // Keep the graph threaded for easy cases. This is improves compile
348 // times. It would be correct to just dethread here.
350 m_graph
.convertToConstant(node
, value
);
351 Node
* phantom
= m_insertionSet
.insertNode(
352 indexInBlock
, SpecNone
, PhantomLocal
, codeOrigin
,
353 OpInfo(variable
), Edge(phi
));
354 block
->variablesAtHead
.operand(variable
->local()) = phantom
;
355 block
->variablesAtTail
.operand(variable
->local()) = phantom
;
365 ASSERT(!node
->hasVariableAccessData());
367 m_graph
.convertToConstant(node
, value
);
368 m_insertionSet
.insertNode(
369 indexInBlock
, SpecNone
, Phantom
, codeOrigin
, children
);
374 m_insertionSet
.execute(block
);
380 bool isCapturedAtOrAfter(BasicBlock
* block
, unsigned indexInBlock
, int operand
)
382 for (; indexInBlock
< block
->size(); ++indexInBlock
) {
383 Node
* node
= block
->at(indexInBlock
);
384 if (!node
->hasLocal())
386 if (node
->local() != operand
)
388 if (node
->variableAccessData()->isCaptured())
393 #endif // !ASSERT_DISABLED
395 void addStructureTransitionCheck(CodeOrigin codeOrigin
, unsigned indexInBlock
, JSCell
* cell
)
397 Node
* weakConstant
= m_insertionSet
.insertNode(
398 indexInBlock
, speculationFromValue(cell
), WeakJSConstant
, codeOrigin
, OpInfo(cell
));
400 if (cell
->structure()->transitionWatchpointSetIsStillValid()) {
401 m_insertionSet
.insertNode(
402 indexInBlock
, SpecNone
, StructureTransitionWatchpoint
, codeOrigin
,
403 OpInfo(cell
->structure()), Edge(weakConstant
, CellUse
));
407 m_insertionSet
.insertNode(
408 indexInBlock
, SpecNone
, CheckStructure
, codeOrigin
,
409 OpInfo(m_graph
.addStructureSet(cell
->structure())), Edge(weakConstant
, CellUse
));
412 // This is necessary because the CFA may reach conclusions about constants based on its
413 // assumption that certain code must exit, but then those constants may lead future
414 // reexecutions of the CFA to believe that the same code will now no longer exit. Thus
415 // to ensure soundness, we must paint unreachable code as such, by inserting an
416 // unconditional ForceOSRExit wherever we find that a node would have always exited.
417 // This will only happen in cases where we are making static speculations, or we're
418 // making totally wrong speculations due to imprecision on the prediction propagator.
419 bool paintUnreachableCode(BlockIndex blockIndex
)
421 bool changed
= false;
423 #if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
424 dataLogF("Painting unreachable code in Block #%u.\n", blockIndex
);
426 BasicBlock
* block
= m_graph
.m_blocks
[blockIndex
].get();
427 m_state
.beginBasicBlock(block
);
429 for (unsigned indexInBlock
= 0; indexInBlock
< block
->size(); ++indexInBlock
) {
430 m_state
.execute(indexInBlock
);
431 if (m_state
.isValid())
434 Node
* node
= block
->at(indexInBlock
);
435 switch (node
->op()) {
439 // Do nothing. These nodes will already do the right thing.
443 m_insertionSet
.insertNode(
444 indexInBlock
, SpecNone
, ForceOSRExit
, node
->codeOrigin
);
451 m_insertionSet
.execute(block
);
456 AbstractState m_state
;
457 InsertionSet m_insertionSet
;
460 bool performConstantFolding(Graph
& graph
)
462 SamplingRegion
samplingRegion("DFG Constant Folding Phase");
463 return runPhase
<ConstantFoldingPhase
>(graph
);
466 } } // namespace JSC::DFG
468 #endif // ENABLE(DFG_JIT)