diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGFixupPhase.cpp')
| -rw-r--r-- | Source/JavaScriptCore/dfg/DFGFixupPhase.cpp | 1926 |
1 files changed, 1256 insertions, 670 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 185f03591..64d7f63c9 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2012-2016 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,12 +28,15 @@ #if ENABLE(DFG_JIT) +#include "ArrayPrototype.h" #include "DFGGraph.h" +#include "DFGInferredTypeCheck.h" #include "DFGInsertionSet.h" #include "DFGPhase.h" #include "DFGPredictionPropagationPhase.h" #include "DFGVariableAccessDataDump.h" -#include "Operations.h" +#include "JSCInlines.h" +#include "TypeLocation.h" namespace JSC { namespace DFG { @@ -61,12 +64,14 @@ public: m_graph.m_argumentPositions[i].mergeArgumentUnboxingAwareness(); for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) - fixupSetLocalsInBlock(m_graph.block(blockIndex)); + fixupGetAndSetLocalsInBlock(m_graph.block(blockIndex)); } for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) - fixupUntypedSetLocalsInBlock(m_graph.block(blockIndex)); - + fixupChecksInBlock(m_graph.block(blockIndex)); + + m_graph.m_planStage = PlanStage::AfterFixup; + return true; } @@ -90,7 +95,7 @@ private: switch (op) { case SetLocal: { - // This gets handled by fixupSetLocalsInBlock(). + // This gets handled by fixupGetAndSetLocalsInBlock(). return; } @@ -100,67 +105,81 @@ private: case BitRShift: case BitLShift: case BitURShift: { - fixBinaryIntEdges(); + if (Node::shouldSpeculateUntypedForBitOps(node->child1().node(), node->child2().node()) + && m_graph.hasExitSite(node->origin.semantic, BadType)) { + fixEdge<UntypedUse>(node->child1()); + fixEdge<UntypedUse>(node->child2()); + break; + } + fixIntConvertingEdge(node->child1()); + fixIntConvertingEdge(node->child2()); break; } case ArithIMul: { - fixBinaryIntEdges(); + fixIntConvertingEdge(node->child1()); + fixIntConvertingEdge(node->child2()); node->setOp(ArithMul); node->setArithMode(Arith::Unchecked); node->child1().setUseKind(Int32Use); node->child2().setUseKind(Int32Use); break; } + + case ArithClz32: { + fixIntConvertingEdge(node->child1()); + node->setArithMode(Arith::Unchecked); + break; + } case UInt32ToNumber: { - fixIntEdge(node->child1()); + fixIntConvertingEdge(node->child1()); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->convertToIdentity(); - else if (nodeCanSpeculateInt32(node->arithNodeFlags())) + else if (node->canSpeculateInt32(FixupPass)) node->setArithMode(Arith::CheckOverflow); - else + else { node->setArithMode(Arith::DoOverflow); + node->setResult(NodeResultDouble); + } break; } case ValueAdd: { if (attemptToMakeIntegerAdd(node)) { node->setOp(ArithAdd); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } - if (Node::shouldSpeculateNumberExpectingDefined(node->child1().node(), node->child2().node())) { - fixEdge<NumberUse>(node->child1()); - fixEdge<NumberUse>(node->child2()); + if (Node::shouldSpeculateNumberOrBooleanExpectingDefined(node->child1().node(), node->child2().node())) { + fixDoubleOrBooleanEdge(node->child1()); + fixDoubleOrBooleanEdge(node->child2()); node->setOp(ArithAdd); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + node->setResult(NodeResultDouble); break; } - // FIXME: Optimize for the case where one of the operands is the - // empty string. Also consider optimizing for the case where we don't - // believe either side is the emtpy string. Both of these things should - // be easy. - - if (node->child1()->shouldSpeculateString() - && attemptToMakeFastStringAdd<StringUse>(node, node->child1(), node->child2())) + if (attemptToMakeFastStringAdd(node)) break; - if (node->child2()->shouldSpeculateString() - && attemptToMakeFastStringAdd<StringUse>(node, node->child2(), node->child1())) - break; - if (node->child1()->shouldSpeculateStringObject() - && attemptToMakeFastStringAdd<StringObjectUse>(node, node->child1(), node->child2())) - break; - if (node->child2()->shouldSpeculateStringObject() - && attemptToMakeFastStringAdd<StringObjectUse>(node, node->child2(), node->child1())) - break; - if (node->child1()->shouldSpeculateStringOrStringObject() - && attemptToMakeFastStringAdd<StringOrStringObjectUse>(node, node->child1(), node->child2())) - break; - if (node->child2()->shouldSpeculateStringOrStringObject() - && attemptToMakeFastStringAdd<StringOrStringObjectUse>(node, node->child2(), node->child1())) + + fixEdge<UntypedUse>(node->child1()); + fixEdge<UntypedUse>(node->child2()); + node->setResult(NodeResultJS); + break; + } + + case StrCat: { + if (attemptToMakeFastStringAdd(node)) break; + + // FIXME: Remove empty string arguments and possibly turn this into a ToString operation. That + // would require a form of ToString that takes a KnownPrimitiveUse. This is necessary because + // the implementation of StrCat doesn't dynamically optimize for empty strings. + // https://bugs.webkit.org/show_bug.cgi?id=148540 + m_graph.doToChildren( + node, + [&] (Edge& edge) { + fixEdge<KnownPrimitiveUse>(edge); + }); break; } @@ -171,16 +190,26 @@ private: case ArithAdd: case ArithSub: { + if (op == ArithSub + && Node::shouldSpeculateUntypedForArithmetic(node->child1().node(), node->child2().node()) + && m_graph.hasExitSite(node->origin.semantic, BadType)) { + + fixEdge<UntypedUse>(node->child1()); + fixEdge<UntypedUse>(node->child2()); + node->setResult(NodeResultJS); + break; + } if (attemptToMakeIntegerAdd(node)) break; - fixEdge<NumberUse>(node->child1()); - fixEdge<NumberUse>(node->child2()); + fixDoubleOrBooleanEdge(node->child1()); + fixDoubleOrBooleanEdge(node->child2()); + node->setResult(NodeResultDouble); break; } case ArithNegate: { - if (m_graph.negateShouldSpeculateInt32(node)) { - fixEdge<Int32Use>(node->child1()); + if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) { + fixIntOrBooleanEdge(node->child1()); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->setArithMode(Arith::Unchecked); else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) @@ -189,22 +218,33 @@ private: node->setArithMode(Arith::CheckOverflowAndNegativeZero); break; } - if (m_graph.negateShouldSpeculateMachineInt(node)) { - fixEdge<MachineIntUse>(node->child1()); + if (m_graph.unaryArithShouldSpeculateMachineInt(node, FixupPass)) { + fixEdge<Int52RepUse>(node->child1()); if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) node->setArithMode(Arith::CheckOverflow); else node->setArithMode(Arith::CheckOverflowAndNegativeZero); + node->setResult(NodeResultInt52); break; } - fixEdge<NumberUse>(node->child1()); + fixDoubleOrBooleanEdge(node->child1()); + node->setResult(NodeResultDouble); break; } case ArithMul: { - if (m_graph.mulShouldSpeculateInt32(node)) { - fixEdge<Int32Use>(node->child1()); - fixEdge<Int32Use>(node->child2()); + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + if (Node::shouldSpeculateUntypedForArithmetic(leftChild.node(), rightChild.node()) + && m_graph.hasExitSite(node->origin.semantic, BadType)) { + fixEdge<UntypedUse>(leftChild); + fixEdge<UntypedUse>(rightChild); + node->setResult(NodeResultJS); + break; + } + if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) { + fixIntOrBooleanEdge(leftChild); + fixIntOrBooleanEdge(rightChild); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->setArithMode(Arith::Unchecked); else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) @@ -213,27 +253,38 @@ private: node->setArithMode(Arith::CheckOverflowAndNegativeZero); break; } - if (m_graph.mulShouldSpeculateMachineInt(node)) { - fixEdge<MachineIntUse>(node->child1()); - fixEdge<MachineIntUse>(node->child2()); + if (m_graph.binaryArithShouldSpeculateMachineInt(node, FixupPass)) { + fixEdge<Int52RepUse>(leftChild); + fixEdge<Int52RepUse>(rightChild); if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) node->setArithMode(Arith::CheckOverflow); else node->setArithMode(Arith::CheckOverflowAndNegativeZero); + node->setResult(NodeResultInt52); break; } - fixEdge<NumberUse>(node->child1()); - fixEdge<NumberUse>(node->child2()); + fixDoubleOrBooleanEdge(leftChild); + fixDoubleOrBooleanEdge(rightChild); + node->setResult(NodeResultDouble); break; } case ArithDiv: case ArithMod: { - if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) - && node->canSpeculateInt32()) { - if (optimizeForX86() || optimizeForARM64() || optimizeForARMv7s()) { - fixEdge<Int32Use>(node->child1()); - fixEdge<Int32Use>(node->child2()); + Edge& leftChild = node->child1(); + Edge& rightChild = node->child2(); + if (op == ArithDiv + && Node::shouldSpeculateUntypedForArithmetic(leftChild.node(), rightChild.node()) + && m_graph.hasExitSite(node->origin.semantic, BadType)) { + fixEdge<UntypedUse>(leftChild); + fixEdge<UntypedUse>(rightChild); + node->setResult(NodeResultJS); + break; + } + if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) { + if (optimizeForX86() || optimizeForARM64() || optimizeForARMv7IDIVSupported()) { + fixIntOrBooleanEdge(leftChild); + fixIntOrBooleanEdge(rightChild); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->setArithMode(Arith::Unchecked); else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) @@ -242,85 +293,133 @@ private: node->setArithMode(Arith::CheckOverflowAndNegativeZero); break; } - Edge child1 = node->child1(); - Edge child2 = node->child2(); - injectInt32ToDoubleNode(node->child1()); - injectInt32ToDoubleNode(node->child2()); - + // This will cause conversion nodes to be inserted later. + fixDoubleOrBooleanEdge(leftChild); + fixDoubleOrBooleanEdge(rightChild); + // We don't need to do ref'ing on the children because we're stealing them from // the original division. Node* newDivision = m_insertionSet.insertNode( - m_indexInBlock, SpecDouble, *node); + m_indexInBlock, SpecBytecodeDouble, *node); + newDivision->setResult(NodeResultDouble); node->setOp(DoubleAsInt32); - node->children.initialize(Edge(newDivision, KnownNumberUse), Edge(), Edge()); + node->children.initialize(Edge(newDivision, DoubleRepUse), Edge(), Edge()); if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) node->setArithMode(Arith::CheckOverflow); else node->setArithMode(Arith::CheckOverflowAndNegativeZero); - - m_insertionSet.insertNode(m_indexInBlock + 1, SpecNone, Phantom, node->codeOrigin, child1, child2); break; } - fixEdge<NumberUse>(node->child1()); - fixEdge<NumberUse>(node->child2()); + fixDoubleOrBooleanEdge(leftChild); + fixDoubleOrBooleanEdge(rightChild); + node->setResult(NodeResultDouble); break; } case ArithMin: case ArithMax: { - if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) - && node->canSpeculateInt32()) { - fixEdge<Int32Use>(node->child1()); - fixEdge<Int32Use>(node->child2()); + if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) { + fixIntOrBooleanEdge(node->child1()); + fixIntOrBooleanEdge(node->child2()); break; } - fixEdge<NumberUse>(node->child1()); - fixEdge<NumberUse>(node->child2()); + fixDoubleOrBooleanEdge(node->child1()); + fixDoubleOrBooleanEdge(node->child2()); + node->setResult(NodeResultDouble); break; } case ArithAbs: { - if (node->child1()->shouldSpeculateInt32ForArithmetic() - && node->canSpeculateInt32()) { - fixEdge<Int32Use>(node->child1()); + if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) { + fixIntOrBooleanEdge(node->child1()); + if (bytecodeCanTruncateInteger(node->arithNodeFlags())) + node->setArithMode(Arith::Unchecked); + else + node->setArithMode(Arith::CheckOverflow); + break; + } + fixDoubleOrBooleanEdge(node->child1()); + node->setResult(NodeResultDouble); + break; + } + + case ArithPow: { + node->setResult(NodeResultDouble); + if (node->child2()->shouldSpeculateInt32OrBooleanForArithmetic()) { + fixDoubleOrBooleanEdge(node->child1()); + fixIntOrBooleanEdge(node->child2()); break; } - fixEdge<NumberUse>(node->child1()); + + fixDoubleOrBooleanEdge(node->child1()); + fixDoubleOrBooleanEdge(node->child2()); + break; + } + + case ArithRandom: { + node->setResult(NodeResultDouble); + break; + } + + case ArithRound: + case ArithFloor: + case ArithCeil: { + if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) { + fixIntOrBooleanEdge(node->child1()); + insertCheck<Int32Use>(m_indexInBlock, node->child1().node()); + node->convertToIdentity(); + break; + } + fixDoubleOrBooleanEdge(node->child1()); + + if (isInt32OrBooleanSpeculation(node->getHeapPrediction()) && m_graph.roundShouldSpeculateInt32(node, FixupPass)) { + node->setResult(NodeResultInt32); + if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) + node->setArithRoundingMode(Arith::RoundingMode::Int32); + else + node->setArithRoundingMode(Arith::RoundingMode::Int32WithNegativeZeroCheck); + } else { + node->setResult(NodeResultDouble); + node->setArithRoundingMode(Arith::RoundingMode::Double); + } break; } case ArithSqrt: + case ArithFRound: case ArithSin: - case ArithCos: { - fixEdge<NumberUse>(node->child1()); + case ArithCos: + case ArithLog: { + fixDoubleOrBooleanEdge(node->child1()); + node->setResult(NodeResultDouble); break; } case LogicalNot: { - if (node->child1()->shouldSpeculateBoolean()) - fixEdge<BooleanUse>(node->child1()); - else if (node->child1()->shouldSpeculateObjectOrOther()) + if (node->child1()->shouldSpeculateBoolean()) { + if (node->child1()->result() == NodeResultBoolean) { + // This is necessary in case we have a bytecode instruction implemented by: + // + // a: CompareEq(...) + // b: LogicalNot(@a) + // + // In that case, CompareEq might have a side-effect. Then, we need to make + // sure that we know that Branch does not exit. + fixEdge<KnownBooleanUse>(node->child1()); + } else + fixEdge<BooleanUse>(node->child1()); + } else if (node->child1()->shouldSpeculateObjectOrOther()) fixEdge<ObjectOrOtherUse>(node->child1()); - else if (node->child1()->shouldSpeculateInt32()) - fixEdge<Int32Use>(node->child1()); + else if (node->child1()->shouldSpeculateInt32OrBoolean()) + fixIntOrBooleanEdge(node->child1()); else if (node->child1()->shouldSpeculateNumber()) - fixEdge<NumberUse>(node->child1()); + fixEdge<DoubleRepUse>(node->child1()); else if (node->child1()->shouldSpeculateString()) fixEdge<StringUse>(node->child1()); - break; - } - - case TypeOf: { - if (node->child1()->shouldSpeculateString()) - fixEdge<StringUse>(node->child1()); - else if (node->child1()->shouldSpeculateCell()) - fixEdge<CellUse>(node->child1()); - break; - } - - case CompareEqConstant: { + else if (node->child1()->shouldSpeculateStringOrOther()) + fixEdge<StringOrOtherUse>(node->child1()); break; } @@ -329,67 +428,97 @@ private: case CompareLessEq: case CompareGreater: case CompareGreaterEq: { - if (Node::shouldSpeculateInt32(node->child1().node(), node->child2().node())) { - fixEdge<Int32Use>(node->child1()); - fixEdge<Int32Use>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + if (node->op() == CompareEq + && Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) { + fixEdge<BooleanUse>(node->child1()); + fixEdge<BooleanUse>(node->child2()); + node->clearFlags(NodeMustGenerate); + break; + } + if (Node::shouldSpeculateInt32OrBoolean(node->child1().node(), node->child2().node())) { + fixIntOrBooleanEdge(node->child1()); + fixIntOrBooleanEdge(node->child2()); + node->clearFlags(NodeMustGenerate); break; } if (enableInt52() && Node::shouldSpeculateMachineInt(node->child1().node(), node->child2().node())) { - fixEdge<MachineIntUse>(node->child1()); - fixEdge<MachineIntUse>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + fixEdge<Int52RepUse>(node->child1()); + fixEdge<Int52RepUse>(node->child2()); + node->clearFlags(NodeMustGenerate); break; } - if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) { - fixEdge<NumberUse>(node->child1()); - fixEdge<NumberUse>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + if (Node::shouldSpeculateNumberOrBoolean(node->child1().node(), node->child2().node())) { + fixDoubleOrBooleanEdge(node->child1()); + fixDoubleOrBooleanEdge(node->child2()); + node->clearFlags(NodeMustGenerate); break; } if (node->op() != CompareEq) break; - if (Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) { - fixEdge<BooleanUse>(node->child1()); - fixEdge<BooleanUse>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + if (Node::shouldSpeculateSymbol(node->child1().node(), node->child2().node())) { + fixEdge<SymbolUse>(node->child1()); + fixEdge<SymbolUse>(node->child2()); + node->clearFlags(NodeMustGenerate); break; } if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) { fixEdge<StringIdentUse>(node->child1()); fixEdge<StringIdentUse>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + node->clearFlags(NodeMustGenerate); break; } if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && GPRInfo::numberOfRegisters >= 7) { fixEdge<StringUse>(node->child1()); fixEdge<StringUse>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + node->clearFlags(NodeMustGenerate); break; } if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) { fixEdge<ObjectUse>(node->child1()); fixEdge<ObjectUse>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + node->clearFlags(NodeMustGenerate); + break; + } + + // If either child can be proved to be Null or Undefined, comparing them is greatly simplified. + bool oneArgumentIsUsedAsSpecOther = false; + if (node->child1()->isUndefinedOrNullConstant()) { + fixEdge<OtherUse>(node->child1()); + oneArgumentIsUsedAsSpecOther = true; + } else if (node->child1()->shouldSpeculateOther()) { + m_insertionSet.insertNode(m_indexInBlock, SpecNone, Check, node->origin, + Edge(node->child1().node(), OtherUse)); + fixEdge<OtherUse>(node->child1()); + oneArgumentIsUsedAsSpecOther = true; + } + if (node->child2()->isUndefinedOrNullConstant()) { + fixEdge<OtherUse>(node->child2()); + oneArgumentIsUsedAsSpecOther = true; + } else if (node->child2()->shouldSpeculateOther()) { + m_insertionSet.insertNode(m_indexInBlock, SpecNone, Check, node->origin, + Edge(node->child2().node(), OtherUse)); + fixEdge<OtherUse>(node->child2()); + oneArgumentIsUsedAsSpecOther = true; + } + if (oneArgumentIsUsedAsSpecOther) { + node->clearFlags(NodeMustGenerate); break; } + if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObjectOrOther()) { fixEdge<ObjectUse>(node->child1()); fixEdge<ObjectOrOtherUse>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + node->clearFlags(NodeMustGenerate); break; } if (node->child1()->shouldSpeculateObjectOrOther() && node->child2()->shouldSpeculateObject()) { fixEdge<ObjectOrOtherUse>(node->child1()); fixEdge<ObjectUse>(node->child2()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + node->clearFlags(NodeMustGenerate); break; } - break; - } - - case CompareStrictEqConstant: { + break; } @@ -406,13 +535,18 @@ private: } if (enableInt52() && Node::shouldSpeculateMachineInt(node->child1().node(), node->child2().node())) { - fixEdge<MachineIntUse>(node->child1()); - fixEdge<MachineIntUse>(node->child2()); + fixEdge<Int52RepUse>(node->child1()); + fixEdge<Int52RepUse>(node->child2()); break; } if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) { - fixEdge<NumberUse>(node->child1()); - fixEdge<NumberUse>(node->child2()); + fixEdge<DoubleRepUse>(node->child1()); + fixEdge<DoubleRepUse>(node->child2()); + break; + } + if (Node::shouldSpeculateSymbol(node->child1().node(), node->child2().node())) { + fixEdge<SymbolUse>(node->child1()); + fixEdge<SymbolUse>(node->child2()); break; } if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) { @@ -420,21 +554,66 @@ private: fixEdge<StringIdentUse>(node->child2()); break; } - if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && GPRInfo::numberOfRegisters >= 7) { + if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 7) || isFTL(m_graph.m_plan.mode))) { fixEdge<StringUse>(node->child1()); fixEdge<StringUse>(node->child2()); break; } - if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) { + WatchpointSet* masqueradesAsUndefinedWatchpoint = m_graph.globalObjectFor(node->origin.semantic)->masqueradesAsUndefinedWatchpoint(); + if (masqueradesAsUndefinedWatchpoint->isStillValid()) { + + if (node->child1()->shouldSpeculateObject()) { + m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint); + fixEdge<ObjectUse>(node->child1()); + break; + } + if (node->child2()->shouldSpeculateObject()) { + m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint); + fixEdge<ObjectUse>(node->child2()); + break; + } + + } else if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) { fixEdge<ObjectUse>(node->child1()); fixEdge<ObjectUse>(node->child2()); break; } + if (node->child1()->shouldSpeculateMisc()) { + fixEdge<MiscUse>(node->child1()); + break; + } + if (node->child2()->shouldSpeculateMisc()) { + fixEdge<MiscUse>(node->child2()); + break; + } + if (node->child1()->shouldSpeculateStringIdent() + && node->child2()->shouldSpeculateNotStringVar()) { + fixEdge<StringIdentUse>(node->child1()); + fixEdge<NotStringVarUse>(node->child2()); + break; + } + if (node->child2()->shouldSpeculateStringIdent() + && node->child1()->shouldSpeculateNotStringVar()) { + fixEdge<StringIdentUse>(node->child2()); + fixEdge<NotStringVarUse>(node->child1()); + break; + } + if (node->child1()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 8) || isFTL(m_graph.m_plan.mode))) { + fixEdge<StringUse>(node->child1()); + break; + } + if (node->child2()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 8) || isFTL(m_graph.m_plan.mode))) { + fixEdge<StringUse>(node->child2()); + break; + } break; } case StringFromCharCode: - fixEdge<Int32Use>(node->child1()); + if (node->child1()->shouldSpeculateInt32()) + fixEdge<Int32Use>(node->child1()); + else + fixEdge<UntypedUse>(node->child1()); break; case StringCharAt: @@ -448,27 +627,84 @@ private: } case GetByVal: { + if (!node->prediction()) { + m_insertionSet.insertNode( + m_indexInBlock, SpecNone, ForceOSRExit, node->origin); + } + node->setArrayMode( node->arrayMode().refine( + m_graph, node, node->child1()->prediction(), node->child2()->prediction(), - SpecNone, node->flags())); + SpecNone)); blessArrayOperation(node->child1(), node->child2(), node->child3()); ArrayMode arrayMode = node->arrayMode(); switch (arrayMode.type()) { + case Array::Contiguous: case Array::Double: if (arrayMode.arrayClass() == Array::OriginalArray - && arrayMode.speculation() == Array::InBounds - && m_graph.globalObjectFor(node->codeOrigin)->arrayPrototypeChainIsSane() - && !(node->flags() & NodeBytecodeUsesAsOther)) - node->setArrayMode(arrayMode.withSpeculation(Array::SaneChain)); + && arrayMode.speculation() == Array::InBounds) { + JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); + if (globalObject->arrayPrototypeChainIsSane()) { + // Check if SaneChain will work on a per-type basis. Note that: + // + // 1) We don't want double arrays to sometimes return undefined, since + // that would require a change to the return type and it would pessimise + // things a lot. So, we'd only want to do that if we actually had + // evidence that we could read from a hole. That's pretty annoying. + // Likely the best way to handle that case is with an equivalent of + // SaneChain for OutOfBounds. For now we just detect when Undefined and + // NaN are indistinguishable according to backwards propagation, and just + // use SaneChain in that case. This happens to catch a lot of cases. + // + // 2) We don't want int32 array loads to have to do a hole check just to + // coerce to Undefined, since that would mean twice the checks. + // + // This has two implications. First, we have to do more checks than we'd + // like. It's unfortunate that we have to do the hole check. Second, + // some accesses that hit a hole will now need to take the full-blown + // out-of-bounds slow path. We can fix that with: + // https://bugs.webkit.org/show_bug.cgi?id=144668 + + bool canDoSaneChain = false; + switch (arrayMode.type()) { + case Array::Contiguous: + // This is happens to be entirely natural. We already would have + // returned any JSValue, and now we'll return Undefined. We still do + // the check but it doesn't require taking any kind of slow path. + canDoSaneChain = true; + break; + + case Array::Double: + if (!(node->flags() & NodeBytecodeUsesAsOther)) { + // Holes look like NaN already, so if the user doesn't care + // about the difference between Undefined and NaN then we can + // do this. + canDoSaneChain = true; + } + break; + + default: + break; + } + + if (canDoSaneChain) { + m_graph.watchpoints().addLazily( + globalObject->arrayPrototype()->structure()->transitionWatchpointSet()); + m_graph.watchpoints().addLazily( + globalObject->objectPrototype()->structure()->transitionWatchpointSet()); + node->setArrayMode(arrayMode.withSpeculation(Array::SaneChain)); + } + } + } break; case Array::String: if ((node->prediction() & ~SpecString) - || m_graph.hasExitSite(node->codeOrigin, OutOfBounds)) + || m_graph.hasExitSite(node->origin.semantic, OutOfBounds)) node->setArrayMode(arrayMode.withSpeculation(Array::OutOfBounds)); break; @@ -476,10 +712,10 @@ private: break; } - switch (node->arrayMode().type()) { + arrayMode = node->arrayMode(); + switch (arrayMode.type()) { case Array::SelectUsingPredictions: case Array::Unprofiled: - case Array::Undecided: RELEASE_ASSERT_NOT_REACHED(); break; case Array::Generic: @@ -495,6 +731,30 @@ private: break; } + switch (arrayMode.type()) { + case Array::Double: + if (!arrayMode.isOutOfBounds()) + node->setResult(NodeResultDouble); + break; + + case Array::Float32Array: + case Array::Float64Array: + node->setResult(NodeResultDouble); + break; + + case Array::Uint32Array: + if (node->shouldSpeculateInt32()) + break; + if (node->shouldSpeculateMachineInt() && enableInt52()) + node->setResult(NodeResultInt52); + else + node->setResult(NodeResultDouble); + break; + + default: + break; + } + break; } @@ -507,6 +767,7 @@ private: node->setArrayMode( node->arrayMode().refine( + m_graph, node, child1->prediction(), child2->prediction(), child3->prediction())); @@ -515,6 +776,7 @@ private: switch (node->arrayMode().modeForPut().type()) { case Array::SelectUsingPredictions: + case Array::SelectUsingArguments: case Array::Unprofiled: case Array::Undecided: RELEASE_ASSERT_NOT_REACHED(); @@ -532,15 +794,11 @@ private: fixEdge<KnownCellUse>(child1); fixEdge<Int32Use>(child2); fixEdge<Int32Use>(child3); - if (child3->prediction() & SpecInt52) - fixEdge<MachineIntUse>(child3); - else - fixEdge<Int32Use>(child3); break; case Array::Double: fixEdge<KnownCellUse>(child1); fixEdge<Int32Use>(child2); - fixEdge<RealNumberUse>(child3); + fixEdge<DoubleRepRealUse>(child3); break; case Array::Int8Array: case Array::Int16Array: @@ -552,25 +810,24 @@ private: fixEdge<KnownCellUse>(child1); fixEdge<Int32Use>(child2); if (child3->shouldSpeculateInt32()) - fixEdge<Int32Use>(child3); + fixIntOrBooleanEdge(child3); else if (child3->shouldSpeculateMachineInt()) - fixEdge<MachineIntUse>(child3); + fixEdge<Int52RepUse>(child3); else - fixEdge<NumberUse>(child3); + fixDoubleOrBooleanEdge(child3); break; case Array::Float32Array: case Array::Float64Array: fixEdge<KnownCellUse>(child1); fixEdge<Int32Use>(child2); - fixEdge<NumberUse>(child3); + fixDoubleOrBooleanEdge(child3); break; case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: - case Array::Arguments: fixEdge<KnownCellUse>(child1); fixEdge<Int32Use>(child2); - insertStoreBarrier(m_indexInBlock, child1, child3); + speculateForBarrier(child3); break; default: fixEdge<KnownCellUse>(child1); @@ -592,6 +849,7 @@ private: // that would break things. node->setArrayMode( node->arrayMode().refine( + m_graph, node, node->child1()->prediction() & SpecCell, SpecInt32, node->child2()->prediction())); @@ -603,11 +861,11 @@ private: fixEdge<Int32Use>(node->child2()); break; case Array::Double: - fixEdge<RealNumberUse>(node->child2()); + fixEdge<DoubleRepRealUse>(node->child2()); break; case Array::Contiguous: case Array::ArrayStorage: - insertStoreBarrier(m_indexInBlock, node->child1(), node->child2()); + speculateForBarrier(node->child2()); break; default: break; @@ -623,54 +881,52 @@ private: case RegExpExec: case RegExpTest: { - fixEdge<CellUse>(node->child1()); - fixEdge<CellUse>(node->child2()); + // FIXME: These should probably speculate something stronger than cell. + // https://bugs.webkit.org/show_bug.cgi?id=154900 + if (node->child1()->shouldSpeculateCell() + && node->child2()->shouldSpeculateCell()) { + fixEdge<CellUse>(node->child1()); + fixEdge<CellUse>(node->child2()); + break; + } + break; + } + + case StringReplace: { + if (node->child1()->shouldSpeculateString() + && node->child2()->shouldSpeculateRegExpObject() + && node->child3()->shouldSpeculateString()) { + fixEdge<StringUse>(node->child1()); + fixEdge<RegExpObjectUse>(node->child2()); + fixEdge<StringUse>(node->child3()); + break; + } break; } case Branch: { - if (node->child1()->shouldSpeculateBoolean()) - fixEdge<BooleanUse>(node->child1()); - else if (node->child1()->shouldSpeculateObjectOrOther()) + if (node->child1()->shouldSpeculateBoolean()) { + if (node->child1()->result() == NodeResultBoolean) { + // This is necessary in case we have a bytecode instruction implemented by: + // + // a: CompareEq(...) + // b: Branch(@a) + // + // In that case, CompareEq might have a side-effect. Then, we need to make + // sure that we know that Branch does not exit. + fixEdge<KnownBooleanUse>(node->child1()); + } else + fixEdge<BooleanUse>(node->child1()); + } else if (node->child1()->shouldSpeculateObjectOrOther()) fixEdge<ObjectOrOtherUse>(node->child1()); - else if (node->child1()->shouldSpeculateInt32()) - fixEdge<Int32Use>(node->child1()); + else if (node->child1()->shouldSpeculateInt32OrBoolean()) + fixIntOrBooleanEdge(node->child1()); else if (node->child1()->shouldSpeculateNumber()) - fixEdge<NumberUse>(node->child1()); - - Node* logicalNot = node->child1().node(); - if (logicalNot->op() == LogicalNot) { - - // Make sure that OSR exit can't observe the LogicalNot. If it can, - // then we must compute it and cannot peephole around it. - bool found = false; - bool ok = true; - for (unsigned i = m_indexInBlock; i--;) { - Node* candidate = m_block->at(i); - if (candidate == logicalNot) { - found = true; - break; - } - if (candidate->canExit()) { - ok = false; - found = true; - break; - } - } - ASSERT_UNUSED(found, found); - - if (ok) { - Edge newChildEdge = logicalNot->child1(); - if (newChildEdge->hasBooleanResult()) { - node->children.setChild1(newChildEdge); - - BasicBlock* toBeTaken = node->notTakenBlock(); - BasicBlock* toBeNotTaken = node->takenBlock(); - node->setTakenBlock(toBeTaken); - node->setNotTakenBlock(toBeNotTaken); - } - } - } + fixEdge<DoubleRepUse>(node->child1()); + else if (node->child1()->shouldSpeculateString()) + fixEdge<StringUse>(node->child1()); + else if (node->child1()->shouldSpeculateStringOrOther()) + fixEdge<StringOrOtherUse>(node->child1()); break; } @@ -691,6 +947,12 @@ private: else if (node->child1()->shouldSpeculateString()) fixEdge<StringUse>(node->child1()); break; + case SwitchCell: + if (node->child1()->shouldSpeculateCell()) + fixEdge<CellUse>(node->child1()); + // else it's fine for this to have UntypedUse; we will handle this by just making + // non-cells take the default case. + break; } break; } @@ -700,8 +962,9 @@ private: break; } - case ToString: { - fixupToString(node); + case ToString: + case CallStringConstructor: { + fixupToStringOrCallStringConstructor(node); break; } @@ -711,6 +974,8 @@ private: } case NewArray: { + watchHavingABadTime(node); + for (unsigned i = m_graph.varArgNumChildren(node); i--;) { node->setIndexingType( leastUpperBoundOfIndexingTypeAndType( @@ -726,7 +991,7 @@ private: // would have already exited by now, but insert a forced exit just to // be safe. m_insertionSet.insertNode( - m_indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin); + m_indexInBlock, SpecNone, ForceOSRExit, node->origin); } break; case ALL_INT32_INDEXING_TYPES: @@ -735,7 +1000,7 @@ private: break; case ALL_DOUBLE_INDEXING_TYPES: for (unsigned operandIndex = 0; operandIndex < node->numChildren(); ++operandIndex) - fixEdge<RealNumberUse>(m_graph.m_varArgChildren[node->firstChild() + operandIndex]); + fixEdge<DoubleRepRealUse>(m_graph.m_varArgChildren[node->firstChild() + operandIndex]); break; case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: @@ -748,68 +1013,49 @@ private: } case NewTypedArray: { + watchHavingABadTime(node); + if (node->child1()->shouldSpeculateInt32()) { fixEdge<Int32Use>(node->child1()); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + node->clearFlags(NodeMustGenerate); break; } break; } case NewArrayWithSize: { + watchHavingABadTime(node); fixEdge<Int32Use>(node->child1()); break; } case ToThis: { - ECMAMode ecmaMode = m_graph.executableFor(node->codeOrigin)->isStrictMode() ? StrictMode : NotStrictMode; - - if (isOtherSpeculation(node->child1()->prediction())) { - if (ecmaMode == StrictMode) { - fixEdge<OtherUse>(node->child1()); - node->convertToIdentity(); - break; - } - - m_insertionSet.insertNode( - m_indexInBlock, SpecNone, Phantom, node->codeOrigin, - Edge(node->child1().node(), OtherUse)); - observeUseKindOnNode<OtherUse>(node->child1().node()); - node->convertToWeakConstant(m_graph.globalThisObjectFor(node->codeOrigin)); - break; - } - - if (isFinalObjectSpeculation(node->child1()->prediction())) { - fixEdge<FinalObjectUse>(node->child1()); - node->convertToIdentity(); - break; - } - + fixupToThis(node); break; } - case GetMyArgumentByVal: - case GetMyArgumentByValSafe: { - fixEdge<Int32Use>(node->child1()); + case PutStructure: { + fixEdge<KnownCellUse>(node->child1()); break; } - case PutStructure: { + case GetClosureVar: + case GetFromArguments: { fixEdge<KnownCellUse>(node->child1()); - insertStoreBarrier(m_indexInBlock, node->child1()); break; } - case PutClosureVar: { + case PutClosureVar: + case PutToArguments: { fixEdge<KnownCellUse>(node->child1()); - insertStoreBarrier(m_indexInBlock, node->child1(), node->child3()); + speculateForBarrier(node->child2()); break; } - case GetClosureRegisters: - case SkipTopScope: case SkipScope: - case GetScope: { + case GetScope: + case GetGetter: + case GetSetter: { fixEdge<KnownCellUse>(node->child1()); break; } @@ -817,48 +1063,80 @@ private: case AllocatePropertyStorage: case ReallocatePropertyStorage: { fixEdge<KnownCellUse>(node->child1()); - insertStoreBarrier(m_indexInBlock + 1, node->child1()); break; } case GetById: case GetByIdFlush: { - if (!node->child1()->shouldSpeculateCell()) - break; - StringImpl* impl = m_graph.identifiers()[node->identifierNumber()]; - if (impl == vm().propertyNames->length.impl()) { - attemptToMakeGetArrayLength(node); - break; - } - if (impl == vm().propertyNames->byteLength.impl()) { - attemptToMakeGetTypedArrayByteLength(node); - break; - } - if (impl == vm().propertyNames->byteOffset.impl()) { - attemptToMakeGetTypedArrayByteOffset(node); - break; + // FIXME: This should be done in the ByteCodeParser based on reading the + // PolymorphicAccess, which will surely tell us that this is a AccessCase::ArrayLength. + // https://bugs.webkit.org/show_bug.cgi?id=154990 + if (node->child1()->shouldSpeculateCellOrOther() + && !m_graph.hasExitSite(node->origin.semantic, BadType) + && !m_graph.hasExitSite(node->origin.semantic, BadCache) + && !m_graph.hasExitSite(node->origin.semantic, BadIndexingType) + && !m_graph.hasExitSite(node->origin.semantic, ExoticObjectMode)) { + auto uid = m_graph.identifiers()[node->identifierNumber()]; + if (uid == vm().propertyNames->length.impl()) { + attemptToMakeGetArrayLength(node); + break; + } } - fixEdge<CellUse>(node->child1()); + + if (node->child1()->shouldSpeculateCell()) + fixEdge<CellUse>(node->child1()); break; } case PutById: + case PutByIdFlush: case PutByIdDirect: { fixEdge<CellUse>(node->child1()); - insertStoreBarrier(m_indexInBlock, node->child1(), node->child2()); break; } - case CheckExecutable: + case PutGetterById: + case PutSetterById: { + fixEdge<KnownCellUse>(node->child1()); + fixEdge<KnownCellUse>(node->child2()); + break; + } + + case PutGetterSetterById: { + fixEdge<KnownCellUse>(node->child1()); + break; + } + + case PutGetterByVal: + case PutSetterByVal: { + fixEdge<KnownCellUse>(node->child1()); + fixEdge<KnownCellUse>(node->child3()); + break; + } + + case GetExecutable: { + fixEdge<FunctionUse>(node->child1()); + break; + } + + case OverridesHasInstance: case CheckStructure: - case StructureTransitionWatchpoint: - case CheckFunction: - case CheckHasInstance: + case CheckCell: case CreateThis: - case GetButterfly: { + case GetButterfly: + case GetButterflyReadOnly: { fixEdge<CellUse>(node->child1()); break; } + + case CheckIdent: { + UniquedStringImpl* uid = node->uidOperand(); + if (uid->isSymbol()) + fixEdge<SymbolUse>(node->child1()); + else + fixEdge<StringIdentUse>(node->child1()); + break; + } case Arrayify: case ArrayifyToStructure: { @@ -868,30 +1146,47 @@ private: break; } - case GetByOffset: { + case GetByOffset: + case GetGetterSetterByOffset: { if (!node->child1()->hasStorageResult()) fixEdge<KnownCellUse>(node->child1()); fixEdge<KnownCellUse>(node->child2()); break; } + case MultiGetByOffset: { + fixEdge<CellUse>(node->child1()); + break; + } + case PutByOffset: { if (!node->child1()->hasStorageResult()) fixEdge<KnownCellUse>(node->child1()); fixEdge<KnownCellUse>(node->child2()); - insertStoreBarrier(m_indexInBlock, node->child2(), node->child3()); + insertInferredTypeCheck( + m_insertionSet, m_indexInBlock, node->origin, node->child3().node(), + node->storageAccessData().inferredType); + speculateForBarrier(node->child3()); + break; + } + + case MultiPutByOffset: { + fixEdge<CellUse>(node->child1()); + speculateForBarrier(node->child2()); break; } case InstanceOf: { - // FIXME: This appears broken: CheckHasInstance already does an unconditional cell - // check. https://bugs.webkit.org/show_bug.cgi?id=107479 if (!(node->child1()->prediction() & ~SpecCell)) fixEdge<CellUse>(node->child1()); fixEdge<CellUse>(node->child2()); break; } - + + case InstanceOfCustom: + fixEdge<CellUse>(node->child2()); + break; + case In: { // FIXME: We should at some point have array profiling on op_in, in which // case we would be able to turn this into a kind of GetByVal. @@ -900,93 +1195,257 @@ private: break; } - case Phantom: - case Identity: case Check: { - switch (node->child1().useKind()) { - case NumberUse: - if (node->child1()->shouldSpeculateInt32ForArithmetic()) - node->child1().setUseKind(Int32Use); - break; - default: - break; - } - observeUseKindOnEdge(node->child1()); + m_graph.doToChildren( + node, + [&] (Edge& edge) { + switch (edge.useKind()) { + case NumberUse: + if (edge->shouldSpeculateInt32ForArithmetic()) + edge.setUseKind(Int32Use); + break; + default: + break; + } + observeUseKindOnEdge(edge); + }); + break; + } + + case Phantom: + // Phantoms are meaningless past Fixup. We recreate them on-demand in the backend. + node->remove(); + break; + + case FiatInt52: { + RELEASE_ASSERT(enableInt52()); + node->convertToIdentity(); + fixEdge<Int52RepUse>(node->child1()); + node->setResult(NodeResultInt52); + break; + } + + case GetArrayLength: { + fixEdge<KnownCellUse>(node->child1()); + break; + } + + case GetTypedArrayByteOffset: { + fixEdge<KnownCellUse>(node->child1()); break; } - case GetArrayLength: case Phi: case Upsilon: - case GetArgument: - case PhantomPutStructure: case GetIndexedPropertyStorage: - case GetTypedArrayByteOffset: case LastNodeType: case CheckTierUpInLoop: case CheckTierUpAtReturn: case CheckTierUpAndOSREnter: - case Int52ToDouble: - case Int52ToValue: + case CheckTierUpWithNestedTriggerAndOSREnter: case InvalidationPoint: case CheckArray: case CheckInBounds: case ConstantStoragePointer: case DoubleAsInt32: - case Int32ToDouble: case ValueToInt32: + case DoubleRep: + case ValueRep: + case Int52Rep: + case Int52Constant: + case Identity: // This should have been cleaned up. + case BooleanToNumber: + case PhantomNewObject: + case PhantomNewFunction: + case PhantomNewGeneratorFunction: + case PhantomCreateActivation: + case PhantomDirectArguments: + case PhantomClonedArguments: + case ForwardVarargs: + case GetMyArgumentByVal: + case PutHint: + case CheckStructureImmediate: + case MaterializeNewObject: + case MaterializeCreateActivation: + case PutStack: + case KillStack: + case GetStack: + case StoreBarrier: // These are just nodes that we don't currently expect to see during fixup. // If we ever wanted to insert them prior to fixup, then we just have to create // fixup rules for them. - RELEASE_ASSERT_NOT_REACHED(); + DFG_CRASH(m_graph, node, "Unexpected node during fixup"); break; - case PutGlobalVar: { - Node* globalObjectNode = m_insertionSet.insertNode(m_indexInBlock, SpecNone, WeakJSConstant, node->codeOrigin, - OpInfo(m_graph.globalObjectFor(node->codeOrigin))); - Node* barrierNode = m_graph.addNode(SpecNone, ConditionalStoreBarrier, m_currentNode->codeOrigin, - Edge(globalObjectNode, KnownCellUse), Edge(node->child1().node(), UntypedUse)); - fixupNode(barrierNode); - m_insertionSet.insert(m_indexInBlock, barrierNode); - break; - } - - case TearOffActivation: { - Node* barrierNode = m_graph.addNode(SpecNone, StoreBarrierWithNullCheck, m_currentNode->codeOrigin, - Edge(node->child1().node(), UntypedUse)); - fixupNode(barrierNode); - m_insertionSet.insert(m_indexInBlock, barrierNode); + case PutGlobalVariable: { + fixEdge<CellUse>(node->child1()); + speculateForBarrier(node->child2()); break; } case IsString: if (node->child1()->shouldSpeculateString()) { - m_insertionSet.insertNode(m_indexInBlock, SpecNone, Phantom, node->codeOrigin, + m_insertionSet.insertNode( + m_indexInBlock, SpecNone, Check, node->origin, Edge(node->child1().node(), StringUse)); m_graph.convertToConstant(node, jsBoolean(true)); observeUseKindOnNode<StringUse>(node); } break; + + case IsObject: + if (node->child1()->shouldSpeculateObject()) { + m_insertionSet.insertNode( + m_indexInBlock, SpecNone, Check, node->origin, + Edge(node->child1().node(), ObjectUse)); + m_graph.convertToConstant(node, jsBoolean(true)); + observeUseKindOnNode<ObjectUse>(node); + } + break; + + case GetEnumerableLength: { + fixEdge<CellUse>(node->child1()); + break; + } + case HasGenericProperty: { + fixEdge<CellUse>(node->child2()); + break; + } + case HasStructureProperty: { + fixEdge<StringUse>(node->child2()); + fixEdge<KnownCellUse>(node->child3()); + break; + } + case HasIndexedProperty: { + node->setArrayMode( + node->arrayMode().refine( + m_graph, node, + node->child1()->prediction(), + node->child2()->prediction(), + SpecNone)); + blessArrayOperation(node->child1(), node->child2(), node->child3()); + fixEdge<CellUse>(node->child1()); + fixEdge<KnownInt32Use>(node->child2()); + break; + } + case GetDirectPname: { + Edge& base = m_graph.varArgChild(node, 0); + Edge& property = m_graph.varArgChild(node, 1); + Edge& index = m_graph.varArgChild(node, 2); + Edge& enumerator = m_graph.varArgChild(node, 3); + fixEdge<CellUse>(base); + fixEdge<KnownCellUse>(property); + fixEdge<KnownInt32Use>(index); + fixEdge<KnownCellUse>(enumerator); + break; + } + case GetPropertyEnumerator: { + fixEdge<CellUse>(node->child1()); + break; + } + case GetEnumeratorStructurePname: { + fixEdge<KnownCellUse>(node->child1()); + fixEdge<KnownInt32Use>(node->child2()); + break; + } + case GetEnumeratorGenericPname: { + fixEdge<KnownCellUse>(node->child1()); + fixEdge<KnownInt32Use>(node->child2()); + break; + } + case ToIndexString: { + fixEdge<KnownInt32Use>(node->child1()); + break; + } + case ProfileType: { + // We want to insert type checks based on the instructionTypeSet of the TypeLocation, not the globalTypeSet. + // Because the instructionTypeSet is contained in globalTypeSet, if we produce a type check for + // type T for the instructionTypeSet, the global type set must also have information for type T. + // So if it the type check succeeds for type T in the instructionTypeSet, a type check for type T + // in the globalTypeSet would've also succeeded. + // (The other direction does not hold in general). + + RefPtr<TypeSet> typeSet = node->typeLocation()->m_instructionTypeSet; + RuntimeTypeMask seenTypes = typeSet->seenTypes(); + if (typeSet->doesTypeConformTo(TypeMachineInt)) { + if (node->child1()->shouldSpeculateInt32()) + fixEdge<Int32Use>(node->child1()); + else + fixEdge<MachineIntUse>(node->child1()); + node->remove(); + } else if (typeSet->doesTypeConformTo(TypeNumber | TypeMachineInt)) { + fixEdge<NumberUse>(node->child1()); + node->remove(); + } else if (typeSet->doesTypeConformTo(TypeString)) { + fixEdge<StringUse>(node->child1()); + node->remove(); + } else if (typeSet->doesTypeConformTo(TypeBoolean)) { + fixEdge<BooleanUse>(node->child1()); + node->remove(); + } else if (typeSet->doesTypeConformTo(TypeUndefined | TypeNull) && (seenTypes & TypeUndefined) && (seenTypes & TypeNull)) { + fixEdge<OtherUse>(node->child1()); + node->remove(); + } else if (typeSet->doesTypeConformTo(TypeObject)) { + StructureSet set = typeSet->structureSet(); + if (!set.isEmpty()) { + fixEdge<CellUse>(node->child1()); + node->convertToCheckStructure(m_graph.addStructureSet(set)); + } + } + + break; + } + + case CreateScopedArguments: + case CreateActivation: + case NewFunction: + case NewGeneratorFunction: { + fixEdge<CellUse>(node->child1()); + break; + } + + case NewArrowFunction: { + fixEdge<CellUse>(node->child1()); + fixEdge<CellUse>(node->child2()); + break; + } + + case CopyRest: { + fixEdge<KnownCellUse>(node->child1()); + fixEdge<KnownInt32Use>(node->child2()); + break; + } + #if !ASSERT_DISABLED // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes. case SetArgument: case JSConstant: - case WeakJSConstant: + case DoubleConstant: case GetLocal: case GetCallee: + case GetArgumentCount: + case GetRestLength: case Flush: case PhantomLocal: case GetLocalUnlinked: - case GetMyScope: - case GetClosureVar: case GetGlobalVar: + case GetGlobalLexicalVariable: case NotifyWrite: - case VariableWatchpoint: case VarInjectionWatchpoint: - case AllocationProfileWatchpoint: case Call: + case CheckTypeInfoFlags: + case TailCallInlinedCaller: case Construct: + case CallVarargs: + case TailCallVarargsInlinedCaller: + case ConstructVarargs: + case CallForwardVarargs: + case ConstructForwardVarargs: + case TailCallForwardVarargs: + case TailCallForwardVarargsInlinedCaller: + case LoadVarargs: + case ProfileControlFlow: case NewObject: case NewArrayBuffer: case NewRegexp: @@ -996,58 +1455,56 @@ private: case IsUndefined: case IsBoolean: case IsNumber: - case IsObject: + case IsObjectOrNull: case IsFunction: - case CreateActivation: - case CreateArguments: - case PhantomArguments: - case TearOffArguments: - case GetMyArgumentsLength: - case GetMyArgumentsLengthSafe: - case CheckArgumentsNotCreated: - case NewFunction: - case NewFunctionNoCheck: - case NewFunctionExpression: + case CreateDirectArguments: + case CreateClonedArguments: case Jump: case Return: + case TailCall: + case TailCallVarargs: case Throw: case ThrowReferenceError: case CountExecution: case ForceOSRExit: + case CheckBadCell: + case CheckNotEmpty: case CheckWatchdogTimer: case Unreachable: case ExtractOSREntryLocal: case LoopHint: - case StoreBarrier: - case ConditionalStoreBarrier: - case StoreBarrierWithNullCheck: - case FunctionReentryWatchpoint: - case TypedArrayWatchpoint: case MovHint: case ZombieHint: + case ExitOK: + case BottomValue: + case TypeOf: break; #else default: break; #endif } - - if (!node->containsMovHint()) - DFG_NODE_DO_TO_CHILDREN(m_graph, node, observeUntypedEdge); } - - void observeUntypedEdge(Node*, Edge& edge) + + void watchHavingABadTime(Node* node) { - if (edge.useKind() != UntypedUse) - return; - fixEdge<UntypedUse>(edge); + JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic); + + // If this global object is not having a bad time, watch it. We go down this path anytime the code + // does an array allocation. The types of array allocations may change if we start to have a bad + // time. It's easier to reason about this if we know that whenever the types change after we start + // optimizing, the code just gets thrown out. Doing this at FixupPhase is just early enough, since + // prior to this point nobody should have been doing optimizations based on the indexing type of + // the allocation. + if (!globalObject->isHavingABadTime()) + m_graph.watchpoints().addLazily(globalObject->havingABadTimeWatchpoint()); } template<UseKind useKind> void createToString(Node* node, Edge& edge) { edge.setNode(m_insertionSet.insertNode( - m_indexInBlock, SpecString, ToString, node->codeOrigin, + m_indexInBlock, SpecString, ToString, node->origin, Edge(edge.node(), useKind))); } @@ -1056,7 +1513,7 @@ private: { ASSERT(arrayMode == ArrayMode(Array::Generic)); - if (!canOptimizeStringObjectAccess(node->codeOrigin)) + if (!m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) return; createToString<useKind>(node, node->child1()); @@ -1079,19 +1536,14 @@ private: void convertStringAddUse(Node* node, Edge& edge) { if (useKind == StringUse) { - // This preserves the binaryUseKind() invariant ot ValueAdd: ValueAdd's - // two edges will always have identical use kinds, which makes the - // decision process much easier. observeUseKindOnNode<StringUse>(edge.node()); m_insertionSet.insertNode( - m_indexInBlock, SpecNone, Phantom, node->codeOrigin, + m_indexInBlock, SpecNone, Check, node->origin, Edge(edge.node(), StringUse)); edge.setUseKind(KnownStringUse); return; } - // FIXME: We ought to be able to have a ToPrimitiveToString node. - observeUseKindOnNode<useKind>(edge.node()); createToString<useKind>(node, edge); } @@ -1109,9 +1561,9 @@ private: if (!edge) break; edge.setUseKind(KnownStringUse); - if (!m_graph.isConstant(edge.node())) + JSString* string = edge->dynamicCastConstant<JSString*>(); + if (!string) continue; - JSString* string = jsCast<JSString*>(m_graph.valueOfJSConstant(edge.node()).asCell()); if (string->length()) continue; @@ -1127,6 +1579,85 @@ private: node->convertToIdentity(); } } + + void fixupToThis(Node* node) + { + ECMAMode ecmaMode = m_graph.executableFor(node->origin.semantic)->isStrictMode() ? StrictMode : NotStrictMode; + + if (ecmaMode == StrictMode) { + if (node->child1()->shouldSpeculateBoolean()) { + fixEdge<BooleanUse>(node->child1()); + node->convertToIdentity(); + return; + } + + if (node->child1()->shouldSpeculateInt32()) { + fixEdge<Int32Use>(node->child1()); + node->convertToIdentity(); + return; + } + + if (enableInt52() && node->child1()->shouldSpeculateMachineInt()) { + fixEdge<Int52RepUse>(node->child1()); + node->convertToIdentity(); + node->setResult(NodeResultInt52); + return; + } + + if (node->child1()->shouldSpeculateNumber()) { + fixEdge<DoubleRepUse>(node->child1()); + node->convertToIdentity(); + node->setResult(NodeResultDouble); + return; + } + + if (node->child1()->shouldSpeculateSymbol()) { + fixEdge<SymbolUse>(node->child1()); + node->convertToIdentity(); + return; + } + + if (node->child1()->shouldSpeculateStringIdent()) { + fixEdge<StringIdentUse>(node->child1()); + node->convertToIdentity(); + return; + } + + if (node->child1()->shouldSpeculateString()) { + fixEdge<StringUse>(node->child1()); + node->convertToIdentity(); + return; + } + } + + if (node->child1()->shouldSpeculateOther()) { + if (ecmaMode == StrictMode) { + fixEdge<OtherUse>(node->child1()); + node->convertToIdentity(); + return; + } + + m_insertionSet.insertNode( + m_indexInBlock, SpecNone, Check, node->origin, + Edge(node->child1().node(), OtherUse)); + observeUseKindOnNode<OtherUse>(node->child1().node()); + m_graph.convertToConstant( + node, m_graph.globalThisObjectFor(node->origin.semantic)); + return; + } + + if (node->child1()->shouldSpeculateStringObject()) { + fixEdge<StringObjectUse>(node->child1()); + node->convertToIdentity(); + return; + } + + if (isFinalObjectSpeculation(node->child1()->prediction())) { + fixEdge<FinalObjectUse>(node->child1()); + node->convertToIdentity(); + return; + } + } void fixupToPrimitive(Node* node) { @@ -1143,21 +1674,21 @@ private: } if (node->child1()->shouldSpeculateStringObject() - && canOptimizeStringObjectAccess(node->codeOrigin)) { + && m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { fixEdge<StringObjectUse>(node->child1()); node->convertToToString(); return; } if (node->child1()->shouldSpeculateStringOrStringObject() - && canOptimizeStringObjectAccess(node->codeOrigin)) { + && m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { fixEdge<StringOrStringObjectUse>(node->child1()); node->convertToToString(); return; } } - void fixupToString(Node* node) + void fixupToStringOrCallStringConstructor(Node* node) { if (node->child1()->shouldSpeculateString()) { fixEdge<StringUse>(node->child1()); @@ -1166,13 +1697,13 @@ private: } if (node->child1()->shouldSpeculateStringObject() - && canOptimizeStringObjectAccess(node->codeOrigin)) { + && m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { fixEdge<StringObjectUse>(node->child1()); return; } if (node->child1()->shouldSpeculateStringOrStringObject() - && canOptimizeStringObjectAccess(node->codeOrigin)) { + && m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { fixEdge<StringOrStringObjectUse>(node->child1()); return; } @@ -1182,110 +1713,50 @@ private: return; } } - - template<UseKind leftUseKind> - bool attemptToMakeFastStringAdd(Node* node, Edge& left, Edge& right) + + bool attemptToMakeFastStringAdd(Node* node) { - Node* originalLeft = left.node(); - Node* originalRight = right.node(); - - ASSERT(leftUseKind == StringUse || leftUseKind == StringObjectUse || leftUseKind == StringOrStringObjectUse); - - if (isStringObjectUse<leftUseKind>() && !canOptimizeStringObjectAccess(node->codeOrigin)) + bool goodToGo = true; + m_graph.doToChildren( + node, + [&] (Edge& edge) { + if (edge->shouldSpeculateString()) + return; + if (m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { + if (edge->shouldSpeculateStringObject()) + return; + if (edge->shouldSpeculateStringOrStringObject()) + return; + } + goodToGo = false; + }); + if (!goodToGo) return false; - - convertStringAddUse<leftUseKind>(node, left); - - if (right->shouldSpeculateString()) - convertStringAddUse<StringUse>(node, right); - else if (right->shouldSpeculateStringObject() && canOptimizeStringObjectAccess(node->codeOrigin)) - convertStringAddUse<StringObjectUse>(node, right); - else if (right->shouldSpeculateStringOrStringObject() && canOptimizeStringObjectAccess(node->codeOrigin)) - convertStringAddUse<StringOrStringObjectUse>(node, right); - else { - // At this point we know that the other operand is something weird. The semantically correct - // way of dealing with this is: - // - // MakeRope(@left, ToString(ToPrimitive(@right))) - // - // So that's what we emit. NB, we need to do all relevant type checks on @left before we do - // anything to @right, since ToPrimitive may be effectful. - - Node* toPrimitive = m_insertionSet.insertNode( - m_indexInBlock, resultOfToPrimitive(right->prediction()), ToPrimitive, node->codeOrigin, - Edge(right.node())); - Node* toString = m_insertionSet.insertNode( - m_indexInBlock, SpecString, ToString, node->codeOrigin, Edge(toPrimitive)); - - fixupToPrimitive(toPrimitive); - fixupToString(toString); - - right.setNode(toString); - } - - // We're doing checks up there, so we need to make sure that the - // *original* inputs to the addition are live up to here. - m_insertionSet.insertNode( - m_indexInBlock, SpecNone, Phantom, node->codeOrigin, - Edge(originalLeft), Edge(originalRight)); + + m_graph.doToChildren( + node, + [&] (Edge& edge) { + if (edge->shouldSpeculateString()) { + convertStringAddUse<StringUse>(node, edge); + return; + } + ASSERT(m_graph.canOptimizeStringObjectAccess(node->origin.semantic)); + if (edge->shouldSpeculateStringObject()) { + convertStringAddUse<StringObjectUse>(node, edge); + return; + } + if (edge->shouldSpeculateStringOrStringObject()) { + convertStringAddUse<StringOrStringObjectUse>(node, edge); + return; + } + RELEASE_ASSERT_NOT_REACHED(); + }); convertToMakeRope(node); return true; } - - bool isStringPrototypeMethodSane(Structure* stringPrototypeStructure, StringImpl* uid) - { - unsigned attributesUnused; - JSCell* specificValue; - PropertyOffset offset = stringPrototypeStructure->getConcurrently( - vm(), uid, attributesUnused, specificValue); - if (!isValidOffset(offset)) - return false; - - if (!specificValue) - return false; - - if (!specificValue->inherits(JSFunction::info())) - return false; - - JSFunction* function = jsCast<JSFunction*>(specificValue); - if (function->executable()->intrinsicFor(CodeForCall) != StringPrototypeValueOfIntrinsic) - return false; - - return true; - } - - bool canOptimizeStringObjectAccess(const CodeOrigin& codeOrigin) - { - if (m_graph.hasExitSite(codeOrigin, NotStringObject)) - return false; - - Structure* stringObjectStructure = m_graph.globalObjectFor(codeOrigin)->stringObjectStructure(); - ASSERT(stringObjectStructure->storedPrototype().isObject()); - ASSERT(stringObjectStructure->storedPrototype().asCell()->classInfo() == StringPrototype::info()); - - JSObject* stringPrototypeObject = asObject(stringObjectStructure->storedPrototype()); - Structure* stringPrototypeStructure = stringPrototypeObject->structure(); - if (!m_graph.watchpoints().isStillValid(stringPrototypeStructure->transitionWatchpointSet())) - return false; - - if (stringPrototypeStructure->isDictionary()) - return false; - - // We're being conservative here. We want DFG's ToString on StringObject to be - // used in both numeric contexts (that would call valueOf()) and string contexts - // (that would call toString()). We don't want the DFG to have to distinguish - // between the two, just because that seems like it would get confusing. So we - // just require both methods to be sane. - if (!isStringPrototypeMethodSane(stringPrototypeStructure, vm().propertyNames->valueOf.impl())) - return false; - if (!isStringPrototypeMethodSane(stringPrototypeStructure, vm().propertyNames->toString.impl())) - return false; - - return true; - } - - void fixupSetLocalsInBlock(BasicBlock* block) + + void fixupGetAndSetLocalsInBlock(BasicBlock* block) { if (!block) return; @@ -1293,28 +1764,52 @@ private: m_block = block; for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) { Node* node = m_currentNode = block->at(m_indexInBlock); - if (node->op() != SetLocal) + if (node->op() != SetLocal && node->op() != GetLocal) continue; VariableAccessData* variable = node->variableAccessData(); - switch (variable->flushFormat()) { - case FlushedJSValue: - break; - case FlushedDouble: - fixEdge<NumberUse>(node->child1()); - break; - case FlushedInt32: - fixEdge<Int32Use>(node->child1()); - break; - case FlushedInt52: - fixEdge<MachineIntUse>(node->child1()); - break; - case FlushedCell: - fixEdge<CellUse>(node->child1()); + switch (node->op()) { + case GetLocal: + switch (variable->flushFormat()) { + case FlushedDouble: + node->setResult(NodeResultDouble); + break; + case FlushedInt52: + node->setResult(NodeResultInt52); + break; + default: + break; + } break; - case FlushedBoolean: - fixEdge<BooleanUse>(node->child1()); + + case SetLocal: + // NOTE: Any type checks we put here may get hoisted by fixupChecksInBlock(). So, if we + // add new type checking use kind for SetLocals, we need to modify that code as well. + + switch (variable->flushFormat()) { + case FlushedJSValue: + break; + case FlushedDouble: + fixEdge<DoubleRepUse>(node->child1()); + break; + case FlushedInt32: + fixEdge<Int32Use>(node->child1()); + break; + case FlushedInt52: + fixEdge<Int52RepUse>(node->child1()); + break; + case FlushedCell: + fixEdge<CellUse>(node->child1()); + break; + case FlushedBoolean: + fixEdge<BooleanUse>(node->child1()); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } break; + default: RELEASE_ASSERT_NOT_REACHED(); break; @@ -1323,54 +1818,38 @@ private: m_insertionSet.execute(block); } - void fixupUntypedSetLocalsInBlock(BasicBlock* block) - { - if (!block) - return; - ASSERT(block->isReachable); - m_block = block; - for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) { - Node* node = m_currentNode = block->at(m_indexInBlock); - if (node->op() != SetLocal) - continue; - - if (node->child1().useKind() == UntypedUse) - fixEdge<UntypedUse>(node->child1()); - } - m_insertionSet.execute(block); - } - - Node* checkArray(ArrayMode arrayMode, const CodeOrigin& codeOrigin, Node* array, Node* index, bool (*storageCheck)(const ArrayMode&) = canCSEStorage) + Node* checkArray(ArrayMode arrayMode, const NodeOrigin& origin, Node* array, Node* index, bool (*storageCheck)(const ArrayMode&) = canCSEStorage) { ASSERT(arrayMode.isSpecific()); if (arrayMode.type() == Array::String) { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, Phantom, codeOrigin, - Edge(array, StringUse)); + m_indexInBlock, SpecNone, Check, origin, Edge(array, StringUse)); } else { - Structure* structure = arrayMode.originalArrayStructure(m_graph, codeOrigin); + // Note that we only need to be using a structure check if we opt for SaneChain, since + // that needs to protect against JSArray's __proto__ being changed. + Structure* structure = arrayMode.originalArrayStructure(m_graph, origin.semantic); Edge indexEdge = index ? Edge(index, Int32Use) : Edge(); - + if (arrayMode.doesConversion()) { if (structure) { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, ArrayifyToStructure, codeOrigin, + m_indexInBlock, SpecNone, ArrayifyToStructure, origin, OpInfo(structure), OpInfo(arrayMode.asWord()), Edge(array, CellUse), indexEdge); } else { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, Arrayify, codeOrigin, + m_indexInBlock, SpecNone, Arrayify, origin, OpInfo(arrayMode.asWord()), Edge(array, CellUse), indexEdge); } } else { if (structure) { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, CheckStructure, codeOrigin, + m_indexInBlock, SpecNone, CheckStructure, origin, OpInfo(m_graph.addStructureSet(structure)), Edge(array, CellUse)); } else { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, CheckArray, codeOrigin, + m_indexInBlock, SpecNone, CheckArray, origin, OpInfo(arrayMode.asWord()), Edge(array, CellUse)); } } @@ -1381,11 +1860,11 @@ private: if (arrayMode.usesButterfly()) { return m_insertionSet.insertNode( - m_indexInBlock, SpecNone, GetButterfly, codeOrigin, Edge(array, CellUse)); + m_indexInBlock, SpecNone, GetButterfly, origin, Edge(array, CellUse)); } return m_insertionSet.insertNode( - m_indexInBlock, SpecNone, GetIndexedPropertyStorage, codeOrigin, + m_indexInBlock, SpecNone, GetIndexedPropertyStorage, origin, OpInfo(arrayMode.asWord()), Edge(array, KnownCellUse)); } @@ -1396,7 +1875,7 @@ private: switch (node->arrayMode().type()) { case Array::ForceExit: { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin); + m_indexInBlock, SpecNone, ForceOSRExit, node->origin); return; } @@ -1409,7 +1888,7 @@ private: return; default: { - Node* storage = checkArray(node->arrayMode(), node->codeOrigin, base.node(), index.node()); + Node* storage = checkArray(node->arrayMode(), node->origin, base.node(), index.node()); if (!storage) return; @@ -1453,29 +1932,35 @@ private: VariableAccessData* variable = node->variableAccessData(); switch (useKind) { case Int32Use: + case KnownInt32Use: if (alwaysUnboxSimplePrimitives() || isInt32Speculation(variable->prediction())) m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true); break; case NumberUse: case RealNumberUse: + case DoubleRepUse: + case DoubleRepRealUse: if (variable->doubleFormatState() == UsingDoubleFormat) m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true); break; case BooleanUse: + case KnownBooleanUse: if (alwaysUnboxSimplePrimitives() || isBooleanSpeculation(variable->prediction())) m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true); break; - case MachineIntUse: + case Int52RepUse: if (isMachineIntSpeculation(variable->prediction())) m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true); break; case CellUse: case KnownCellUse: case ObjectUse: + case FunctionUse: case StringUse: case KnownStringUse: + case SymbolUse: case StringObjectUse: case StringOrStringObjectUse: if (alwaysUnboxSimplePrimitives() @@ -1487,179 +1972,131 @@ private: } } - // Set the use kind of the edge and perform any actions that need to be done for - // that use kind, like inserting intermediate conversion nodes. Never call this - // with useKind = UntypedUse explicitly; edges have UntypedUse implicitly and any - // edge that survives fixup and still has UntypedUse will have this method called - // from observeUntypedEdge(). Also, make sure that if you do change the type of an - // edge, you either call fixEdge() or perform the equivalent functionality - // yourself. Obviously, you should have a really good reason if you do the latter. template<UseKind useKind> void fixEdge(Edge& edge) { - if (isDouble(useKind)) { - if (edge->shouldSpeculateInt32ForArithmetic()) { - injectInt32ToDoubleNode(edge, useKind); - return; - } + observeUseKindOnNode<useKind>(edge.node()); + edge.setUseKind(useKind); + } + + void speculateForBarrier(Edge value) + { + // Currently, the DFG won't take advantage of this speculation. But, we want to do it in + // the DFG anyway because if such a speculation would be wrong, we want to know before + // we do an expensive compile. + + if (value->shouldSpeculateInt32()) { + insertCheck<Int32Use>(m_indexInBlock, value.node()); + return; + } - if (enableInt52() && edge->shouldSpeculateMachineInt()) { - // Make all double uses of int52 values have an intermediate Int52ToDouble. - // This is for the same reason as Int52ToValue (see below) except that - // Int8ToDouble will convert int52's that fit in an int32 into a double - // rather than trying to create a boxed int32 like Int52ToValue does. - - Node* result = m_insertionSet.insertNode( - m_indexInBlock, SpecInt52AsDouble, Int52ToDouble, - m_currentNode->codeOrigin, Edge(edge.node(), NumberUse)); - edge = Edge(result, useKind); - return; - } + if (value->shouldSpeculateBoolean()) { + insertCheck<BooleanUse>(m_indexInBlock, value.node()); + return; } - - if (enableInt52() && useKind != MachineIntUse - && edge->shouldSpeculateMachineInt() && !edge->shouldSpeculateInt32()) { - // We make all non-int52 uses of int52 values have an intermediate Int52ToValue - // node to ensure that we handle this properly: - // - // a: SomeInt52 - // b: ArithAdd(@a, ...) - // c: Call(..., @a) - // d: ArithAdd(@a, ...) - // - // Without an intermediate node and just labeling the uses, we will get: - // - // a: SomeInt52 - // b: ArithAdd(Int52:@a, ...) - // c: Call(..., Untyped:@a) - // d: ArithAdd(Int52:@a, ...) - // - // And now the c->Untyped:@a edge will box the value of @a into a double. This - // is bad, because now the d->Int52:@a edge will either have to do double-to-int - // conversions, or will have to OSR exit unconditionally. Alternatively we could - // have the c->Untyped:@a edge box the value by copying rather than in-place. - // But these boxings are also costly so this wouldn't be great. - // - // The solution we use is to always have non-Int52 uses of predicted Int52's use - // an intervening Int52ToValue node: - // - // a: SomeInt52 - // b: ArithAdd(Int52:@a, ...) - // x: Int52ToValue(Int52:@a) - // c: Call(..., Untyped:@x) - // d: ArithAdd(Int52:@a, ...) - // - // Note that even if we had multiple non-int52 uses of @a, the multiple - // Int52ToValue's would get CSE'd together. So the boxing would only happen once. - // At the same time, @a would continue to be represented as a native int52. - // - // An alternative would have been to insert ToNativeInt52 nodes on int52 uses of - // int52's. This would have handled the above example but would fall over for: - // - // a: SomeInt52 - // b: Call(..., @a) - // c: ArithAdd(@a, ...) - // - // But the solution we use handles the above gracefully. - - Node* result = m_insertionSet.insertNode( - m_indexInBlock, SpecInt52, Int52ToValue, - m_currentNode->codeOrigin, Edge(edge.node(), UntypedUse)); - edge = Edge(result, useKind); + + if (value->shouldSpeculateOther()) { + insertCheck<OtherUse>(m_indexInBlock, value.node()); + return; + } + + if (value->shouldSpeculateNumber()) { + insertCheck<NumberUse>(m_indexInBlock, value.node()); + return; + } + + if (value->shouldSpeculateNotCell()) { + insertCheck<NotCellUse>(m_indexInBlock, value.node()); return; } - - observeUseKindOnNode<useKind>(edge.node()); - - edge.setUseKind(useKind); } - void insertStoreBarrier(unsigned indexInBlock, Edge child1, Edge child2 = Edge()) + template<UseKind useKind> + void insertCheck(unsigned indexInBlock, Node* node) { - Node* barrierNode; - if (!child2) - barrierNode = m_graph.addNode(SpecNone, StoreBarrier, m_currentNode->codeOrigin, Edge(child1.node(), child1.useKind())); - else { - barrierNode = m_graph.addNode(SpecNone, ConditionalStoreBarrier, m_currentNode->codeOrigin, - Edge(child1.node(), child1.useKind()), Edge(child2.node(), child2.useKind())); - } - fixupNode(barrierNode); - m_insertionSet.insert(indexInBlock, barrierNode); + observeUseKindOnNode<useKind>(node); + m_insertionSet.insertNode( + indexInBlock, SpecNone, Check, m_currentNode->origin, Edge(node, useKind)); } - bool fixIntEdge(Edge& edge) + void fixIntConvertingEdge(Edge& edge) { Node* node = edge.node(); - if (node->shouldSpeculateInt32()) { - fixEdge<Int32Use>(edge); - return false; + if (node->shouldSpeculateInt32OrBoolean()) { + fixIntOrBooleanEdge(edge); + return; } UseKind useKind; if (node->shouldSpeculateMachineInt()) - useKind = MachineIntUse; + useKind = Int52RepUse; else if (node->shouldSpeculateNumber()) - useKind = NumberUse; - else if (node->shouldSpeculateBoolean()) - useKind = BooleanUse; + useKind = DoubleRepUse; else useKind = NotCellUse; Node* newNode = m_insertionSet.insertNode( - m_indexInBlock, SpecInt32, ValueToInt32, m_currentNode->codeOrigin, + m_indexInBlock, SpecInt32, ValueToInt32, m_currentNode->origin, Edge(node, useKind)); observeUseKindOnNode(node, useKind); edge = Edge(newNode, KnownInt32Use); - return true; } - void fixBinaryIntEdges() + void fixIntOrBooleanEdge(Edge& edge) { - AdjacencyList children = m_currentNode->children; + Node* node = edge.node(); + if (!node->sawBooleans()) { + fixEdge<Int32Use>(edge); + return; + } - // Call fixIntEdge() on both edges. - bool needPhantom = - fixIntEdge(m_currentNode->child1()) | fixIntEdge(m_currentNode->child2()); + UseKind useKind; + if (node->shouldSpeculateBoolean()) + useKind = BooleanUse; + else + useKind = UntypedUse; + Node* newNode = m_insertionSet.insertNode( + m_indexInBlock, SpecInt32, BooleanToNumber, m_currentNode->origin, + Edge(node, useKind)); + observeUseKindOnNode(node, useKind); - if (!needPhantom) - return; - m_insertionSet.insertNode(m_indexInBlock + 1, SpecNone, Phantom, m_currentNode->codeOrigin, children); + edge = Edge(newNode, Int32Use); } - void injectInt32ToDoubleNode(Edge& edge, UseKind useKind = NumberUse) + void fixDoubleOrBooleanEdge(Edge& edge) { - Node* result = m_insertionSet.insertNode( - m_indexInBlock, SpecInt52AsDouble, Int32ToDouble, - m_currentNode->codeOrigin, Edge(edge.node(), NumberUse)); + Node* node = edge.node(); + if (!node->sawBooleans()) { + fixEdge<DoubleRepUse>(edge); + return; + } + + UseKind useKind; + if (node->shouldSpeculateBoolean()) + useKind = BooleanUse; + else + useKind = UntypedUse; + Node* newNode = m_insertionSet.insertNode( + m_indexInBlock, SpecInt32, BooleanToNumber, m_currentNode->origin, + Edge(node, useKind)); + observeUseKindOnNode(node, useKind); - edge = Edge(result, useKind); + edge = Edge(newNode, DoubleRepUse); } void truncateConstantToInt32(Edge& edge) { Node* oldNode = edge.node(); - ASSERT(oldNode->hasConstant()); - JSValue value = m_graph.valueOfJSConstant(oldNode); + JSValue value = oldNode->asJSValue(); if (value.isInt32()) return; value = jsNumber(JSC::toInt32(value.asNumber())); ASSERT(value.isInt32()); - unsigned constantRegister; - if (!codeBlock()->findConstant(value, constantRegister)) { - constantRegister = codeBlock()->addConstantLazily(); - initializeLazyWriteBarrierForConstant( - m_graph.m_plan.writeBarriers, - codeBlock()->constants()[constantRegister], - codeBlock(), - constantRegister, - codeBlock()->ownerExecutable(), - value); - } edge.setNode(m_insertionSet.insertNode( - m_indexInBlock, SpecInt32, JSConstant, m_currentNode->codeOrigin, - OpInfo(constantRegister))); + m_indexInBlock, SpecInt32, JSConstant, m_currentNode->origin, + OpInfo(m_graph.freeze(value)))); } void truncateConstantsIfNecessary(Node* node, AddSpeculationMode mode) @@ -1676,11 +2113,11 @@ private: bool attemptToMakeIntegerAdd(Node* node) { - AddSpeculationMode mode = m_graph.addSpeculationMode(node); + AddSpeculationMode mode = m_graph.addSpeculationMode(node, FixupPass); if (mode != DontSpeculateInt32) { truncateConstantsIfNecessary(node, mode); - fixEdge<Int32Use>(node->child1()); - fixEdge<Int32Use>(node->child2()); + fixIntOrBooleanEdge(node->child1()); + fixIntOrBooleanEdge(node->child2()); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->setArithMode(Arith::Unchecked); else @@ -1689,9 +2126,10 @@ private: } if (m_graph.addShouldSpeculateMachineInt(node)) { - fixEdge<MachineIntUse>(node->child1()); - fixEdge<MachineIntUse>(node->child2()); + fixEdge<Int52RepUse>(node->child1()); + fixEdge<Int52RepUse>(node->child2()); node->setArithMode(Arith::CheckOverflow); + node->setResult(NodeResultInt52); return true; } @@ -1702,9 +2140,9 @@ private: { if (!isInt32Speculation(node->prediction())) return false; - CodeBlock* profiledBlock = m_graph.baselineCodeBlockFor(node->codeOrigin); + CodeBlock* profiledBlock = m_graph.baselineCodeBlockFor(node->origin.semantic); ArrayProfile* arrayProfile = - profiledBlock->getArrayProfile(node->codeOrigin.bytecodeIndex); + profiledBlock->getArrayProfile(node->origin.semantic.bytecodeIndex); ArrayMode arrayMode = ArrayMode(Array::SelectUsingPredictions); if (arrayProfile) { ConcurrentJITLocker locker(profiledBlock->m_lock); @@ -1722,7 +2160,8 @@ private: } } - arrayMode = arrayMode.refine(node->child1()->prediction(), node->prediction()); + arrayMode = arrayMode.refine( + m_graph, node, node->child1()->prediction(), node->prediction()); if (arrayMode.type() == Array::Generic) { // Check if the input is something that we can't get array length for, but for which we @@ -1740,80 +2179,227 @@ private: convertToGetArrayLength(node, arrayMode); return true; } - - bool attemptToMakeGetTypedArrayByteLength(Node* node) - { - if (!isInt32Speculation(node->prediction())) - return false; - - TypedArrayType type = typedArrayTypeFromSpeculation(node->child1()->prediction()); - if (!isTypedView(type)) - return false; - - if (elementSize(type) == 1) { - convertToGetArrayLength(node, ArrayMode(toArrayType(type))); - return true; - } - - Node* length = prependGetArrayLength( - node->codeOrigin, node->child1().node(), ArrayMode(toArrayType(type))); - - Node* shiftAmount = m_insertionSet.insertNode( - m_indexInBlock, SpecInt32, JSConstant, node->codeOrigin, - OpInfo(m_graph.constantRegisterForConstant(jsNumber(logElementSize(type))))); - - // We can use a BitLShift here because typed arrays will never have a byteLength - // that overflows int32. - node->setOp(BitLShift); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); - observeUseKindOnNode(length, Int32Use); - observeUseKindOnNode(shiftAmount, Int32Use); - node->child1() = Edge(length, Int32Use); - node->child2() = Edge(shiftAmount, Int32Use); - return true; - } - + void convertToGetArrayLength(Node* node, ArrayMode arrayMode) { node->setOp(GetArrayLength); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + node->clearFlags(NodeMustGenerate); fixEdge<KnownCellUse>(node->child1()); node->setArrayMode(arrayMode); - Node* storage = checkArray(arrayMode, node->codeOrigin, node->child1().node(), 0, lengthNeedsStorage); + Node* storage = checkArray(arrayMode, node->origin, node->child1().node(), 0, lengthNeedsStorage); if (!storage) return; node->child2() = Edge(storage); } - Node* prependGetArrayLength(CodeOrigin codeOrigin, Node* child, ArrayMode arrayMode) + Node* prependGetArrayLength(NodeOrigin origin, Node* child, ArrayMode arrayMode) { - Node* storage = checkArray(arrayMode, codeOrigin, child, 0, lengthNeedsStorage); + Node* storage = checkArray(arrayMode, origin, child, 0, lengthNeedsStorage); return m_insertionSet.insertNode( - m_indexInBlock, SpecInt32, GetArrayLength, codeOrigin, + m_indexInBlock, SpecInt32, GetArrayLength, origin, OpInfo(arrayMode.asWord()), Edge(child, KnownCellUse), Edge(storage)); } - bool attemptToMakeGetTypedArrayByteOffset(Node* node) + void fixupChecksInBlock(BasicBlock* block) { - if (!isInt32Speculation(node->prediction())) - return false; - - TypedArrayType type = typedArrayTypeFromSpeculation(node->child1()->prediction()); - if (!isTypedView(type)) - return false; - - checkArray( - ArrayMode(toArrayType(type)), node->codeOrigin, node->child1().node(), - 0, neverNeedsStorage); + if (!block) + return; + ASSERT(block->isReachable); + m_block = block; + unsigned indexForChecks = UINT_MAX; + NodeOrigin originForChecks; + for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { + Node* node = block->at(indexInBlock); + + // If this is a node at which we could exit, then save its index. If nodes after this one + // cannot exit, then we will hoist checks to here. + if (node->origin.exitOK) { + indexForChecks = indexInBlock; + originForChecks = node->origin; + } + + originForChecks = originForChecks.withSemantic(node->origin.semantic); + + // First, try to relax the representational demands of each node, in order to have + // fewer conversions. + switch (node->op()) { + case MovHint: + case Check: + m_graph.doToChildren( + node, + [&] (Edge& edge) { + switch (edge.useKind()) { + case DoubleRepUse: + case DoubleRepRealUse: + if (edge->hasDoubleResult()) + break; + + if (edge->hasInt52Result()) + edge.setUseKind(Int52RepUse); + else if (edge.useKind() == DoubleRepUse) + edge.setUseKind(NumberUse); + break; + + case Int52RepUse: + // Nothing we can really do. + break; + + case UntypedUse: + case NumberUse: + if (edge->hasDoubleResult()) + edge.setUseKind(DoubleRepUse); + else if (edge->hasInt52Result()) + edge.setUseKind(Int52RepUse); + break; + + case RealNumberUse: + if (edge->hasDoubleResult()) + edge.setUseKind(DoubleRepRealUse); + else if (edge->hasInt52Result()) + edge.setUseKind(Int52RepUse); + break; + + default: + break; + } + }); + break; + + case ValueToInt32: + if (node->child1().useKind() == DoubleRepUse + && !node->child1()->hasDoubleResult()) { + node->child1().setUseKind(NumberUse); + break; + } + break; + + default: + break; + } + + // Now, insert type conversions if necessary. + m_graph.doToChildren( + node, + [&] (Edge& edge) { + Node* result = nullptr; + + switch (edge.useKind()) { + case DoubleRepUse: + case DoubleRepRealUse: + case DoubleRepMachineIntUse: { + if (edge->hasDoubleResult()) + break; + + if (edge->isNumberConstant()) { + result = m_insertionSet.insertNode( + indexForChecks, SpecBytecodeDouble, DoubleConstant, originForChecks, + OpInfo(m_graph.freeze(jsDoubleNumber(edge->asNumber())))); + } else if (edge->hasInt52Result()) { + result = m_insertionSet.insertNode( + indexForChecks, SpecInt52AsDouble, DoubleRep, originForChecks, + Edge(edge.node(), Int52RepUse)); + } else { + UseKind useKind; + if (edge->shouldSpeculateDoubleReal()) + useKind = RealNumberUse; + else if (edge->shouldSpeculateNumber()) + useKind = NumberUse; + else + useKind = NotCellUse; + + result = m_insertionSet.insertNode( + indexForChecks, SpecBytecodeDouble, DoubleRep, originForChecks, + Edge(edge.node(), useKind)); + } + + edge.setNode(result); + break; + } + + case Int52RepUse: { + if (edge->hasInt52Result()) + break; + + if (edge->isMachineIntConstant()) { + result = m_insertionSet.insertNode( + indexForChecks, SpecMachineInt, Int52Constant, originForChecks, + OpInfo(edge->constant())); + } else if (edge->hasDoubleResult()) { + result = m_insertionSet.insertNode( + indexForChecks, SpecMachineInt, Int52Rep, originForChecks, + Edge(edge.node(), DoubleRepMachineIntUse)); + } else if (edge->shouldSpeculateInt32ForArithmetic()) { + result = m_insertionSet.insertNode( + indexForChecks, SpecInt32, Int52Rep, originForChecks, + Edge(edge.node(), Int32Use)); + } else { + result = m_insertionSet.insertNode( + indexForChecks, SpecMachineInt, Int52Rep, originForChecks, + Edge(edge.node(), MachineIntUse)); + } + + edge.setNode(result); + break; + } + + default: { + if (!edge->hasDoubleResult() && !edge->hasInt52Result()) + break; + + if (edge->hasDoubleResult()) { + result = m_insertionSet.insertNode( + indexForChecks, SpecBytecodeDouble, ValueRep, originForChecks, + Edge(edge.node(), DoubleRepUse)); + } else { + result = m_insertionSet.insertNode( + indexForChecks, SpecInt32 | SpecInt52AsDouble, ValueRep, + originForChecks, Edge(edge.node(), Int52RepUse)); + } + + edge.setNode(result); + break; + } } + + // It's remotely possible that this node cannot do type checks, but we now have a + // type check on this node. We don't have to handle the general form of this + // problem. It only arises when ByteCodeParser emits an immediate SetLocal, rather + // than a delayed one. So, we only worry about those checks that we may have put on + // a SetLocal. Note that "indexForChecks != indexInBlock" is just another way of + // saying "!node->origin.exitOK". + if (indexForChecks != indexInBlock && mayHaveTypeCheck(edge.useKind())) { + UseKind knownUseKind; + + switch (edge.useKind()) { + case Int32Use: + knownUseKind = KnownInt32Use; + break; + case CellUse: + knownUseKind = KnownCellUse; + break; + case BooleanUse: + knownUseKind = KnownBooleanUse; + break; + default: + // This can only arise if we have a Check node, and in that case, we can + // just remove the original check. + DFG_ASSERT(m_graph, node, node->op() == Check); + knownUseKind = UntypedUse; + break; + } + + m_insertionSet.insertNode( + indexForChecks, SpecNone, Check, originForChecks, edge); + + edge.setUseKind(knownUseKind); + } + }); + } - node->setOp(GetTypedArrayByteOffset); - node->clearFlags(NodeMustGenerate | NodeClobbersWorld); - fixEdge<KnownCellUse>(node->child1()); - return true; + m_insertionSet.execute(block); } - + BasicBlock* m_block; unsigned m_indexInBlock; Node* m_currentNode; |
