diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp | 896 |
1 files changed, 690 insertions, 206 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp index f58761160..114f01def 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 @@ -29,13 +29,15 @@ #if ENABLE(DFG_JIT) #include "DFGAbstractInterpreterInlines.h" -#include "DFGBasicBlock.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 { @@ -53,15 +55,60 @@ public: bool run() { bool changed = false; - - for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) { - BasicBlock* block = m_graph.block(blockIndex); - if (!block) - continue; + + for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { 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; + Vector<Node*> nodesToDelete; + 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) + nodesToDelete.append(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.invalidateNodeLiveness(); + + for (Node* node : nodesToDelete) + m_graph.deleteNode(node); + + m_graph.invalidateCFG(); + m_graph.resetReachability(); + m_graph.killUnreachableBlocks(); + } + } + return changed; } @@ -76,284 +123,535 @@ 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 ArrayifyToStructure: { AbstractValue& value = m_state.forNode(node->child1()); - StructureSet set; + RegisteredStructureSet set; if (node->op() == ArrayifyToStructure) set = node->structure(); else set = node->structureSet(); - if (value.m_currentKnownStructure.isSubsetOf(set)) { + if (value.m_structure.isSubsetOf(set)) { m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell. - node->convertToPhantom(); + node->remove(); eliminated = true; break; } - 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); + break; + } + + case CheckDOM: { + JSValue constant = m_state.forNode(node->child1()).value(); + if (constant) { + if (constant.isCell() && constant.asCell()->inherits(m_graph.m_vm, node->classInfo())) { + m_interpreter.execute(indexInBlock); + node->remove(); + eliminated = true; + break; + } + } + + AbstractValue& value = m_state.forNode(node->child1()); + + if (value.m_structure.isSubClassOf(node->classInfo())) { + m_interpreter.execute(indexInBlock); + node->remove(); 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 + 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()); + const RegisteredStructureSet& set = node->structureSet(); + + if (value.value()) { + if (Structure* structure = jsDynamicCast<Structure*>(m_graph.m_vm, value.value())) { + if (set.contains(m_graph.registerStructure(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*>(m_graph.m_vm)) { + if (set.contains(m_graph.registerStructure(structure))) + return; + } + allGood = false; + }); + if (allGood) { + m_interpreter.execute(indexInBlock); + node->remove(); + eliminated = true; + break; + } + } + break; + } + case CheckArray: 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 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 CheckStringIdent: { + UniquedStringImpl* uid = node->uidOperand(); + const UniquedStringImpl* constantUid = nullptr; + + JSValue childConstant = m_state.forNode(node->child1()).value(); + if (childConstant) { + 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->convertToPhantom(); + node->remove(); eliminated = true; break; } 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 GetMyArgumentByVal: + case GetMyArgumentByValOutOfBounds: { + JSValue indexValue = m_state.forNode(node->child2()).value(); + if (!indexValue || !indexValue.isInt32()) break; + + unsigned index = indexValue.asUInt32() + node->numberOfArgumentsToSkip(); - Structure* structure = m_state.forNode(child).bestProvenStructure(); - if (!structure) - 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 >= inlineCallFrame->arguments.size() - 1) + break; + } else { + if (index >= m_state.variables().numberOfArguments() - 1) + break; + } - bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); - bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell; + m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before. - GetByIdStatus status = GetByIdStatus::computeFor( - vm(), structure, m_graph.identifiers()[identifierNumber]); + StackAccessData* data; + if (inlineCallFrame) { + data = m_graph.m_stackAccessData.add( + VirtualRegister( + inlineCallFrame->stackOffset + + CallFrame::argumentOffset(index)), + FlushedJSValue); + } else { + data = m_graph.m_stackAccessData.add( + virtualRegisterForArgument(index + 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 < inlineCallFrame->arguments.size() - 1) { + node->convertToGetStack(data); + eliminated = true; break; } - ASSERT(status.structureSet().size() == 1); - ASSERT(!status.chain()); - ASSERT(status.structureSet().singletonStructure() == structure); + if (node->op() == GetMyArgumentByValOutOfBounds) + break; - // 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); + 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) { - 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().genericFilter([&] (Structure* structure) -> bool { + return baseValue.contains(m_graph.registerStructure(structure)); + }); + + 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.isFinite() + || (node->child1().useKind() == UntypedUse || (baseValue.m_type & ~SpecCell))) + break; - Edge propertyStorage; + GetByIdStatus status = GetByIdStatus::computeFor( + baseValue.m_structure.toStructureSet(), 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 = 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( + *m_graph.addStructureSet(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.isFinite()) 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, + m_graph.globalObjectFor(origin.semantic), + baseValue.m_structure.toStructureSet(), 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_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); + 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()); - } - - 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()); + 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())); - 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); + if (status.numVariants() == 1) { + emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber); + break; } - 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); - } + ASSERT(isFTL(m_graph.m_plan.mode)); - 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)); + 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 = status.offset(); - storageAccessData.identifierNumber = identifierNumber; - m_graph.m_storageAccessData.append(storageAccessData); + node->convertToIdentity(); + changed = true; break; } - case ConditionalStoreBarrier: { - if (!m_interpreter.needsTypeCheck(node->child2().node(), ~SpecCell)) { - node->convertToPhantom(); - eliminated = true; - } + case ToThis: { + if (!isToThisAnIdentity(m_graph.executableFor(node->origin.semantic)->isStrictMode(), m_state.forNode(node->child1()))) + break; + + node->convertToIdentity(); + changed = true; break; } - case StoreBarrier: - case StoreBarrierWithNullCheck: { + case ToNumber: { + if (m_state.forNode(node->child1()).m_type & ~SpecBytecodeNumber) + break; + + node->convertToIdentity(); + changed = true; break; } - default: + case ParseInt: { + AbstractValue& value = m_state.forNode(node->child1()); + if (!value.m_type || (value.m_type & ~SpecInt32Only)) + break; + + JSValue radix; + if (!node->child2()) + radix = jsNumber(0); + else + radix = m_state.forNode(node->child2()).m_value; + + if (!radix.isNumber()) + break; + + if (radix.asNumber() == 0 || radix.asNumber() == 10) { + node->child2() = Edge(); + 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; } + 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 @@ -372,31 +670,23 @@ private: } if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant()) continue; - JSValue value = m_state.forNode(node).value(); - if (!value) - continue; - // 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)) + // 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) + 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())); m_graph.dethread(); - else - ASSERT(!node->hasVariableAccessData(m_graph)); - + } else + m_insertionSet.insertCheck(indexInBlock, node->origin, node->children); m_graph.convertToConstant(node, value); - m_insertionSet.insertNode( - indexInBlock, SpecNone, Phantom, codeOrigin, children); changed = true; } @@ -405,22 +695,217 @@ 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, *m_graph.addStructureSet(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) + { + 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, childEdge); + } - void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell) + void emitPutByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const PutByIdVariant& variant, unsigned identifierNumber) { - Node* weakConstant = m_insertionSet.insertNode( - indexInBlock, speculationFromValue(cell), WeakJSConstant, codeOrigin, OpInfo(cell)); + 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( + m_graph.registerStructure(variant.oldStructureForTransition()), m_graph.registerStructure(variant.newStructure())); + } + + Edge propertyStorage; + + DFG_ASSERT(m_graph, node, origin.exitOK); + bool canExit = true; + bool didAllocateStorage = false; + + 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, OpInfo(transition), childEdge); + propertyStorage = Edge(allocatePropertyStorage); + didAllocateStorage = true; + } 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, + OpInfo(transition), childEdge, + Edge(m_insertionSet.insertNode( + indexInBlock, SpecNone, GetButterfly, origin, childEdge))); + propertyStorage = Edge(reallocatePropertyStorage); + didAllocateStorage = true; + } + + StorageAccessData& data = *m_graph.m_storageAccessData.add(); + data.offset = variant.offset(); + data.identifierNumber = identifierNumber; - if (m_graph.watchpoints().isStillValid(cell->structure()->transitionWatchpointSet())) { + node->convertToPutByOffset(data, propertyStorage, childEdge); + node->origin.exitOK = canExit; + + if (variant.kind() == PutByIdVariant::Transition) { + if (didAllocateStorage) { + m_insertionSet.insertNode( + indexInBlock + 1, SpecNone, NukeStructureAndSetButterfly, + origin.withInvalidExit(), childEdge, propertyStorage); + } + + // 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) + { + addBaseCheck(indexInBlock, node, baseValue, *m_graph.addStructureSet(set)); + } + + void addBaseCheck( + unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const RegisteredStructureSet& 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.toStructureSet())), node->child1()); return; } + + if (baseValue.m_type & ~SpecCell) + m_insertionSet.insertCheck(indexInBlock, node->origin, node->child1()); + } + + void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell, Structure* structure) + { + { + StructureRegistrationResult result; + m_graph.registerStructure(cell->structure(), result); + if (result == 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, codeOrigin, - OpInfo(m_graph.addStructureSet(cell->structure())), Edge(weakConstant, CellUse)); + 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; + } + } } InPlaceAbstractState m_state; @@ -430,7 +915,6 @@ private: bool performConstantFolding(Graph& graph) { - SamplingRegion samplingRegion("DFG Constant Folding Phase"); return runPhase<ConstantFoldingPhase>(graph); } |