X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/12899fa232562c774004a3a9d7d3149944dec712..refs/heads/master:/dfg/DFGConstantFoldingPhase.cpp diff --git a/dfg/DFGConstantFoldingPhase.cpp b/dfg/DFGConstantFoldingPhase.cpp index ad699e7..fd8df4d 100644 --- a/dfg/DFGConstantFoldingPhase.cpp +++ b/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,15 @@ #if ENABLE(DFG_JIT) -#include "DFGAbstractState.h" +#include "DFGAbstractInterpreterInlines.h" +#include "DFGArgumentsUtilities.h" #include "DFGBasicBlock.h" #include "DFGGraph.h" +#include "DFGInPlaceAbstractState.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 +46,7 @@ public: ConstantFoldingPhase(Graph& graph) : Phase(graph, "constant folding") , m_state(graph) + , m_interpreter(graph, m_state) , m_insertionSet(graph) { } @@ -52,26 +55,30 @@ public: { bool changed = false; - for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) { - BasicBlock* block = m_graph.m_blocks[blockIndex].get(); + for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) { + BasicBlock* block = m_graph.block(blockIndex); if (!block) continue; - if (!block->cfaDidFinish) - changed |= paintUnreachableCode(blockIndex); 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 (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { + BasicBlock* block = m_graph.block(blockIndex); + if (!block) + continue; + fixUpsilons(block); + } + } + 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 +87,18 @@ 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 CheckStructure: - case ForwardCheckStructure: case ArrayifyToStructure: { AbstractValue& value = m_state.forNode(node->child1()); StructureSet set; @@ -102,21 +106,71 @@ 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(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()) { + if (set.contains(structure)) + return; + } + allGood = false; + }); + if (allGood) { + m_interpreter.execute(indexInBlock); + node->remove(); + eliminated = true; + break; + } + } break; } @@ -124,249 +178,349 @@ 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 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(left.asInt32()) < static_cast(right.asInt32())) { + node->remove(); + eliminated = true; + break; + } + + break; + } - Structure* structure = m_state.forNode(child).bestProvenStructure(); - if (!structure) + 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; + } - GetByIdStatus status = GetByIdStatus::computeFor( - vm(), structure, codeBlock()->identifier(identifierNumber)); + m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before. - if (!status.isSimple()) { - // FIXME: We could handle prototype cases. - // https://bugs.webkit.org/show_bug.cgi?id=110386 - break; + 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); } - ASSERT(status.structureSet().size() == 1); - ASSERT(status.chain().isEmpty()); - ASSERT(status.structureSet().singletonStructure() == structure); + if (inlineCallFrame && !inlineCallFrame->isVarargs() + && index.asUInt32() < inlineCallFrame->arguments.size() - 1) { + node->convertToGetStack(data); + eliminated = true; + 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_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. - 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); + 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) { + GetByIdVariant& variant = data.variants[i]; + variant.structureSet().filter(baseValue); + if (variant.structureSet().isEmpty()) { + data.variants[i--] = data.variants.last(); + data.variants.removeLast(); + changed = true; + } + } + + if (data.variants.size() != 1) + break; + + emitGetByOffset( + indexInBlock, node, baseValue, data.variants[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. + + + 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()); + changed = true; + } } + + if (data.variants.size() != 1) + break; - childEdge.setUseKind(KnownCellUse); + 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(); - Edge propertyStorage; + 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; - if (isInlineOffset(status.offset())) - propertyStorage = childEdge; - else { - propertyStorage = Edge(m_insertionSet.insertNode( - indexInBlock, SpecNone, GetButterfly, codeOrigin, childEdge)); + 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].constantChecks().isEmpty() + || status[i].alternateBase()) { + // 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; + } + + if (!isFTL(m_graph.m_plan.mode)) + break; - StorageAccessData storageAccessData; - storageAccessData.offset = indexRelativeToBase(status.offset()); - storageAccessData.identifierNumber = identifierNumber; - m_graph.m_storageAccessData.append(storageAccessData); + MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add(); + data->variants = status.variants(); + 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); + + 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.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; + ASSERT(status.numVariants()); - 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); - } + if (status.numVariants() > 1 && !isFTL(m_graph.m_plan.mode)) + break; - childEdge.setUseKind(KnownCellUse); + changed = true; - 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* it = status.structureChain()->head(); *it; ++it) { - JSValue prototype = (*it)->storedPrototype(); - if (prototype.isNull()) - continue; - ASSERT(prototype.isCell()); - addStructureTransitionCheck( - codeOrigin, indexInBlock, prototype.asCell()); - } - } - } + for (unsigned i = status.numVariants(); i--;) + addChecks(origin, indexInBlock, status[i].constantChecks()); - 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.numVariants() == 1) { + emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber); + break; } - if (status.isSimpleTransition()) { - m_insertionSet.insertNode( - indexInBlock, SpecNone, PutStructure, codeOrigin, - OpInfo(transitionData), childEdge); - } + 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; - node->convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorage); + node->convertToIdentity(); + changed = true; + break; + } - StorageAccessData storageAccessData; - storageAccessData.offset = indexRelativeToBase(status.offset()); - storageAccessData.identifierNumber = identifierNumber; - m_graph.m_storageAccessData.append(storageAccessData); + 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; } @@ -375,85 +529,168 @@ private: return changed; } - -#if !ASSERT_DISABLED - bool isCapturedAtOrAfter(BasicBlock* block, unsigned indexInBlock, int operand) + + void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const GetByIdVariant& variant, 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; + NodeOrigin origin = node->origin; + Edge childEdge = node->child1(); + + addBaseCheck(indexInBlock, node, baseValue, variant.structureSet()); + + JSValue baseForLoad; + if (variant.alternateBase()) + baseForLoad = variant.alternateBase(); + else + baseForLoad = baseValue.m_value; + if (JSValue value = m_graph.tryGetConstantProperty(baseForLoad, variant.baseStructure(), variant.offset())) { + m_graph.convertToConstant(node, m_graph.freeze(value)); + return; } - return false; + + if (variant.alternateBase()) { + Node* child = m_insertionSet.insertConstant(indexInBlock, origin, variant.alternateBase()); + childEdge = Edge(child, KnownCellUse); + } else + childEdge.setUseKind(KnownCellUse); + + Edge propertyStorage; + + if (isInlineOffset(variant.offset())) + propertyStorage = childEdge; + else { + propertyStorage = Edge(m_insertionSet.insertNode( + indexInBlock, SpecNone, GetButterfly, origin, childEdge)); + } + + StorageAccessData& data = *m_graph.m_storageAccessData.add(); + data.offset = variant.offset(); + data.identifierNumber = identifierNumber; + + node->convertToGetByOffset(data, propertyStorage); } -#endif // !ASSERT_DISABLED - - 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(); - if (cell->structure()->transitionWatchpointSetIsStillValid()) { + addBaseCheck(indexInBlock, node, baseValue, variant.oldStructure()); + + childEdge.setUseKind(KnownCellUse); + + Transition* transition = 0; + if (variant.kind() == PutByIdVariant::Transition) { + transition = m_graph.m_transitions.add( + variant.oldStructureForTransition(), variant.newStructure()); + } + + Edge propertyStorage; + + 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); + } 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); + } + + StorageAccessData& data = *m_graph.m_storageAccessData.add(); + data.offset = variant.offset(); + data.identifierNumber = identifierNumber; + + node->convertToPutByOffset(data, propertyStorage); + + 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, 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. + 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 addChecks( + NodeOrigin origin, unsigned indexInBlock, const ConstantStructureCheckVector& checks) { - bool changed = false; + for (unsigned i = 0; i < checks.size(); ++i) { + addStructureTransitionCheck( + origin, indexInBlock, checks[i].constant(), checks[i].structure()); + } + } + + void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell, Structure* structure) + { + 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 Unreachable: - 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 m_interpreter; InsertionSet m_insertionSet; };