summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGFixupPhase.cpp')
-rw-r--r--Source/JavaScriptCore/dfg/DFGFixupPhase.cpp2683
1 files changed, 1987 insertions, 696 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 185f03591..84b4732da 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,84 @@ private:
case BitRShift:
case BitLShift:
case BitURShift: {
- fixBinaryIntEdges();
+ if (Node::shouldSpeculateUntypedForBitOps(node->child1().node(), node->child2().node())) {
+ 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: {
+ if (node->child1()->shouldSpeculateNotCell()) {
+ fixIntConvertingEdge(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ } else
+ fixEdge<UntypedUse>(node->child1());
+ 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->clearFlags(NodeMustGenerate);
+ node->setResult(enableInt52() ? NodeResultInt52 : 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,69 +193,107 @@ private:
case ArithAdd:
case ArithSub: {
+ if (op == ArithSub
+ && Node::shouldSpeculateUntypedForArithmetic(node->child1().node(), node->child2().node())) {
+ 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 (node->child1()->shouldSpeculateInt32OrBoolean() && node->canSpeculateInt32(FixupPass)) {
+ fixIntOrBooleanEdge(node->child1());
if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
node->setArithMode(Arith::Unchecked);
else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
node->setArithMode(Arith::CheckOverflow);
else
node->setArithMode(Arith::CheckOverflowAndNegativeZero);
+ node->setResult(NodeResultInt32);
+ node->clearFlags(NodeMustGenerate);
break;
}
- if (m_graph.negateShouldSpeculateMachineInt(node)) {
- fixEdge<MachineIntUse>(node->child1());
+ if (m_graph.unaryArithShouldSpeculateAnyInt(node, FixupPass)) {
+ fixEdge<Int52RepUse>(node->child1());
if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
node->setArithMode(Arith::CheckOverflow);
else
node->setArithMode(Arith::CheckOverflowAndNegativeZero);
+ node->setResult(NodeResultInt52);
+ node->clearFlags(NodeMustGenerate);
break;
}
- fixEdge<NumberUse>(node->child1());
+ if (node->child1()->shouldSpeculateNotCell()) {
+ fixDoubleOrBooleanEdge(node->child1());
+ node->setResult(NodeResultDouble);
+ node->clearFlags(NodeMustGenerate);
+ } else
+ fixEdge<UntypedUse>(node->child1());
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())) {
+ 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()))
+ else if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())
+ || leftChild.node() == rightChild.node())
node->setArithMode(Arith::CheckOverflow);
else
node->setArithMode(Arith::CheckOverflowAndNegativeZero);
break;
}
- if (m_graph.mulShouldSpeculateMachineInt(node)) {
- fixEdge<MachineIntUse>(node->child1());
- fixEdge<MachineIntUse>(node->child2());
- if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
+ if (m_graph.binaryArithShouldSpeculateAnyInt(node, FixupPass)) {
+ fixEdge<Int52RepUse>(leftChild);
+ fixEdge<Int52RepUse>(rightChild);
+ if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags())
+ || leftChild.node() == rightChild.node())
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 +302,155 @@ 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 (node->child1()->shouldSpeculateInt32OrBoolean()
+ && node->canSpeculateInt32(FixupPass)) {
+ fixIntOrBooleanEdge(node->child1());
+ if (bytecodeCanTruncateInteger(node->arithNodeFlags()))
+ node->setArithMode(Arith::Unchecked);
+ else
+ node->setArithMode(Arith::CheckOverflow);
+ node->clearFlags(NodeMustGenerate);
+ node->setResult(NodeResultInt32);
break;
}
- fixEdge<NumberUse>(node->child1());
+
+ if (node->child1()->shouldSpeculateNotCell()) {
+ fixDoubleOrBooleanEdge(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ } else
+ fixEdge<UntypedUse>(node->child1());
+ node->setResult(NodeResultDouble);
break;
}
-
- case ArithSqrt:
+
+ case ArithPow: {
+ 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:
+ case ArithTrunc: {
+ if (node->child1()->shouldSpeculateInt32OrBoolean() && m_graph.roundShouldSpeculateInt32(node, FixupPass)) {
+ fixIntOrBooleanEdge(node->child1());
+ insertCheck<Int32Use>(node->child1().node());
+ node->convertToIdentity();
+ break;
+ }
+ if (node->child1()->shouldSpeculateNotCell()) {
+ 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);
+ }
+ node->clearFlags(NodeMustGenerate);
+ } else
+ fixEdge<UntypedUse>(node->child1());
+ break;
+ }
+
+ case ArithCos:
+ case ArithFRound:
+ case ArithLog:
case ArithSin:
- case ArithCos: {
- fixEdge<NumberUse>(node->child1());
+ case ArithSqrt:
+ case ArithTan: {
+ Edge& child1 = node->child1();
+ if (child1->shouldSpeculateNotCell()) {
+ fixDoubleOrBooleanEdge(child1);
+ node->clearFlags(NodeMustGenerate);
+ } else
+ fixEdge<UntypedUse>(child1);
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());
+ else {
+ WatchpointSet* masqueradesAsUndefinedWatchpoint = m_graph.globalObjectFor(node->origin.semantic)->masqueradesAsUndefinedWatchpoint();
+ if (masqueradesAsUndefinedWatchpoint->isStillValid())
+ m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
+ }
break;
}
@@ -329,67 +459,110 @@ 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 (enableInt52()
- && Node::shouldSpeculateMachineInt(node->child1().node(), node->child2().node())) {
- fixEdge<MachineIntUse>(node->child1());
- fixEdge<MachineIntUse>(node->child2());
- node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
+ if (Node::shouldSpeculateInt32OrBoolean(node->child1().node(), node->child2().node())) {
+ fixIntOrBooleanEdge(node->child1());
+ fixIntOrBooleanEdge(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 (enableInt52()
+ && Node::shouldSpeculateAnyInt(node->child1().node(), node->child2().node())) {
+ fixEdge<Int52RepUse>(node->child1());
+ fixEdge<Int52RepUse>(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::shouldSpeculateNumberOrBoolean(node->child1().node(), node->child2().node())) {
+ fixDoubleOrBooleanEdge(node->child1());
+ fixDoubleOrBooleanEdge(node->child2());
+ }
+ if (node->op() != CompareEq
+ && node->child1()->shouldSpeculateNotCell()
+ && node->child2()->shouldSpeculateNotCell()) {
+ if (node->child1()->shouldSpeculateNumberOrBoolean())
+ fixDoubleOrBooleanEdge(node->child1());
+ else
+ fixEdge<DoubleRepUse>(node->child1());
+ if (node->child2()->shouldSpeculateNumberOrBoolean())
+ fixDoubleOrBooleanEdge(node->child2());
+ else
+ fixEdge<DoubleRepUse>(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->op() != CompareEq)
+ break;
+ if (Node::shouldSpeculateSymbol(node->child1().node(), node->child2().node())) {
+ fixEdge<SymbolUse>(node->child1());
+ fixEdge<SymbolUse>(node->child2());
+ 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;
}
@@ -405,14 +578,19 @@ private:
break;
}
if (enableInt52()
- && Node::shouldSpeculateMachineInt(node->child1().node(), node->child2().node())) {
- fixEdge<MachineIntUse>(node->child1());
- fixEdge<MachineIntUse>(node->child2());
+ && Node::shouldSpeculateAnyInt(node->child1().node(), node->child2().node())) {
+ 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 +598,74 @@ 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()->shouldSpeculateSymbol()) {
+ fixEdge<SymbolUse>(node->child1());
+ break;
+ }
+ if (node->child2()->shouldSpeculateSymbol()) {
+ fixEdge<SymbolUse>(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 +679,85 @@ 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());
+ if (globalObject->arrayPrototypeChainIsSane())
+ 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 +765,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 +784,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->shouldSpeculateAnyInt() && enableInt52())
+ node->setResult(NodeResultInt52);
+ else
+ node->setResult(NodeResultDouble);
+ break;
+
+ default:
+ break;
+ }
+
break;
}
@@ -507,6 +820,7 @@ private:
node->setArrayMode(
node->arrayMode().refine(
+ m_graph, node,
child1->prediction(),
child2->prediction(),
child3->prediction()));
@@ -515,6 +829,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 +847,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 +863,24 @@ private:
fixEdge<KnownCellUse>(child1);
fixEdge<Int32Use>(child2);
if (child3->shouldSpeculateInt32())
- fixEdge<Int32Use>(child3);
- else if (child3->shouldSpeculateMachineInt())
- fixEdge<MachineIntUse>(child3);
+ fixIntOrBooleanEdge(child3);
+ else if (child3->shouldSpeculateAnyInt())
+ 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,8 +902,9 @@ private:
// that would break things.
node->setArrayMode(
node->arrayMode().refine(
+ m_graph, node,
node->child1()->prediction() & SpecCell,
- SpecInt32,
+ SpecInt32Only,
node->child2()->prediction()));
blessArrayOperation(node->child1(), Edge(), node->child3());
fixEdge<KnownCellUse>(node->child1());
@@ -603,11 +914,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;
@@ -620,56 +931,82 @@ private:
fixEdge<KnownCellUse>(node->child1());
break;
}
+
+ case ArraySlice: {
+ fixEdge<KnownCellUse>(m_graph.varArgChild(node, 0));
+ fixEdge<Int32Use>(m_graph.varArgChild(node, 1));
+ if (node->numChildren() == 4)
+ fixEdge<Int32Use>(m_graph.varArgChild(node, 2));
+ break;
+ }
case RegExpExec:
case RegExpTest: {
- fixEdge<CellUse>(node->child1());
- fixEdge<CellUse>(node->child2());
+ fixEdge<KnownCellUse>(node->child1());
+
+ if (node->child2()->shouldSpeculateRegExpObject()) {
+ fixEdge<RegExpObjectUse>(node->child2());
+
+ if (node->child3()->shouldSpeculateString())
+ fixEdge<StringUse>(node->child3());
+ }
+ break;
+ }
+
+ case StringReplace:
+ case StringReplaceRegExp: {
+ if (node->child2()->shouldSpeculateString()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Check, node->origin,
+ Edge(node->child2().node(), StringUse));
+ fixEdge<StringUse>(node->child2());
+ } else if (op == StringReplace) {
+ if (node->child2()->shouldSpeculateRegExpObject())
+ addStringReplacePrimordialChecks(node->child2().node());
+ else
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, ForceOSRExit, node->origin);
+ }
+
+ 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());
+ else {
+ WatchpointSet* masqueradesAsUndefinedWatchpoint = m_graph.globalObjectFor(node->origin.semantic)->masqueradesAsUndefinedWatchpoint();
+ if (masqueradesAsUndefinedWatchpoint->isStillValid())
+ m_graph.watchpoints().addLazily(masqueradesAsUndefinedWatchpoint);
}
break;
}
@@ -691,6 +1028,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;
}
@@ -699,9 +1042,15 @@ private:
fixupToPrimitive(node);
break;
}
+
+ case ToNumber: {
+ fixupToNumber(node);
+ break;
+ }
- case ToString: {
- fixupToString(node);
+ case ToString:
+ case CallStringConstructor: {
+ fixupToStringOrCallStringConstructor(node);
break;
}
@@ -709,8 +1058,49 @@ private:
fixEdge<KnownStringUse>(node->child1());
break;
}
+
+ case NewArrayWithSpread: {
+ watchHavingABadTime(node);
+
+ BitVector* bitVector = node->bitVector();
+ for (unsigned i = node->numChildren(); i--;) {
+ if (bitVector->get(i))
+ fixEdge<KnownCellUse>(m_graph.m_varArgChildren[node->firstChild() + i]);
+ else
+ fixEdge<UntypedUse>(m_graph.m_varArgChildren[node->firstChild() + i]);
+ }
+
+ break;
+ }
+
+ case Spread: {
+ // Note: We care about performing the protocol on our child's global object, not necessarily ours.
+
+ watchHavingABadTime(node->child1().node());
+
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(node->child1()->origin.semantic);
+ // When we go down the fast path, we don't consult the prototype chain, so we must prove
+ // that it doesn't contain any indexed properties, and that any holes will result in
+ // jsUndefined().
+ InlineWatchpointSet& objectPrototypeTransition = globalObject->objectPrototype()->structure()->transitionWatchpointSet();
+ InlineWatchpointSet& arrayPrototypeTransition = globalObject->arrayPrototype()->structure()->transitionWatchpointSet();
+ if (node->child1()->shouldSpeculateArray()
+ && arrayPrototypeTransition.isStillValid()
+ && objectPrototypeTransition.isStillValid()
+ && globalObject->arrayPrototypeChainIsSane()
+ && m_graph.isWatchingArrayIteratorProtocolWatchpoint(node->child1().node())
+ && m_graph.isWatchingHavingABadTimeWatchpoint(node->child1().node())) {
+ m_graph.watchpoints().addLazily(objectPrototypeTransition);
+ m_graph.watchpoints().addLazily(arrayPrototypeTransition);
+ fixEdge<ArrayUse>(node->child1());
+ } else
+ fixEdge<CellUse>(node->child1());
+ break;
+ }
case NewArray: {
+ watchHavingABadTime(node);
+
for (unsigned i = m_graph.varArgNumChildren(node); i--;) {
node->setIndexingType(
leastUpperBoundOfIndexingTypeAndType(
@@ -726,7 +1116,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 +1125,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 +1138,66 @@ 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;
- }
+ case NewArrayBuffer: {
+ watchHavingABadTime(node);
+ 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());
+ case CallObjectConstructor: {
+ if (node->child1()->shouldSpeculateObject()) {
+ fixEdge<ObjectUse>(node->child1());
node->convertToIdentity();
break;
}
-
+
+ fixEdge<UntypedUse>(node->child1());
break;
}
-
- case GetMyArgumentByVal:
- case GetMyArgumentByValSafe: {
- fixEdge<Int32Use>(node->child1());
+
+ case ToThis: {
+ fixupToThis(node);
break;
}
case PutStructure: {
fixEdge<KnownCellUse>(node->child1());
- insertStoreBarrier(m_indexInBlock, node->child1());
+ break;
+ }
+
+ case GetClosureVar:
+ case GetFromArguments: {
+ fixEdge<KnownCellUse>(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:
+ case GetGlobalObject: {
fixEdge<KnownCellUse>(node->child1());
break;
}
@@ -817,48 +1205,113 @@ private:
case AllocatePropertyStorage:
case ReallocatePropertyStorage: {
fixEdge<KnownCellUse>(node->child1());
- insertStoreBarrier(m_indexInBlock + 1, node->child1());
+ break;
+ }
+
+ case NukeStructureAndSetButterfly: {
+ fixEdge<KnownCellUse>(node->child1());
+ break;
+ }
+
+ case TryGetById: {
+ if (node->child1()->shouldSpeculateCell())
+ fixEdge<CellUse>(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;
+ }
+
+ if (uid == vm().propertyNames->lastIndex.impl()
+ && node->child1()->shouldSpeculateRegExpObject()) {
+ node->setOp(GetRegExpObjectLastIndex);
+ node->clearFlags(NodeMustGenerate);
+ fixEdge<RegExpObjectUse>(node->child1());
+ break;
+ }
}
- fixEdge<CellUse>(node->child1());
+
+ if (node->child1()->shouldSpeculateCell())
+ fixEdge<CellUse>(node->child1());
break;
}
-
+
case PutById:
+ case PutByIdFlush:
case PutByIdDirect: {
+ 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->lastIndex.impl()
+ && node->child1()->shouldSpeculateRegExpObject()) {
+ node->setOp(SetRegExpObjectLastIndex);
+ fixEdge<RegExpObjectUse>(node->child1());
+ speculateForBarrier(node->child2());
+ break;
+ }
+ }
+
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: {
fixEdge<CellUse>(node->child1());
break;
}
+
+ case CheckStringIdent: {
+ fixEdge<StringIdentUse>(node->child1());
+ break;
+ }
case Arrayify:
case ArrayifyToStructure: {
@@ -868,186 +1321,607 @@ 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());
+ unsigned index = indexForChecks();
+ insertInferredTypeCheck(
+ m_insertionSet, index, originForCheck(index), node->child3().node(),
+ node->storageAccessData().inferredType);
+ speculateForBarrier(node->child3());
+ break;
+ }
+
+ case MultiPutByOffset: {
+ fixEdge<CellUse>(node->child1());
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 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.
-
+
+ case InstanceOfCustom:
fixEdge<CellUse>(node->child2());
break;
- }
- case Phantom:
- case Identity:
- case Check: {
- switch (node->child1().useKind()) {
- case NumberUse:
- if (node->child1()->shouldSpeculateInt32ForArithmetic())
- node->child1().setUseKind(Int32Use);
- break;
- default:
+ case In: {
+ if (node->child2()->shouldSpeculateInt32()) {
+ convertToHasIndexedProperty(node);
break;
}
- observeUseKindOnEdge(node->child1());
+
+ fixEdge<CellUse>(node->child1());
+ break;
+ }
+
+ case HasOwnProperty: {
+ fixEdge<ObjectUse>(node->child1());
+#if CPU(X86) && USE(JSVALUE32_64)
+ // We don't have enough registers to do anything interesting on x86.
+ fixEdge<UntypedUse>(node->child2());
+#else
+ if (node->child2()->shouldSpeculateString())
+ fixEdge<StringUse>(node->child2());
+ else if (node->child2()->shouldSpeculateSymbol())
+ fixEdge<SymbolUse>(node->child2());
+ else
+ fixEdge<UntypedUse>(node->child2());
+#endif
+ 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());
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 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 PhantomNewAsyncFunction:
+ case PhantomCreateActivation:
+ case PhantomDirectArguments:
+ case PhantomCreateRest:
+ case PhantomSpread:
+ case PhantomNewArrayWithSpread:
+ case PhantomClonedArguments:
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValOutOfBounds:
+ case PutHint:
+ case CheckStructureImmediate:
+ case MaterializeNewObject:
+ case MaterializeCreateActivation:
+ case PutStack:
+ case KillStack:
+ case GetStack:
+ case StoreBarrier:
+ case FencedStoreBarrier:
+ case GetRegExpObjectLastIndex:
+ case SetRegExpObjectLastIndex:
+ case RecordRegExpCachedResult:
// 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);
+ case PutGlobalVariable: {
+ fixEdge<CellUse>(node->child1());
+ speculateForBarrier(node->child2());
+ 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 IsCellWithType: {
+ fixupIsCellWithType(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(TypeAnyInt)) {
+ if (node->child1()->shouldSpeculateInt32()) {
+ fixEdge<Int32Use>(node->child1());
+ node->remove();
+ break;
+ }
+
+ if (enableInt52()) {
+ fixEdge<AnyIntUse>(node->child1());
+ node->remove();
+ break;
+ }
+
+ // Must not perform fixEdge<NumberUse> here since the type set only includes TypeAnyInt. Double values should be logged.
+ }
+
+ if (typeSet->doesTypeConformTo(TypeNumber | TypeAnyInt)) {
+ 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;
+ {
+ ConcurrentJSLocker locker(typeSet->m_lock);
+ set = typeSet->structureSet(locker);
+ }
+ if (!set.isEmpty()) {
+ fixEdge<CellUse>(node->child1());
+ node->convertToCheckStructure(m_graph.addStructureSet(set));
+ }
+ }
+
+ break;
+ }
+
+ case CreateClonedArguments: {
+ watchHavingABadTime(node);
+ break;
+ }
+
+ case CreateScopedArguments:
+ case CreateActivation:
+ case NewFunction:
+ case NewGeneratorFunction:
+ case NewAsyncFunction: {
+ fixEdge<CellUse>(node->child1());
+ break;
+ }
+
+ case SetFunctionName: {
+ // The first child is guaranteed to be a cell because op_set_function_name is only used
+ // on a newly instantiated function object (the first child).
+ fixEdge<KnownCellUse>(node->child1());
+ fixEdge<UntypedUse>(node->child2());
+ break;
+ }
+
+ case CreateRest: {
+ watchHavingABadTime(node);
+ fixEdge<KnownInt32Use>(node->child1());
+ break;
+ }
+
+ case ResolveScope:
+ case GetDynamicVar:
+ case PutDynamicVar: {
+ fixEdge<KnownCellUse>(node->child1());
+ break;
+ }
+
+ case LogShadowChickenPrologue: {
+ fixEdge<KnownCellUse>(node->child1());
+ break;
+ }
+ case LogShadowChickenTail: {
+ fixEdge<UntypedUse>(node->child1());
+ fixEdge<KnownCellUse>(node->child2());
+ break;
+ }
+
+ case GetMapBucket:
+ if (node->child1().useKind() == MapObjectUse)
+ fixEdge<MapObjectUse>(node->child1());
+ else if (node->child1().useKind() == SetObjectUse)
+ fixEdge<SetObjectUse>(node->child1());
+ else
+ RELEASE_ASSERT_NOT_REACHED();
+
+#if USE(JSVALUE64)
+ if (node->child2()->shouldSpeculateBoolean())
+ fixEdge<BooleanUse>(node->child2());
+ else if (node->child2()->shouldSpeculateInt32())
+ fixEdge<Int32Use>(node->child2());
+ else if (node->child2()->shouldSpeculateSymbol())
+ fixEdge<SymbolUse>(node->child2());
+ else if (node->child2()->shouldSpeculateObject())
+ fixEdge<ObjectUse>(node->child2());
+ else if (node->child2()->shouldSpeculateString())
+ fixEdge<StringUse>(node->child2());
+ else if (node->child2()->shouldSpeculateCell())
+ fixEdge<CellUse>(node->child2());
+ else
+ fixEdge<UntypedUse>(node->child2());
+#else
+ fixEdge<UntypedUse>(node->child2());
+#endif // USE(JSVALUE64)
+
+ fixEdge<Int32Use>(node->child3());
+ break;
+
+ case LoadFromJSMapBucket:
+ fixEdge<KnownCellUse>(node->child1());
+ break;
+
+ case IsNonEmptyMapBucket:
+ fixEdge<KnownCellUse>(node->child1());
+ break;
+
+ case MapHash: {
+#if USE(JSVALUE64)
+ if (node->child1()->shouldSpeculateBoolean()) {
+ fixEdge<BooleanUse>(node->child1());
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateInt32()) {
+ fixEdge<Int32Use>(node->child1());
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateSymbol()) {
+ fixEdge<SymbolUse>(node->child1());
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateObject()) {
+ fixEdge<ObjectUse>(node->child1());
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateString()) {
+ fixEdge<StringUse>(node->child1());
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateCell()) {
+ fixEdge<CellUse>(node->child1());
+ break;
+ }
+
+ fixEdge<UntypedUse>(node->child1());
+#else
+ fixEdge<UntypedUse>(node->child1());
+#endif // USE(JSVALUE64)
+ break;
+ }
+
+ case DefineDataProperty: {
+ fixEdge<CellUse>(m_graph.varArgChild(node, 0));
+ Edge& propertyEdge = m_graph.varArgChild(node, 1);
+ if (propertyEdge->shouldSpeculateSymbol())
+ fixEdge<SymbolUse>(propertyEdge);
+ else if (propertyEdge->shouldSpeculateStringIdent())
+ fixEdge<StringIdentUse>(propertyEdge);
+ else if (propertyEdge->shouldSpeculateString())
+ fixEdge<StringUse>(propertyEdge);
+ else
+ fixEdge<UntypedUse>(propertyEdge);
+ fixEdge<UntypedUse>(m_graph.varArgChild(node, 2));
+ fixEdge<KnownInt32Use>(m_graph.varArgChild(node, 3));
+ break;
+ }
+
+ case ToLowerCase: {
+ // We currently only support StringUse since that will ensure that
+ // ToLowerCase is a pure operation. If we decide to update this with
+ // more types in the future, we need to ensure that the clobberize rules
+ // are correct.
+ fixEdge<StringUse>(node->child1());
+ break;
+ }
+
+ case NumberToStringWithRadix: {
+ if (node->child1()->shouldSpeculateInt32())
+ fixEdge<Int32Use>(node->child1());
+ else if (enableInt52() && node->child1()->shouldSpeculateAnyInt())
+ fixEdge<Int52RepUse>(node->child1());
+ else
+ fixEdge<DoubleRepUse>(node->child1());
+ fixEdge<Int32Use>(node->child2());
+ break;
+ }
+
+ case DefineAccessorProperty: {
+ fixEdge<CellUse>(m_graph.varArgChild(node, 0));
+ Edge& propertyEdge = m_graph.varArgChild(node, 1);
+ if (propertyEdge->shouldSpeculateSymbol())
+ fixEdge<SymbolUse>(propertyEdge);
+ else if (propertyEdge->shouldSpeculateStringIdent())
+ fixEdge<StringIdentUse>(propertyEdge);
+ else if (propertyEdge->shouldSpeculateString())
+ fixEdge<StringUse>(propertyEdge);
+ else
+ fixEdge<UntypedUse>(propertyEdge);
+ fixEdge<CellUse>(m_graph.varArgChild(node, 2));
+ fixEdge<CellUse>(m_graph.varArgChild(node, 3));
+ fixEdge<KnownInt32Use>(m_graph.varArgChild(node, 4));
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 CheckDOM: {
+ fixupCheckDOM(node);
break;
}
- case IsString:
+ case CallDOMGetter: {
+ DOMJIT::CallDOMGetterPatchpoint* patchpoint = node->callDOMGetterData()->patchpoint;
+ fixEdge<CellUse>(node->child1()); // DOM.
+ if (patchpoint->requireGlobalObject)
+ fixEdge<KnownCellUse>(node->child2()); // GlobalObject.
+ break;
+ }
+
+ case CallDOM: {
+ fixupCallDOM(node);
+ break;
+ }
+
+ case Call: {
+ attemptToMakeCallDOM(node);
+ break;
+ }
+
+ case ParseInt: {
+ if (node->child1()->shouldSpeculateInt32() && !node->child2()) {
+ fixEdge<Int32Use>(node->child1());
+ node->convertToIdentity();
+ break;
+ }
+
if (node->child1()->shouldSpeculateString()) {
- m_insertionSet.insertNode(m_indexInBlock, SpecNone, Phantom, node->codeOrigin,
- Edge(node->child1().node(), StringUse));
- m_graph.convertToConstant(node, jsBoolean(true));
- observeUseKindOnNode<StringUse>(node);
+ fixEdge<StringUse>(node->child1());
+ node->clearFlags(NodeMustGenerate);
}
+
+ if (node->child2())
+ fixEdge<Int32Use>(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 LazyJSConstant:
+ case DoubleConstant:
case GetLocal:
case GetCallee:
+ case GetArgumentCountIncludingThis:
+ case GetRestLength:
+ case GetArgument:
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 DirectCall:
+ case CheckTypeInfoFlags:
+ case TailCallInlinedCaller:
+ case DirectTailCallInlinedCaller:
case Construct:
+ case DirectConstruct:
+ case CallVarargs:
+ case CallEval:
+ case TailCallVarargsInlinedCaller:
+ case ConstructVarargs:
+ case CallForwardVarargs:
+ case ConstructForwardVarargs:
+ case TailCallForwardVarargs:
+ case TailCallForwardVarargsInlinedCaller:
+ case LoadVarargs:
+ case ForwardVarargs:
+ case ProfileControlFlow:
case NewObject:
- case NewArrayBuffer:
case NewRegexp:
- case Breakpoint:
- case ProfileWillCall:
- case ProfileDidCall:
+ case DeleteById:
+ case DeleteByVal:
+ case IsTypedArrayView:
+ case IsEmpty:
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 Jump:
case Return:
+ case TailCall:
+ case DirectTailCall:
+ case TailCallVarargs:
case Throw:
- case ThrowReferenceError:
+ case ThrowStaticError:
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:
+ case GetByIdWithThis:
+ case PutByIdWithThis:
+ case PutByValWithThis:
+ case GetByValWithThis:
+ case CompareEqPtr:
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());
+ m_graph.freeze(globalObject);
+ }
}
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 +1930,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 +1953,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 +1978,9 @@ private:
if (!edge)
break;
edge.setUseKind(KnownStringUse);
- if (!m_graph.isConstant(edge.node()))
+ JSString* string = edge->dynamicCastConstant<JSString*>(vm());
+ if (!string)
continue;
- JSString* string = jsCast<JSString*>(m_graph.valueOfJSConstant(edge.node()).asCell());
if (string->length())
continue;
@@ -1127,6 +1996,162 @@ private:
node->convertToIdentity();
}
}
+
+ void fixupIsCellWithType(Node* node)
+ {
+ switch (node->speculatedTypeForQuery()) {
+ case SpecString:
+ if (node->child1()->shouldSpeculateString()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Check, node->origin,
+ Edge(node->child1().node(), StringUse));
+ m_graph.convertToConstant(node, jsBoolean(true));
+ observeUseKindOnNode<StringUse>(node);
+ return;
+ }
+ break;
+
+ case SpecProxyObject:
+ if (node->child1()->shouldSpeculateProxyObject()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Check, node->origin,
+ Edge(node->child1().node(), ProxyObjectUse));
+ m_graph.convertToConstant(node, jsBoolean(true));
+ observeUseKindOnNode<ProxyObjectUse>(node);
+ return;
+ }
+ break;
+
+ case SpecRegExpObject:
+ if (node->child1()->shouldSpeculateRegExpObject()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Check, node->origin,
+ Edge(node->child1().node(), RegExpObjectUse));
+ m_graph.convertToConstant(node, jsBoolean(true));
+ observeUseKindOnNode<RegExpObjectUse>(node);
+ return;
+ }
+ break;
+
+ case SpecArray:
+ if (node->child1()->shouldSpeculateArray()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Check, node->origin,
+ Edge(node->child1().node(), ArrayUse));
+ m_graph.convertToConstant(node, jsBoolean(true));
+ observeUseKindOnNode<ArrayUse>(node);
+ return;
+ }
+ break;
+
+ case SpecDerivedArray:
+ if (node->child1()->shouldSpeculateDerivedArray()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Check, node->origin,
+ Edge(node->child1().node(), DerivedArrayUse));
+ m_graph.convertToConstant(node, jsBoolean(true));
+ observeUseKindOnNode<DerivedArrayUse>(node);
+ return;
+ }
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateCell()) {
+ fixEdge<CellUse>(node->child1());
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateNotCell()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Check, node->origin,
+ Edge(node->child1().node(), NotCellUse));
+ m_graph.convertToConstant(node, jsBoolean(false));
+ observeUseKindOnNode<NotCellUse>(node);
+ return;
+ }
+ }
+
+ 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()->shouldSpeculateAnyInt()) {
+ 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;
+ }
+
+ // FIXME: This should cover other use cases but we don't have use kinds for them. It's not critical,
+ // however, since we cover all the missing cases in constant folding.
+ // https://bugs.webkit.org/show_bug.cgi?id=157213
+ 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 +2168,54 @@ 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 fixupToNumber(Node* node)
+ {
+ // If the prediction of the child is Number, we attempt to convert ToNumber to Identity.
+ if (node->child1()->shouldSpeculateNumber()) {
+ if (isInt32Speculation(node->getHeapPrediction())) {
+ // If the both predictions of this node and the child is Int32, we just convert ToNumber to Identity, that's simple.
+ if (node->child1()->shouldSpeculateInt32()) {
+ fixEdge<Int32Use>(node->child1());
+ node->convertToIdentity();
+ return;
+ }
+
+ // The another case is that the predicted type of the child is Int32, but the heap prediction tell the users that this will produce non Int32 values.
+ // In that case, let's receive the child value as a Double value and convert it to Int32. This case happens in misc-bugs-847389-jpeg2000.
+ fixEdge<DoubleRepUse>(node->child1());
+ node->setOp(DoubleAsInt32);
+ if (bytecodeCanIgnoreNegativeZero(node->arithNodeFlags()))
+ node->setArithMode(Arith::CheckOverflow);
+ else
+ node->setArithMode(Arith::CheckOverflowAndNegativeZero);
+ return;
+ }
+
+ fixEdge<DoubleRepUse>(node->child1());
+ node->convertToIdentity();
+ node->setResult(NodeResultDouble);
+ return;
+ }
+
+ fixEdge<UntypedUse>(node->child1());
+ node->setResult(NodeResultJS);
+ }
- void fixupToString(Node* node)
+ void fixupToStringOrCallStringConstructor(Node* node)
{
if (node->child1()->shouldSpeculateString()) {
fixEdge<StringUse>(node->child1());
@@ -1166,13 +2224,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;
}
@@ -1181,111 +2239,79 @@ private:
fixEdge<CellUse>(node->child1());
return;
}
- }
-
- template<UseKind leftUseKind>
- bool attemptToMakeFastStringAdd(Node* node, Edge& left, Edge& right)
- {
- Node* originalLeft = left.node();
- Node* originalRight = right.node();
-
- ASSERT(leftUseKind == StringUse || leftUseKind == StringObjectUse || leftUseKind == StringOrStringObjectUse);
-
- if (isStringObjectUse<leftUseKind>() && !canOptimizeStringObjectAccess(node->codeOrigin))
- 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);
+
+ if (node->child1()->shouldSpeculateInt32()) {
+ fixEdge<Int32Use>(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ return;
+ }
+
+ if (enableInt52() && node->child1()->shouldSpeculateAnyInt()) {
+ fixEdge<Int52RepUse>(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateNumber()) {
+ fixEdge<DoubleRepUse>(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ return;
+ }
+
+ // ToString(Symbol) throws an error. So if the child1 can include Symbols,
+ // we need to care about it in the clobberize. In the following case,
+ // since NotCellUse edge filter is used and this edge filters Symbols,
+ // we can say that ToString never throws an error!
+ if (node->child1()->shouldSpeculateNotCell()) {
+ fixEdge<NotCellUse>(node->child1());
+ node->clearFlags(NodeMustGenerate);
+ return;
}
-
- // 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;
- }
-
- 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)
+
+ bool attemptToMakeFastStringAdd(Node* node)
{
- 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()))
+ 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;
+
+ 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;
}
-
- void fixupSetLocalsInBlock(BasicBlock* block)
+
+ void fixupGetAndSetLocalsInBlock(BasicBlock* block)
{
if (!block)
return;
@@ -1293,28 +2319,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,69 +2373,86 @@ private:
m_insertionSet.execute(block);
}
- void fixupUntypedSetLocalsInBlock(BasicBlock* block)
+ void addStringReplacePrimordialChecks(Node* searchRegExp)
{
- 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* node = m_currentNode;
+
+ // Check that structure of searchRegExp is RegExp object
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Check, node->origin,
+ Edge(searchRegExp, RegExpObjectUse));
+
+ auto emitPrimordialCheckFor = [&] (JSValue primordialProperty, UniquedStringImpl* propertyUID) {
+ unsigned index = m_graph.identifiers().ensure(propertyUID);
+
+ Node* actualProperty = m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, TryGetById, node->origin,
+ OpInfo(index), OpInfo(SpecFunction), Edge(searchRegExp, CellUse));
+
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, CheckCell, node->origin,
+ OpInfo(m_graph.freeze(primordialProperty)), Edge(actualProperty, CellUse));
+ };
+
+ JSGlobalObject* globalObject = m_graph.globalObjectFor(node->origin.semantic);
+
+ // Check that searchRegExp.exec is the primordial RegExp.prototype.exec
+ emitPrimordialCheckFor(globalObject->regExpProtoExecFunction(), vm().propertyNames->exec.impl());
+ // Check that searchRegExp.global is the primordial RegExp.prototype.global
+ emitPrimordialCheckFor(globalObject->regExpProtoGlobalGetter(), vm().propertyNames->global.impl());
+ // Check that searchRegExp.unicode is the primordial RegExp.prototype.unicode
+ emitPrimordialCheckFor(globalObject->regExpProtoUnicodeGetter(), vm().propertyNames->unicode.impl());
+ // Check that searchRegExp[Symbol.match] is the primordial RegExp.prototype[Symbol.replace]
+ emitPrimordialCheckFor(globalObject->regExpProtoSymbolReplaceFunction(), vm().propertyNames->replaceSymbol.impl());
}
-
- 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,
- OpInfo(structure), OpInfo(arrayMode.asWord()), Edge(array, CellUse), indexEdge);
+ m_indexInBlock, SpecNone, ArrayifyToStructure, origin,
+ OpInfo(m_graph.registerStructure(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));
}
}
}
if (!storageCheck(arrayMode))
- return 0;
+ return nullptr;
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 +2463,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 +2476,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 +2520,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:
- if (isMachineIntSpeculation(variable->prediction()))
+ case Int52RepUse:
+ if (isAnyIntSpeculation(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 +2560,144 @@ 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);
+ }
+
+ unsigned indexForChecks()
+ {
+ unsigned index = m_indexInBlock;
+ while (!m_block->at(index)->origin.exitOK)
+ index--;
+ return index;
+ }
+
+ NodeOrigin originForCheck(unsigned index)
+ {
+ return m_block->at(index)->origin.withSemantic(m_currentNode->origin.semantic);
+ }
+
+ 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>(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>(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>(value.node());
+ return;
+ }
+
+ if (value->shouldSpeculateNumber()) {
+ insertCheck<NumberUse>(value.node());
+ return;
+ }
+
+ if (value->shouldSpeculateNotCell()) {
+ insertCheck<NotCellUse>(value.node());
return;
}
-
- observeUseKindOnNode<useKind>(edge.node());
-
- edge.setUseKind(useKind);
}
- void insertStoreBarrier(unsigned indexInBlock, Edge child1, Edge child2 = Edge())
+ template<UseKind useKind>
+ void insertCheck(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);
+ unsigned index = indexForChecks();
+ m_insertionSet.insertNode(index, SpecNone, Check, originForCheck(index), 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;
+ if (node->shouldSpeculateAnyInt())
+ 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, SpecInt32Only, 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, SpecInt32Only, 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, SpecInt32Only, 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, SpecInt32Only, JSConstant, m_currentNode->origin,
+ OpInfo(m_graph.freeze(value))));
}
void truncateConstantsIfNecessary(Node* node, AddSpeculationMode mode)
@@ -1673,14 +2711,14 @@ private:
else
truncateConstantToInt32(node->child2());
}
-
+
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
@@ -1688,10 +2726,11 @@ private:
return true;
}
- if (m_graph.addShouldSpeculateMachineInt(node)) {
- fixEdge<MachineIntUse>(node->child1());
- fixEdge<MachineIntUse>(node->child2());
+ if (m_graph.addShouldSpeculateAnyInt(node)) {
+ fixEdge<Int52RepUse>(node->child1());
+ fixEdge<Int52RepUse>(node->child2());
node->setArithMode(Arith::CheckOverflow);
+ node->setResult(NodeResultInt52);
return true;
}
@@ -1702,12 +2741,12 @@ 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);
+ ConcurrentJSLocker locker(profiledBlock->m_lock);
arrayProfile->computeUpdatedPrediction(locker, profiledBlock);
arrayMode = ArrayMode::fromObserved(locker, arrayProfile, Array::Read, false);
if (arrayMode.type() == Array::Unprofiled) {
@@ -1722,7 +2761,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
@@ -1734,86 +2774,338 @@ private:
attemptToForceStringArrayModeByToStringConversion<StringOrStringObjectUse>(arrayMode, node);
}
- if (!arrayMode.supportsLength())
+ if (!arrayMode.supportsSelfLength())
return false;
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, SpecInt32Only, GetArrayLength, origin,
OpInfo(arrayMode.asWord()), Edge(child, KnownCellUse), Edge(storage));
}
-
- bool attemptToMakeGetTypedArrayByteOffset(Node* node)
+
+ void convertToHasIndexedProperty(Node* node)
{
- if (!isInt32Speculation(node->prediction()))
+ node->setOp(HasIndexedProperty);
+ node->clearFlags(NodeMustGenerate);
+ node->setArrayMode(
+ node->arrayMode().refine(
+ m_graph, node,
+ node->child1()->prediction(),
+ node->child2()->prediction(),
+ SpecNone));
+ node->setInternalMethodType(PropertySlot::InternalMethodType::HasProperty);
+
+ blessArrayOperation(node->child1(), node->child2(), node->child3());
+
+ fixEdge<CellUse>(node->child1());
+ fixEdge<Int32Use>(node->child2());
+ }
+
+ bool attemptToMakeCallDOM(Node* node)
+ {
+ if (m_graph.hasExitSite(node->origin.semantic, BadType))
return false;
-
- TypedArrayType type = typedArrayTypeFromSpeculation(node->child1()->prediction());
- if (!isTypedView(type))
+
+ const DOMJIT::Signature* signature = node->signature();
+ if (!signature)
return false;
-
- checkArray(
- ArrayMode(toArrayType(type)), node->codeOrigin, node->child1().node(),
- 0, neverNeedsStorage);
-
- node->setOp(GetTypedArrayByteOffset);
- node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
- fixEdge<KnownCellUse>(node->child1());
+
+ {
+ unsigned index = 0;
+ bool shouldConvertToCallDOM = true;
+ m_graph.doToChildren(node, [&](Edge& edge) {
+ // Callee. Ignore this. DFGByteCodeParser already emit appropriate checks.
+ if (!index)
+ return;
+
+ if (index == 1) {
+ // DOM node case.
+ if (edge->shouldSpeculateNotCell())
+ shouldConvertToCallDOM = false;
+ } else {
+ switch (signature->arguments[index - 2]) {
+ case SpecString:
+ if (edge->shouldSpeculateNotString())
+ shouldConvertToCallDOM = false;
+ break;
+ case SpecInt32Only:
+ if (edge->shouldSpeculateNotInt32())
+ shouldConvertToCallDOM = false;
+ break;
+ case SpecBoolean:
+ if (edge->shouldSpeculateNotBoolean())
+ shouldConvertToCallDOM = false;
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
+ }
+ ++index;
+ });
+ if (!shouldConvertToCallDOM)
+ return false;
+ }
+
+ Node* thisNode = m_graph.varArgChild(node, 1).node();
+ Ref<DOMJIT::Patchpoint> checkDOMPatchpoint = signature->checkDOM();
+ m_graph.m_domJITPatchpoints.append(checkDOMPatchpoint.ptr());
+ Node* checkDOM = m_insertionSet.insertNode(m_indexInBlock, SpecNone, CheckDOM, node->origin, OpInfo(checkDOMPatchpoint.ptr()), OpInfo(signature->classInfo), Edge(thisNode));
+ node->convertToCallDOM(m_graph);
+ fixupCheckDOM(checkDOM);
+ fixupCallDOM(node);
return true;
}
+ void fixupCheckDOM(Node* node)
+ {
+ fixEdge<CellUse>(node->child1());
+ }
+
+ void fixupCallDOM(Node* node)
+ {
+ const DOMJIT::Signature* signature = node->signature();
+ auto fixup = [&](Edge& edge, unsigned argumentIndex) {
+ if (!edge)
+ return;
+ switch (signature->arguments[argumentIndex]) {
+ case SpecString:
+ fixEdge<StringUse>(edge);
+ break;
+ case SpecInt32Only:
+ fixEdge<Int32Use>(edge);
+ break;
+ case SpecBoolean:
+ fixEdge<BooleanUse>(edge);
+ break;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
+ };
+ fixEdge<CellUse>(node->child1()); // DOM.
+ fixup(node->child2(), 0);
+ fixup(node->child3(), 1);
+ }
+
+ void fixupChecksInBlock(BasicBlock* block)
+ {
+ 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 DoubleRepAnyIntUse: {
+ 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, SpecAnyIntAsDouble, 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->isAnyIntConstant()) {
+ result = m_insertionSet.insertNode(
+ indexForChecks, SpecAnyInt, Int52Constant, originForChecks,
+ OpInfo(edge->constant()));
+ } else if (edge->hasDoubleResult()) {
+ result = m_insertionSet.insertNode(
+ indexForChecks, SpecAnyInt, Int52Rep, originForChecks,
+ Edge(edge.node(), DoubleRepAnyIntUse));
+ } else if (edge->shouldSpeculateInt32ForArithmetic()) {
+ result = m_insertionSet.insertNode(
+ indexForChecks, SpecInt32Only, Int52Rep, originForChecks,
+ Edge(edge.node(), Int32Use));
+ } else {
+ result = m_insertionSet.insertNode(
+ indexForChecks, SpecAnyInt, Int52Rep, originForChecks,
+ Edge(edge.node(), AnyIntUse));
+ }
+
+ 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, SpecInt32Only | SpecAnyIntAsDouble, 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);
+ }
+ });
+ }
+
+ m_insertionSet.execute(block);
+ }
+
BasicBlock* m_block;
unsigned m_indexInBlock;
Node* m_currentNode;
@@ -1823,7 +3115,6 @@ private:
bool performFixup(Graph& graph)
{
- SamplingRegion samplingRegion("DFG Fixup Phase");
return runPhase<FixupPhase>(graph);
}