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.cpp1495
1 files changed, 1171 insertions, 324 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index b98d824f5..ac2842322 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,6 +31,9 @@
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
+#include "DFGPredictionPropagationPhase.h"
+#include "DFGVariableAccessDataDump.h"
+#include "Operations.h"
namespace JSC { namespace DFG {
@@ -38,13 +41,29 @@ class FixupPhase : public Phase {
public:
FixupPhase(Graph& graph)
: Phase(graph, "fixup")
+ , m_insertionSet(graph)
{
}
bool run()
{
+ ASSERT(m_graph.m_fixpointState == BeforeFixpoint);
+ ASSERT(m_graph.m_form == ThreadedCPS);
+
+ m_profitabilityChanged = false;
for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex)
fixupBlock(m_graph.m_blocks[blockIndex].get());
+
+ while (m_profitabilityChanged) {
+ m_profitabilityChanged = false;
+
+ for (unsigned i = m_graph.m_argumentPositions.size(); i--;)
+ m_graph.m_argumentPositions[i].mergeArgumentUnboxingAwareness();
+
+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex)
+ fixupSetLocalsInBlock(m_graph.m_blocks[blockIndex].get());
+ }
+
return true;
}
@@ -54,104 +73,418 @@ private:
if (!block)
return;
ASSERT(block->isReachable);
+ m_block = block;
for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
- m_compileIndex = block->at(m_indexInBlock);
- fixupNode(m_graph[m_compileIndex]);
+ m_currentNode = block->at(m_indexInBlock);
+ fixupNode(m_currentNode);
}
- m_insertionSet.execute(*block);
+ m_insertionSet.execute(block);
}
- void fixupNode(Node& node)
+ void fixupNode(Node* node)
{
- if (!node.shouldGenerate())
- return;
-
- NodeType op = node.op();
+ NodeType op = node->op();
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF(" %s @%u: ", Graph::opName(op), m_compileIndex);
+ dataLogF(" %s @%u: ", Graph::opName(op), node->index());
#endif
switch (op) {
- case GetById: {
- if (m_graph.m_fixpointState > BeforeFixpoint)
+ case SetLocal: {
+ // This gets handled by fixupSetLocalsInBlock().
+ break;
+ }
+
+ case BitAnd:
+ case BitOr:
+ case BitXor:
+ case BitRShift:
+ case BitLShift:
+ case BitURShift:
+ case ArithIMul: {
+ fixIntEdge(node->child1());
+ fixIntEdge(node->child2());
+ break;
+ }
+
+ case UInt32ToNumber: {
+ setUseKindAndUnboxIfProfitable<KnownInt32Use>(node->child1());
+ break;
+ }
+
+ case DoubleAsInt32: {
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
+
+ case ValueToInt32: {
+ if (node->child1()->shouldSpeculateInteger()) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ break;
+ }
+
+ if (node->child1()->shouldSpeculateNumber()) {
+ setUseKindAndUnboxIfProfitable<NumberUse>(node->child1());
break;
+ }
+
+ if (node->child1()->shouldSpeculateBoolean()) {
+ setUseKindAndUnboxIfProfitable<BooleanUse>(node->child1());
+ break;
+ }
- Node* nodePtr = &node;
+ setUseKindAndUnboxIfProfitable<NotCellUse>(node->child1());
+ break;
+ }
+
+ case Int32ToDouble: {
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ }
- if (!isInt32Speculation(m_graph[m_compileIndex].prediction()))
+ case ValueAdd: {
+ if (attemptToMakeIntegerAdd(node))
break;
- if (codeBlock()->identifier(nodePtr->identifierNumber()) != globalData().propertyNames->length)
+ if (Node::shouldSpeculateNumberExpectingDefined(node->child1().node(), node->child2().node())) {
+ fixDoubleEdge<NumberUse>(node->child1());
+ fixDoubleEdge<NumberUse>(node->child2());
break;
- ArrayProfile* arrayProfile =
- m_graph.baselineCodeBlockFor(nodePtr->codeOrigin)->getArrayProfile(
- nodePtr->codeOrigin.bytecodeIndex);
- ArrayMode arrayMode = ArrayMode(Array::SelectUsingPredictions);
- if (arrayProfile) {
- arrayProfile->computeUpdatedPrediction(m_graph.baselineCodeBlockFor(node.codeOrigin));
- arrayMode = ArrayMode::fromObserved(arrayProfile, Array::Read, false);
- arrayMode = arrayMode.refine(
- m_graph[node.child1()].prediction(),
- m_graph[m_compileIndex].prediction());
- if (arrayMode.supportsLength() && arrayProfile->hasDefiniteStructure()) {
- m_graph.ref(nodePtr->child1());
- Node checkStructure(CheckStructure, nodePtr->codeOrigin, OpInfo(m_graph.addStructureSet(arrayProfile->expectedStructure())), nodePtr->child1().index());
- checkStructure.ref();
- NodeIndex checkStructureIndex = m_graph.size();
- m_graph.append(checkStructure);
- m_insertionSet.append(m_indexInBlock, checkStructureIndex);
- nodePtr = &m_graph[m_compileIndex];
+ }
+
+ // FIXME: Optimize for the case where one of the operands is the
+ // empty string. Also consider optimizing for the case where we don't
+ // believe either side is the emtpy string. Both of these things should
+ // be easy.
+
+ if (node->child1()->shouldSpeculateString()
+ && attemptToMakeFastStringAdd<StringUse>(node, node->child1(), node->child2()))
+ break;
+ if (node->child2()->shouldSpeculateString()
+ && attemptToMakeFastStringAdd<StringUse>(node, node->child2(), node->child1()))
+ break;
+ if (node->child1()->shouldSpeculateStringObject()
+ && attemptToMakeFastStringAdd<StringObjectUse>(node, node->child1(), node->child2()))
+ break;
+ if (node->child2()->shouldSpeculateStringObject()
+ && attemptToMakeFastStringAdd<StringObjectUse>(node, node->child2(), node->child1()))
+ break;
+ if (node->child1()->shouldSpeculateStringOrStringObject()
+ && attemptToMakeFastStringAdd<StringOrStringObjectUse>(node, node->child1(), node->child2()))
+ break;
+ if (node->child2()->shouldSpeculateStringOrStringObject()
+ && attemptToMakeFastStringAdd<StringOrStringObjectUse>(node, node->child2(), node->child1()))
+ break;
+ break;
+ }
+
+ case MakeRope: {
+ fixupMakeRope(node);
+ break;
+ }
+
+ case ArithAdd:
+ case ArithSub: {
+ if (attemptToMakeIntegerAdd(node))
+ break;
+ fixDoubleEdge<NumberUse>(node->child1());
+ fixDoubleEdge<NumberUse>(node->child2());
+ break;
+ }
+
+ case ArithNegate: {
+ if (m_graph.negateShouldSpeculateInteger(node)) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ break;
+ }
+ fixDoubleEdge<NumberUse>(node->child1());
+ break;
+ }
+
+ case ArithMul: {
+ if (m_graph.mulShouldSpeculateInteger(node)) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ break;
+ }
+ fixDoubleEdge<NumberUse>(node->child1());
+ fixDoubleEdge<NumberUse>(node->child2());
+ break;
+ }
+
+ case ArithDiv: {
+ if (Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node())
+ && node->canSpeculateInteger()) {
+ if (isX86() || isARMv7s()) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ break;
}
- } else {
- arrayMode = arrayMode.refine(
- m_graph[node.child1()].prediction(),
- m_graph[m_compileIndex].prediction());
+ injectInt32ToDoubleNode(node->child1());
+ injectInt32ToDoubleNode(node->child2());
+
+ // We don't need to do ref'ing on the children because we're stealing them from
+ // the original division.
+ Node* newDivision = m_insertionSet.insertNode(
+ m_indexInBlock, SpecDouble, *node);
+
+ node->setOp(DoubleAsInt32);
+ node->children.initialize(Edge(newDivision, KnownNumberUse), Edge(), Edge());
+ break;
}
- if (!arrayMode.supportsLength())
+ fixDoubleEdge<NumberUse>(node->child1());
+ fixDoubleEdge<NumberUse>(node->child2());
+ break;
+ }
+
+ case ArithMin:
+ case ArithMax:
+ case ArithMod: {
+ if (Node::shouldSpeculateIntegerForArithmetic(node->child1().node(), node->child2().node())
+ && node->canSpeculateInteger()) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
break;
- nodePtr->setOp(GetArrayLength);
- ASSERT(nodePtr->flags() & NodeMustGenerate);
- nodePtr->clearFlags(NodeMustGenerate | NodeClobbersWorld);
- m_graph.deref(m_compileIndex);
- nodePtr->setArrayMode(arrayMode);
+ }
+ fixDoubleEdge<NumberUse>(node->child1());
+ fixDoubleEdge<NumberUse>(node->child2());
+ break;
+ }
- NodeIndex storage = checkArray(arrayMode, nodePtr->codeOrigin, nodePtr->child1().index(), NoNode, lengthNeedsStorage, nodePtr->shouldGenerate());
- if (storage == NoNode)
+ case ArithAbs: {
+ if (node->child1()->shouldSpeculateIntegerForArithmetic()
+ && node->canSpeculateInteger()) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
break;
+ }
+ fixDoubleEdge<NumberUse>(node->child1());
+ break;
+ }
+
+ case ArithSqrt: {
+ fixDoubleEdge<NumberUse>(node->child1());
+ break;
+ }
+
+ case LogicalNot: {
+ if (node->child1()->shouldSpeculateBoolean())
+ setUseKindAndUnboxIfProfitable<BooleanUse>(node->child1());
+ else if (node->child1()->shouldSpeculateObjectOrOther())
+ setUseKindAndUnboxIfProfitable<ObjectOrOtherUse>(node->child1());
+ else if (node->child1()->shouldSpeculateInteger())
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ else if (node->child1()->shouldSpeculateNumber())
+ fixDoubleEdge<NumberUse>(node->child1());
+ break;
+ }
- nodePtr = &m_graph[m_compileIndex];
- nodePtr->children.child2() = Edge(storage);
+ case TypeOf: {
+ if (node->child1()->shouldSpeculateString())
+ setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+ else if (node->child1()->shouldSpeculateCell())
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
break;
}
- case GetIndexedPropertyStorage: {
- ASSERT(node.arrayMode().canCSEStorage());
+
+ case CompareEqConstant: {
break;
}
+
+ case CompareEq:
+ case CompareLess:
+ case CompareLessEq:
+ case CompareGreater:
+ case CompareGreaterEq: {
+ if (Node::shouldSpeculateInteger(node->child1().node(), node->child2().node())) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ break;
+ }
+ if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) {
+ fixDoubleEdge<NumberUse>(node->child1());
+ fixDoubleEdge<NumberUse>(node->child2());
+ break;
+ }
+ if (node->op() != CompareEq)
+ break;
+ if (Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) {
+ setUseKindAndUnboxIfProfitable<BooleanUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<BooleanUse>(node->child2());
+ break;
+ }
+ if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && GPRInfo::numberOfRegisters >= 7) {
+ setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<StringUse>(node->child2());
+ break;
+ }
+ if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
+ setUseKindAndUnboxIfProfitable<ObjectUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<ObjectUse>(node->child2());
+ break;
+ }
+ if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObjectOrOther()) {
+ setUseKindAndUnboxIfProfitable<ObjectUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<ObjectOrOtherUse>(node->child2());
+ break;
+ }
+ if (node->child1()->shouldSpeculateObjectOrOther() && node->child2()->shouldSpeculateObject()) {
+ setUseKindAndUnboxIfProfitable<ObjectOrOtherUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<ObjectUse>(node->child2());
+ break;
+ }
+ break;
+ }
+
+ case CompareStrictEqConstant: {
+ break;
+ }
+
+ case CompareStrictEq: {
+ if (Node::shouldSpeculateBoolean(node->child1().node(), node->child2().node())) {
+ setUseKindAndUnboxIfProfitable<BooleanUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<BooleanUse>(node->child2());
+ break;
+ }
+ if (Node::shouldSpeculateInteger(node->child1().node(), node->child2().node())) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ break;
+ }
+ if (Node::shouldSpeculateNumber(node->child1().node(), node->child2().node())) {
+ fixDoubleEdge<NumberUse>(node->child1());
+ fixDoubleEdge<NumberUse>(node->child2());
+ break;
+ }
+ if (node->child1()->shouldSpeculateString() && node->child2()->shouldSpeculateString() && GPRInfo::numberOfRegisters >= 7) {
+ setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<StringUse>(node->child2());
+ break;
+ }
+ if (node->child1()->shouldSpeculateObject() && node->child2()->shouldSpeculateObject()) {
+ setUseKindAndUnboxIfProfitable<ObjectUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<ObjectUse>(node->child2());
+ break;
+ }
+ break;
+ }
+
+ case StringFromCharCode:
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ break;
+
+ case StringCharAt:
+ case StringCharCodeAt: {
+ // Currently we have no good way of refining these.
+ ASSERT(node->arrayMode() == ArrayMode(Array::String));
+ blessArrayOperation(node->child1(), node->child2(), node->child3());
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ break;
+ }
+
case GetByVal: {
- node.setArrayMode(
- node.arrayMode().refine(
- m_graph[node.child1()].prediction(),
- m_graph[node.child2()].prediction()));
+ node->setArrayMode(
+ node->arrayMode().refine(
+ node->child1()->prediction(),
+ node->child2()->prediction(),
+ SpecNone, node->flags()));
- blessArrayOperation(node.child1(), node.child2(), 2);
+ blessArrayOperation(node->child1(), node->child2(), node->child3());
- Node* nodePtr = &m_graph[m_compileIndex];
- ArrayMode arrayMode = nodePtr->arrayMode();
+ ArrayMode arrayMode = node->arrayMode();
if (arrayMode.type() == Array::Double
&& arrayMode.arrayClass() == Array::OriginalArray
&& arrayMode.speculation() == Array::InBounds
&& arrayMode.conversion() == Array::AsIs
- && m_graph.globalObjectFor(nodePtr->codeOrigin)->arrayPrototypeChainIsSane()
- && !(nodePtr->flags() & NodeUsedAsOther))
- nodePtr->setArrayMode(arrayMode.withSpeculation(Array::SaneChain));
+ && m_graph.globalObjectFor(node->codeOrigin)->arrayPrototypeChainIsSane()
+ && !(node->flags() & NodeUsedAsOther))
+ node->setArrayMode(arrayMode.withSpeculation(Array::SaneChain));
+
+ switch (node->arrayMode().type()) {
+ case Array::SelectUsingPredictions:
+ case Array::Unprofiled:
+ case Array::Undecided:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ case Array::Generic:
+#if USE(JSVALUE32_64)
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1()); // Speculating cell due to register pressure on 32-bit.
+#endif
+ break;
+ case Array::ForceExit:
+ break;
+ default:
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ break;
+ }
break;
}
- case StringCharAt:
- case StringCharCodeAt: {
- // Currently we have no good way of refining these.
- ASSERT(node.arrayMode() == ArrayMode(Array::String));
- blessArrayOperation(node.child1(), node.child2(), 2);
+
+ case PutByVal:
+ case PutByValAlias: {
+ Edge& child1 = m_graph.varArgChild(node, 0);
+ Edge& child2 = m_graph.varArgChild(node, 1);
+ Edge& child3 = m_graph.varArgChild(node, 2);
+
+ node->setArrayMode(
+ node->arrayMode().refine(
+ child1->prediction(),
+ child2->prediction(),
+ child3->prediction()));
+
+ blessArrayOperation(child1, child2, m_graph.varArgChild(node, 3));
+
+ switch (node->arrayMode().modeForPut().type()) {
+ case Array::SelectUsingPredictions:
+ case Array::Unprofiled:
+ case Array::Undecided:
+ RELEASE_ASSERT_NOT_REACHED();
+ break;
+ case Array::ForceExit:
+ case Array::Generic:
+#if USE(JSVALUE32_64)
+ // Due to register pressure on 32-bit, we speculate cell and
+ // ignore the base-is-not-cell case entirely by letting the
+ // baseline JIT handle it.
+ setUseKindAndUnboxIfProfitable<CellUse>(child1);
+#endif
+ break;
+ case Array::Int32:
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(child1);
+ setUseKindAndUnboxIfProfitable<Int32Use>(child2);
+ setUseKindAndUnboxIfProfitable<Int32Use>(child3);
+ break;
+ case Array::Double:
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(child1);
+ setUseKindAndUnboxIfProfitable<Int32Use>(child2);
+ fixDoubleEdge<RealNumberUse>(child3);
+ break;
+ case Array::Int8Array:
+ case Array::Int16Array:
+ case Array::Int32Array:
+ case Array::Uint8Array:
+ case Array::Uint8ClampedArray:
+ case Array::Uint16Array:
+ case Array::Uint32Array:
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(child1);
+ setUseKindAndUnboxIfProfitable<Int32Use>(child2);
+ if (child3->shouldSpeculateInteger())
+ setUseKindAndUnboxIfProfitable<Int32Use>(child3);
+ else
+ fixDoubleEdge<NumberUse>(child3);
+ break;
+ case Array::Float32Array:
+ case Array::Float64Array:
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(child1);
+ setUseKindAndUnboxIfProfitable<Int32Use>(child2);
+ fixDoubleEdge<NumberUse>(child3);
+ break;
+ default:
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(child1);
+ setUseKindAndUnboxIfProfitable<Int32Use>(child2);
+ break;
+ }
break;
}
@@ -165,17 +498,20 @@ private:
// ignored. That's because ArrayPush can't handle any array modes that aren't
// array-related - so if refine() turned this into a "Generic" ArrayPush then
// that would break things.
- node.setArrayMode(
- node.arrayMode().refine(
- m_graph[node.child1()].prediction() & SpecCell,
+ node->setArrayMode(
+ node->arrayMode().refine(
+ node->child1()->prediction() & SpecCell,
SpecInt32,
- m_graph[node.child2()].prediction()));
- blessArrayOperation(node.child1(), node.child2(), 2);
+ node->child2()->prediction()));
+ blessArrayOperation(node->child1(), Edge(), node->child3());
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child1());
- Node* nodePtr = &m_graph[m_compileIndex];
- switch (nodePtr->arrayMode().type()) {
+ switch (node->arrayMode().type()) {
+ case Array::Int32:
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ break;
case Array::Double:
- fixDoubleEdge(1);
+ fixDoubleEdge<RealNumberUse>(node->child2());
break;
default:
break;
@@ -184,398 +520,909 @@ private:
}
case ArrayPop: {
- blessArrayOperation(node.child1(), node.child2(), 1);
+ blessArrayOperation(node->child1(), Edge(), node->child2());
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child1());
break;
}
- case ValueToInt32: {
- if (m_graph[node.child1()].shouldSpeculateNumber()
- && node.mustGenerate()) {
- node.clearFlags(NodeMustGenerate);
- m_graph.deref(m_compileIndex);
+ case RegExpExec:
+ case RegExpTest: {
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child2());
+ break;
+ }
+
+ case Branch: {
+ if (node->child1()->shouldSpeculateBoolean())
+ setUseKindAndUnboxIfProfitable<BooleanUse>(node->child1());
+ else if (node->child1()->shouldSpeculateObjectOrOther())
+ setUseKindAndUnboxIfProfitable<ObjectOrOtherUse>(node->child1());
+ else if (node->child1()->shouldSpeculateInteger())
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ else if (node->child1()->shouldSpeculateNumber())
+ fixDoubleEdge<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);
+
+ BlockIndex toBeTaken = node->notTakenBlockIndex();
+ BlockIndex toBeNotTaken = node->takenBlockIndex();
+ node->setTakenBlockIndex(toBeTaken);
+ node->setNotTakenBlockIndex(toBeNotTaken);
+ }
+ }
}
break;
}
- case BitAnd:
- case BitOr:
- case BitXor:
- case BitRShift:
- case BitLShift:
- case BitURShift: {
- fixIntEdge(node.children.child1());
- fixIntEdge(node.children.child2());
+ case ToPrimitive: {
+ fixupToPrimitive(node);
break;
}
- case CompareEq:
- case CompareLess:
- case CompareLessEq:
- case CompareGreater:
- case CompareGreaterEq:
- case CompareStrictEq: {
- if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()]))
- break;
- if (!Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()]))
- break;
- fixDoubleEdge(0);
- fixDoubleEdge(1);
+ case ToString: {
+ fixupToString(node);
break;
}
- case LogicalNot: {
- if (m_graph[node.child1()].shouldSpeculateInteger())
- break;
- if (!m_graph[node.child1()].shouldSpeculateNumber())
- break;
- fixDoubleEdge(0);
+ case NewStringObject: {
+ setUseKindAndUnboxIfProfitable<KnownStringUse>(node->child1());
break;
}
- case Branch: {
- if (!m_graph[node.child1()].shouldSpeculateInteger()
- && m_graph[node.child1()].shouldSpeculateNumber())
- fixDoubleEdge(0);
-
- Node& myNode = m_graph[m_compileIndex]; // reload because the graph may have changed
- Edge logicalNotEdge = myNode.child1();
- Node& logicalNot = m_graph[logicalNotEdge];
- if (logicalNot.op() == LogicalNot
- && logicalNot.adjustedRefCount() == 1) {
- Edge newChildEdge = logicalNot.child1();
- if (m_graph[newChildEdge].hasBooleanResult()) {
- m_graph.ref(newChildEdge);
- m_graph.deref(logicalNotEdge);
- myNode.children.setChild1(newChildEdge);
-
- BlockIndex toBeTaken = myNode.notTakenBlockIndex();
- BlockIndex toBeNotTaken = myNode.takenBlockIndex();
- myNode.setTakenBlockIndex(toBeTaken);
- myNode.setNotTakenBlockIndex(toBeNotTaken);
+ case NewArray: {
+ for (unsigned i = m_graph.varArgNumChildren(node); i--;) {
+ node->setIndexingType(
+ leastUpperBoundOfIndexingTypeAndType(
+ node->indexingType(), m_graph.varArgChild(node, i)->prediction()));
+ }
+ switch (node->indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ CRASH();
+ break;
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ if (node->numChildren()) {
+ // This will only happen if the children have no type predictions. We
+ // would have already exited by now, but insert a forced exit just to
+ // be safe.
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin);
}
+ break;
+ case ALL_INT32_INDEXING_TYPES:
+ for (unsigned operandIndex = 0; operandIndex < node->numChildren(); ++operandIndex)
+ setUseKindAndUnboxIfProfitable<Int32Use>(m_graph.m_varArgChildren[node->firstChild() + operandIndex]);
+ break;
+ case ALL_DOUBLE_INDEXING_TYPES:
+ for (unsigned operandIndex = 0; operandIndex < node->numChildren(); ++operandIndex)
+ setUseKindAndUnboxIfProfitable<RealNumberUse>(m_graph.m_varArgChildren[node->firstChild() + operandIndex]);
+ break;
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES:
+ break;
+ default:
+ CRASH();
+ break;
}
break;
}
- case SetLocal: {
- if (node.variableAccessData()->isCaptured())
- break;
- if (!node.variableAccessData()->shouldUseDoubleFormat())
- break;
- fixDoubleEdge(0);
+ case NewArrayWithSize: {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
break;
}
- case ArithAdd:
- case ValueAdd: {
- if (m_graph.addShouldSpeculateInteger(node))
+ case ConvertThis: {
+ if (isOtherSpeculation(node->child1()->prediction())) {
+ 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 (!Node::shouldSpeculateNumberExpectingDefined(m_graph[node.child1()], m_graph[node.child2()]))
+ }
+
+ if (isObjectSpeculation(node->child1()->prediction())) {
+ setUseKindAndUnboxIfProfitable<ObjectUse>(node->child1());
+ node->convertToIdentity();
break;
- fixDoubleEdge(0);
- fixDoubleEdge(1);
+ }
+
break;
}
- case ArithSub: {
- if (m_graph.addShouldSpeculateInteger(node)
- && node.canSpeculateInteger())
- break;
- fixDoubleEdge(0);
- fixDoubleEdge(1);
+ case CreateThis: {
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
break;
}
- case ArithNegate: {
- if (m_graph.negateShouldSpeculateInteger(node))
- break;
- fixDoubleEdge(0);
+ case GetMyArgumentByVal:
+ case GetMyArgumentByValSafe: {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
break;
}
- case ArithMin:
- case ArithMax:
- case ArithMod: {
- if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()])
- && node.canSpeculateInteger())
- break;
- fixDoubleEdge(0);
- fixDoubleEdge(1);
+ case GetScopeRegisters:
+ case PutScopedVar:
+ case SkipTopScope:
+ case SkipScope:
+ case SetCallee:
+ case SetMyScope:
+ case PutStructure:
+ case AllocatePropertyStorage:
+ case ReallocatePropertyStorage:
+ case GetScope:
+ case GetButterfly: {
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child1());
break;
}
- case ArithMul: {
- if (m_graph.mulShouldSpeculateInteger(node))
+ case GetById: {
+ if (!node->child1()->shouldSpeculateCell())
break;
- fixDoubleEdge(0);
- fixDoubleEdge(1);
- break;
- }
-
- case ArithDiv: {
- if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()])
- && node.canSpeculateInteger()) {
- if (isX86())
- break;
- injectInt32ToDoubleNode(0);
- injectInt32ToDoubleNode(1);
-
- Node& oldDivision = m_graph[m_compileIndex];
-
- Node newDivision = oldDivision;
- newDivision.setRefCount(2);
- newDivision.predict(SpecDouble);
- NodeIndex newDivisionIndex = m_graph.size();
-
- oldDivision.setOp(DoubleAsInt32);
- oldDivision.children.initialize(Edge(newDivisionIndex, DoubleUse), Edge(), Edge());
-
- m_graph.append(newDivision);
- m_insertionSet.append(m_indexInBlock, newDivisionIndex);
-
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
+ if (!isInt32Speculation(node->prediction()))
break;
+ if (codeBlock()->identifier(node->identifierNumber()) != vm().propertyNames->length)
+ break;
+ ArrayProfile* arrayProfile =
+ m_graph.baselineCodeBlockFor(node->codeOrigin)->getArrayProfile(
+ node->codeOrigin.bytecodeIndex);
+ ArrayMode arrayMode = ArrayMode(Array::SelectUsingPredictions);
+ if (arrayProfile) {
+ arrayProfile->computeUpdatedPrediction(m_graph.baselineCodeBlockFor(node->codeOrigin));
+ arrayMode = ArrayMode::fromObserved(arrayProfile, Array::Read, false);
+ arrayMode = arrayMode.refine(
+ node->child1()->prediction(), node->prediction());
+ if (arrayMode.supportsLength() && arrayProfile->hasDefiniteStructure()) {
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, CheckStructure, node->codeOrigin,
+ OpInfo(m_graph.addStructureSet(arrayProfile->expectedStructure())),
+ node->child1());
+ }
+ } else
+ arrayMode = arrayMode.refine(node->child1()->prediction(), node->prediction());
+
+ if (arrayMode.type() == Array::Generic) {
+ // Check if the input is something that we can't get array length for, but for which we
+ // could insert some conversions in order to transform it into something that we can do it
+ // for.
+ if (node->child1()->shouldSpeculateStringObject())
+ attemptToForceStringArrayModeByToStringConversion<StringObjectUse>(arrayMode, node);
+ else if (node->child1()->shouldSpeculateStringOrStringObject())
+ attemptToForceStringArrayModeByToStringConversion<StringOrStringObjectUse>(arrayMode, node);
}
- fixDoubleEdge(0);
- fixDoubleEdge(1);
- break;
- }
- case ArithAbs: {
- if (m_graph[node.child1()].shouldSpeculateIntegerForArithmetic()
- && node.canSpeculateInteger())
+ if (!arrayMode.supportsLength())
break;
- fixDoubleEdge(0);
+ node->setOp(GetArrayLength);
+ ASSERT(node->flags() & NodeMustGenerate);
+ node->clearFlags(NodeMustGenerate | NodeClobbersWorld);
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child1());
+ node->setArrayMode(arrayMode);
+
+ Node* storage = checkArray(arrayMode, node->codeOrigin, node->child1().node(), 0, lengthNeedsStorage);
+ if (!storage)
+ break;
+
+ node->child2() = Edge(storage);
break;
}
- case ArithSqrt: {
- fixDoubleEdge(0);
+ case GetByIdFlush: {
+ if (node->child1()->shouldSpeculateCell())
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
break;
}
- case PutByVal:
- case PutByValAlias: {
- Edge child1 = m_graph.varArgChild(node, 0);
- Edge child2 = m_graph.varArgChild(node, 1);
- Edge child3 = m_graph.varArgChild(node, 2);
-
- node.setArrayMode(
- node.arrayMode().refine(
- m_graph[child1].prediction(),
- m_graph[child2].prediction(),
- m_graph[child3].prediction()));
-
- blessArrayOperation(child1, child2, 3);
-
- Node* nodePtr = &m_graph[m_compileIndex];
+ case CheckExecutable:
+ case CheckStructure:
+ case ForwardCheckStructure:
+ case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint:
+ case CheckFunction:
+ case PutById:
+ case PutByIdDirect:
+ case CheckHasInstance: {
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
+ break;
+ }
- switch (nodePtr->arrayMode().modeForPut().type()) {
- case Array::Double:
- fixDoubleEdge(2);
- break;
- case Array::Int8Array:
- case Array::Int16Array:
- case Array::Int32Array:
- case Array::Uint8Array:
- case Array::Uint8ClampedArray:
- case Array::Uint16Array:
- case Array::Uint32Array:
- if (!m_graph[child3].shouldSpeculateInteger())
- fixDoubleEdge(2);
- break;
- case Array::Float32Array:
- case Array::Float64Array:
- fixDoubleEdge(2);
+ case CheckArray: {
+ switch (node->arrayMode().type()) {
+ case Array::String:
+ setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
break;
default:
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
break;
}
break;
}
- case NewArray: {
- for (unsigned i = m_graph.varArgNumChildren(node); i--;) {
- node.setIndexingType(
- leastUpperBoundOfIndexingTypeAndType(
- node.indexingType(), m_graph[m_graph.varArgChild(node, i)].prediction()));
- }
- if (node.indexingType() == ArrayWithDouble) {
- for (unsigned i = m_graph.varArgNumChildren(node); i--;)
- fixDoubleEdge(i);
- }
+ case Arrayify:
+ case ArrayifyToStructure: {
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
+ if (node->child2())
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ break;
+ }
+
+ case GetByOffset: {
+ if (!node->child1()->hasStorageResult())
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child1());
break;
}
+ case PutByOffset: {
+ if (!node->child1()->hasStorageResult())
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<KnownCellUse>(node->child2());
+ break;
+ }
+
+ case InstanceOf: {
+ // FIXME: This appears broken: CheckHasInstance already does an unconditional cell
+ // check. https://bugs.webkit.org/show_bug.cgi?id=107479
+ if (!(node->child1()->prediction() & ~SpecCell))
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child2());
+ break;
+ }
+
+ case Phantom:
+ case Identity: {
+ switch (node->child1().useKind()) {
+ case NumberUse:
+ if (node->child1()->shouldSpeculateIntegerForArithmetic())
+ node->child1().setUseKind(Int32Use);
+ break;
+ default:
+ break;
+ }
+ observeUseKindOnEdge(node->child1());
+ break;
+ }
+
+ case GetArrayLength:
+ case Nop:
+ case Phi:
+ case ForwardInt32ToDouble:
+ case PhantomPutStructure:
+ case GetIndexedPropertyStorage:
+ case LastNodeType:
+ case MovHint:
+ case MovHintAndCheck:
+ case ZombieHint:
+ RELEASE_ASSERT_NOT_REACHED();
+ 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 GetLocal:
+ case GetCallee:
+ case Flush:
+ case PhantomLocal:
+ case GetLocalUnlinked:
+ case InlineStart:
+ case GetMyScope:
+ case GetScopedVar:
+ case GetGlobalVar:
+ case PutGlobalVar:
+ case GlobalVarWatchpoint:
+ case PutGlobalVarCheck:
+ case AllocationProfileWatchpoint:
+ case Call:
+ case Construct:
+ case NewObject:
+ case NewArrayBuffer:
+ case NewRegexp:
+ case Resolve:
+ case ResolveBase:
+ case ResolveBaseStrictPut:
+ case ResolveGlobal:
+ case Breakpoint:
+ case IsUndefined:
+ case IsBoolean:
+ case IsNumber:
+ case IsString:
+ case IsObject:
+ case IsFunction:
+ case CreateActivation:
+ case TearOffActivation:
+ case CreateArguments:
+ case PhantomArguments:
+ case TearOffArguments:
+ case GetMyArgumentsLength:
+ case GetMyArgumentsLengthSafe:
+ case CheckArgumentsNotCreated:
+ case NewFunction:
+ case NewFunctionNoCheck:
+ case NewFunctionExpression:
+ case Jump:
+ case Return:
+ case Throw:
+ case ThrowReferenceError:
+ case GarbageValue:
+ case CountExecution:
+ case ForceOSRExit:
+ case CheckWatchdogTimer:
+ break;
+#else
default:
break;
+#endif
}
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- if (!(node.flags() & NodeHasVarArgs)) {
+ if (!(node->flags() & NodeHasVarArgs)) {
dataLogF("new children: ");
- node.dumpChildren(WTF::dataFile());
+ node->dumpChildren(WTF::dataFile());
}
dataLogF("\n");
#endif
}
- NodeIndex addNode(const Node& node, bool shouldGenerate)
+ template<UseKind useKind>
+ void createToString(Node* node, Edge& edge)
+ {
+ edge.setNode(m_insertionSet.insertNode(
+ m_indexInBlock, SpecString, ToString, node->codeOrigin,
+ Edge(edge.node(), useKind)));
+ }
+
+ template<UseKind useKind>
+ void attemptToForceStringArrayModeByToStringConversion(ArrayMode& arrayMode, Node* node)
{
- NodeIndex nodeIndex = m_graph.size();
- m_graph.append(node);
- m_insertionSet.append(m_indexInBlock, nodeIndex);
- if (shouldGenerate)
- m_graph[nodeIndex].ref();
- return nodeIndex;
+ ASSERT(arrayMode == ArrayMode(Array::Generic));
+
+ if (!canOptimizeStringObjectAccess(node->codeOrigin))
+ return;
+
+ createToString<useKind>(node, node->child1());
+ arrayMode = ArrayMode(Array::String);
}
- NodeIndex checkArray(ArrayMode arrayMode, CodeOrigin codeOrigin, NodeIndex array, NodeIndex index, bool (*storageCheck)(const ArrayMode&) = canCSEStorage, bool shouldGenerate = true)
+ template<UseKind useKind>
+ bool isStringObjectUse()
+ {
+ switch (useKind) {
+ case StringObjectUse:
+ case StringOrStringObjectUse:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ template<UseKind useKind>
+ 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,
+ 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);
+ }
+
+ void convertToMakeRope(Node* node)
+ {
+ node->setOpAndDefaultFlags(MakeRope);
+ fixupMakeRope(node);
+ }
+
+ void fixupMakeRope(Node* node)
+ {
+ for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
+ Edge& edge = node->children.child(i);
+ if (!edge)
+ break;
+ edge.setUseKind(KnownStringUse);
+ if (!m_graph.isConstant(edge.node()))
+ continue;
+ JSString* string = jsCast<JSString*>(m_graph.valueOfJSConstant(edge.node()).asCell());
+ if (string->length())
+ continue;
+
+ // Don't allow the MakeRope to have zero children.
+ if (!i && !node->child2())
+ break;
+
+ node->children.removeEdge(i--);
+ }
+
+ if (!node->child2()) {
+ ASSERT(!node->child3());
+ node->convertToIdentity();
+ }
+ }
+
+ void fixupToPrimitive(Node* node)
+ {
+ if (node->child1()->shouldSpeculateInteger()) {
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ node->convertToIdentity();
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateString()) {
+ setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+ node->convertToIdentity();
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateStringObject()
+ && canOptimizeStringObjectAccess(node->codeOrigin)) {
+ setUseKindAndUnboxIfProfitable<StringObjectUse>(node->child1());
+ node->convertToToString();
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateStringOrStringObject()
+ && canOptimizeStringObjectAccess(node->codeOrigin)) {
+ setUseKindAndUnboxIfProfitable<StringOrStringObjectUse>(node->child1());
+ node->convertToToString();
+ return;
+ }
+ }
+
+ void fixupToString(Node* node)
+ {
+ if (node->child1()->shouldSpeculateString()) {
+ setUseKindAndUnboxIfProfitable<StringUse>(node->child1());
+ node->convertToIdentity();
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateStringObject()
+ && canOptimizeStringObjectAccess(node->codeOrigin)) {
+ setUseKindAndUnboxIfProfitable<StringObjectUse>(node->child1());
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateStringOrStringObject()
+ && canOptimizeStringObjectAccess(node->codeOrigin)) {
+ setUseKindAndUnboxIfProfitable<StringOrStringObjectUse>(node->child1());
+ return;
+ }
+
+ if (node->child1()->shouldSpeculateCell()) {
+ setUseKindAndUnboxIfProfitable<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);
+ }
+
+ // 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, const Identifier& ident)
+ {
+ unsigned attributesUnused;
+ JSCell* specificValue;
+ PropertyOffset offset = stringPrototypeStructure->get(
+ vm(), ident, attributesUnused, specificValue);
+ if (!isValidOffset(offset))
+ return false;
+
+ if (!specificValue)
+ return false;
+
+ if (!specificValue->inherits(&JSFunction::s_info))
+ return false;
+
+ JSFunction* function = jsCast<JSFunction*>(specificValue);
+ if (function->executable()->intrinsicFor(CodeForCall) != StringPrototypeValueOfIntrinsic)
+ return false;
+
+ return true;
+ }
+
+ bool canOptimizeStringObjectAccess(const CodeOrigin& codeOrigin)
+ {
+ if (m_graph.hasExitSite(codeOrigin, NotStringObject))
+ return false;
+
+ Structure* stringObjectStructure = m_graph.globalObjectFor(codeOrigin)->stringObjectStructure();
+ ASSERT(stringObjectStructure->storedPrototype().isObject());
+ ASSERT(stringObjectStructure->storedPrototype().asCell()->classInfo() == &StringPrototype::s_info);
+
+ JSObject* stringPrototypeObject = asObject(stringObjectStructure->storedPrototype());
+ Structure* stringPrototypeStructure = stringPrototypeObject->structure();
+ if (stringPrototypeStructure->transitionWatchpointSetHasBeenInvalidated())
+ 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))
+ return false;
+ if (!isStringPrototypeMethodSane(stringPrototypeStructure, vm().propertyNames->toString))
+ return false;
+
+ return true;
+ }
+
+ void fixupSetLocalsInBlock(BasicBlock* block)
+ {
+ if (!block)
+ return;
+ ASSERT(block->isReachable);
+ m_block = block;
+ for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
+ Node* node = m_currentNode = block->at(m_indexInBlock);
+ if (node->op() != SetLocal)
+ continue;
+
+ VariableAccessData* variable = node->variableAccessData();
+
+ if (!variable->shouldUnboxIfPossible())
+ continue;
+
+ if (variable->shouldUseDoubleFormat()) {
+ fixDoubleEdge<NumberUse>(node->child1(), ForwardSpeculation);
+ continue;
+ }
+
+ SpeculatedType predictedType = variable->argumentAwarePrediction();
+ if (isInt32Speculation(predictedType))
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ else if (isCellSpeculation(predictedType))
+ setUseKindAndUnboxIfProfitable<CellUse>(node->child1());
+ else if (isBooleanSpeculation(predictedType))
+ setUseKindAndUnboxIfProfitable<BooleanUse>(node->child1());
+ }
+ m_insertionSet.execute(block);
+ }
+
+ void findAndRemoveUnnecessaryStructureCheck(Node* array, const CodeOrigin& codeOrigin)
+ {
+ for (unsigned index = m_indexInBlock; index--;) {
+ Node* previousNode = m_block->at(index);
+ if (previousNode->codeOrigin != codeOrigin)
+ return;
+
+ if (previousNode->op() != CheckStructure)
+ continue;
+
+ if (previousNode->child1() != array)
+ continue;
+
+ previousNode->child1() = Edge();
+ previousNode->convertToPhantom();
+ return; // Assume we were smart enough to only insert one CheckStructure on the array.
+ }
+ }
+
+ Node* checkArray(ArrayMode arrayMode, const CodeOrigin& codeOrigin, Node* array, Node* index, bool (*storageCheck)(const ArrayMode&) = canCSEStorage)
{
ASSERT(arrayMode.isSpecific());
- m_graph.ref(array);
-
Structure* structure = arrayMode.originalArrayStructure(m_graph, codeOrigin);
+ Edge indexEdge = index ? Edge(index, Int32Use) : Edge();
+
if (arrayMode.doesConversion()) {
- if (index != NoNode)
- m_graph.ref(index);
-
if (structure) {
- Node arrayify(ArrayifyToStructure, codeOrigin, OpInfo(structure), OpInfo(arrayMode.asWord()), array, index);
- arrayify.ref();
- NodeIndex arrayifyIndex = m_graph.size();
- m_graph.append(arrayify);
- m_insertionSet.append(m_indexInBlock, arrayifyIndex);
+ if (m_indexInBlock > 0) {
+ // If the previous node was a CheckStructure inserted because of stuff
+ // that the array profile told us, then remove it, since we're going to be
+ // doing arrayification instead.
+ findAndRemoveUnnecessaryStructureCheck(array, codeOrigin);
+ }
+
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, ArrayifyToStructure, codeOrigin,
+ OpInfo(structure), OpInfo(arrayMode.asWord()), Edge(array, CellUse), indexEdge);
} else {
- Node arrayify(Arrayify, codeOrigin, OpInfo(arrayMode.asWord()), array, index);
- arrayify.ref();
- NodeIndex arrayifyIndex = m_graph.size();
- m_graph.append(arrayify);
- m_insertionSet.append(m_indexInBlock, arrayifyIndex);
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, Arrayify, codeOrigin,
+ OpInfo(arrayMode.asWord()), Edge(array, CellUse), indexEdge);
}
} else {
if (structure) {
- Node checkStructure(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(structure)), array);
- checkStructure.ref();
- NodeIndex checkStructureIndex = m_graph.size();
- m_graph.append(checkStructure);
- m_insertionSet.append(m_indexInBlock, checkStructureIndex);
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, CheckStructure, codeOrigin,
+ OpInfo(m_graph.addStructureSet(structure)), Edge(array, CellUse));
} else {
- Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode.asWord()), array);
- checkArray.ref();
- NodeIndex checkArrayIndex = m_graph.size();
- m_graph.append(checkArray);
- m_insertionSet.append(m_indexInBlock, checkArrayIndex);
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, CheckArray, codeOrigin,
+ OpInfo(arrayMode.asWord()), Edge(array, CellUse));
}
}
if (!storageCheck(arrayMode))
- return NoNode;
-
- if (shouldGenerate)
- m_graph.ref(array);
+ return 0;
- if (arrayMode.usesButterfly())
- return addNode(Node(GetButterfly, codeOrigin, array), shouldGenerate);
+ if (arrayMode.usesButterfly()) {
+ return m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, GetButterfly, codeOrigin, Edge(array, KnownCellUse));
+ }
- return addNode(Node(GetIndexedPropertyStorage, codeOrigin, OpInfo(arrayMode.asWord()), array), shouldGenerate);
+ return m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, GetIndexedPropertyStorage, codeOrigin,
+ OpInfo(arrayMode.asWord()), Edge(array, KnownCellUse));
}
- void blessArrayOperation(Edge base, Edge index, unsigned storageChildIdx)
+ void blessArrayOperation(Edge base, Edge index, Edge& storageChild)
{
- if (m_graph.m_fixpointState > BeforeFixpoint)
- return;
-
- Node* nodePtr = &m_graph[m_compileIndex];
+ Node* node = m_currentNode;
- switch (nodePtr->arrayMode().type()) {
+ switch (node->arrayMode().type()) {
case Array::ForceExit: {
- Node forceExit(ForceOSRExit, nodePtr->codeOrigin);
- forceExit.ref();
- NodeIndex forceExitIndex = m_graph.size();
- m_graph.append(forceExit);
- m_insertionSet.append(m_indexInBlock, forceExitIndex);
+ m_insertionSet.insertNode(
+ m_indexInBlock, SpecNone, ForceOSRExit, node->codeOrigin);
return;
}
case Array::SelectUsingPredictions:
case Array::Unprofiled:
- ASSERT_NOT_REACHED();
+ RELEASE_ASSERT_NOT_REACHED();
return;
case Array::Generic:
+ findAndRemoveUnnecessaryStructureCheck(base.node(), node->codeOrigin);
return;
default: {
- NodeIndex storage = checkArray(nodePtr->arrayMode(), nodePtr->codeOrigin, base.index(), index.indexUnchecked());
- if (storage == NoNode)
+ Node* storage = checkArray(node->arrayMode(), node->codeOrigin, base.node(), index.node());
+ if (!storage)
return;
- m_graph.child(m_graph[m_compileIndex], storageChildIdx) = Edge(storage);
+ storageChild = Edge(storage);
return;
} }
}
- void fixIntEdge(Edge& edge)
+ bool alwaysUnboxSimplePrimitives()
{
- Node& node = m_graph[edge];
- if (node.op() != ValueToInt32)
+#if USE(JSVALUE64)
+ return false;
+#else
+ // Any boolean, int, or cell value is profitable to unbox on 32-bit because it
+ // reduces traffic.
+ return true;
+#endif
+ }
+
+ template<UseKind useKind>
+ void observeUseKindOnNode(Node* node)
+ {
+ observeUseKindOnNode(node, useKind);
+ }
+
+ void observeUseKindOnEdge(Edge edge)
+ {
+ observeUseKindOnNode(edge.node(), edge.useKind());
+ }
+
+ void observeUseKindOnNode(Node* node, UseKind useKind)
+ {
+ if (node->op() != GetLocal)
return;
- if (!m_graph[node.child1()].shouldSpeculateInteger())
+ VariableAccessData* variable = node->variableAccessData();
+ switch (useKind) {
+ case Int32Use:
+ if (alwaysUnboxSimplePrimitives()
+ || isInt32Speculation(variable->prediction()))
+ m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true);
+ break;
+ case NumberUse:
+ case RealNumberUse:
+ if (variable->doubleFormatState() == UsingDoubleFormat)
+ m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true);
+ break;
+ case BooleanUse:
+ if (alwaysUnboxSimplePrimitives()
+ || isBooleanSpeculation(variable->prediction()))
+ m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true);
+ break;
+ case CellUse:
+ case ObjectUse:
+ case StringUse:
+ case KnownStringUse:
+ case StringObjectUse:
+ case StringOrStringObjectUse:
+ if (alwaysUnboxSimplePrimitives()
+ || isCellSpeculation(variable->prediction()))
+ m_profitabilityChanged |= variable->mergeIsProfitableToUnbox(true);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Set the use kind of the edge. In the future (https://bugs.webkit.org/show_bug.cgi?id=110433),
+ // this can be used to notify the GetLocal that the variable is profitable to unbox.
+ template<UseKind useKind>
+ void setUseKindAndUnboxIfProfitable(Edge& edge)
+ {
+ observeUseKindOnNode<useKind>(edge.node());
+ edge.setUseKind(useKind);
+ }
+
+ void fixIntEdge(Edge& edge)
+ {
+ Node* node = edge.node();
+ if (node->op() != ValueToInt32) {
+ setUseKindAndUnboxIfProfitable<KnownInt32Use>(edge);
return;
+ }
- Edge oldEdge = edge;
- Edge newEdge = node.child1();
+ Edge newEdge = node->child1();
- m_graph.ref(newEdge);
- m_graph.deref(oldEdge);
+ if (newEdge.useKind() != Int32Use) {
+ edge.setUseKind(KnownInt32Use);
+ return;
+ }
+ ASSERT(newEdge->shouldSpeculateInteger());
edge = newEdge;
}
- void fixDoubleEdge(unsigned childIndex)
+ template<UseKind useKind>
+ void fixDoubleEdge(Edge& edge, SpeculationDirection direction = BackwardSpeculation)
{
- Node& source = m_graph[m_compileIndex];
- Edge& edge = m_graph.child(source, childIndex);
+ ASSERT(useKind == NumberUse || useKind == KnownNumberUse || useKind == RealNumberUse);
- if (m_graph[edge].prediction() & SpecDouble) {
- edge.setUseKind(DoubleUse);
+ if (edge->prediction() & SpecDouble) {
+ setUseKindAndUnboxIfProfitable<useKind>(edge);
return;
}
- injectInt32ToDoubleNode(childIndex);
+ injectInt32ToDoubleNode(edge, useKind, direction);
}
- void injectInt32ToDoubleNode(unsigned childIndex)
+ void injectInt32ToDoubleNode(Edge& edge, UseKind useKind = NumberUse, SpeculationDirection direction = BackwardSpeculation)
{
- Node& source = m_graph[m_compileIndex];
- Edge& edge = m_graph.child(source, childIndex);
-
- NodeIndex resultIndex = (NodeIndex)m_graph.size();
+ Node* result = m_insertionSet.insertNode(
+ m_indexInBlock, SpecDouble,
+ direction == BackwardSpeculation ? Int32ToDouble : ForwardInt32ToDouble,
+ m_currentNode->codeOrigin, Edge(edge.node(), NumberUse));
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLogF("(replacing @%u->@%u with @%u->@%u) ",
- m_compileIndex, edge.index(), m_compileIndex, resultIndex);
+ dataLogF(
+ "(replacing @%u->@%u with @%u->@%u) ",
+ m_currentNode->index(), edge->index(), m_currentNode->index(), result->index());
#endif
-
- // Fix the edge up here because it's a reference that will be clobbered by
- // the append() below.
- NodeIndex oldIndex = edge.index();
- edge = Edge(resultIndex, DoubleUse);
- m_graph.append(Node(Int32ToDouble, source.codeOrigin, oldIndex));
- m_insertionSet.append(m_indexInBlock, resultIndex);
+ edge = Edge(result, useKind);
+ }
+
+ void truncateConstantToInt32(Edge& edge)
+ {
+ Node* oldNode = edge.node();
+
+ ASSERT(oldNode->hasConstant());
+ JSValue value = m_graph.valueOfJSConstant(oldNode);
+ if (value.isInt32())
+ return;
- Node& int32ToDouble = m_graph[resultIndex];
- int32ToDouble.predict(SpecDouble);
- int32ToDouble.ref();
+ value = jsNumber(JSC::toInt32(value.asNumber()));
+ ASSERT(value.isInt32());
+ edge.setNode(m_insertionSet.insertNode(
+ m_indexInBlock, SpecInt32, JSConstant, m_currentNode->codeOrigin,
+ OpInfo(codeBlock()->addOrFindConstant(value))));
}
+ void truncateConstantsIfNecessary(Node* node, AddSpeculationMode mode)
+ {
+ if (mode != SpeculateIntegerAndTruncateConstants)
+ return;
+
+ ASSERT(node->child1()->hasConstant() || node->child2()->hasConstant());
+ if (node->child1()->hasConstant())
+ truncateConstantToInt32(node->child1());
+ else
+ truncateConstantToInt32(node->child2());
+ }
+
+ bool attemptToMakeIntegerAdd(Node* node)
+ {
+ AddSpeculationMode mode = m_graph.addSpeculationMode(node);
+ if (mode == DontSpeculateInteger)
+ return false;
+
+ truncateConstantsIfNecessary(node, mode);
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child1());
+ setUseKindAndUnboxIfProfitable<Int32Use>(node->child2());
+ return true;
+ }
+
+ BasicBlock* m_block;
unsigned m_indexInBlock;
- NodeIndex m_compileIndex;
- InsertionSet<NodeIndex> m_insertionSet;
+ Node* m_currentNode;
+ InsertionSet m_insertionSet;
+ bool m_profitabilityChanged;
};
bool performFixup(Graph& graph)