blob: 990472954ccf42053ea8f3fc085c9221049d5cc7 [file] [log] [blame]
/*
* Copyright (C) 2013 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"
#if ENABLE(DFG_JIT)
#include "DFGInPlaceAbstractState.h"
#include "CodeBlock.h"
#include "DFGBasicBlock.h"
#include "GetByIdStatus.h"
#include "Operations.h"
#include "PutByIdStatus.h"
#include "StringObject.h"
namespace JSC { namespace DFG {
InPlaceAbstractState::InPlaceAbstractState(Graph& graph)
: m_graph(graph)
, m_variables(m_graph.m_codeBlock->numParameters(), graph.m_localVars)
, m_block(0)
{
}
InPlaceAbstractState::~InPlaceAbstractState() { }
void InPlaceAbstractState::beginBasicBlock(BasicBlock* basicBlock)
{
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++)
forNode(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).hasClobberableState()) {
m_haveStructures = true;
break;
}
}
for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) {
if (m_variables.local(i).hasClobberableState()) {
m_haveStructures = true;
break;
}
}
if (m_graph.m_form == SSA) {
HashMap<Node*, AbstractValue>::iterator iter = basicBlock->ssa->valuesAtHead.begin();
HashMap<Node*, AbstractValue>::iterator end = basicBlock->ssa->valuesAtHead.end();
for (; iter != end; ++iter) {
forNode(iter->key) = iter->value;
if (iter->value.hasClobberableState())
m_haveStructures = true;
}
}
basicBlock->cfaShouldRevisit = false;
basicBlock->cfaHasVisited = true;
m_block = basicBlock;
m_isValid = true;
m_foundConstants = false;
m_branchDirection = InvalidBranchDirection;
}
static void setLiveValues(HashMap<Node*, AbstractValue>& values, HashSet<Node*>& live)
{
values.clear();
HashSet<Node*>::iterator iter = live.begin();
HashSet<Node*>::iterator end = live.end();
for (; iter != end; ++iter)
values.add(*iter, AbstractValue());
}
void InPlaceAbstractState::initialize()
{
BasicBlock* root = m_graph.block(0);
root->cfaShouldRevisit = true;
root->cfaHasVisited = false;
root->cfaFoundConstants = false;
for (size_t i = 0; i < root->valuesAtHead.numberOfArguments(); ++i) {
root->valuesAtTail.argument(i).clear();
if (m_graph.m_form == SSA) {
root->valuesAtHead.argument(i).makeHeapTop();
continue;
}
Node* node = root->variablesAtHead.argument(i);
ASSERT(node->op() == SetArgument);
if (!node->variableAccessData()->shouldUnboxIfPossible()) {
root->valuesAtHead.argument(i).makeHeapTop();
continue;
}
SpeculatedType prediction =
node->variableAccessData()->argumentAwarePrediction();
if (isInt32Speculation(prediction))
root->valuesAtHead.argument(i).setType(SpecInt32);
else if (isBooleanSpeculation(prediction))
root->valuesAtHead.argument(i).setType(SpecBoolean);
else if (isCellSpeculation(prediction))
root->valuesAtHead.argument(i).setType(SpecCell);
else
root->valuesAtHead.argument(i).makeHeapTop();
}
for (size_t i = 0; i < root->valuesAtHead.numberOfLocals(); ++i) {
Node* node = root->variablesAtHead.local(i);
if (node && node->variableAccessData()->isCaptured())
root->valuesAtHead.local(i).makeHeapTop();
else
root->valuesAtHead.local(i).clear();
root->valuesAtTail.local(i).clear();
}
for (BlockIndex blockIndex = 1 ; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
continue;
ASSERT(block->isReachable);
block->cfaShouldRevisit = false;
block->cfaHasVisited = false;
block->cfaFoundConstants = false;
for (size_t i = 0; i < block->valuesAtHead.numberOfArguments(); ++i) {
block->valuesAtHead.argument(i).clear();
block->valuesAtTail.argument(i).clear();
}
for (size_t i = 0; i < block->valuesAtHead.numberOfLocals(); ++i) {
block->valuesAtHead.local(i).clear();
block->valuesAtTail.local(i).clear();
}
if (!block->isOSRTarget)
continue;
if (block->bytecodeBegin != m_graph.m_plan.osrEntryBytecodeIndex)
continue;
for (size_t i = 0; i < m_graph.m_mustHandleAbstractValues.size(); ++i) {
AbstractValue value = m_graph.m_mustHandleAbstractValues[i];
int operand = m_graph.m_mustHandleAbstractValues.operandForIndex(i);
block->valuesAtHead.operand(operand).merge(value);
}
block->cfaShouldRevisit = true;
}
if (m_graph.m_form == SSA) {
for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
BasicBlock* block = m_graph.block(blockIndex);
if (!block)
continue;
setLiveValues(block->ssa->valuesAtHead, block->ssa->liveAtHead);
setLiveValues(block->ssa->valuesAtTail, block->ssa->liveAtTail);
}
}
}
bool InPlaceAbstractState::endBasicBlock(MergeMode mergeMode)
{
ASSERT(m_block);
BasicBlock* block = m_block; // Save the block for successor merging.
block->cfaFoundConstants = m_foundConstants;
block->cfaDidFinish = m_isValid;
block->cfaBranchDirection = m_branchDirection;
if (!m_isValid) {
reset();
return false;
}
bool changed = false;
if (mergeMode != DontMerge || !ASSERT_DISABLED) {
switch (m_graph.m_form) {
case ThreadedCPS: {
for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) {
AbstractValue& destination = block->valuesAtTail.argument(argument);
changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument));
}
for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) {
AbstractValue& destination = block->valuesAtTail.local(local);
changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local));
}
break;
}
case SSA: {
for (size_t i = 0; i < block->valuesAtTail.size(); ++i)
changed |= block->valuesAtTail[i].merge(m_variables[i]);
HashSet<Node*>::iterator iter = block->ssa->liveAtTail.begin();
HashSet<Node*>::iterator end = block->ssa->liveAtTail.end();
for (; iter != end; ++iter) {
Node* node = *iter;
changed |= block->ssa->valuesAtTail.find(node)->value.merge(forNode(node));
}
break;
}
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
ASSERT(mergeMode != DontMerge || !changed);
reset();
if (mergeMode != MergeToSuccessors)
return changed;
return mergeToSuccessors(block);
}
void InPlaceAbstractState::reset()
{
m_block = 0;
m_isValid = false;
m_branchDirection = InvalidBranchDirection;
}
bool InPlaceAbstractState::mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, Node* node)
{
if (!node)
return false;
AbstractValue source;
if (node->variableAccessData()->isCaptured()) {
// If it's captured then we know that whatever value was stored into the variable last is the
// one we care about. This is true even if the variable at tail is dead, which might happen if
// the last thing we did to the variable was a GetLocal and then ended up now using the
// GetLocal's result.
source = inVariable;
} else {
switch (node->op()) {
case Phi:
case SetArgument:
case PhantomLocal:
case Flush:
// The block transfers the value from head to tail.
source = inVariable;
break;
case GetLocal:
// The block refines the value with additional speculations.
source = forNode(node);
break;
case SetLocal:
// The block sets the variable, and potentially refines it, both
// before and after setting it.
source = forNode(node->child1());
if (node->variableAccessData()->flushFormat() == FlushedDouble) {
ASSERT(!(source.m_type & ~SpecFullNumber));
ASSERT(!!(source.m_type & ~SpecDouble) == !!(source.m_type & SpecMachineInt));
if (!(source.m_type & ~SpecDouble)) {
source.merge(SpecInt52AsDouble);
source.filter(SpecDouble);
}
}
break;
default:
RELEASE_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.
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;
return true;
}
bool InPlaceAbstractState::merge(BasicBlock* from, BasicBlock* to)
{
ASSERT(from->variablesAtTail.numberOfArguments() == to->variablesAtHead.numberOfArguments());
ASSERT(from->variablesAtTail.numberOfLocals() == to->variablesAtHead.numberOfLocals());
bool changed = false;
switch (m_graph.m_form) {
case ThreadedCPS: {
for (size_t argument = 0; argument < from->variablesAtTail.numberOfArguments(); ++argument) {
AbstractValue& destination = to->valuesAtHead.argument(argument);
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);
changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.local(local), to->variablesAtHead.local(local), from->variablesAtTail.local(local));
}
break;
}
case SSA: {
for (size_t i = from->valuesAtTail.size(); i--;)
changed |= to->valuesAtHead[i].merge(from->valuesAtTail[i]);
HashSet<Node*>::iterator iter = to->ssa->liveAtHead.begin();
HashSet<Node*>::iterator end = to->ssa->liveAtHead.end();
for (; iter != end; ++iter) {
Node* node = *iter;
changed |= to->ssa->valuesAtHead.find(node)->value.merge(
from->ssa->valuesAtTail.find(node)->value);
}
break;
}
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
if (!to->cfaHasVisited)
changed = true;
to->cfaShouldRevisit |= changed;
return changed;
}
inline bool InPlaceAbstractState::mergeToSuccessors(BasicBlock* basicBlock)
{
Node* terminal = basicBlock->last();
ASSERT(terminal->isTerminal());
switch (terminal->op()) {
case Jump: {
ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
return merge(basicBlock, terminal->takenBlock());
}
case Branch: {
ASSERT(basicBlock->cfaBranchDirection != InvalidBranchDirection);
bool changed = false;
if (basicBlock->cfaBranchDirection != TakeFalse)
changed |= merge(basicBlock, terminal->takenBlock());
if (basicBlock->cfaBranchDirection != TakeTrue)
changed |= merge(basicBlock, terminal->notTakenBlock());
return changed;
}
case Switch: {
// FIXME: It would be cool to be sparse conditional for Switch's. Currently
// we're not. However I somehow doubt that this will ever be a big deal.
ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
SwitchData* data = terminal->switchData();
bool changed = merge(basicBlock, data->fallThrough);
for (unsigned i = data->cases.size(); i--;)
changed |= merge(basicBlock, data->cases[i].target);
return changed;
}
case Return:
case Unreachable:
ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
return false;
default:
RELEASE_ASSERT_NOT_REACHED();
return false;
}
}
inline bool InPlaceAbstractState::mergeVariableBetweenBlocks(AbstractValue& destination, AbstractValue& source, Node* destinationNode, Node* sourceNode)
{
if (!destinationNode)
return false;
ASSERT_UNUSED(sourceNode, sourceNode);
// FIXME: We could do some sparse conditional propagation here!
return destination.merge(source);
}
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)