summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp898
1 files changed, 631 insertions, 267 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index 39ac2ff7a..e5be8efb5 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -28,13 +28,16 @@
#if ENABLE(DFG_JIT)
-#include "DFGAbstractState.h"
-#include "DFGBasicBlock.h"
+#include "DFGAbstractInterpreterInlines.h"
+#include "DFGArgumentsUtilities.h"
+#include "DFGBasicBlockInlines.h"
#include "DFGGraph.h"
+#include "DFGInPlaceAbstractState.h"
+#include "DFGInferredTypeCheck.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "GetByIdStatus.h"
-#include "Operations.h"
+#include "JSCInlines.h"
#include "PutByIdStatus.h"
namespace JSC { namespace DFG {
@@ -44,6 +47,7 @@ public:
ConstantFoldingPhase(Graph& graph)
: Phase(graph, "constant folding")
, m_state(graph)
+ , m_interpreter(graph, m_state)
, m_insertionSet(graph)
{
}
@@ -51,27 +55,59 @@ public:
bool run()
{
bool changed = false;
-
- for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
- BasicBlock* block = m_graph.m_blocks[blockIndex].get();
- if (!block)
- continue;
- if (!block->cfaDidFinish)
- changed |= paintUnreachableCode(blockIndex);
+
+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
if (block->cfaFoundConstants)
- changed |= foldConstants(blockIndex);
+ changed |= foldConstants(block);
}
+ if (changed && m_graph.m_form == SSA) {
+ // It's now possible that we have Upsilons pointed at JSConstants. Fix that.
+ for (BasicBlock* block : m_graph.blocksInNaturalOrder())
+ fixUpsilons(block);
+ }
+
+ if (m_graph.m_form == SSA) {
+ // It's now possible to simplify basic blocks by placing an Unreachable terminator right
+ // after anything that invalidates AI.
+ bool didClipBlock = false;
+ for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
+ m_state.beginBasicBlock(block);
+ for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) {
+ if (block->at(nodeIndex)->isTerminal()) {
+ // It's possible that we have something after the terminal. It could be a
+ // no-op Check node, for example. We don't want the logic below to turn that
+ // node into Unreachable, since then we'd have two terminators.
+ break;
+ }
+ if (!m_state.isValid()) {
+ NodeOrigin origin = block->at(nodeIndex)->origin;
+ for (unsigned killIndex = nodeIndex; killIndex < block->size(); ++killIndex)
+ m_graph.m_allocator.free(block->at(killIndex));
+ block->resize(nodeIndex);
+ block->appendNode(m_graph, SpecNone, Unreachable, origin);
+ didClipBlock = true;
+ break;
+ }
+ m_interpreter.execute(nodeIndex);
+ }
+ m_state.reset();
+ }
+
+ if (didClipBlock) {
+ changed = true;
+ m_graph.invalidateCFG();
+ m_graph.resetReachability();
+ m_graph.killUnreachableBlocks();
+ }
+ }
+
return changed;
}
private:
- bool foldConstants(BlockIndex blockIndex)
+ bool foldConstants(BasicBlock* block)
{
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF("Constant folding considering Block #%u.\n", blockIndex);
-#endif
- BasicBlock* block = m_graph.m_blocks[blockIndex].get();
bool changed = false;
m_state.beginBasicBlock(block);
for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
@@ -80,21 +116,26 @@ private:
Node* node = block->at(indexInBlock);
+ bool alreadyHandled = false;
bool eliminated = false;
switch (node->op()) {
- case CheckArgumentsNotCreated: {
- if (!isEmptySpeculation(
- m_state.variables().operand(
- m_graph.argumentsRegisterFor(node->codeOrigin)).m_type))
- break;
- node->convertToPhantom();
- eliminated = true;
+ case BooleanToNumber: {
+ if (node->child1().useKind() == UntypedUse
+ && !m_interpreter.needsTypeCheck(node->child1(), SpecBoolean))
+ node->child1().setUseKind(BooleanUse);
break;
}
-
+
+ case CompareEq: {
+ if (!m_interpreter.needsTypeCheck(node->child1(), SpecOther))
+ node->child1().setUseKind(OtherUse);
+ if (!m_interpreter.needsTypeCheck(node->child2(), SpecOther))
+ node->child2().setUseKind(OtherUse);
+ break;
+ }
+
case CheckStructure:
- case ForwardCheckStructure:
case ArrayifyToStructure: {
AbstractValue& value = m_state.forNode(node->child1());
StructureSet set;
@@ -102,20 +143,70 @@ private:
set = node->structure();
else
set = node->structureSet();
- if (value.m_currentKnownStructure.isSubsetOf(set)) {
- m_state.execute(indexInBlock); // Catch the fact that we may filter on cell.
- node->convertToPhantom();
+ if (value.m_structure.isSubsetOf(set)) {
+ m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell.
+ node->remove();
eliminated = true;
break;
}
- StructureAbstractValue& structureValue = value.m_futurePossibleStructure;
- if (structureValue.isSubsetOf(set)
- && structureValue.hasSingleton()) {
- Structure* structure = structureValue.singleton();
- m_state.execute(indexInBlock); // Catch the fact that we may filter on cell.
- node->convertToStructureTransitionWatchpoint(structure);
- eliminated = true;
+ break;
+ }
+
+ case GetIndexedPropertyStorage: {
+ JSArrayBufferView* view = m_graph.tryGetFoldableView(
+ m_state.forNode(node->child1()).m_value, node->arrayMode());
+ if (!view)
break;
+
+ if (view->mode() == FastTypedArray) {
+ // FIXME: It would be awesome to be able to fold the property storage for
+ // these GC-allocated typed arrays. For now it doesn't matter because the
+ // most common use-cases for constant typed arrays involve large arrays with
+ // aliased buffer views.
+ // https://bugs.webkit.org/show_bug.cgi?id=125425
+ break;
+ }
+
+ m_interpreter.execute(indexInBlock);
+ eliminated = true;
+
+ m_insertionSet.insertCheck(indexInBlock, node->origin, node->children);
+ node->convertToConstantStoragePointer(view->vector());
+ break;
+ }
+
+ case CheckStructureImmediate: {
+ AbstractValue& value = m_state.forNode(node->child1());
+ StructureSet& set = node->structureSet();
+
+ if (value.value()) {
+ if (Structure* structure = jsDynamicCast<Structure*>(value.value())) {
+ if (set.contains(structure)) {
+ m_interpreter.execute(indexInBlock);
+ node->remove();
+ eliminated = true;
+ break;
+ }
+ }
+ }
+
+ if (PhiChildren* phiChildren = m_interpreter.phiChildren()) {
+ bool allGood = true;
+ phiChildren->forAllTransitiveIncomingValues(
+ node,
+ [&] (Node* incoming) {
+ if (Structure* structure = incoming->dynamicCastConstant<Structure*>()) {
+ if (set.contains(structure))
+ return;
+ }
+ allGood = false;
+ });
+ if (allGood) {
+ m_interpreter.execute(indexInBlock);
+ node->remove();
+ eliminated = true;
+ break;
+ }
}
break;
}
@@ -124,249 +215,406 @@ private:
case Arrayify: {
if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1())))
break;
- node->convertToPhantom();
+ node->remove();
eliminated = true;
break;
}
- case CheckFunction: {
- if (m_state.forNode(node->child1()).value() != node->function())
+ case PutStructure: {
+ if (m_state.forNode(node->child1()).m_structure.onlyStructure() != node->transition()->next)
break;
- node->convertToPhantom();
+
+ node->remove();
eliminated = true;
break;
}
- case GetById:
- case GetByIdFlush: {
- CodeOrigin codeOrigin = node->codeOrigin;
- Edge childEdge = node->child1();
- Node* child = childEdge.node();
- unsigned identifierNumber = node->identifierNumber();
-
- if (childEdge.useKind() != CellUse)
+ case CheckCell: {
+ if (m_state.forNode(node->child1()).value() != node->cellOperand()->value())
+ break;
+ node->remove();
+ eliminated = true;
+ break;
+ }
+
+ case CheckNotEmpty: {
+ if (m_state.forNode(node->child1()).m_type & SpecEmpty)
+ break;
+ node->remove();
+ eliminated = true;
+ break;
+ }
+
+ case CheckIdent: {
+ UniquedStringImpl* uid = node->uidOperand();
+ const UniquedStringImpl* constantUid = nullptr;
+
+ JSValue childConstant = m_state.forNode(node->child1()).value();
+ if (childConstant) {
+ if (uid->isSymbol()) {
+ if (childConstant.isSymbol())
+ constantUid = asSymbol(childConstant)->privateName().uid();
+ } else {
+ if (childConstant.isString()) {
+ if (const auto* impl = asString(childConstant)->tryGetValueImpl()) {
+ // Edge filtering requires that a value here should be StringIdent.
+ // However, a constant value propagated in DFG is not filtered.
+ // So here, we check the propagated value is actually an atomic string.
+ // And if it's not, we just ignore.
+ if (impl->isAtomic())
+ constantUid = static_cast<const UniquedStringImpl*>(impl);
+ }
+ }
+ }
+ }
+
+ if (constantUid == uid) {
+ node->remove();
+ eliminated = true;
+ }
+ break;
+ }
+
+ case CheckInBounds: {
+ JSValue left = m_state.forNode(node->child1()).value();
+ JSValue right = m_state.forNode(node->child2()).value();
+ if (left && right && left.isInt32() && right.isInt32()
+ && static_cast<uint32_t>(left.asInt32()) < static_cast<uint32_t>(right.asInt32())) {
+ node->remove();
+ eliminated = true;
break;
+ }
- Structure* structure = m_state.forNode(child).bestProvenStructure();
- if (!structure)
+ break;
+ }
+
+ case GetMyArgumentByVal: {
+ JSValue index = m_state.forNode(node->child2()).value();
+ if (!index || !index.isInt32())
break;
- bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
- bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
+ Node* arguments = node->child1().node();
+ InlineCallFrame* inlineCallFrame = arguments->origin.semantic.inlineCallFrame;
+
+ // Don't try to do anything if the index is known to be outside our static bounds. Note
+ // that our static bounds are usually strictly larger than the dynamic bounds. The
+ // exception is something like this, assuming foo() is not inlined:
+ //
+ // function foo() { return arguments[5]; }
+ //
+ // Here the static bound on number of arguments is 0, and we're accessing index 5. We
+ // will not strength-reduce this to GetStack because GetStack is otherwise assumed by the
+ // compiler to access those variables that are statically accounted for; for example if
+ // we emitted a GetStack on arg6 we would have out-of-bounds access crashes anywhere that
+ // uses an Operands<> map. There is not much cost to continuing to use a
+ // GetMyArgumentByVal in such statically-out-of-bounds accesses; we just lose CFA unless
+ // GCSE removes the access entirely.
+ if (inlineCallFrame) {
+ if (index.asUInt32() >= inlineCallFrame->arguments.size() - 1)
+ break;
+ } else {
+ if (index.asUInt32() >= m_state.variables().numberOfArguments() - 1)
+ break;
+ }
+
+ m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
- GetByIdStatus status = GetByIdStatus::computeFor(
- vm(), structure, codeBlock()->identifier(identifierNumber));
+ StackAccessData* data;
+ if (inlineCallFrame) {
+ data = m_graph.m_stackAccessData.add(
+ VirtualRegister(
+ inlineCallFrame->stackOffset +
+ CallFrame::argumentOffset(index.asInt32())),
+ FlushedJSValue);
+ } else {
+ data = m_graph.m_stackAccessData.add(
+ virtualRegisterForArgument(index.asInt32() + 1), FlushedJSValue);
+ }
- if (!status.isSimple()) {
- // FIXME: We could handle prototype cases.
- // https://bugs.webkit.org/show_bug.cgi?id=110386
+ if (inlineCallFrame && !inlineCallFrame->isVarargs()
+ && index.asUInt32() < inlineCallFrame->arguments.size() - 1) {
+ node->convertToGetStack(data);
+ eliminated = true;
break;
}
- ASSERT(status.structureSet().size() == 1);
- ASSERT(status.chain().isEmpty());
- ASSERT(status.structureSet().singletonStructure() == structure);
-
- // Now before we do anything else, push the CFA forward over the GetById
- // and make sure we signal to the loop that it should continue and not
- // do any eliminations.
- m_state.execute(indexInBlock);
+ Node* length = emitCodeToGetArgumentsArrayLength(
+ m_insertionSet, arguments, indexInBlock, node->origin);
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, CheckInBounds, node->origin,
+ node->child2(), Edge(length, Int32Use));
+ node->convertToGetStack(data);
eliminated = true;
+ break;
+ }
+
+ case MultiGetByOffset: {
+ Edge baseEdge = node->child1();
+ Node* base = baseEdge.node();
+ MultiGetByOffsetData& data = node->multiGetByOffsetData();
+
+ // First prune the variants, then check if the MultiGetByOffset can be
+ // strength-reduced to a GetByOffset.
+
+ AbstractValue baseValue = m_state.forNode(base);
+
+ m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+ alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+
+ for (unsigned i = 0; i < data.cases.size(); ++i) {
+ MultiGetByOffsetCase& getCase = data.cases[i];
+ getCase.set().filter(baseValue);
+ if (getCase.set().isEmpty()) {
+ data.cases[i--] = data.cases.last();
+ data.cases.removeLast();
+ changed = true;
+ }
+ }
+
+ if (data.cases.size() != 1)
+ break;
+
+ emitGetByOffset(indexInBlock, node, baseValue, data.cases[0], data.identifierNumber);
+ changed = true;
+ break;
+ }
- if (needsWatchpoint) {
- ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
- OpInfo(structure), childEdge);
- } else if (needsCellCheck) {
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
+ case MultiPutByOffset: {
+ Edge baseEdge = node->child1();
+ Node* base = baseEdge.node();
+ MultiPutByOffsetData& data = node->multiPutByOffsetData();
+
+ AbstractValue baseValue = m_state.forNode(base);
+
+ m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+ alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+
+
+ for (unsigned i = 0; i < data.variants.size(); ++i) {
+ PutByIdVariant& variant = data.variants[i];
+ variant.oldStructure().filter(baseValue);
+
+ if (variant.oldStructure().isEmpty()) {
+ data.variants[i--] = data.variants.last();
+ data.variants.removeLast();
+ changed = true;
+ continue;
+ }
+
+ if (variant.kind() == PutByIdVariant::Transition
+ && variant.oldStructure().onlyStructure() == variant.newStructure()) {
+ variant = PutByIdVariant::replace(
+ variant.oldStructure(),
+ variant.offset(),
+ variant.requiredType());
+ changed = true;
+ }
}
+
+ if (data.variants.size() != 1)
+ break;
+
+ emitPutByOffset(
+ indexInBlock, node, baseValue, data.variants[0], data.identifierNumber);
+ changed = true;
+ break;
+ }
+
+ case GetById:
+ case GetByIdFlush: {
+ Edge childEdge = node->child1();
+ Node* child = childEdge.node();
+ unsigned identifierNumber = node->identifierNumber();
- childEdge.setUseKind(KnownCellUse);
+ AbstractValue baseValue = m_state.forNode(child);
+
+ m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+ alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+
+ if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered()
+ || (node->child1().useKind() == UntypedUse || (baseValue.m_type & ~SpecCell)))
+ break;
- Edge propertyStorage;
+ GetByIdStatus status = GetByIdStatus::computeFor(
+ baseValue.m_structure.set(), m_graph.identifiers()[identifierNumber]);
+ if (!status.isSimple())
+ break;
- if (isInlineOffset(status.offset()))
- propertyStorage = childEdge;
- else {
- propertyStorage = Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
+ for (unsigned i = status.numVariants(); i--;) {
+ if (!status[i].conditionSet().isEmpty()) {
+ // FIXME: We could handle prototype cases.
+ // https://bugs.webkit.org/show_bug.cgi?id=110386
+ break;
+ }
}
- node->convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
+ if (status.numVariants() == 1) {
+ emitGetByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
+ changed = true;
+ break;
+ }
- StorageAccessData storageAccessData;
- storageAccessData.offset = indexRelativeToBase(status.offset());
- storageAccessData.identifierNumber = identifierNumber;
- m_graph.m_storageAccessData.append(storageAccessData);
+ if (!isFTL(m_graph.m_plan.mode))
+ break;
+
+ MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add();
+ for (const GetByIdVariant& variant : status.variants()) {
+ data->cases.append(
+ MultiGetByOffsetCase(
+ variant.structureSet(),
+ GetByOffsetMethod::load(variant.offset())));
+ }
+ data->identifierNumber = identifierNumber;
+ node->convertToMultiGetByOffset(data);
+ changed = true;
break;
}
case PutById:
- case PutByIdDirect: {
- CodeOrigin codeOrigin = node->codeOrigin;
+ case PutByIdDirect:
+ case PutByIdFlush: {
+ NodeOrigin origin = node->origin;
Edge childEdge = node->child1();
Node* child = childEdge.node();
unsigned identifierNumber = node->identifierNumber();
ASSERT(childEdge.useKind() == CellUse);
- Structure* structure = m_state.forNode(child).bestProvenStructure();
- if (!structure)
+ AbstractValue baseValue = m_state.forNode(child);
+ AbstractValue valueValue = m_state.forNode(node->child2());
+
+ m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+ alreadyHandled = true; // Don't allow the default constant folder to do things to this.
+
+ if (baseValue.m_structure.isTop() || baseValue.m_structure.isClobbered())
break;
- bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
- bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
-
PutByIdStatus status = PutByIdStatus::computeFor(
- vm(),
- m_graph.globalObjectFor(codeOrigin),
- structure,
- codeBlock()->identifier(identifierNumber),
+ m_graph.globalObjectFor(origin.semantic),
+ baseValue.m_structure.set(),
+ m_graph.identifiers()[identifierNumber],
node->op() == PutByIdDirect);
- if (!status.isSimpleReplace() && !status.isSimpleTransition())
+ if (!status.isSimple())
break;
+
+ ASSERT(status.numVariants());
- ASSERT(status.oldStructure() == structure);
-
- // Now before we do anything else, push the CFA forward over the PutById
- // and make sure we signal to the loop that it should continue and not
- // do any eliminations.
- m_state.execute(indexInBlock);
- eliminated = true;
-
- if (needsWatchpoint) {
- ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
- OpInfo(structure), childEdge);
- } else if (needsCellCheck) {
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
- }
-
- childEdge.setUseKind(KnownCellUse);
+ if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode))
+ break;
- StructureTransitionData* transitionData = 0;
- if (status.isSimpleTransition()) {
- transitionData = m_graph.addStructureTransitionData(
- StructureTransitionData(structure, status.newStructure()));
-
- if (node->op() == PutById) {
- if (!structure->storedPrototype().isNull()) {
- addStructureTransitionCheck(
- codeOrigin, indexInBlock,
- structure->storedPrototype().asCell());
- }
-
- for (WriteBarrier<Structure>* it = status.structureChain()->head(); *it; ++it) {
- JSValue prototype = (*it)->storedPrototype();
- if (prototype.isNull())
- continue;
- ASSERT(prototype.isCell());
- addStructureTransitionCheck(
- codeOrigin, indexInBlock, prototype.asCell());
+ changed = true;
+
+ bool allGood = true;
+ for (const PutByIdVariant& variant : status.variants()) {
+ if (!allGood)
+ break;
+ for (const ObjectPropertyCondition& condition : variant.conditionSet()) {
+ if (m_graph.watchCondition(condition))
+ continue;
+
+ Structure* structure = condition.object()->structure();
+ if (!condition.structureEnsuresValidity(structure)) {
+ allGood = false;
+ break;
}
+
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, CheckStructure, node->origin,
+ OpInfo(m_graph.addStructureSet(structure)),
+ m_insertionSet.insertConstantForUse(
+ indexInBlock, node->origin, condition.object(), KnownCellUse));
}
}
+
+ if (!allGood)
+ break;
- Edge propertyStorage;
-
- if (isInlineOffset(status.offset()))
- propertyStorage = childEdge;
- else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) {
- propertyStorage = Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
- } else if (!structure->outOfLineCapacity()) {
- ASSERT(status.newStructure()->outOfLineCapacity());
- ASSERT(!isInlineOffset(status.offset()));
- propertyStorage = Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, AllocatePropertyStorage,
- codeOrigin, OpInfo(transitionData), childEdge));
- } else {
- ASSERT(structure->outOfLineCapacity());
- ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
- ASSERT(!isInlineOffset(status.offset()));
-
- propertyStorage = Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, ReallocatePropertyStorage, codeOrigin,
- OpInfo(transitionData), childEdge,
- Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge))));
- }
-
- if (status.isSimpleTransition()) {
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, PutStructure, codeOrigin,
- OpInfo(transitionData), childEdge);
+ if (status.numVariants() == 1) {
+ emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
+ break;
}
- node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
+ ASSERT(isFTL(m_graph.m_plan.mode));
+
+ MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
+ data->variants = status.variants();
+ data->identifierNumber = identifierNumber;
+ node->convertToMultiPutByOffset(data);
+ break;
+ }
+
+ case ToPrimitive: {
+ if (m_state.forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))
+ break;
- StorageAccessData storageAccessData;
- storageAccessData.offset = indexRelativeToBase(status.offset());
- storageAccessData.identifierNumber = identifierNumber;
- m_graph.m_storageAccessData.append(storageAccessData);
+ node->convertToIdentity();
+ changed = true;
+ break;
+ }
+
+ case Check: {
+ alreadyHandled = true;
+ m_interpreter.execute(indexInBlock);
+ for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
+ Edge edge = node->children.child(i);
+ if (!edge)
+ break;
+ if (edge.isProved() || edge.willNotHaveCheck()) {
+ node->children.removeEdge(i--);
+ changed = true;
+ }
+ }
break;
}
default:
break;
}
-
+
if (eliminated) {
changed = true;
continue;
}
- m_state.execute(indexInBlock);
+ if (alreadyHandled)
+ continue;
+
+ m_interpreter.execute(indexInBlock);
+ if (!m_state.isValid()) {
+ // If we invalidated then we shouldn't attempt to constant-fold. Here's an
+ // example:
+ //
+ // c: JSConstant(4.2)
+ // x: ValueToInt32(Check:Int32:@const)
+ //
+ // It would be correct for an analysis to assume that execution cannot
+ // proceed past @x. Therefore, constant-folding @x could be rather bad. But,
+ // the CFA may report that it found a constant even though it also reported
+ // that everything has been invalidated. This will only happen in a couple of
+ // the constant folding cases; most of them are also separately defensive
+ // about such things.
+ break;
+ }
if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
continue;
- JSValue value = m_state.forNode(node).value();
- if (!value)
+
+ // Interesting fact: this freezing that we do right here may turn an fragile value into
+ // a weak value. See DFGValueStrength.h.
+ FrozenValue* value = m_graph.freeze(m_state.forNode(node).value());
+ if (!*value)
continue;
-
- CodeOrigin codeOrigin = node->codeOrigin;
- AdjacencyList children = node->children;
if (node->op() == GetLocal) {
- // GetLocals without a Phi child are guaranteed dead. We don't have to
- // do anything about them.
- if (!node->child1())
- continue;
-
- if (m_graph.m_form != LoadStore) {
- VariableAccessData* variable = node->variableAccessData();
- Node* phi = node->child1().node();
- if (phi->op() == Phi
- && block->variablesAtHead.operand(variable->local()) == phi
- && block->variablesAtTail.operand(variable->local()) == node) {
-
- // Keep the graph threaded for easy cases. This is improves compile
- // times. It would be correct to just dethread here.
-
- m_graph.convertToConstant(node, value);
- Node* phantom = m_insertionSet.insertNode(
- indexInBlock, SpecNone, PhantomLocal, codeOrigin,
- OpInfo(variable), Edge(phi));
- block->variablesAtHead.operand(variable->local()) = phantom;
- block->variablesAtTail.operand(variable->local()) = phantom;
-
- changed = true;
-
- continue;
- }
-
- m_graph.dethread();
- }
+ // Need to preserve bytecode liveness in ThreadedCPS form. This wouldn't be necessary
+ // if it wasn't for https://bugs.webkit.org/show_bug.cgi?id=144086.
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, PhantomLocal, node->origin,
+ OpInfo(node->variableAccessData()));
+ m_graph.dethread();
} else
- ASSERT(!node->hasVariableAccessData());
-
+ m_insertionSet.insertCheck(indexInBlock, node->origin, node->children);
m_graph.convertToConstant(node, value);
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, Phantom, codeOrigin, children);
changed = true;
}
@@ -376,85 +624,201 @@ private:
return changed;
}
-#if !ASSERT_DISABLED
- bool isCapturedAtOrAfter(BasicBlock* block, unsigned indexInBlock, int operand)
+ void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const MultiGetByOffsetCase& getCase, unsigned identifierNumber)
{
- for (; indexInBlock < block->size(); ++indexInBlock) {
- Node* node = block->at(indexInBlock);
- if (!node->hasLocal())
- continue;
- if (node->local() != operand)
- continue;
- if (node->variableAccessData()->isCaptured())
- return true;
+ // When we get to here we have already emitted all of the requisite checks for everything.
+ // So, we just need to emit what the method object tells us to emit.
+
+ addBaseCheck(indexInBlock, node, baseValue, getCase.set());
+
+ GetByOffsetMethod method = getCase.method();
+
+ switch (method.kind()) {
+ case GetByOffsetMethod::Invalid:
+ RELEASE_ASSERT_NOT_REACHED();
+ return;
+
+ case GetByOffsetMethod::Constant:
+ m_graph.convertToConstant(node, method.constant());
+ return;
+
+ case GetByOffsetMethod::Load:
+ emitGetByOffset(indexInBlock, node, node->child1(), identifierNumber, method.offset());
+ return;
+
+ case GetByOffsetMethod::LoadFromPrototype: {
+ Node* child = m_insertionSet.insertConstant(
+ indexInBlock, node->origin, method.prototype());
+ emitGetByOffset(
+ indexInBlock, node, Edge(child, KnownCellUse), identifierNumber, method.offset());
+ return;
+ } }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+
+ void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const GetByIdVariant& variant, unsigned identifierNumber)
+ {
+ Edge childEdge = node->child1();
+
+ addBaseCheck(indexInBlock, node, baseValue, variant.structureSet());
+
+ // We aren't set up to handle prototype stuff.
+ DFG_ASSERT(m_graph, node, variant.conditionSet().isEmpty());
+
+ if (JSValue value = m_graph.tryGetConstantProperty(baseValue.m_value, variant.structureSet(), variant.offset())) {
+ m_graph.convertToConstant(node, m_graph.freeze(value));
+ return;
}
- return false;
+
+ emitGetByOffset(indexInBlock, node, childEdge, identifierNumber, variant.offset());
}
-#endif // !ASSERT_DISABLED
- void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell)
+ void emitGetByOffset(
+ unsigned indexInBlock, Node* node, Edge childEdge, unsigned identifierNumber,
+ PropertyOffset offset, const InferredType::Descriptor& inferredType = InferredType::Top)
{
- Node* weakConstant = m_insertionSet.insertNode(
- indexInBlock, speculationFromValue(cell), WeakJSConstant, codeOrigin, OpInfo(cell));
+ childEdge.setUseKind(KnownCellUse);
+
+ Edge propertyStorage;
+
+ if (isInlineOffset(offset))
+ propertyStorage = childEdge;
+ else {
+ propertyStorage = Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, node->origin, childEdge));
+ }
- if (cell->structure()->transitionWatchpointSetIsStillValid()) {
+ StorageAccessData& data = *m_graph.m_storageAccessData.add();
+ data.offset = offset;
+ data.identifierNumber = identifierNumber;
+ data.inferredType = inferredType;
+
+ node->convertToGetByOffset(data, propertyStorage);
+ }
+
+ void emitPutByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const PutByIdVariant& variant, unsigned identifierNumber)
+ {
+ NodeOrigin origin = node->origin;
+ Edge childEdge = node->child1();
+
+ addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure());
+ insertInferredTypeCheck(
+ m_insertionSet, indexInBlock, origin, node->child2().node(), variant.requiredType());
+
+ node->child1().setUseKind(KnownCellUse);
+ childEdge.setUseKind(KnownCellUse);
+
+ Transition* transition = 0;
+ if (variant.kind() == PutByIdVariant::Transition) {
+ transition = m_graph.m_transitions.add(
+ variant.oldStructureForTransition(), variant.newStructure());
+ }
+
+ Edge propertyStorage;
+
+ DFG_ASSERT(m_graph, node, origin.exitOK);
+ bool canExit = true;
+
+ if (isInlineOffset(variant.offset()))
+ propertyStorage = childEdge;
+ else if (!variant.reallocatesStorage()) {
+ propertyStorage = Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, origin, childEdge));
+ } else if (!variant.oldStructureForTransition()->outOfLineCapacity()) {
+ ASSERT(variant.newStructure()->outOfLineCapacity());
+ ASSERT(!isInlineOffset(variant.offset()));
+ Node* allocatePropertyStorage = m_insertionSet.insertNode(
+ indexInBlock, SpecNone, AllocatePropertyStorage,
+ origin.takeValidExit(canExit), OpInfo(transition), childEdge);
+ propertyStorage = Edge(allocatePropertyStorage);
+ } else {
+ ASSERT(variant.oldStructureForTransition()->outOfLineCapacity());
+ ASSERT(variant.newStructure()->outOfLineCapacity() > variant.oldStructureForTransition()->outOfLineCapacity());
+ ASSERT(!isInlineOffset(variant.offset()));
+
+ Node* reallocatePropertyStorage = m_insertionSet.insertNode(
+ indexInBlock, SpecNone, ReallocatePropertyStorage, origin.takeValidExit(canExit),
+ OpInfo(transition), childEdge,
+ Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, origin, childEdge)));
+ propertyStorage = Edge(reallocatePropertyStorage);
+ }
+
+ StorageAccessData& data = *m_graph.m_storageAccessData.add();
+ data.offset = variant.offset();
+ data.identifierNumber = identifierNumber;
+
+ node->convertToPutByOffset(data, propertyStorage);
+ node->origin.exitOK = canExit;
+
+ if (variant.kind() == PutByIdVariant::Transition) {
+ // FIXME: PutStructure goes last until we fix either
+ // https://bugs.webkit.org/show_bug.cgi?id=142921 or
+ // https://bugs.webkit.org/show_bug.cgi?id=142924.
m_insertionSet.insertNode(
- indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
- OpInfo(cell->structure()), Edge(weakConstant, CellUse));
+ indexInBlock + 1, SpecNone, PutStructure, origin.withInvalidExit(), OpInfo(transition),
+ childEdge);
+ }
+ }
+
+ void addBaseCheck(
+ unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const StructureSet& set)
+ {
+ if (!baseValue.m_structure.isSubsetOf(set)) {
+ // Arises when we prune MultiGetByOffset. We could have a
+ // MultiGetByOffset with a single variant that checks for structure S,
+ // and the input has structures S and T, for example.
+ ASSERT(node->child1());
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, CheckStructure, node->origin,
+ OpInfo(m_graph.addStructureSet(set)), node->child1());
return;
}
-
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, CheckStructure, codeOrigin,
- OpInfo(m_graph.addStructureSet(cell->structure())), Edge(weakConstant, CellUse));
+
+ if (baseValue.m_type & ~SpecCell)
+ m_insertionSet.insertCheck(indexInBlock, node->origin, node->child1());
}
- // This is necessary because the CFA may reach conclusions about constants based on its
- // assumption that certain code must exit, but then those constants may lead future
- // reexecutions of the CFA to believe that the same code will now no longer exit. Thus
- // to ensure soundness, we must paint unreachable code as such, by inserting an
- // unconditional ForceOSRExit wherever we find that a node would have always exited.
- // This will only happen in cases where we are making static speculations, or we're
- // making totally wrong speculations due to imprecision on the prediction propagator.
- bool paintUnreachableCode(BlockIndex blockIndex)
+ void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell, Structure* structure)
{
- bool changed = false;
+ if (m_graph.registerStructure(cell->structure()) == StructureRegisteredAndWatched)
+ return;
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF("Painting unreachable code in Block #%u.\n", blockIndex);
-#endif
- BasicBlock* block = m_graph.m_blocks[blockIndex].get();
- m_state.beginBasicBlock(block);
+ m_graph.registerStructure(structure);
+
+ Node* weakConstant = m_insertionSet.insertNode(
+ indexInBlock, speculationFromValue(cell), JSConstant, origin,
+ OpInfo(m_graph.freeze(cell)));
- for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
- m_state.execute(indexInBlock);
- if (m_state.isValid())
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, CheckStructure, origin,
+ OpInfo(m_graph.addStructureSet(structure)), Edge(weakConstant, CellUse));
+ }
+
+ void fixUpsilons(BasicBlock* block)
+ {
+ for (unsigned nodeIndex = block->size(); nodeIndex--;) {
+ Node* node = block->at(nodeIndex);
+ if (node->op() != Upsilon)
continue;
-
- Node* node = block->at(indexInBlock);
- switch (node->op()) {
- case Return:
- case Throw:
- case ThrowReferenceError:
- case ForceOSRExit:
- // Do nothing. These nodes will already do the right thing.
+ switch (node->phi()->op()) {
+ case Phi:
+ break;
+ case JSConstant:
+ case DoubleConstant:
+ case Int52Constant:
+ node->remove();
break;
-
default:
- m_insertionSet.insertNode(
- indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin);
- changed = true;
+ DFG_CRASH(m_graph, node, "Bad Upsilon phi() pointer");
break;
}
- break;
}
- m_state.reset();
- m_insertionSet.execute(block);
-
- return changed;
}
-
- AbstractState m_state;
+
+ InPlaceAbstractState m_state;
+ AbstractInterpreter<InPlaceAbstractState> m_interpreter;
InsertionSet m_insertionSet;
};