blob: c93fc15c616996496e4a5b0b6fd7450479206bce [file] [log] [blame]
/*
* Copyright (C) 2011 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "DFGAbstractState.h"
#if ENABLE(DFG_JIT)
#include "CodeBlock.h"
#include "DFGBasicBlock.h"
namespace JSC { namespace DFG {
#define CFA_PROFILING 0
#if CFA_PROFILING
#define PROFILE(flag) SamplingFlags::ScopedFlag scopedFlag(flag)
#else
#define PROFILE(flag) do { } while (false)
#endif
// Profiling flags
#define FLAG_FOR_BLOCK_INITIALIZATION 17
#define FLAG_FOR_BLOCK_END 18
#define FLAG_FOR_EXECUTION 19
#define FLAG_FOR_MERGE_TO_SUCCESSORS 20
#define FLAG_FOR_STRUCTURE_CLOBBERING 21
AbstractState::AbstractState(Graph& graph)
: m_codeBlock(graph.m_codeBlock)
, m_graph(graph)
, m_variables(m_codeBlock->numParameters(), graph.m_localVars)
, m_block(0)
{
m_nodes.resize(graph.size());
}
AbstractState::~AbstractState() { }
void AbstractState::beginBasicBlock(BasicBlock* basicBlock)
{
PROFILE(FLAG_FOR_BLOCK_INITIALIZATION);
ASSERT(!m_block);
ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->valuesAtHead.numberOfLocals());
ASSERT(basicBlock->variablesAtTail.numberOfLocals() == basicBlock->valuesAtTail.numberOfLocals());
ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->variablesAtTail.numberOfLocals());
for (size_t i = 0; i < basicBlock->size(); i++)
m_nodes[basicBlock->at(i)].clear();
m_variables = basicBlock->valuesAtHead;
m_haveStructures = false;
for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) {
if (m_variables.argument(i).m_structure.isNeitherClearNorTop()) {
m_haveStructures = true;
break;
}
}
for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) {
if (m_variables.local(i).m_structure.isNeitherClearNorTop()) {
m_haveStructures = true;
break;
}
}
basicBlock->cfaShouldRevisit = false;
basicBlock->cfaHasVisited = true;
m_block = basicBlock;
m_isValid = true;
}
void AbstractState::initialize(Graph& graph)
{
PROFILE(FLAG_FOR_BLOCK_INITIALIZATION);
BasicBlock* root = graph.m_blocks[0].get();
root->cfaShouldRevisit = true;
for (size_t i = 0; i < root->valuesAtHead.numberOfArguments(); ++i) {
Node& node = graph[root->variablesAtHead.argument(i)];
ASSERT(node.op() == SetArgument);
if (!node.shouldGenerate()) {
// The argument is dead. We don't do any checks for such arguments, and so
// for the purpose of the analysis, they contain no value.
root->valuesAtHead.argument(i).clear();
continue;
}
if (graph.argumentIsCaptured(i)) {
root->valuesAtHead.argument(i).makeTop();
continue;
}
PredictedType prediction = node.variableAccessData()->prediction();
if (isInt32Prediction(prediction))
root->valuesAtHead.argument(i).set(PredictInt32);
else if (isArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictArray);
else if (isByteArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictByteArray);
else if (isBooleanPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictBoolean);
else if (isInt8ArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictInt8Array);
else if (isInt16ArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictInt16Array);
else if (isInt32ArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictInt32Array);
else if (isUint8ArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictUint8Array);
else if (isUint8ClampedArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictUint8ClampedArray);
else if (isUint16ArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictUint16Array);
else if (isUint32ArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictUint32Array);
else if (isFloat32ArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictFloat32Array);
else if (isFloat64ArrayPrediction(prediction))
root->valuesAtHead.argument(i).set(PredictFloat64Array);
else
root->valuesAtHead.argument(i).makeTop();
}
for (size_t i = 0; i < root->valuesAtHead.numberOfLocals(); ++i) {
if (!graph.localIsCaptured(i))
continue;
root->valuesAtHead.local(i).makeTop();
}
}
bool AbstractState::endBasicBlock(MergeMode mergeMode)
{
PROFILE(FLAG_FOR_BLOCK_END);
ASSERT(m_block);
BasicBlock* block = m_block; // Save the block for successor merging.
if (!m_isValid) {
reset();
return false;
}
bool changed = false;
if (mergeMode != DontMerge || !ASSERT_DISABLED) {
for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" Merging state for argument %zu.\n", argument);
#endif
AbstractValue& destination = block->valuesAtTail.argument(argument);
if (m_graph.argumentIsCaptured(argument)) {
if (!destination.isTop()) {
destination.makeTop();
changed = true;
}
} else
changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument));
}
for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" Merging state for local %zu.\n", local);
#endif
AbstractValue& destination = block->valuesAtTail.local(local);
if (m_graph.localIsCaptured(local)) {
if (!destination.isTop()) {
destination.makeTop();
changed = true;
}
} else
changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local));
}
}
ASSERT(mergeMode != DontMerge || !changed);
reset();
if (mergeMode != MergeToSuccessors)
return changed;
return mergeToSuccessors(m_graph, block);
}
void AbstractState::reset()
{
m_block = 0;
m_isValid = false;
}
bool AbstractState::execute(unsigned indexInBlock)
{
PROFILE(FLAG_FOR_EXECUTION);
ASSERT(m_block);
ASSERT(m_isValid);
NodeIndex nodeIndex = m_block->at(indexInBlock);
Node& node = m_graph[nodeIndex];
if (!node.shouldGenerate())
return true;
switch (node.op()) {
case JSConstant:
case WeakJSConstant: {
JSValue value = m_graph.valueOfJSConstant(nodeIndex);
// Have to be careful here! It's tempting to call set(value), but
// that would be wrong, since that would constitute a proof that this
// value will always have the same structure. The whole point of a value
// having a structure is that it may change in the future - for example
// between when we compile the code and when we run it.
forNode(nodeIndex).set(predictionFromValue(value));
break;
}
case GetLocal: {
if (m_graph.isCaptured(node.local()))
forNode(nodeIndex).makeTop();
else
forNode(nodeIndex) = m_variables.operand(node.local());
break;
}
case SetLocal: {
if (m_graph.isCaptured(node.local()))
break;
if (node.variableAccessData()->shouldUseDoubleFormat()) {
forNode(node.child1()).filter(PredictNumber);
m_variables.operand(node.local()).set(PredictDouble);
break;
}
PredictedType predictedType = node.variableAccessData()->prediction();
if (isInt32Prediction(predictedType))
forNode(node.child1()).filter(PredictInt32);
else if (isArrayPrediction(predictedType))
forNode(node.child1()).filter(PredictArray);
else if (isByteArrayPrediction(predictedType))
forNode(node.child1()).filter(PredictByteArray);
else if (isBooleanPrediction(predictedType))
forNode(node.child1()).filter(PredictBoolean);
m_variables.operand(node.local()) = forNode(node.child1());
break;
}
case SetArgument:
// Assert that the state of arguments has been set.
ASSERT(!m_block->valuesAtHead.operand(node.local()).isClear());
break;
case BitAnd:
case BitOr:
case BitXor:
case BitRShift:
case BitLShift:
case BitURShift:
forNode(node.child1()).filter(PredictInt32);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
case UInt32ToNumber:
if (!node.canSpeculateInteger())
forNode(nodeIndex).set(PredictDouble);
else
forNode(nodeIndex).set(PredictInt32);
break;
case ValueToInt32:
if (m_graph[node.child1()].shouldSpeculateInteger())
forNode(node.child1()).filter(PredictInt32);
else if (m_graph[node.child1()].shouldSpeculateNumber())
forNode(node.child1()).filter(PredictNumber);
else if (m_graph[node.child1()].shouldSpeculateBoolean())
forNode(node.child1()).filter(PredictBoolean);
forNode(nodeIndex).set(PredictInt32);
break;
case ValueAdd:
case ArithAdd: {
if (m_graph.addShouldSpeculateInteger(node)) {
forNode(node.child1()).filter(PredictInt32);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
if (Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) {
forNode(node.child1()).filter(PredictNumber);
forNode(node.child2()).filter(PredictNumber);
forNode(nodeIndex).set(PredictDouble);
break;
}
if (node.op() == ValueAdd) {
clobberStructures(indexInBlock);
forNode(nodeIndex).set(PredictString | PredictInt32 | PredictNumber);
break;
}
// We don't handle this yet. :-(
m_isValid = false;
break;
}
case ArithSub: {
if (m_graph.addShouldSpeculateInteger(node)) {
forNode(node.child1()).filter(PredictInt32);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
forNode(node.child1()).filter(PredictNumber);
forNode(node.child2()).filter(PredictNumber);
forNode(nodeIndex).set(PredictDouble);
break;
}
case ArithNegate: {
if (m_graph.negateShouldSpeculateInteger(node)) {
forNode(node.child1()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
forNode(node.child1()).filter(PredictNumber);
forNode(nodeIndex).set(PredictDouble);
break;
}
case ArithMul:
case ArithDiv:
case ArithMin:
case ArithMax: {
if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()]) && node.canSpeculateInteger()) {
forNode(node.child1()).filter(PredictInt32);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
forNode(node.child1()).filter(PredictNumber);
forNode(node.child2()).filter(PredictNumber);
forNode(nodeIndex).set(PredictDouble);
break;
}
case ArithMod: {
if (m_graph[node.child1()].shouldNotSpeculateInteger() || m_graph[node.child2()].shouldNotSpeculateInteger() || !node.canSpeculateInteger()) {
forNode(node.child1()).filter(PredictNumber);
forNode(node.child2()).filter(PredictNumber);
forNode(nodeIndex).set(PredictDouble);
break;
}
forNode(node.child1()).filter(PredictInt32);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
case ArithAbs:
if (m_graph[node.child1()].shouldSpeculateInteger() && node.canSpeculateInteger()) {
forNode(node.child1()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
forNode(node.child1()).filter(PredictNumber);
forNode(nodeIndex).set(PredictDouble);
break;
case ArithSqrt:
forNode(node.child1()).filter(PredictNumber);
forNode(nodeIndex).set(PredictDouble);
break;
case LogicalNot: {
Node& child = m_graph[node.child1()];
if (isBooleanPrediction(child.prediction()) || !child.prediction())
forNode(node.child1()).filter(PredictBoolean);
else if (child.shouldSpeculateFinalObjectOrOther())
forNode(node.child1()).filter(PredictFinalObject | PredictOther);
else if (child.shouldSpeculateArrayOrOther())
forNode(node.child1()).filter(PredictArray | PredictOther);
else if (child.shouldSpeculateInteger())
forNode(node.child1()).filter(PredictInt32);
else if (child.shouldSpeculateNumber())
forNode(node.child1()).filter(PredictNumber);
else
clobberStructures(indexInBlock);
forNode(nodeIndex).set(PredictBoolean);
break;
}
case CompareLess:
case CompareLessEq:
case CompareGreater:
case CompareGreaterEq:
case CompareEq: {
Node& left = m_graph[node.child1()];
Node& right = m_graph[node.child2()];
PredictedType filter;
if (Node::shouldSpeculateInteger(left, right))
filter = PredictInt32;
else if (Node::shouldSpeculateNumber(left, right))
filter = PredictNumber;
else if (node.op() == CompareEq && Node::shouldSpeculateFinalObject(left, right))
filter = PredictFinalObject;
else if (node.op() == CompareEq && Node::shouldSpeculateArray(left, right))
filter = PredictArray;
else {
filter = PredictTop;
clobberStructures(indexInBlock);
}
forNode(node.child1()).filter(filter);
forNode(node.child2()).filter(filter);
forNode(nodeIndex).set(PredictBoolean);
break;
}
case CompareStrictEq:
forNode(nodeIndex).set(PredictBoolean);
break;
case StringCharCodeAt:
forNode(node.child1()).filter(PredictString);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
case StringCharAt:
forNode(node.child1()).filter(PredictString);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictString);
break;
case GetByVal: {
if (!node.prediction() || !m_graph[node.child1()].prediction() || !m_graph[node.child2()].prediction()) {
m_isValid = false;
break;
}
if (!isActionableArrayPrediction(m_graph[node.child1()].prediction()) || !m_graph[node.child2()].shouldSpeculateInteger()) {
clobberStructures(indexInBlock);
forNode(nodeIndex).makeTop();
break;
}
if (m_graph[node.child1()].prediction() == PredictString) {
forNode(node.child1()).filter(PredictString);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictString);
break;
}
if (m_graph[node.child1()].shouldSpeculateByteArray()) {
forNode(node.child1()).filter(PredictByteArray);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
if (m_graph[node.child1()].shouldSpeculateInt8Array()) {
forNode(node.child1()).filter(PredictInt8Array);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
if (m_graph[node.child1()].shouldSpeculateInt16Array()) {
forNode(node.child1()).filter(PredictInt16Array);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
if (m_graph[node.child1()].shouldSpeculateInt32Array()) {
forNode(node.child1()).filter(PredictInt32Array);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint8Array()) {
forNode(node.child1()).filter(PredictUint8Array);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint8ClampedArray()) {
forNode(node.child1()).filter(PredictUint8ClampedArray);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint16Array()) {
forNode(node.child1()).filter(PredictUint16Array);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint32Array()) {
forNode(node.child1()).filter(PredictUint32Array);
forNode(node.child2()).filter(PredictInt32);
if (node.shouldSpeculateInteger())
forNode(nodeIndex).set(PredictInt32);
else
forNode(nodeIndex).set(PredictDouble);
break;
}
if (m_graph[node.child1()].shouldSpeculateFloat32Array()) {
forNode(node.child1()).filter(PredictFloat32Array);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictDouble);
break;
}
if (m_graph[node.child1()].shouldSpeculateFloat64Array()) {
forNode(node.child1()).filter(PredictFloat64Array);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).set(PredictDouble);
break;
}
ASSERT(m_graph[node.child1()].shouldSpeculateArray());
forNode(node.child1()).filter(PredictArray);
forNode(node.child2()).filter(PredictInt32);
forNode(nodeIndex).makeTop();
break;
}
case PutByVal:
case PutByValAlias: {
if (!m_graph[node.child1()].prediction() || !m_graph[node.child2()].prediction()) {
m_isValid = false;
break;
}
if (!m_graph[node.child2()].shouldSpeculateInteger() || !isActionableMutableArrayPrediction(m_graph[node.child1()].prediction())) {
ASSERT(node.op() == PutByVal);
clobberStructures(indexInBlock);
forNode(nodeIndex).makeTop();
break;
}
if (m_graph[node.child1()].shouldSpeculateByteArray()) {
forNode(node.child1()).filter(PredictByteArray);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateInt8Array()) {
forNode(node.child1()).filter(PredictInt8Array);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateInt16Array()) {
forNode(node.child1()).filter(PredictInt16Array);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateInt32Array()) {
forNode(node.child1()).filter(PredictInt32Array);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint8Array()) {
forNode(node.child1()).filter(PredictUint8Array);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint8ClampedArray()) {
forNode(node.child1()).filter(PredictUint8ClampedArray);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint16Array()) {
forNode(node.child1()).filter(PredictUint16Array);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint32Array()) {
forNode(node.child1()).filter(PredictUint32Array);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateFloat32Array()) {
forNode(node.child1()).filter(PredictFloat32Array);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
if (m_graph[node.child1()].shouldSpeculateFloat64Array()) {
forNode(node.child1()).filter(PredictFloat64Array);
forNode(node.child2()).filter(PredictInt32);
forNode(node.child3()).filter(PredictNumber);
break;
}
ASSERT(m_graph[node.child1()].shouldSpeculateArray());
forNode(node.child1()).filter(PredictArray);
forNode(node.child2()).filter(PredictInt32);
break;
}
case ArrayPush:
forNode(node.child1()).filter(PredictArray);
forNode(nodeIndex).set(PredictNumber);
break;
case ArrayPop:
forNode(node.child1()).filter(PredictArray);
forNode(nodeIndex).makeTop();
break;
case Jump:
break;
case Branch: {
// There is probably profit to be found in doing sparse conditional constant
// propagation, and to take it one step further, where a variable's value
// is specialized on each direction of a branch. For now, we don't do this.
Node& child = m_graph[node.child1()];
if (isBooleanPrediction(child.prediction()) || !child.prediction())
forNode(node.child1()).filter(PredictBoolean);
else if (child.shouldSpeculateFinalObjectOrOther())
forNode(node.child1()).filter(PredictFinalObject | PredictOther);
else if (child.shouldSpeculateArrayOrOther())
forNode(node.child1()).filter(PredictArray | PredictOther);
else if (child.shouldSpeculateInteger())
forNode(node.child1()).filter(PredictInt32);
else if (child.shouldSpeculateNumber())
forNode(node.child1()).filter(PredictNumber);
break;
}
case Return:
case Throw:
case ThrowReferenceError:
m_isValid = false;
break;
case ToPrimitive: {
Node& child = m_graph[node.child1()];
if (child.shouldSpeculateInteger()) {
forNode(node.child1()).filter(PredictInt32);
forNode(nodeIndex).set(PredictInt32);
break;
}
AbstractValue& source = forNode(node.child1());
AbstractValue& destination = forNode(nodeIndex);
PredictedType type = source.m_type;
if (type & ~(PredictNumber | PredictString | PredictBoolean)) {
type &= (PredictNumber | PredictString | PredictBoolean);
type |= PredictString;
}
destination.set(type);
break;
}
case StrCat:
forNode(nodeIndex).set(PredictString);
break;
case NewArray:
case NewArrayBuffer:
forNode(nodeIndex).set(m_codeBlock->globalObject()->arrayStructure());
m_haveStructures = true;
break;
case NewRegexp:
forNode(nodeIndex).set(m_codeBlock->globalObject()->regExpStructure());
m_haveStructures = true;
break;
case ConvertThis: {
Node& child = m_graph[node.child1()];
AbstractValue& source = forNode(node.child1());
AbstractValue& destination = forNode(nodeIndex);
if (isObjectPrediction(source.m_type)) {
// This is the simple case. We already know that the source is an
// object, so there's nothing to do. I don't think this case will
// be hit, but then again, you never know.
destination = source;
break;
}
if (isOtherPrediction(child.prediction())) {
source.filter(PredictOther);
destination.set(PredictObjectOther);
break;
}
if (isObjectPrediction(child.prediction())) {
source.filter(PredictObjectMask);
destination = source;
break;
}
destination = source;
destination.merge(PredictObjectOther);
break;
}
case CreateThis: {
Node& child = m_graph[node.child1()];
AbstractValue& source = forNode(node.child1());
AbstractValue& destination = forNode(nodeIndex);
if (child.shouldSpeculateFinalObject())
source.filter(PredictFinalObject);
destination.set(PredictFinalObject);
break;
}
case NewObject:
forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->emptyObjectStructure());
m_haveStructures = true;
break;
case CreateActivation:
forNode(nodeIndex).set(m_graph.m_globalData.activationStructure.get());
m_haveStructures = true;
break;
case TearOffActivation:
// Does nothing that is user-visible.
break;
case NewFunction:
case NewFunctionExpression:
case NewFunctionNoCheck:
forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->functionStructure());
break;
case GetCallee:
forNode(nodeIndex).set(PredictFunction);
break;
case GetScopeChain:
forNode(nodeIndex).set(PredictCellOther);
break;
case GetScopedVar:
forNode(nodeIndex).makeTop();
break;
case PutScopedVar:
clobberStructures(indexInBlock);
break;
case GetById:
case GetByIdFlush:
if (!node.prediction()) {
m_isValid = false;
break;
}
if (isCellPrediction(m_graph[node.child1()].prediction()))
forNode(node.child1()).filter(PredictCell);
clobberStructures(indexInBlock);
forNode(nodeIndex).makeTop();
break;
case GetArrayLength:
forNode(node.child1()).filter(PredictArray);
forNode(nodeIndex).set(PredictInt32);
break;
case GetStringLength:
forNode(node.child1()).filter(PredictString);
forNode(nodeIndex).set(PredictInt32);
break;
case GetByteArrayLength:
forNode(node.child1()).filter(PredictByteArray);
forNode(nodeIndex).set(PredictInt32);
break;
case GetInt8ArrayLength:
forNode(node.child1()).filter(PredictInt8Array);
forNode(nodeIndex).set(PredictInt32);
break;
case GetInt16ArrayLength:
forNode(node.child1()).filter(PredictInt16Array);
forNode(nodeIndex).set(PredictInt32);
break;
case GetInt32ArrayLength:
forNode(node.child1()).filter(PredictInt32Array);
forNode(nodeIndex).set(PredictInt32);
break;
case GetUint8ArrayLength:
forNode(node.child1()).filter(PredictUint8Array);
forNode(nodeIndex).set(PredictInt32);
break;
case GetUint8ClampedArrayLength:
forNode(node.child1()).filter(PredictUint8ClampedArray);
forNode(nodeIndex).set(PredictInt32);
break;
case GetUint16ArrayLength:
forNode(node.child1()).filter(PredictUint16Array);
forNode(nodeIndex).set(PredictInt32);
break;
case GetUint32ArrayLength:
forNode(node.child1()).filter(PredictUint32Array);
forNode(nodeIndex).set(PredictInt32);
break;
case GetFloat32ArrayLength:
forNode(node.child1()).filter(PredictFloat32Array);
forNode(nodeIndex).set(PredictInt32);
break;
case GetFloat64ArrayLength:
forNode(node.child1()).filter(PredictFloat64Array);
forNode(nodeIndex).set(PredictInt32);
break;
case CheckStructure:
// FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes).
forNode(node.child1()).filter(node.structureSet());
m_haveStructures = true;
break;
case PutStructure:
clobberStructures(indexInBlock);
forNode(node.child1()).set(node.structureTransitionData().newStructure);
m_haveStructures = true;
break;
case GetPropertyStorage:
forNode(node.child1()).filter(PredictCell);
forNode(nodeIndex).clear(); // The result is not a JS value.
break;
case GetIndexedPropertyStorage: {
PredictedType basePrediction = m_graph[node.child2()].prediction();
if (!(basePrediction & PredictInt32) && basePrediction) {
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].prediction() == PredictString) {
forNode(node.child1()).filter(PredictString);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateByteArray()) {
forNode(node.child1()).filter(PredictByteArray);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateInt8Array()) {
forNode(node.child1()).filter(PredictInt8Array);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateInt16Array()) {
forNode(node.child1()).filter(PredictInt16Array);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateInt32Array()) {
forNode(node.child1()).filter(PredictInt32Array);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateUint8Array()) {
forNode(node.child1()).filter(PredictUint8Array);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateUint8ClampedArray()) {
forNode(node.child1()).filter(PredictUint8ClampedArray);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateUint16Array()) {
forNode(node.child1()).filter(PredictUint16Array);
forNode(nodeIndex).set(PredictOther);
break;
}
if (m_graph[node.child1()].shouldSpeculateUint32Array()) {
forNode(node.child1()).filter(PredictUint32Array);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateFloat32Array()) {
forNode(node.child1()).filter(PredictFloat32Array);
forNode(nodeIndex).clear();
break;
}
if (m_graph[node.child1()].shouldSpeculateFloat64Array()) {
forNode(node.child1()).filter(PredictFloat64Array);
forNode(nodeIndex).clear();
break;
}
forNode(node.child1()).filter(PredictArray);
forNode(nodeIndex).clear();
break;
}
case GetByOffset:
forNode(node.child1()).filter(PredictCell);
forNode(nodeIndex).makeTop();
break;
case PutByOffset:
forNode(node.child1()).filter(PredictCell);
break;
case CheckFunction:
forNode(node.child1()).filter(PredictFunction);
// FIXME: Should be able to propagate the fact that we know what the function is.
break;
case PutById:
case PutByIdDirect:
forNode(node.child1()).filter(PredictCell);
clobberStructures(indexInBlock);
break;
case GetGlobalVar:
forNode(nodeIndex).makeTop();
break;
case PutGlobalVar:
break;
case CheckHasInstance:
forNode(node.child1()).filter(PredictCell);
// Sadly, we don't propagate the fact that we've done CheckHasInstance
break;
case InstanceOf:
// Again, sadly, we don't propagate the fact that we've done InstanceOf
if (!(m_graph[node.child1()].prediction() & ~PredictCell) && !(forNode(node.child1()).m_type & ~PredictCell))
forNode(node.child1()).filter(PredictCell);
forNode(node.child3()).filter(PredictCell);
forNode(nodeIndex).set(PredictBoolean);
break;
case Phi:
case Flush:
break;
case Breakpoint:
break;
case Call:
case Construct:
case Resolve:
case ResolveBase:
case ResolveBaseStrictPut:
case ResolveGlobal:
clobberStructures(indexInBlock);
forNode(nodeIndex).makeTop();
break;
case ForceOSRExit:
m_isValid = false;
break;
case Phantom:
case InlineStart:
case Nop:
break;
case LastNodeType:
ASSERT_NOT_REACHED();
break;
}
return m_isValid;
}
inline void AbstractState::clobberStructures(unsigned indexInBlock)
{
PROFILE(FLAG_FOR_STRUCTURE_CLOBBERING);
if (!m_haveStructures)
return;
for (size_t i = indexInBlock + 1; i--;)
forNode(m_block->at(i)).clobberStructures();
for (size_t i = 0; i < m_variables.numberOfArguments(); ++i)
m_variables.argument(i).clobberStructures();
for (size_t i = 0; i < m_variables.numberOfLocals(); ++i)
m_variables.local(i).clobberStructures();
m_haveStructures = false;
}
inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, NodeIndex nodeIndex)
{
if (nodeIndex == NoNode)
return false;
AbstractValue source;
Node& node = m_graph[nodeIndex];
if (!node.refCount())
return false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" It's live, node @%u.\n", nodeIndex);
#endif
switch (node.op()) {
case Phi:
case SetArgument:
case Flush:
// The block transfers the value from head to tail.
source = inVariable;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" Transfering from head to tail.\n");
#endif
break;
case GetLocal:
// The block refines the value with additional speculations.
source = forNode(nodeIndex);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" Refining.\n");
#endif
break;
case SetLocal:
// The block sets the variable, and potentially refines it, both
// before and after setting it.
if (node.variableAccessData()->shouldUseDoubleFormat())
source.set(PredictDouble);
else
source = forNode(node.child1());
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" Setting.\n");
#endif
break;
default:
ASSERT_NOT_REACHED();
break;
}
if (destination == source) {
// Abstract execution did not change the output value of the variable, for this
// basic block, on this iteration.
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" Not changed!\n");
#endif
return false;
}
// Abstract execution reached a new conclusion about the speculations reached about
// this variable after execution of this basic block. Update the state, and return
// true to indicate that the fixpoint must go on!
destination = source;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" Changed!\n");
#endif
return true;
}
inline bool AbstractState::merge(BasicBlock* from, BasicBlock* to)
{
ASSERT(from->variablesAtTail.numberOfArguments() == to->variablesAtHead.numberOfArguments());
ASSERT(from->variablesAtTail.numberOfLocals() == to->variablesAtHead.numberOfLocals());
bool changed = false;
for (size_t argument = 0; argument < from->variablesAtTail.numberOfArguments(); ++argument) {
AbstractValue& destination = to->valuesAtHead.argument(argument);
if (m_graph.argumentIsCaptured(argument)) {
if (destination.isTop())
continue;
destination.makeTop();
changed = true;
continue;
}
changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.argument(argument), to->variablesAtHead.argument(argument), from->variablesAtTail.argument(argument));
}
for (size_t local = 0; local < from->variablesAtTail.numberOfLocals(); ++local) {
AbstractValue& destination = to->valuesAtHead.local(local);
if (m_graph.localIsCaptured(local)) {
if (destination.isTop())
continue;
destination.makeTop();
changed = true;
continue;
}
changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.local(local), to->variablesAtHead.local(local), from->variablesAtTail.local(local));
}
if (!to->cfaHasVisited)
changed = true;
to->cfaShouldRevisit |= changed;
return changed;
}
inline bool AbstractState::mergeToSuccessors(Graph& graph, BasicBlock* basicBlock)
{
PROFILE(FLAG_FOR_MERGE_TO_SUCCESSORS);
Node& terminal = graph[basicBlock->last()];
ASSERT(terminal.isTerminal());
switch (terminal.op()) {
case Jump:
return merge(basicBlock, graph.m_blocks[terminal.takenBlockIndex()].get());
case Branch:
return merge(basicBlock, graph.m_blocks[terminal.takenBlockIndex()].get())
| merge(basicBlock, graph.m_blocks[terminal.notTakenBlockIndex()].get());
case Return:
case Throw:
case ThrowReferenceError:
return false;
default:
ASSERT_NOT_REACHED();
return false;
}
}
inline bool AbstractState::mergeVariableBetweenBlocks(AbstractValue& destination, AbstractValue& source, NodeIndex destinationNodeIndex, NodeIndex sourceNodeIndex)
{
if (destinationNodeIndex == NoNode)
return false;
ASSERT_UNUSED(sourceNodeIndex, sourceNodeIndex != NoNode);
// FIXME: We could do some sparse conditional propagation here!
return destination.merge(source);
}
#ifndef NDEBUG
void AbstractState::dump(FILE* out)
{
bool first = true;
for (size_t i = 0; i < m_block->size(); ++i) {
NodeIndex index = m_block->at(i);
AbstractValue& value = m_nodes[index];
if (value.isClear())
continue;
if (first)
first = false;
else
fprintf(out, " ");
fprintf(out, "@%lu:", static_cast<unsigned long>(index));
value.dump(out);
}
}
#endif
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)