2 * Copyright (C) 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 "DFGConstantFoldingPhase.h"
31 #include "DFGAbstractInterpreterInlines.h"
32 #include "DFGBasicBlock.h"
34 #include "DFGInPlaceAbstractState.h"
35 #include "DFGInsertionSet.h"
37 #include "GetByIdStatus.h"
38 #include "JSCInlines.h"
39 #include "PutByIdStatus.h"
41 namespace JSC
{ namespace DFG
{
43 class ConstantFoldingPhase
: public Phase
{
45 ConstantFoldingPhase(Graph
& graph
)
46 : Phase(graph
, "constant folding")
48 , m_interpreter(graph
, m_state
)
49 , m_insertionSet(graph
)
57 for (BlockIndex blockIndex
= 0; blockIndex
< m_graph
.numBlocks(); ++blockIndex
) {
58 BasicBlock
* block
= m_graph
.block(blockIndex
);
61 if (block
->cfaFoundConstants
)
62 changed
|= foldConstants(block
);
69 bool foldConstants(BasicBlock
* block
)
72 m_state
.beginBasicBlock(block
);
73 for (unsigned indexInBlock
= 0; indexInBlock
< block
->size(); ++indexInBlock
) {
74 if (!m_state
.isValid())
77 Node
* node
= block
->at(indexInBlock
);
79 bool eliminated
= false;
82 case BooleanToNumber
: {
83 if (node
->child1().useKind() == UntypedUse
84 && !m_interpreter
.needsTypeCheck(node
->child1(), SpecBoolean
))
85 node
->child1().setUseKind(BooleanUse
);
89 case CheckArgumentsNotCreated
: {
90 if (!isEmptySpeculation(
91 m_state
.variables().operand(
92 m_graph
.argumentsRegisterFor(node
->origin
.semantic
)).m_type
))
94 node
->convertToPhantom();
100 case ArrayifyToStructure
: {
101 AbstractValue
& value
= m_state
.forNode(node
->child1());
103 if (node
->op() == ArrayifyToStructure
)
104 set
= node
->structure();
106 set
= node
->structureSet();
107 if (value
.m_currentKnownStructure
.isSubsetOf(set
)) {
108 m_interpreter
.execute(indexInBlock
); // Catch the fact that we may filter on cell.
109 node
->convertToPhantom();
113 StructureAbstractValue
& structureValue
= value
.m_futurePossibleStructure
;
114 if (structureValue
.isSubsetOf(set
)
115 && structureValue
.hasSingleton()) {
116 Structure
* structure
= structureValue
.singleton();
117 m_interpreter
.execute(indexInBlock
); // Catch the fact that we may filter on cell.
118 AdjacencyList children
= node
->children
;
119 children
.removeEdge(0);
120 if (!!children
.child1())
121 m_insertionSet
.insertNode(indexInBlock
, SpecNone
, Phantom
, node
->origin
, children
);
122 node
->children
.setChild2(Edge());
123 node
->children
.setChild3(Edge());
124 node
->convertToStructureTransitionWatchpoint(structure
);
133 if (!node
->arrayMode().alreadyChecked(m_graph
, node
, m_state
.forNode(node
->child1())))
135 node
->convertToPhantom();
140 case CheckFunction
: {
141 if (m_state
.forNode(node
->child1()).value() != node
->function())
143 node
->convertToPhantom();
148 case CheckInBounds
: {
149 JSValue left
= m_state
.forNode(node
->child1()).value();
150 JSValue right
= m_state
.forNode(node
->child2()).value();
151 if (left
&& right
&& left
.isInt32() && right
.isInt32()
152 && static_cast<uint32_t>(left
.asInt32()) < static_cast<uint32_t>(right
.asInt32())) {
153 node
->convertToPhantom();
161 case MultiGetByOffset
: {
162 Edge childEdge
= node
->child1();
163 Node
* child
= childEdge
.node();
164 MultiGetByOffsetData
& data
= node
->multiGetByOffsetData();
166 Structure
* structure
= m_state
.forNode(child
).bestProvenStructure();
170 for (unsigned i
= data
.variants
.size(); i
--;) {
171 const GetByIdVariant
& variant
= data
.variants
[i
];
172 if (!variant
.structureSet().contains(structure
))
178 emitGetByOffset(indexInBlock
, node
, structure
, variant
, data
.identifierNumber
);
185 case MultiPutByOffset
: {
186 Edge childEdge
= node
->child1();
187 Node
* child
= childEdge
.node();
188 MultiPutByOffsetData
& data
= node
->multiPutByOffsetData();
190 Structure
* structure
= m_state
.forNode(child
).bestProvenStructure();
194 for (unsigned i
= data
.variants
.size(); i
--;) {
195 const PutByIdVariant
& variant
= data
.variants
[i
];
196 if (variant
.oldStructure() != structure
)
199 emitPutByOffset(indexInBlock
, node
, structure
, variant
, data
.identifierNumber
);
208 Edge childEdge
= node
->child1();
209 Node
* child
= childEdge
.node();
210 unsigned identifierNumber
= node
->identifierNumber();
212 if (childEdge
.useKind() != CellUse
)
215 Structure
* structure
= m_state
.forNode(child
).bestProvenStructure();
219 GetByIdStatus status
= GetByIdStatus::computeFor(
220 vm(), structure
, m_graph
.identifiers()[identifierNumber
]);
222 if (!status
.isSimple() || status
.numVariants() != 1) {
223 // FIXME: We could handle prototype cases.
224 // https://bugs.webkit.org/show_bug.cgi?id=110386
228 emitGetByOffset(indexInBlock
, node
, structure
, status
[0], identifierNumber
);
234 case PutByIdDirect
: {
235 NodeOrigin origin
= node
->origin
;
236 Edge childEdge
= node
->child1();
237 Node
* child
= childEdge
.node();
238 unsigned identifierNumber
= node
->identifierNumber();
240 ASSERT(childEdge
.useKind() == CellUse
);
242 Structure
* structure
= m_state
.forNode(child
).bestProvenStructure();
246 PutByIdStatus status
= PutByIdStatus::computeFor(
248 m_graph
.globalObjectFor(origin
.semantic
),
250 m_graph
.identifiers()[identifierNumber
],
251 node
->op() == PutByIdDirect
);
253 if (!status
.isSimple())
255 if (status
.numVariants() != 1)
258 emitPutByOffset(indexInBlock
, node
, structure
, status
[0], identifierNumber
);
264 if (m_state
.forNode(node
->child1()).m_type
& ~(SpecFullNumber
| SpecBoolean
| SpecString
))
267 node
->convertToIdentity();
280 m_interpreter
.execute(indexInBlock
);
281 if (!m_state
.isValid()) {
282 // If we invalidated then we shouldn't attempt to constant-fold. Here's an
285 // c: JSConstant(4.2)
286 // x: ValueToInt32(Check:Int32:@const)
288 // It would be correct for an analysis to assume that execution cannot
289 // proceed past @x. Therefore, constant-folding @x could be rather bad. But,
290 // the CFA may report that it found a constant even though it also reported
291 // that everything has been invalidated. This will only happen in a couple of
292 // the constant folding cases; most of them are also separately defensive
293 // about such things.
296 if (!node
->shouldGenerate() || m_state
.didClobber() || node
->hasConstant())
298 JSValue value
= m_state
.forNode(node
).value();
302 // Check if merging the abstract value of the constant into the abstract value
303 // we've proven for this node wouldn't widen the proof. If it widens the proof
304 // (i.e. says that the set contains more things in it than it previously did)
305 // then we refuse to fold.
306 AbstractValue oldValue
= m_state
.forNode(node
);
307 AbstractValue constantValue
;
308 constantValue
.set(m_graph
, value
);
309 constantValue
.fixTypeForRepresentation(node
);
310 if (oldValue
.merge(constantValue
))
313 NodeOrigin origin
= node
->origin
;
314 AdjacencyList children
= node
->children
;
316 if (node
->op() == GetLocal
)
319 ASSERT(!node
->hasVariableAccessData(m_graph
));
321 m_graph
.convertToConstant(node
, value
);
322 m_insertionSet
.insertNode(
323 indexInBlock
, SpecNone
, Phantom
, origin
, children
);
328 m_insertionSet
.execute(block
);
333 void emitGetByOffset(unsigned indexInBlock
, Node
* node
, Structure
* structure
, const GetByIdVariant
& variant
, unsigned identifierNumber
)
335 NodeOrigin origin
= node
->origin
;
336 Edge childEdge
= node
->child1();
337 Node
* child
= childEdge
.node();
339 bool needsWatchpoint
= !m_state
.forNode(child
).m_currentKnownStructure
.hasSingleton();
340 bool needsCellCheck
= m_state
.forNode(child
).m_type
& ~SpecCell
;
342 ASSERT(!variant
.chain());
343 ASSERT(variant
.structureSet().contains(structure
));
345 // Now before we do anything else, push the CFA forward over the GetById
346 // and make sure we signal to the loop that it should continue and not
347 // do any eliminations.
348 m_interpreter
.execute(indexInBlock
);
350 if (needsWatchpoint
) {
351 m_insertionSet
.insertNode(
352 indexInBlock
, SpecNone
, StructureTransitionWatchpoint
, origin
,
353 OpInfo(structure
), childEdge
);
354 } else if (needsCellCheck
) {
355 m_insertionSet
.insertNode(
356 indexInBlock
, SpecNone
, Phantom
, origin
, childEdge
);
359 if (variant
.specificValue()) {
360 m_graph
.convertToConstant(node
, variant
.specificValue());
364 childEdge
.setUseKind(KnownCellUse
);
366 Edge propertyStorage
;
368 if (isInlineOffset(variant
.offset()))
369 propertyStorage
= childEdge
;
371 propertyStorage
= Edge(m_insertionSet
.insertNode(
372 indexInBlock
, SpecNone
, GetButterfly
, origin
, childEdge
));
375 node
->convertToGetByOffset(m_graph
.m_storageAccessData
.size(), propertyStorage
);
377 StorageAccessData storageAccessData
;
378 storageAccessData
.offset
= variant
.offset();
379 storageAccessData
.identifierNumber
= identifierNumber
;
380 m_graph
.m_storageAccessData
.append(storageAccessData
);
383 void emitPutByOffset(unsigned indexInBlock
, Node
* node
, Structure
* structure
, const PutByIdVariant
& variant
, unsigned identifierNumber
)
385 NodeOrigin origin
= node
->origin
;
386 Edge childEdge
= node
->child1();
387 Node
* child
= childEdge
.node();
389 ASSERT(variant
.oldStructure() == structure
);
391 bool needsWatchpoint
= !m_state
.forNode(child
).m_currentKnownStructure
.hasSingleton();
392 bool needsCellCheck
= m_state
.forNode(child
).m_type
& ~SpecCell
;
394 // Now before we do anything else, push the CFA forward over the PutById
395 // and make sure we signal to the loop that it should continue and not
396 // do any eliminations.
397 m_interpreter
.execute(indexInBlock
);
399 if (needsWatchpoint
) {
400 m_insertionSet
.insertNode(
401 indexInBlock
, SpecNone
, StructureTransitionWatchpoint
, origin
,
402 OpInfo(structure
), childEdge
);
403 } else if (needsCellCheck
) {
404 m_insertionSet
.insertNode(
405 indexInBlock
, SpecNone
, Phantom
, origin
, childEdge
);
408 childEdge
.setUseKind(KnownCellUse
);
410 StructureTransitionData
* transitionData
= 0;
411 if (variant
.kind() == PutByIdVariant::Transition
) {
412 transitionData
= m_graph
.addStructureTransitionData(
413 StructureTransitionData(structure
, variant
.newStructure()));
415 if (node
->op() == PutById
) {
416 if (!structure
->storedPrototype().isNull()) {
417 addStructureTransitionCheck(
418 origin
, indexInBlock
,
419 structure
->storedPrototype().asCell());
422 m_graph
.chains().addLazily(variant
.structureChain());
424 for (unsigned i
= 0; i
< variant
.structureChain()->size(); ++i
) {
425 JSValue prototype
= variant
.structureChain()->at(i
)->storedPrototype();
426 if (prototype
.isNull())
428 ASSERT(prototype
.isCell());
429 addStructureTransitionCheck(
430 origin
, indexInBlock
, prototype
.asCell());
435 Edge propertyStorage
;
437 if (isInlineOffset(variant
.offset()))
438 propertyStorage
= childEdge
;
440 variant
.kind() == PutByIdVariant::Replace
441 || structure
->outOfLineCapacity() == variant
.newStructure()->outOfLineCapacity()) {
442 propertyStorage
= Edge(m_insertionSet
.insertNode(
443 indexInBlock
, SpecNone
, GetButterfly
, origin
, childEdge
));
444 } else if (!structure
->outOfLineCapacity()) {
445 ASSERT(variant
.newStructure()->outOfLineCapacity());
446 ASSERT(!isInlineOffset(variant
.offset()));
447 Node
* allocatePropertyStorage
= m_insertionSet
.insertNode(
448 indexInBlock
, SpecNone
, AllocatePropertyStorage
,
449 origin
, OpInfo(transitionData
), childEdge
);
450 m_insertionSet
.insertNode(indexInBlock
, SpecNone
, StoreBarrier
, origin
, Edge(node
->child1().node(), KnownCellUse
));
451 propertyStorage
= Edge(allocatePropertyStorage
);
453 ASSERT(structure
->outOfLineCapacity());
454 ASSERT(variant
.newStructure()->outOfLineCapacity() > structure
->outOfLineCapacity());
455 ASSERT(!isInlineOffset(variant
.offset()));
457 Node
* reallocatePropertyStorage
= m_insertionSet
.insertNode(
458 indexInBlock
, SpecNone
, ReallocatePropertyStorage
, origin
,
459 OpInfo(transitionData
), childEdge
,
460 Edge(m_insertionSet
.insertNode(
461 indexInBlock
, SpecNone
, GetButterfly
, origin
, childEdge
)));
462 m_insertionSet
.insertNode(indexInBlock
, SpecNone
, StoreBarrier
, origin
, Edge(node
->child1().node(), KnownCellUse
));
463 propertyStorage
= Edge(reallocatePropertyStorage
);
466 if (variant
.kind() == PutByIdVariant::Transition
) {
467 Node
* putStructure
= m_graph
.addNode(SpecNone
, PutStructure
, origin
, OpInfo(transitionData
), childEdge
);
468 m_insertionSet
.insertNode(indexInBlock
, SpecNone
, StoreBarrier
, origin
, Edge(node
->child1().node(), KnownCellUse
));
469 m_insertionSet
.insert(indexInBlock
, putStructure
);
472 node
->convertToPutByOffset(m_graph
.m_storageAccessData
.size(), propertyStorage
);
473 m_insertionSet
.insertNode(
474 indexInBlock
, SpecNone
, StoreBarrier
, origin
,
475 Edge(node
->child2().node(), KnownCellUse
));
477 StorageAccessData storageAccessData
;
478 storageAccessData
.offset
= variant
.offset();
479 storageAccessData
.identifierNumber
= identifierNumber
;
480 m_graph
.m_storageAccessData
.append(storageAccessData
);
483 void addStructureTransitionCheck(NodeOrigin origin
, unsigned indexInBlock
, JSCell
* cell
)
485 Node
* weakConstant
= m_insertionSet
.insertNode(
486 indexInBlock
, speculationFromValue(cell
), WeakJSConstant
, origin
, OpInfo(cell
));
488 if (m_graph
.watchpoints().isStillValid(cell
->structure()->transitionWatchpointSet())) {
489 m_insertionSet
.insertNode(
490 indexInBlock
, SpecNone
, StructureTransitionWatchpoint
, origin
,
491 OpInfo(cell
->structure()), Edge(weakConstant
, CellUse
));
495 m_insertionSet
.insertNode(
496 indexInBlock
, SpecNone
, CheckStructure
, origin
,
497 OpInfo(m_graph
.addStructureSet(cell
->structure())), Edge(weakConstant
, CellUse
));
500 InPlaceAbstractState m_state
;
501 AbstractInterpreter
<InPlaceAbstractState
> m_interpreter
;
502 InsertionSet m_insertionSet
;
505 bool performConstantFolding(Graph
& graph
)
507 SamplingRegion
samplingRegion("DFG Constant Folding Phase");
508 return runPhase
<ConstantFoldingPhase
>(graph
);
511 } } // namespace JSC::DFG
513 #endif // ENABLE(DFG_JIT)