diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGFixupPhase.cpp')
| -rw-r--r-- | Source/JavaScriptCore/dfg/DFGFixupPhase.cpp | 1926 |
1 files changed, 670 insertions, 1256 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp index 64d7f63c9..185f03591 100644 --- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp +++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2016 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013, 2014 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,15 +28,12 @@ #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 "JSCInlines.h" -#include "TypeLocation.h" +#include "Operations.h" namespace JSC { namespace DFG { @@ -64,14 +61,12 @@ public: m_graph.m_argumentPositions[i].mergeArgumentUnboxingAwareness(); for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) - fixupGetAndSetLocalsInBlock(m_graph.block(blockIndex)); + fixupSetLocalsInBlock(m_graph.block(blockIndex)); } for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) - fixupChecksInBlock(m_graph.block(blockIndex)); - - m_graph.m_planStage = PlanStage::AfterFixup; - + fixupUntypedSetLocalsInBlock(m_graph.block(blockIndex)); + return true; } @@ -95,7 +90,7 @@ private: switch (op) { case SetLocal: { - // This gets handled by fixupGetAndSetLocalsInBlock(). + // This gets handled by fixupSetLocalsInBlock(). return; } @@ -105,81 +100,67 @@ private: case BitRShift: case BitLShift: case BitURShift: { - 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()); + fixBinaryIntEdges(); break; } case ArithIMul: { - fixIntConvertingEdge(node->child1()); - fixIntConvertingEdge(node->child2()); + fixBinaryIntEdges(); 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: { - fixIntConvertingEdge(node->child1()); + fixIntEdge(node->child1()); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->convertToIdentity(); - else if (node->canSpeculateInt32(FixupPass)) + else if (nodeCanSpeculateInt32(node->arithNodeFlags())) 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::shouldSpeculateNumberOrBooleanExpectingDefined(node->child1().node(), node->child2().node())) { - fixDoubleOrBooleanEdge(node->child1()); - fixDoubleOrBooleanEdge(node->child2()); + if (Node::shouldSpeculateNumberExpectingDefined(node->child1().node(), node->child2().node())) { + fixEdge<NumberUse>(node->child1()); + fixEdge<NumberUse>(node->child2()); node->setOp(ArithAdd); - node->setResult(NodeResultDouble); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } - if (attemptToMakeFastStringAdd(node)) + // 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())) break; - - fixEdge<UntypedUse>(node->child1()); - fixEdge<UntypedUse>(node->child2()); - node->setResult(NodeResultJS); - break; - } - - case StrCat: { - if (attemptToMakeFastStringAdd(node)) + 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())) 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; } @@ -190,26 +171,16 @@ 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; - fixDoubleOrBooleanEdge(node->child1()); - fixDoubleOrBooleanEdge(node->child2()); - node->setResult(NodeResultDouble); + fixEdge<NumberUse>(node->child1()); + fixEdge<NumberUse>(node->child2()); break; } case ArithNegate: { - if (m_graph.unaryArithShouldSpeculateInt32(node, FixupPass)) { - fixIntOrBooleanEdge(node->child1()); + if (m_graph.negateShouldSpeculateInt32(node)) { + fixEdge<Int32Use>(node->child1()); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->setArithMode(Arith::Unchecked); else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) @@ -218,33 +189,22 @@ private: node->setArithMode(Arith::CheckOverflowAndNegativeZero); break; } - if (m_graph.unaryArithShouldSpeculateMachineInt(node, FixupPass)) { - fixEdge<Int52RepUse>(node->child1()); + if (m_graph.negateShouldSpeculateMachineInt(node)) { + fixEdge<MachineIntUse>(node->child1()); if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) node->setArithMode(Arith::CheckOverflow); else node->setArithMode(Arith::CheckOverflowAndNegativeZero); - node->setResult(NodeResultInt52); break; } - fixDoubleOrBooleanEdge(node->child1()); - node->setResult(NodeResultDouble); + fixEdge<NumberUse>(node->child1()); break; } case ArithMul: { - 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 (m_graph.mulShouldSpeculateInt32(node)) { + fixEdge<Int32Use>(node->child1()); + fixEdge<Int32Use>(node->child2()); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->setArithMode(Arith::Unchecked); else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) @@ -253,38 +213,27 @@ private: node->setArithMode(Arith::CheckOverflowAndNegativeZero); break; } - if (m_graph.binaryArithShouldSpeculateMachineInt(node, FixupPass)) { - fixEdge<Int52RepUse>(leftChild); - fixEdge<Int52RepUse>(rightChild); + if (m_graph.mulShouldSpeculateMachineInt(node)) { + fixEdge<MachineIntUse>(node->child1()); + fixEdge<MachineIntUse>(node->child2()); if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) node->setArithMode(Arith::CheckOverflow); else node->setArithMode(Arith::CheckOverflowAndNegativeZero); - node->setResult(NodeResultInt52); break; } - fixDoubleOrBooleanEdge(leftChild); - fixDoubleOrBooleanEdge(rightChild); - node->setResult(NodeResultDouble); + fixEdge<NumberUse>(node->child1()); + fixEdge<NumberUse>(node->child2()); break; } case ArithDiv: case ArithMod: { - 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 (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) + && node->canSpeculateInt32()) { + if (optimizeForX86() || optimizeForARM64() || optimizeForARMv7s()) { + fixEdge<Int32Use>(node->child1()); + fixEdge<Int32Use>(node->child2()); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->setArithMode(Arith::Unchecked); else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())) @@ -293,133 +242,85 @@ private: node->setArithMode(Arith::CheckOverflowAndNegativeZero); break; } + Edge child1 = node->child1(); + Edge child2 = node->child2(); - // This will cause conversion nodes to be inserted later. - fixDoubleOrBooleanEdge(leftChild); - fixDoubleOrBooleanEdge(rightChild); - + injectInt32ToDoubleNode(node->child1()); + injectInt32ToDoubleNode(node->child2()); + // 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, SpecBytecodeDouble, *node); - newDivision->setResult(NodeResultDouble); + m_indexInBlock, SpecDouble, *node); node->setOp(DoubleAsInt32); - node->children.initialize(Edge(newDivision, DoubleRepUse), Edge(), Edge()); + node->children.initialize(Edge(newDivision, KnownNumberUse), 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; } - fixDoubleOrBooleanEdge(leftChild); - fixDoubleOrBooleanEdge(rightChild); - node->setResult(NodeResultDouble); + fixEdge<NumberUse>(node->child1()); + fixEdge<NumberUse>(node->child2()); break; } case ArithMin: case ArithMax: { - if (m_graph.binaryArithShouldSpeculateInt32(node, FixupPass)) { - fixIntOrBooleanEdge(node->child1()); - fixIntOrBooleanEdge(node->child2()); + if (Node::shouldSpeculateInt32ForArithmetic(node->child1().node(), node->child2().node()) + && node->canSpeculateInt32()) { + fixEdge<Int32Use>(node->child1()); + fixEdge<Int32Use>(node->child2()); break; } - fixDoubleOrBooleanEdge(node->child1()); - fixDoubleOrBooleanEdge(node->child2()); - node->setResult(NodeResultDouble); + fixEdge<NumberUse>(node->child1()); + fixEdge<NumberUse>(node->child2()); break; } case ArithAbs: { - 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; - } - - 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(); + if (node->child1()->shouldSpeculateInt32ForArithmetic() + && node->canSpeculateInt32()) { + fixEdge<Int32Use>(node->child1()); 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); - } + fixEdge<NumberUse>(node->child1()); break; } case ArithSqrt: - case ArithFRound: case ArithSin: - case ArithCos: - case ArithLog: { - fixDoubleOrBooleanEdge(node->child1()); - node->setResult(NodeResultDouble); + case ArithCos: { + fixEdge<NumberUse>(node->child1()); break; } case LogicalNot: { - 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()) + if (node->child1()->shouldSpeculateBoolean()) + fixEdge<BooleanUse>(node->child1()); + else if (node->child1()->shouldSpeculateObjectOrOther()) fixEdge<ObjectOrOtherUse>(node->child1()); - else if (node->child1()->shouldSpeculateInt32OrBoolean()) - fixIntOrBooleanEdge(node->child1()); + else if (node->child1()->shouldSpeculateInt32()) + fixEdge<Int32Use>(node->child1()); else if (node->child1()->shouldSpeculateNumber()) - fixEdge<DoubleRepUse>(node->child1()); + fixEdge<NumberUse>(node->child1()); else if (node->child1()->shouldSpeculateString()) fixEdge<StringUse>(node->child1()); - else if (node->child1()->shouldSpeculateStringOrOther()) - fixEdge<StringOrOtherUse>(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: { break; } @@ -428,97 +329,67 @@ private: case CompareLessEq: case CompareGreater: case CompareGreaterEq: { - 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); + if (Node::shouldSpeculateInt32(node->child1().node(), node->child2().node())) { + fixEdge<Int32Use>(node->child1()); + fixEdge<Int32Use>(node->child2()); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } if (enableInt52() && Node::shouldSpeculateMachineInt(node->child1().node(), node->child2().node())) { - fixEdge<Int52RepUse>(node->child1()); - fixEdge<Int52RepUse>(node->child2()); - node->clearFlags(NodeMustGenerate); + fixEdge<MachineIntUse>(node->child1()); + fixEdge<MachineIntUse>(node->child2()); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } - if (Node::shouldSpeculateNumberOrBoolean(node->child1().node(), node->child2().node())) { - fixDoubleOrBooleanEdge(node->child1()); - fixDoubleOrBooleanEdge(node->child2()); - node->clearFlags(NodeMustGenerate); + if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) { + fixEdge<NumberUse>(node->child1()); + fixEdge<NumberUse>(node->child2()); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } if (node->op() != CompareEq) break; - if (Node::shouldSpeculateSymbol(node->child1().node(), node->child2().node())) { - fixEdge<SymbolUse>(node->child1()); - fixEdge<SymbolUse>(node->child2()); - node->clearFlags(NodeMustGenerate); + if (Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) { + fixEdge<BooleanUse>(node->child1()); + fixEdge<BooleanUse>(node->child2()); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) { fixEdge<StringIdentUse>(node->child1()); fixEdge<StringIdentUse>(node->child2()); - node->clearFlags(NodeMustGenerate); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && GPRInfo::numberOfRegisters >= 7) { fixEdge<StringUse>(node->child1()); fixEdge<StringUse>(node->child2()); - node->clearFlags(NodeMustGenerate); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) { fixEdge<ObjectUse>(node->child1()); fixEdge<ObjectUse>(node->child2()); - 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); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } - if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObjectOrOther()) { fixEdge<ObjectUse>(node->child1()); fixEdge<ObjectOrOtherUse>(node->child2()); - node->clearFlags(NodeMustGenerate); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } if (node->child1()->shouldSpeculateObjectOrOther() && node->child2()->shouldSpeculateObject()) { fixEdge<ObjectOrOtherUse>(node->child1()); fixEdge<ObjectUse>(node->child2()); - node->clearFlags(NodeMustGenerate); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } - + break; + } + + case CompareStrictEqConstant: { break; } @@ -535,18 +406,13 @@ private: } if (enableInt52() && Node::shouldSpeculateMachineInt(node->child1().node(), node->child2().node())) { - fixEdge<Int52RepUse>(node->child1()); - fixEdge<Int52RepUse>(node->child2()); + fixEdge<MachineIntUse>(node->child1()); + fixEdge<MachineIntUse>(node->child2()); break; } if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) { - 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()); + fixEdge<NumberUse>(node->child1()); + fixEdge<NumberUse>(node->child2()); break; } if (node->child1()->shouldSpeculateStringIdent() && node->child2()->shouldSpeculateStringIdent()) { @@ -554,66 +420,21 @@ private: fixEdge<StringIdentUse>(node->child2()); break; } - if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && ((GPRInfo::numberOfRegisters >= 7) || isFTL(m_graph.m_plan.mode))) { + if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && GPRInfo::numberOfRegisters >= 7) { fixEdge<StringUse>(node->child1()); fixEdge<StringUse>(node->child2()); break; } - 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()) { + 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: - if (node->child1()->shouldSpeculateInt32()) - fixEdge<Int32Use>(node->child1()); - else - fixEdge<UntypedUse>(node->child1()); + fixEdge<Int32Use>(node->child1()); break; case StringCharAt: @@ -627,84 +448,27 @@ 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)); + SpecNone, node->flags())); 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) { - 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)); - } - } - } + && arrayMode.speculation() == Array::InBounds + && m_graph.globalObjectFor(node->codeOrigin)->arrayPrototypeChainIsSane() + && !(node->flags() & NodeBytecodeUsesAsOther)) + node->setArrayMode(arrayMode.withSpeculation(Array::SaneChain)); break; case Array::String: if ((node->prediction() & ~SpecString) - || m_graph.hasExitSite(node->origin.semantic, OutOfBounds)) + || m_graph.hasExitSite(node->codeOrigin, OutOfBounds)) node->setArrayMode(arrayMode.withSpeculation(Array::OutOfBounds)); break; @@ -712,10 +476,10 @@ private: break; } - arrayMode = node->arrayMode(); - switch (arrayMode.type()) { + switch (node->arrayMode().type()) { case Array::SelectUsingPredictions: case Array::Unprofiled: + case Array::Undecided: RELEASE_ASSERT_NOT_REACHED(); break; case Array::Generic: @@ -731,30 +495,6 @@ 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; } @@ -767,7 +507,6 @@ private: node->setArrayMode( node->arrayMode().refine( - m_graph, node, child1->prediction(), child2->prediction(), child3->prediction())); @@ -776,7 +515,6 @@ private: switch (node->arrayMode().modeForPut().type()) { case Array::SelectUsingPredictions: - case Array::SelectUsingArguments: case Array::Unprofiled: case Array::Undecided: RELEASE_ASSERT_NOT_REACHED(); @@ -794,11 +532,15 @@ 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<DoubleRepRealUse>(child3); + fixEdge<RealNumberUse>(child3); break; case Array::Int8Array: case Array::Int16Array: @@ -810,24 +552,25 @@ private: fixEdge<KnownCellUse>(child1); fixEdge<Int32Use>(child2); if (child3->shouldSpeculateInt32()) - fixIntOrBooleanEdge(child3); + fixEdge<Int32Use>(child3); else if (child3->shouldSpeculateMachineInt()) - fixEdge<Int52RepUse>(child3); + fixEdge<MachineIntUse>(child3); else - fixDoubleOrBooleanEdge(child3); + fixEdge<NumberUse>(child3); break; case Array::Float32Array: case Array::Float64Array: fixEdge<KnownCellUse>(child1); fixEdge<Int32Use>(child2); - fixDoubleOrBooleanEdge(child3); + fixEdge<NumberUse>(child3); break; case Array::Contiguous: case Array::ArrayStorage: case Array::SlowPutArrayStorage: + case Array::Arguments: fixEdge<KnownCellUse>(child1); fixEdge<Int32Use>(child2); - speculateForBarrier(child3); + insertStoreBarrier(m_indexInBlock, child1, child3); break; default: fixEdge<KnownCellUse>(child1); @@ -849,7 +592,6 @@ private: // that would break things. node->setArrayMode( node->arrayMode().refine( - m_graph, node, node->child1()->prediction() & SpecCell, SpecInt32, node->child2()->prediction())); @@ -861,11 +603,11 @@ private: fixEdge<Int32Use>(node->child2()); break; case Array::Double: - fixEdge<DoubleRepRealUse>(node->child2()); + fixEdge<RealNumberUse>(node->child2()); break; case Array::Contiguous: case Array::ArrayStorage: - speculateForBarrier(node->child2()); + insertStoreBarrier(m_indexInBlock, node->child1(), node->child2()); break; default: break; @@ -881,52 +623,54 @@ private: case RegExpExec: case RegExpTest: { - // 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; - } + fixEdge<CellUse>(node->child1()); + fixEdge<CellUse>(node->child2()); break; } case Branch: { - 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()) + if (node->child1()->shouldSpeculateBoolean()) + fixEdge<BooleanUse>(node->child1()); + else if (node->child1()->shouldSpeculateObjectOrOther()) fixEdge<ObjectOrOtherUse>(node->child1()); - else if (node->child1()->shouldSpeculateInt32OrBoolean()) - fixIntOrBooleanEdge(node->child1()); + else if (node->child1()->shouldSpeculateInt32()) + fixEdge<Int32Use>(node->child1()); else if (node->child1()->shouldSpeculateNumber()) - fixEdge<DoubleRepUse>(node->child1()); - else if (node->child1()->shouldSpeculateString()) - fixEdge<StringUse>(node->child1()); - else if (node->child1()->shouldSpeculateStringOrOther()) - fixEdge<StringOrOtherUse>(node->child1()); + 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); + } + } + } break; } @@ -947,12 +691,6 @@ 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; } @@ -962,9 +700,8 @@ private: break; } - case ToString: - case CallStringConstructor: { - fixupToStringOrCallStringConstructor(node); + case ToString: { + fixupToString(node); break; } @@ -974,8 +711,6 @@ private: } case NewArray: { - watchHavingABadTime(node); - for (unsigned i = m_graph.varArgNumChildren(node); i--;) { node->setIndexingType( leastUpperBoundOfIndexingTypeAndType( @@ -991,7 +726,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->origin); + m_indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin); } break; case ALL_INT32_INDEXING_TYPES: @@ -1000,7 +735,7 @@ private: break; case ALL_DOUBLE_INDEXING_TYPES: for (unsigned operandIndex = 0; operandIndex < node->numChildren(); ++operandIndex) - fixEdge<DoubleRepRealUse>(m_graph.m_varArgChildren[node->firstChild() + operandIndex]); + fixEdge<RealNumberUse>(m_graph.m_varArgChildren[node->firstChild() + operandIndex]); break; case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: @@ -1013,49 +748,68 @@ private: } case NewTypedArray: { - watchHavingABadTime(node); - if (node->child1()->shouldSpeculateInt32()) { fixEdge<Int32Use>(node->child1()); - node->clearFlags(NodeMustGenerate); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); break; } break; } case NewArrayWithSize: { - watchHavingABadTime(node); fixEdge<Int32Use>(node->child1()); break; } case ToThis: { - fixupToThis(node); + 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; + } + break; } - case PutStructure: { - fixEdge<KnownCellUse>(node->child1()); + case GetMyArgumentByVal: + case GetMyArgumentByValSafe: { + fixEdge<Int32Use>(node->child1()); break; } - case GetClosureVar: - case GetFromArguments: { + case PutStructure: { fixEdge<KnownCellUse>(node->child1()); + insertStoreBarrier(m_indexInBlock, node->child1()); break; } - case PutClosureVar: - case PutToArguments: { + case PutClosureVar: { fixEdge<KnownCellUse>(node->child1()); - speculateForBarrier(node->child2()); + insertStoreBarrier(m_indexInBlock, node->child1(), node->child3()); break; } + case GetClosureRegisters: + case SkipTopScope: case SkipScope: - case GetScope: - case GetGetter: - case GetSetter: { + case GetScope: { fixEdge<KnownCellUse>(node->child1()); break; } @@ -1063,80 +817,48 @@ private: case AllocatePropertyStorage: case ReallocatePropertyStorage: { fixEdge<KnownCellUse>(node->child1()); + insertStoreBarrier(m_indexInBlock + 1, node->child1()); break; } case GetById: case GetByIdFlush: { - // 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; - } + if (!node->child1()->shouldSpeculateCell()) + break; + StringImpl* impl = m_graph.identifiers()[node->identifierNumber()]; + if (impl == vm().propertyNames->length.impl()) { + attemptToMakeGetArrayLength(node); + break; } - - if (node->child1()->shouldSpeculateCell()) - fixEdge<CellUse>(node->child1()); + if (impl == vm().propertyNames->byteLength.impl()) { + attemptToMakeGetTypedArrayByteLength(node); + break; + } + if (impl == vm().propertyNames->byteOffset.impl()) { + attemptToMakeGetTypedArrayByteOffset(node); + break; + } + fixEdge<CellUse>(node->child1()); break; } case PutById: - case PutByIdFlush: case PutByIdDirect: { fixEdge<CellUse>(node->child1()); + insertStoreBarrier(m_indexInBlock, node->child1(), node->child2()); break; } - 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 CheckExecutable: case CheckStructure: - case CheckCell: + case StructureTransitionWatchpoint: + case CheckFunction: + case CheckHasInstance: case CreateThis: - case GetButterfly: - case GetButterflyReadOnly: { + case GetButterfly: { 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: { @@ -1146,47 +868,30 @@ private: break; } - case GetByOffset: - case GetGetterSetterByOffset: { + case GetByOffset: { 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()); - 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()); + insertStoreBarrier(m_indexInBlock, node->child2(), node->child3()); 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. @@ -1195,257 +900,93 @@ private: break; } - case Check: { - 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()); + case Identity: + case Check: { + switch (node->child1().useKind()) { + case NumberUse: + if (node->child1()->shouldSpeculateInt32ForArithmetic()) + node->child1().setUseKind(Int32Use); + break; + default: + break; + } + observeUseKindOnEdge(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 CheckTierUpWithNestedTriggerAndOSREnter: + case Int52ToDouble: + case Int52ToValue: 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. - DFG_CRASH(m_graph, node, "Unexpected node during fixup"); + RELEASE_ASSERT_NOT_REACHED(); break; - case PutGlobalVariable: { - fixEdge<CellUse>(node->child1()); - speculateForBarrier(node->child2()); + 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); break; } case IsString: if (node->child1()->shouldSpeculateString()) { - m_insertionSet.insertNode( - m_indexInBlock, SpecNone, Check, node->origin, + m_insertionSet.insertNode(m_indexInBlock, SpecNone, Phantom, node->codeOrigin, 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 DoubleConstant: + case WeakJSConstant: 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: @@ -1455,56 +996,58 @@ private: case IsUndefined: case IsBoolean: case IsNumber: - case IsObjectOrNull: + case IsObject: case IsFunction: - case CreateDirectArguments: - case CreateClonedArguments: + case CreateActivation: + case CreateArguments: + case PhantomArguments: + case TearOffArguments: + case GetMyArgumentsLength: + case GetMyArgumentsLengthSafe: + case CheckArgumentsNotCreated: + case NewFunction: + case NewFunctionNoCheck: + case NewFunctionExpression: 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 watchHavingABadTime(Node* node) + + void observeUntypedEdge(Node*, Edge& 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()); + if (edge.useKind() != UntypedUse) + return; + fixEdge<UntypedUse>(edge); } template<UseKind useKind> void createToString(Node* node, Edge& edge) { edge.setNode(m_insertionSet.insertNode( - m_indexInBlock, SpecString, ToString, node->origin, + m_indexInBlock, SpecString, ToString, node->codeOrigin, Edge(edge.node(), useKind))); } @@ -1513,7 +1056,7 @@ private: { ASSERT(arrayMode == ArrayMode(Array::Generic)); - if (!m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) + if (!canOptimizeStringObjectAccess(node->codeOrigin)) return; createToString<useKind>(node, node->child1()); @@ -1536,14 +1079,19 @@ 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, Check, node->origin, + m_indexInBlock, SpecNone, Phantom, node->codeOrigin, 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); } @@ -1561,9 +1109,9 @@ private: if (!edge) break; edge.setUseKind(KnownStringUse); - JSString* string = edge->dynamicCastConstant<JSString*>(); - if (!string) + if (!m_graph.isConstant(edge.node())) continue; + JSString* string = jsCast<JSString*>(m_graph.valueOfJSConstant(edge.node()).asCell()); if (string->length()) continue; @@ -1579,85 +1127,6 @@ 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) { @@ -1674,21 +1143,21 @@ private: } if (node->child1()->shouldSpeculateStringObject() - && m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { + && canOptimizeStringObjectAccess(node->codeOrigin)) { fixEdge<StringObjectUse>(node->child1()); node->convertToToString(); return; } if (node->child1()->shouldSpeculateStringOrStringObject() - && m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { + && canOptimizeStringObjectAccess(node->codeOrigin)) { fixEdge<StringOrStringObjectUse>(node->child1()); node->convertToToString(); return; } } - void fixupToStringOrCallStringConstructor(Node* node) + void fixupToString(Node* node) { if (node->child1()->shouldSpeculateString()) { fixEdge<StringUse>(node->child1()); @@ -1697,13 +1166,13 @@ private: } if (node->child1()->shouldSpeculateStringObject() - && m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { + && canOptimizeStringObjectAccess(node->codeOrigin)) { fixEdge<StringObjectUse>(node->child1()); return; } if (node->child1()->shouldSpeculateStringOrStringObject() - && m_graph.canOptimizeStringObjectAccess(node->origin.semantic)) { + && canOptimizeStringObjectAccess(node->codeOrigin)) { fixEdge<StringOrStringObjectUse>(node->child1()); return; } @@ -1713,50 +1182,110 @@ private: return; } } - - bool attemptToMakeFastStringAdd(Node* node) + + template<UseKind leftUseKind> + bool attemptToMakeFastStringAdd(Node* node, Edge& left, Edge& right) { - 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) + Node* originalLeft = left.node(); + Node* originalRight = right.node(); + + ASSERT(leftUseKind == StringUse || leftUseKind == StringObjectUse || leftUseKind == StringOrStringObjectUse); + + if (isStringObjectUse<leftUseKind>() && !canOptimizeStringObjectAccess(node->codeOrigin)) return false; - - 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(); - }); + + 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)); convertToMakeRope(node); return true; } - - void fixupGetAndSetLocalsInBlock(BasicBlock* block) + + 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) { if (!block) return; @@ -1764,52 +1293,28 @@ 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 && node->op() != GetLocal) + if (node->op() != SetLocal) continue; VariableAccessData* variable = node->variableAccessData(); - switch (node->op()) { - case GetLocal: - switch (variable->flushFormat()) { - case FlushedDouble: - node->setResult(NodeResultDouble); - break; - case FlushedInt52: - node->setResult(NodeResultInt52); - break; - default: - break; - } + switch (variable->flushFormat()) { + case FlushedJSValue: break; - - 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; - } + 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()); + break; + case FlushedBoolean: + fixEdge<BooleanUse>(node->child1()); break; - default: RELEASE_ASSERT_NOT_REACHED(); break; @@ -1818,38 +1323,54 @@ private: m_insertionSet.execute(block); } - Node* checkArray(ArrayMode arrayMode, const NodeOrigin& origin, Node* array, Node* index, bool (*storageCheck)(const ArrayMode&) = canCSEStorage) + 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) { ASSERT(arrayMode.isSpecific()); if (arrayMode.type() == Array::String) { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, Check, origin, Edge(array, StringUse)); + m_indexInBlock, SpecNone, Phantom, codeOrigin, + Edge(array, StringUse)); } else { - // 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); + Structure* structure = arrayMode.originalArrayStructure(m_graph, codeOrigin); Edge indexEdge = index ? Edge(index, Int32Use) : Edge(); - + if (arrayMode.doesConversion()) { if (structure) { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, ArrayifyToStructure, origin, + m_indexInBlock, SpecNone, ArrayifyToStructure, codeOrigin, OpInfo(structure), OpInfo(arrayMode.asWord()), Edge(array, CellUse), indexEdge); } else { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, Arrayify, origin, + m_indexInBlock, SpecNone, Arrayify, codeOrigin, OpInfo(arrayMode.asWord()), Edge(array, CellUse), indexEdge); } } else { if (structure) { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, CheckStructure, origin, + m_indexInBlock, SpecNone, CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(structure)), Edge(array, CellUse)); } else { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, CheckArray, origin, + m_indexInBlock, SpecNone, CheckArray, codeOrigin, OpInfo(arrayMode.asWord()), Edge(array, CellUse)); } } @@ -1860,11 +1381,11 @@ private: if (arrayMode.usesButterfly()) { return m_insertionSet.insertNode( - m_indexInBlock, SpecNone, GetButterfly, origin, Edge(array, CellUse)); + m_indexInBlock, SpecNone, GetButterfly, codeOrigin, Edge(array, CellUse)); } return m_insertionSet.insertNode( - m_indexInBlock, SpecNone, GetIndexedPropertyStorage, origin, + m_indexInBlock, SpecNone, GetIndexedPropertyStorage, codeOrigin, OpInfo(arrayMode.asWord()), Edge(array, KnownCellUse)); } @@ -1875,7 +1396,7 @@ private: switch (node->arrayMode().type()) { case Array::ForceExit: { m_insertionSet.insertNode( - m_indexInBlock, SpecNone, ForceOSRExit, node->origin); + m_indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin); return; } @@ -1888,7 +1409,7 @@ private: return; default: { - Node* storage = checkArray(node->arrayMode(), node->origin, base.node(), index.node()); + Node* storage = checkArray(node->arrayMode(), node->codeOrigin, base.node(), index.node()); if (!storage) return; @@ -1932,35 +1453,29 @@ 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 Int52RepUse: + case MachineIntUse: 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() @@ -1972,131 +1487,179 @@ 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) { - 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 (value->shouldSpeculateBoolean()) { - insertCheck<BooleanUse>(m_indexInBlock, value.node()); - return; - } - - if (value->shouldSpeculateOther()) { - insertCheck<OtherUse>(m_indexInBlock, value.node()); - return; - } + if (isDouble(useKind)) { + if (edge->shouldSpeculateInt32ForArithmetic()) { + injectInt32ToDoubleNode(edge, useKind); + return; + } - if (value->shouldSpeculateNumber()) { - insertCheck<NumberUse>(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->shouldSpeculateNotCell()) { - insertCheck<NotCellUse>(m_indexInBlock, value.node()); + + 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); return; } + + observeUseKindOnNode<useKind>(edge.node()); + + edge.setUseKind(useKind); } - template<UseKind useKind> - void insertCheck(unsigned indexInBlock, Node* node) + void insertStoreBarrier(unsigned indexInBlock, Edge child1, Edge child2 = Edge()) { - observeUseKindOnNode<useKind>(node); - m_insertionSet.insertNode( - indexInBlock, SpecNone, Check, m_currentNode->origin, Edge(node, useKind)); + 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); } - void fixIntConvertingEdge(Edge& edge) + bool fixIntEdge(Edge& edge) { Node* node = edge.node(); - if (node->shouldSpeculateInt32OrBoolean()) { - fixIntOrBooleanEdge(edge); - return; + if (node->shouldSpeculateInt32()) { + fixEdge<Int32Use>(edge); + return false; } UseKind useKind; if (node->shouldSpeculateMachineInt()) - useKind = Int52RepUse; + useKind = MachineIntUse; else if (node->shouldSpeculateNumber()) - useKind = DoubleRepUse; + useKind = NumberUse; + else if (node->shouldSpeculateBoolean()) + useKind = BooleanUse; else useKind = NotCellUse; Node* newNode = m_insertionSet.insertNode( - m_indexInBlock, SpecInt32, ValueToInt32, m_currentNode->origin, + m_indexInBlock, SpecInt32, ValueToInt32, m_currentNode->codeOrigin, Edge(node, useKind)); observeUseKindOnNode(node, useKind); edge = Edge(newNode, KnownInt32Use); + return true; } - void fixIntOrBooleanEdge(Edge& edge) + void fixBinaryIntEdges() { - Node* node = edge.node(); - if (!node->sawBooleans()) { - fixEdge<Int32Use>(edge); - return; - } + AdjacencyList children = m_currentNode->children; - 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); + // Call fixIntEdge() on both edges. + bool needPhantom = + fixIntEdge(m_currentNode->child1()) | fixIntEdge(m_currentNode->child2()); - edge = Edge(newNode, Int32Use); + if (!needPhantom) + return; + m_insertionSet.insertNode(m_indexInBlock + 1, SpecNone, Phantom, m_currentNode->codeOrigin, children); } - void fixDoubleOrBooleanEdge(Edge& edge) + void injectInt32ToDoubleNode(Edge& edge, UseKind useKind = 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); + Node* result = m_insertionSet.insertNode( + m_indexInBlock, SpecInt52AsDouble, Int32ToDouble, + m_currentNode->codeOrigin, Edge(edge.node(), NumberUse)); - edge = Edge(newNode, DoubleRepUse); + edge = Edge(result, useKind); } void truncateConstantToInt32(Edge& edge) { Node* oldNode = edge.node(); - JSValue value = oldNode->asJSValue(); + ASSERT(oldNode->hasConstant()); + JSValue value = m_graph.valueOfJSConstant(oldNode); 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->origin, - OpInfo(m_graph.freeze(value)))); + m_indexInBlock, SpecInt32, JSConstant, m_currentNode->codeOrigin, + OpInfo(constantRegister))); } void truncateConstantsIfNecessary(Node* node, AddSpeculationMode mode) @@ -2113,11 +1676,11 @@ private: bool attemptToMakeIntegerAdd(Node* node) { - AddSpeculationMode mode = m_graph.addSpeculationMode(node, FixupPass); + AddSpeculationMode mode = m_graph.addSpeculationMode(node); if (mode != DontSpeculateInt32) { truncateConstantsIfNecessary(node, mode); - fixIntOrBooleanEdge(node->child1()); - fixIntOrBooleanEdge(node->child2()); + fixEdge<Int32Use>(node->child1()); + fixEdge<Int32Use>(node->child2()); if (bytecodeCanTruncateInteger(node->arithNodeFlags())) node->setArithMode(Arith::Unchecked); else @@ -2126,10 +1689,9 @@ private: } if (m_graph.addShouldSpeculateMachineInt(node)) { - fixEdge<Int52RepUse>(node->child1()); - fixEdge<Int52RepUse>(node->child2()); + fixEdge<MachineIntUse>(node->child1()); + fixEdge<MachineIntUse>(node->child2()); node->setArithMode(Arith::CheckOverflow); - node->setResult(NodeResultInt52); return true; } @@ -2140,9 +1702,9 @@ private: { if (!isInt32Speculation(node->prediction())) return false; - CodeBlock* profiledBlock = m_graph.baselineCodeBlockFor(node->origin.semantic); + CodeBlock* profiledBlock = m_graph.baselineCodeBlockFor(node->codeOrigin); ArrayProfile* arrayProfile = - profiledBlock->getArrayProfile(node->origin.semantic.bytecodeIndex); + profiledBlock->getArrayProfile(node->codeOrigin.bytecodeIndex); ArrayMode arrayMode = ArrayMode(Array::SelectUsingPredictions); if (arrayProfile) { ConcurrentJITLocker locker(profiledBlock->m_lock); @@ -2160,8 +1722,7 @@ private: } } - arrayMode = arrayMode.refine( - m_graph, node, node->child1()->prediction(), node->prediction()); + arrayMode = arrayMode.refine(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 @@ -2179,227 +1740,80 @@ 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); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); fixEdge<KnownCellUse>(node->child1()); node->setArrayMode(arrayMode); - Node* storage = checkArray(arrayMode, node->origin, node->child1().node(), 0, lengthNeedsStorage); + Node* storage = checkArray(arrayMode, node->codeOrigin, node->child1().node(), 0, lengthNeedsStorage); if (!storage) return; node->child2() = Edge(storage); } - Node* prependGetArrayLength(NodeOrigin origin, Node* child, ArrayMode arrayMode) + Node* prependGetArrayLength(CodeOrigin codeOrigin, Node* child, ArrayMode arrayMode) { - Node* storage = checkArray(arrayMode, origin, child, 0, lengthNeedsStorage); + Node* storage = checkArray(arrayMode, codeOrigin, child, 0, lengthNeedsStorage); return m_insertionSet.insertNode( - m_indexInBlock, SpecInt32, GetArrayLength, origin, + m_indexInBlock, SpecInt32, GetArrayLength, codeOrigin, OpInfo(arrayMode.asWord()), Edge(child, KnownCellUse), Edge(storage)); } - void fixupChecksInBlock(BasicBlock* block) + bool attemptToMakeGetTypedArrayByteOffset(Node* node) { - 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); - } - }); - } + if (!isInt32Speculation(node->prediction())) + return false; - m_insertionSet.execute(block); + TypedArrayType type = typedArrayTypeFromSpeculation(node->child1()->prediction()); + if (!isTypedView(type)) + return false; + + checkArray( + ArrayMode(toArrayType(type)), node->codeOrigin, node->child1().node(), + 0, neverNeedsStorage); + + node->setOp(GetTypedArrayByteOffset); + node->clearFlags(NodeMustGenerate | NodeClobbersWorld); + fixEdge<KnownCellUse>(node->child1()); + return true; } - + BasicBlock* m_block; unsigned m_indexInBlock; Node* m_currentNode; |
