summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-04-10 09:28:39 +0000
commit32761a6cee1d0dee366b885b7b9c777e67885688 (patch)
treed6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
parenta4e969f4965059196ca948db781e52f7cfebf19e (diff)
downloadWebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp810
1 files changed, 208 insertions, 602 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index e5be8efb5..f58761160 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 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
@@ -29,15 +29,13 @@
#if ENABLE(DFG_JIT)
#include "DFGAbstractInterpreterInlines.h"
-#include "DFGArgumentsUtilities.h"
-#include "DFGBasicBlockInlines.h"
+#include "DFGBasicBlock.h"
#include "DFGGraph.h"
#include "DFGInPlaceAbstractState.h"
-#include "DFGInferredTypeCheck.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
#include "GetByIdStatus.h"
-#include "JSCInlines.h"
+#include "Operations.h"
#include "PutByIdStatus.h"
namespace JSC { namespace DFG {
@@ -55,53 +53,15 @@ public:
bool run()
{
bool changed = false;
-
- for (BasicBlock* block : m_graph.blocksInNaturalOrder()) {
+
+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) {
+ BasicBlock* block = m_graph.block(blockIndex);
+ if (!block)
+ continue;
if (block->cfaFoundConstants)
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;
}
@@ -116,25 +76,19 @@ private:
Node* node = block->at(indexInBlock);
- bool alreadyHandled = false;
bool eliminated = false;
switch (node->op()) {
- 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);
+ case CheckArgumentsNotCreated: {
+ if (!isEmptySpeculation(
+ m_state.variables().operand(
+ m_graph.argumentsRegisterFor(node->codeOrigin)).m_type))
+ break;
+ node->convertToPhantom();
+ eliminated = true;
break;
}
-
+
case CheckStructure:
case ArrayifyToStructure: {
AbstractValue& value = m_state.forNode(node->child1());
@@ -143,71 +97,27 @@ private:
set = node->structure();
else
set = node->structureSet();
- if (value.m_structure.isSubsetOf(set)) {
+ if (value.m_currentKnownStructure.isSubsetOf(set)) {
m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell.
- node->remove();
+ node->convertToPhantom();
eliminated = true;
break;
}
- 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
+ StructureAbstractValue& structureValue = value.m_futurePossibleStructure;
+ if (structureValue.isSubsetOf(set)
+ && structureValue.hasSingleton()) {
+ Structure* structure = structureValue.singleton();
+ m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell.
+ AdjacencyList children = node->children;
+ children.removeEdge(0);
+ if (!!children.child1())
+ m_insertionSet.insertNode(indexInBlock, SpecNone, Phantom, node->codeOrigin, children);
+ node->children.setChild2(Edge());
+ node->children.setChild3(Edge());
+ node->convertToStructureTransitionWatchpoint(structure);
+ eliminated = true;
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;
}
@@ -215,371 +125,235 @@ private:
case Arrayify: {
if (!node->arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node->child1())))
break;
- node->remove();
+ node->convertToPhantom();
eliminated = true;
break;
}
- case PutStructure: {
- if (m_state.forNode(node->child1()).m_structure.onlyStructure() != node->transition()->next)
+ case CheckFunction: {
+ if (m_state.forNode(node->child1()).value() != node->function())
break;
-
- node->remove();
+ node->convertToPhantom();
eliminated = true;
break;
}
- 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();
+ node->convertToPhantom();
eliminated = true;
break;
}
break;
}
+
+ case GetById:
+ case GetByIdFlush: {
+ CodeOrigin codeOrigin = node->codeOrigin;
+ Edge childEdge = node->child1();
+ Node* child = childEdge.node();
+ unsigned identifierNumber = node->identifierNumber();
- case GetMyArgumentByVal: {
- JSValue index = m_state.forNode(node->child2()).value();
- if (!index || !index.isInt32())
+ if (childEdge.useKind() != CellUse)
break;
- 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;
- }
+ Structure* structure = m_state.forNode(child).bestProvenStructure();
+ if (!structure)
+ break;
- m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
+ bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
+ bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
- 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);
- }
+ GetByIdStatus status = GetByIdStatus::computeFor(
+ vm(), structure, m_graph.identifiers()[identifierNumber]);
- if (inlineCallFrame && !inlineCallFrame->isVarargs()
- && index.asUInt32() < inlineCallFrame->arguments.size() - 1) {
- node->convertToGetStack(data);
- eliminated = true;
+ if (!status.isSimple()) {
+ // FIXME: We could handle prototype cases.
+ // https://bugs.webkit.org/show_bug.cgi?id=110386
break;
}
- 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;
- }
+ ASSERT(status.structureSet().size() == 1);
+ ASSERT(!status.chain());
+ ASSERT(status.structureSet().singletonStructure() == structure);
- 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.
+ // 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_interpreter.execute(indexInBlock);
+ eliminated = true;
- 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 (needsWatchpoint) {
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
+ OpInfo(structure), childEdge);
+ } else if (needsCellCheck) {
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
}
- if (data.cases.size() != 1)
- break;
+ childEdge.setUseKind(KnownCellUse);
- emitGetByOffset(indexInBlock, node, baseValue, data.cases[0], data.identifierNumber);
- changed = true;
- break;
- }
-
- 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.
+ Edge propertyStorage;
-
- 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 (isInlineOffset(status.offset()))
+ propertyStorage = childEdge;
+ else {
+ propertyStorage = Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge));
}
-
- 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();
-
- 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;
+ node->convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
- GetByIdStatus status = GetByIdStatus::computeFor(
- baseValue.m_structure.set(), m_graph.identifiers()[identifierNumber]);
- if (!status.isSimple())
- break;
-
- 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;
- }
- }
-
- if (status.numVariants() == 1) {
- emitGetByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
- changed = true;
- break;
- }
-
- 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;
+ StorageAccessData storageAccessData;
+ storageAccessData.offset = status.offset();
+ storageAccessData.identifierNumber = identifierNumber;
+ m_graph.m_storageAccessData.append(storageAccessData);
break;
}
case PutById:
- case PutByIdDirect:
- case PutByIdFlush: {
- NodeOrigin origin = node->origin;
+ case PutByIdDirect: {
+ CodeOrigin codeOrigin = node->codeOrigin;
Edge childEdge = node->child1();
Node* child = childEdge.node();
unsigned identifierNumber = node->identifierNumber();
ASSERT(childEdge.useKind() == CellUse);
- 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())
+ Structure* structure = m_state.forNode(child).bestProvenStructure();
+ if (!structure)
break;
+ bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
+ bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell;
+
PutByIdStatus status = PutByIdStatus::computeFor(
- m_graph.globalObjectFor(origin.semantic),
- baseValue.m_structure.set(),
+ vm(),
+ m_graph.globalObjectFor(codeOrigin),
+ structure,
m_graph.identifiers()[identifierNumber],
node->op() == PutByIdDirect);
- if (!status.isSimple())
+ if (!status.isSimpleReplace() && !status.isSimpleTransition())
break;
-
- ASSERT(status.numVariants());
- if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode))
- break;
+ ASSERT(status.oldStructure() == structure);
- 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;
+ // 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_interpreter.execute(indexInBlock);
+ eliminated = true;
+
+ if (needsWatchpoint) {
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
+ OpInfo(structure), childEdge);
+ } else if (needsCellCheck) {
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, Phantom, codeOrigin, childEdge);
+ }
+
+ childEdge.setUseKind(KnownCellUse);
+
+ 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());
+ }
+
+ m_graph.chains().addLazily(status.structureChain());
+
+ for (unsigned i = 0; i < status.structureChain()->size(); ++i) {
+ JSValue prototype = status.structureChain()->at(i)->storedPrototype();
+ if (prototype.isNull())
+ continue;
+ ASSERT(prototype.isCell());
+ addStructureTransitionCheck(
+ codeOrigin, indexInBlock, prototype.asCell());
}
-
- 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;
- if (status.numVariants() == 1) {
- emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
- 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()));
+ Node* allocatePropertyStorage = m_insertionSet.insertNode(
+ indexInBlock, SpecNone, AllocatePropertyStorage,
+ codeOrigin, OpInfo(transitionData), childEdge);
+ m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, codeOrigin, Edge(node->child1().node(), KnownCellUse));
+ propertyStorage = Edge(allocatePropertyStorage);
+ } else {
+ ASSERT(structure->outOfLineCapacity());
+ ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
+ ASSERT(!isInlineOffset(status.offset()));
+
+ Node* reallocatePropertyStorage = m_insertionSet.insertNode(
+ indexInBlock, SpecNone, ReallocatePropertyStorage, codeOrigin,
+ OpInfo(transitionData), childEdge,
+ Edge(m_insertionSet.insertNode(
+ indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge)));
+ m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, codeOrigin, Edge(node->child1().node(), KnownCellUse));
+ propertyStorage = Edge(reallocatePropertyStorage);
}
- ASSERT(isFTL(m_graph.m_plan.mode));
+ if (status.isSimpleTransition()) {
+ Node* putStructure = m_graph.addNode(SpecNone, PutStructure, codeOrigin, OpInfo(transitionData), childEdge);
+ m_insertionSet.insertNode(indexInBlock, SpecNone, StoreBarrier, codeOrigin, Edge(node->child1().node(), KnownCellUse));
+ m_insertionSet.insert(indexInBlock, putStructure);
+ }
- MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
- data->variants = status.variants();
- data->identifierNumber = identifierNumber;
- node->convertToMultiPutByOffset(data);
+ node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage);
+ m_insertionSet.insertNode(indexInBlock, SpecNone, ConditionalStoreBarrier, codeOrigin,
+ Edge(node->child2().node(), KnownCellUse), Edge(node->child3().node(), UntypedUse));
+
+ StorageAccessData storageAccessData;
+ storageAccessData.offset = status.offset();
+ storageAccessData.identifierNumber = identifierNumber;
+ m_graph.m_storageAccessData.append(storageAccessData);
break;
}
- case ToPrimitive: {
- if (m_state.forNode(node->child1()).m_type & ~(SpecFullNumber | SpecBoolean | SpecString | SpecSymbol))
- break;
-
- node->convertToIdentity();
- changed = true;
+ case ConditionalStoreBarrier: {
+ if (!m_interpreter.needsTypeCheck(node->child2().node(), ~SpecCell)) {
+ node->convertToPhantom();
+ eliminated = 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;
- }
- }
+ case StoreBarrier:
+ case StoreBarrierWithNullCheck: {
break;
}
-
+
default:
break;
}
-
+
if (eliminated) {
changed = true;
continue;
}
- 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
@@ -598,23 +372,31 @@ private:
}
if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant())
continue;
+ JSValue value = m_state.forNode(node).value();
+ if (!value)
+ continue;
- // 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)
+ // Check if merging the abstract value of the constant into the abstract value
+ // we've proven for this node wouldn't widen the proof. If it widens the proof
+ // (i.e. says that the set contains more things in it than it previously did)
+ // then we refuse to fold.
+ AbstractValue oldValue = m_state.forNode(node);
+ AbstractValue constantValue;
+ constantValue.set(m_graph, value);
+ if (oldValue.merge(constantValue))
continue;
+
+ CodeOrigin codeOrigin = node->codeOrigin;
+ AdjacencyList children = node->children;
- if (node->op() == GetLocal) {
- // 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()));
+ if (node->op() == GetLocal)
m_graph.dethread();
- } else
- m_insertionSet.insertCheck(indexInBlock, node->origin, node->children);
+ else
+ ASSERT(!node->hasVariableAccessData(m_graph));
+
m_graph.convertToConstant(node, value);
+ m_insertionSet.insertNode(
+ indexInBlock, SpecNone, Phantom, codeOrigin, children);
changed = true;
}
@@ -623,198 +405,22 @@ private:
return changed;
}
-
- void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const MultiGetByOffsetCase& getCase, unsigned identifierNumber)
- {
- // 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;
- }
-
- emitGetByOffset(indexInBlock, node, childEdge, identifierNumber, variant.offset());
- }
-
- void emitGetByOffset(
- unsigned indexInBlock, Node* node, Edge childEdge, unsigned identifierNumber,
- PropertyOffset offset, const InferredType::Descriptor& inferredType = InferredType::Top)
+ void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell)
{
- childEdge.setUseKind(KnownCellUse);
-
- Edge propertyStorage;
-
- if (isInlineOffset(offset))
- propertyStorage = childEdge;
- else {
- propertyStorage = Edge(m_insertionSet.insertNode(
- indexInBlock, SpecNone, GetButterfly, node->origin, childEdge));
- }
-
- 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* weakConstant = m_insertionSet.insertNode(
+ indexInBlock, speculationFromValue(cell), WeakJSConstant, codeOrigin, OpInfo(cell));
- 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.
+ if (m_graph.watchpoints().isStillValid(cell->structure()->transitionWatchpointSet())) {
m_insertionSet.insertNode(
- 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());
+ indexInBlock, SpecNone, StructureTransitionWatchpoint, codeOrigin,
+ OpInfo(cell->structure()), Edge(weakConstant, CellUse));
return;
}
-
- if (baseValue.m_type & ~SpecCell)
- m_insertionSet.insertCheck(indexInBlock, node->origin, node->child1());
- }
-
- void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell, Structure* structure)
- {
- if (m_graph.registerStructure(cell->structure()) == StructureRegisteredAndWatched)
- return;
-
- m_graph.registerStructure(structure);
- Node* weakConstant = m_insertionSet.insertNode(
- indexInBlock, speculationFromValue(cell), JSConstant, origin,
- OpInfo(m_graph.freeze(cell)));
-
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;
- switch (node->phi()->op()) {
- case Phi:
- break;
- case JSConstant:
- case DoubleConstant:
- case Int52Constant:
- node->remove();
- break;
- default:
- DFG_CRASH(m_graph, node, "Bad Upsilon phi() pointer");
- break;
- }
- }
+ indexInBlock, SpecNone, CheckStructure, codeOrigin,
+ OpInfo(m_graph.addStructureSet(cell->structure())), Edge(weakConstant, CellUse));
}
InPlaceAbstractState m_state;