summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-08-21 10:57:44 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-08-21 10:57:44 +0200
commit5ef7c8a6a70875d4430752d146bdcb069605d71d (patch)
treef6256640b6c46d7da221435803cae65326817ba2 /Source/JavaScriptCore/dfg
parentdecad929f578d8db641febc8740649ca6c574638 (diff)
downloadqtwebkit-5ef7c8a6a70875d4430752d146bdcb069605d71d.tar.gz
Imported WebKit commit 356d83016b090995d08ad568f2d2c243aa55e831 (http://svn.webkit.org/repository/webkit/trunk@126147)
New snapshot including various build fixes for newer Qt 5
Diffstat (limited to 'Source/JavaScriptCore/dfg')
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractState.cpp87
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractValue.h536
-rw-r--r--Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp24
-rw-r--r--Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp39
-rw-r--r--Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp4
-rw-r--r--Source/JavaScriptCore/dfg/DFGCSEPhase.cpp13
-rw-r--r--Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp15
-rw-r--r--Source/JavaScriptCore/dfg/DFGDriver.cpp45
-rw-r--r--Source/JavaScriptCore/dfg/DFGDriver.h8
-rw-r--r--Source/JavaScriptCore/dfg/DFGFixupPhase.cpp31
-rw-r--r--Source/JavaScriptCore/dfg/DFGGraph.h83
-rw-r--r--Source/JavaScriptCore/dfg/DFGNode.h22
-rw-r--r--Source/JavaScriptCore/dfg/DFGNodeType.h1
-rw-r--r--Source/JavaScriptCore/dfg/DFGOSREntry.cpp4
-rw-r--r--Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp1
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp84
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h10
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp76
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp60
-rw-r--r--Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h327
-rw-r--r--Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp22
21 files changed, 948 insertions, 544 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index 4f02ee793..5c53f6d78 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -62,13 +62,13 @@ void AbstractState::beginBasicBlock(BasicBlock* basicBlock)
m_variables = basicBlock->valuesAtHead;
m_haveStructures = false;
for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) {
- if (m_variables.argument(i).m_structure.isNeitherClearNorTop()) {
+ if (m_variables.argument(i).m_currentKnownStructure.isNeitherClearNorTop()) {
m_haveStructures = true;
break;
}
}
for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) {
- if (m_variables.local(i).m_structure.isNeitherClearNorTop()) {
+ if (m_variables.local(i).m_currentKnownStructure.isNeitherClearNorTop()) {
m_haveStructures = true;
break;
}
@@ -106,8 +106,6 @@ void AbstractState::initialize(Graph& graph)
SpeculatedType prediction = node.variableAccessData()->prediction();
if (isInt32Speculation(prediction))
root->valuesAtHead.argument(i).set(SpecInt32);
- else if (isArraySpeculation(prediction))
- root->valuesAtHead.argument(i).set(SpecArray);
else if (isBooleanSpeculation(prediction))
root->valuesAtHead.argument(i).set(SpecBoolean);
else if (isInt8ArraySpeculation(prediction))
@@ -160,6 +158,16 @@ void AbstractState::initialize(Graph& graph)
block->valuesAtHead.local(i).clear();
block->valuesAtTail.local(i).clear();
}
+ if (!block->isOSRTarget)
+ continue;
+ if (block->bytecodeBegin != graph.m_osrEntryBytecodeIndex)
+ continue;
+ for (size_t i = 0; i < graph.m_mustHandleValues.size(); ++i) {
+ AbstractValue value;
+ value.setMostSpecific(graph.m_mustHandleValues[i]);
+ block->valuesAtHead.operand(graph.m_mustHandleValues.operandForIndex(i)).merge(value);
+ }
+ block->cfaShouldRevisit = true;
}
}
@@ -290,10 +298,7 @@ bool AbstractState::execute(unsigned indexInBlock)
SpeculatedType predictedType = node.variableAccessData()->argumentAwarePrediction();
if (isInt32Speculation(predictedType))
speculateInt32Unary(node);
- else if (isArraySpeculation(predictedType)) {
- node.setCanExit(!isArraySpeculation(forNode(node.child1()).m_type));
- forNode(node.child1()).filter(SpecArray);
- } else if (isCellSpeculation(predictedType)) {
+ else if (isCellSpeculation(predictedType)) {
node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
forNode(node.child1()).filter(SpecCell);
} else if (isBooleanSpeculation(predictedType))
@@ -867,7 +872,7 @@ bool AbstractState::execute(unsigned indexInBlock)
m_isValid = false;
break;
}
- if (!isActionableArraySpeculation(m_graph[node.child1()].prediction()) || !m_graph[node.child2()].shouldSpeculateInteger()) {
+ if (!m_graph[node.child2()].shouldSpeculateInteger() || (!node.child3() && !m_graph[node.child1()].shouldSpeculateArguments())) {
clobberWorld(node.codeOrigin, indexInBlock);
forNode(nodeIndex).makeTop();
break;
@@ -942,8 +947,7 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(nodeIndex).set(SpecDouble);
break;
}
- ASSERT(m_graph[node.child1()].shouldSpeculateArray());
- forNode(node.child1()).filter(SpecArray);
+ forNode(node.child1()).filter(SpecCell);
forNode(node.child2()).filter(SpecInt32);
forNode(nodeIndex).makeTop();
break;
@@ -962,7 +966,7 @@ bool AbstractState::execute(unsigned indexInBlock)
m_isValid = false;
break;
}
- if (!m_graph[child2].shouldSpeculateInteger() || !isActionableMutableArraySpeculation(m_graph[child1].prediction())
+ if (!m_graph[child2].shouldSpeculateInteger()
#if USE(JSVALUE32_64)
|| m_graph[child1].shouldSpeculateArguments()
#endif
@@ -1053,8 +1057,7 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(child3).filter(SpecNumber);
break;
}
- ASSERT(m_graph[child1].shouldSpeculateArray());
- forNode(child1).filter(SpecArray);
+ forNode(child1).filter(SpecCell);
forNode(child2).filter(SpecInt32);
if (node.op() == PutByValSafe)
clobberWorld(node.codeOrigin, indexInBlock);
@@ -1063,13 +1066,13 @@ bool AbstractState::execute(unsigned indexInBlock)
case ArrayPush:
node.setCanExit(true);
- forNode(node.child1()).filter(SpecArray);
+ forNode(node.child1()).filter(SpecCell);
forNode(nodeIndex).set(SpecNumber);
break;
case ArrayPop:
node.setCanExit(true);
- forNode(node.child1()).filter(SpecArray);
+ forNode(node.child1()).filter(SpecCell);
forNode(nodeIndex).makeTop();
break;
@@ -1354,7 +1357,7 @@ bool AbstractState::execute(unsigned indexInBlock)
case GetArrayLength:
node.setCanExit(true);
- forNode(node.child1()).filter(SpecArray);
+ forNode(node.child1()).filter(SpecCell);
forNode(nodeIndex).set(SpecInt32);
break;
@@ -1420,19 +1423,52 @@ bool AbstractState::execute(unsigned indexInBlock)
case ForwardCheckStructure: {
// FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes).
AbstractValue& value = forNode(node.child1());
+ // If this structure check is attempting to prove knowledge already held in
+ // the futurePossibleStructure set then the constant folding phase should
+ // turn this into a watchpoint instead.
+ StructureSet& set = node.structureSet();
+ if (value.m_futurePossibleStructure.isSubsetOf(set))
+ m_foundConstants = true;
node.setCanExit(
- !value.m_structure.isSubsetOf(node.structureSet())
+ !value.m_currentKnownStructure.isSubsetOf(set)
|| !isCellSpeculation(value.m_type));
- value.filter(node.structureSet());
+ value.filter(set);
+ // This is likely to be unnecessary, but it's conservative, and that's a good thing.
+ // This is trying to avoid situations where the CFA proves that this structure check
+ // must fail due to a future structure proof. We have two options at that point. We
+ // can either compile all subsequent code as we would otherwise, or we can ensure
+ // that the subsequent code is never reachable. The former is correct because the
+ // Proof Is Infallible (TM) -- hence even if we don't force the subsequent code to
+ // be unreachable, it must be unreachable nonetheless. But imagine what would happen
+ // if the proof was borked. In the former case, we'd get really bizarre bugs where
+ // we assumed that the structure of this object was known even though it wasn't. In
+ // the latter case, we'd have a slight performance pathology because this would be
+ // turned into an OSR exit unnecessarily. Which would you rather have?
+ if (value.m_currentKnownStructure.isClear()
+ || value.m_futurePossibleStructure.isClear())
+ m_isValid = false;
m_haveStructures = true;
break;
}
- case StructureTransitionWatchpoint: {
- // FIXME: Turn CheckStructure into StructureTransitionWatchpoint when possible!
+ case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint: {
AbstractValue& value = forNode(node.child1());
+
+ // It's only valid to issue a structure transition watchpoint if we already
+ // know that the watchpoint covers a superset of the structures known to
+ // belong to the set of future structures that this value may have.
+ // Currently, we only issue singleton watchpoints (that check one structure)
+ // and our futurePossibleStructure set can only contain zero, one, or an
+ // infinity of structures.
+ ASSERT(value.m_futurePossibleStructure.isSubsetOf(StructureSet(node.structure())));
+
ASSERT(value.isClear() || isCellSpeculation(value.m_type)); // Value could be clear if we've proven must-exit due to a speculation statically known to be bad.
value.filter(node.structure());
+ // See comment in CheckStructure for why this is here.
+ if (value.m_currentKnownStructure.isClear()
+ || value.m_futurePossibleStructure.isClear())
+ m_isValid = false;
m_haveStructures = true;
node.setCanExit(true);
break;
@@ -1453,12 +1489,9 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(nodeIndex).clear(); // The result is not a JS value.
break;
case GetIndexedPropertyStorage: {
+ ASSERT(m_graph[node.child1()].prediction());
+ ASSERT(m_graph[node.child2()].shouldSpeculateInteger());
node.setCanExit(true); // Lies, but this is (almost) always followed by GetByVal, which does exit. So no point in trying to be more precise.
- SpeculatedType basePrediction = m_graph[node.child2()].prediction();
- if (!(basePrediction & SpecInt32) && basePrediction) {
- forNode(nodeIndex).clear();
- break;
- }
if (m_graph[node.child1()].shouldSpeculateArguments()) {
ASSERT_NOT_REACHED();
break;
@@ -1514,7 +1547,7 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(nodeIndex).clear();
break;
}
- forNode(node.child1()).filter(SpecArray);
+ forNode(node.child1()).filter(SpecCell);
forNode(nodeIndex).clear();
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
index 402fd0fcd..ff1c6d205 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,283 +30,13 @@
#if ENABLE(DFG_JIT)
+#include "DFGStructureAbstractValue.h"
#include "JSCell.h"
#include "SpeculatedType.h"
#include "StructureSet.h"
namespace JSC { namespace DFG {
-class StructureAbstractValue {
-public:
- StructureAbstractValue()
- : m_structure(0)
- {
- }
-
- StructureAbstractValue(Structure* structure)
- : m_structure(structure)
- {
- }
-
- StructureAbstractValue(const StructureSet& set)
- {
- switch (set.size()) {
- case 0:
- m_structure = 0;
- break;
-
- case 1:
- m_structure = set[0];
- break;
-
- default:
- m_structure = topValue();
- break;
- }
- }
-
- void clear()
- {
- m_structure = 0;
- }
-
- void makeTop()
- {
- m_structure = topValue();
- }
-
- static StructureAbstractValue top()
- {
- StructureAbstractValue value;
- value.makeTop();
- return value;
- }
-
- void add(Structure* structure)
- {
- ASSERT(!contains(structure) && !isTop());
- if (m_structure)
- makeTop();
- else
- m_structure = structure;
- }
-
- bool addAll(const StructureSet& other)
- {
- if (isTop() || !other.size())
- return false;
- if (other.size() > 1) {
- makeTop();
- return true;
- }
- if (!m_structure) {
- m_structure = other[0];
- return true;
- }
- if (m_structure == other[0])
- return false;
- makeTop();
- return true;
- }
-
- bool addAll(const StructureAbstractValue& other)
- {
- if (!other.m_structure)
- return false;
- if (isTop())
- return false;
- if (other.isTop()) {
- makeTop();
- return true;
- }
- if (m_structure) {
- if (m_structure == other.m_structure)
- return false;
- makeTop();
- return true;
- }
- m_structure = other.m_structure;
- return true;
- }
-
- bool contains(Structure* structure) const
- {
- if (isTop())
- return true;
- if (m_structure == structure)
- return true;
- return false;
- }
-
- bool isSubsetOf(const StructureSet& other) const
- {
- if (isTop())
- return false;
- if (!m_structure)
- return true;
- return other.contains(m_structure);
- }
-
- bool doesNotContainAnyOtherThan(Structure* structure) const
- {
- if (isTop())
- return false;
- if (!m_structure)
- return true;
- return m_structure == structure;
- }
-
- bool isSupersetOf(const StructureSet& other) const
- {
- if (isTop())
- return true;
- if (!other.size())
- return true;
- if (other.size() > 1)
- return false;
- return m_structure == other[0];
- }
-
- bool isSubsetOf(const StructureAbstractValue& other) const
- {
- if (other.isTop())
- return true;
- if (isTop())
- return false;
- if (m_structure) {
- if (other.m_structure)
- return m_structure == other.m_structure;
- return false;
- }
- return true;
- }
-
- bool isSupersetOf(const StructureAbstractValue& other) const
- {
- return other.isSubsetOf(*this);
- }
-
- void filter(const StructureSet& other)
- {
- if (!m_structure)
- return;
-
- if (isTop()) {
- switch (other.size()) {
- case 0:
- m_structure = 0;
- return;
-
- case 1:
- m_structure = other[0];
- return;
-
- default:
- return;
- }
- }
-
- if (other.contains(m_structure))
- return;
-
- m_structure = 0;
- }
-
- void filter(const StructureAbstractValue& other)
- {
- if (isTop()) {
- m_structure = other.m_structure;
- return;
- }
- if (m_structure == other.m_structure)
- return;
- if (other.isTop())
- return;
- m_structure = 0;
- }
-
- void filter(SpeculatedType other)
- {
- if (!(other & SpecCell)) {
- clear();
- return;
- }
-
- if (isClearOrTop())
- return;
-
- if (!(speculationFromStructure(m_structure) & other))
- m_structure = 0;
- }
-
- bool isClear() const
- {
- return !m_structure;
- }
-
- bool isTop() const { return m_structure == topValue(); }
-
- bool isClearOrTop() const { return m_structure <= topValue(); }
- bool isNeitherClearNorTop() const { return !isClearOrTop(); }
-
- size_t size() const
- {
- ASSERT(!isTop());
- return !!m_structure;
- }
-
- Structure* at(size_t i) const
- {
- ASSERT(!isTop());
- ASSERT(m_structure);
- ASSERT_UNUSED(i, !i);
- return m_structure;
- }
-
- Structure* operator[](size_t i) const
- {
- return at(i);
- }
-
- Structure* last() const
- {
- return at(0);
- }
-
- SpeculatedType speculationFromStructures() const
- {
- if (isTop())
- return SpecCell;
- if (isClear())
- return SpecNone;
- return speculationFromStructure(m_structure);
- }
-
- bool operator==(const StructureAbstractValue& other) const
- {
- return m_structure == other.m_structure;
- }
-
- void dump(FILE* out) const
- {
- if (isTop()) {
- fprintf(out, "TOP");
- return;
- }
-
- fprintf(out, "[");
- if (m_structure)
- fprintf(out, "%p", m_structure);
- fprintf(out, "]");
- }
-
-private:
- static Structure* topValue() { return reinterpret_cast<Structure*>(1); }
-
- // This can only remember one structure at a time.
- Structure* m_structure;
-};
-
struct AbstractValue {
AbstractValue()
: m_type(SpecNone)
@@ -316,15 +46,15 @@ struct AbstractValue {
void clear()
{
m_type = SpecNone;
- m_structure.clear();
- m_unclobberedStructure.clear();
+ m_currentKnownStructure.clear();
+ m_futurePossibleStructure.clear();
m_value = JSValue();
checkConsistency();
}
bool isClear() const
{
- bool result = m_type == SpecNone && m_structure.isClear() && m_unclobberedStructure.isClear();
+ bool result = m_type == SpecNone && m_currentKnownStructure.isClear() && m_futurePossibleStructure.isClear();
if (result)
ASSERT(!m_value);
return result;
@@ -333,8 +63,8 @@ struct AbstractValue {
void makeTop()
{
m_type = SpecTop;
- m_structure.makeTop();
- m_unclobberedStructure.makeTop();
+ m_currentKnownStructure.makeTop();
+ m_futurePossibleStructure.makeTop();
m_value = JSValue();
checkConsistency();
}
@@ -342,9 +72,9 @@ struct AbstractValue {
void clobberStructures()
{
if (m_type & SpecCell)
- m_structure.makeTop();
+ m_currentKnownStructure.makeTop();
else
- ASSERT(m_structure.isClear());
+ ASSERT(m_currentKnownStructure.isClear());
checkConsistency();
}
@@ -355,7 +85,7 @@ struct AbstractValue {
bool isTop() const
{
- return m_type == SpecTop && m_structure.isTop() && m_unclobberedStructure.isTop();
+ return m_type == SpecTop && m_currentKnownStructure.isTop() && m_futurePossibleStructure.isTop();
}
bool valueIsTop() const
@@ -375,20 +105,29 @@ struct AbstractValue {
return result;
}
- void set(JSValue value)
+ void setFuturePossibleStructure(Structure* structure)
+ {
+ if (structure->transitionWatchpointSetIsStillValid())
+ m_futurePossibleStructure = structure;
+ else
+ m_futurePossibleStructure.makeTop();
+ }
+
+ void filterFuturePossibleStructure(Structure* structure)
+ {
+ if (structure->transitionWatchpointSetIsStillValid())
+ m_futurePossibleStructure.filter(StructureAbstractValue(structure));
+ }
+
+ void setMostSpecific(JSValue value)
{
if (!!value && value.isCell()) {
- // Have to be careful here! It's tempting to set the structure to the
- // value's structure, but that would be wrong, since that would
- // constitute a proof that this value will always have the same
- // structure. The whole point of a value having a structure is that
- // it may change in the future - for example between when we compile
- // the code and when we run it.
- m_structure.makeTop();
- m_unclobberedStructure.makeTop(); // FIXME: Consider not clobbering this.
+ Structure* structure = value.asCell()->structure();
+ m_currentKnownStructure = structure;
+ setFuturePossibleStructure(structure);
} else {
- m_structure.clear();
- m_unclobberedStructure.clear();
+ m_currentKnownStructure.clear();
+ m_futurePossibleStructure.clear();
}
m_type = speculationFromValue(value);
@@ -397,14 +136,26 @@ struct AbstractValue {
checkConsistency();
}
- void set(Structure* structure)
+ void set(JSValue value)
{
- m_structure.clear();
- m_structure.add(structure);
+ if (!!value && value.isCell()) {
+ m_currentKnownStructure.makeTop();
+ setFuturePossibleStructure(value.asCell()->structure());
+ } else {
+ m_currentKnownStructure.clear();
+ m_futurePossibleStructure.clear();
+ }
- m_unclobberedStructure.clear();
- m_unclobberedStructure.add(structure);
+ m_type = speculationFromValue(value);
+ m_value = value;
+ checkConsistency();
+ }
+
+ void set(Structure* structure)
+ {
+ m_currentKnownStructure = structure;
+ setFuturePossibleStructure(structure);
m_type = speculationFromStructure(structure);
m_value = JSValue();
@@ -414,11 +165,11 @@ struct AbstractValue {
void set(SpeculatedType type)
{
if (type & SpecCell) {
- m_structure.makeTop();
- m_unclobberedStructure.makeTop();
+ m_currentKnownStructure.makeTop();
+ m_futurePossibleStructure.makeTop();
} else {
- m_structure.clear();
- m_unclobberedStructure.clear();
+ m_currentKnownStructure.clear();
+ m_futurePossibleStructure.clear();
}
m_type = type;
m_value = JSValue();
@@ -428,8 +179,8 @@ struct AbstractValue {
bool operator==(const AbstractValue& other) const
{
return m_type == other.m_type
- && m_structure == other.m_structure
- && m_unclobberedStructure == other.m_unclobberedStructure
+ && m_currentKnownStructure == other.m_currentKnownStructure
+ && m_futurePossibleStructure == other.m_futurePossibleStructure
&& m_value == other.m_value;
}
bool operator!=(const AbstractValue& other) const
@@ -448,8 +199,8 @@ struct AbstractValue {
result = !other.isClear();
} else {
result |= mergeSpeculation(m_type, other.m_type);
- result |= m_structure.addAll(other.m_structure);
- result |= m_unclobberedStructure.addAll(other.m_unclobberedStructure);
+ result |= m_currentKnownStructure.addAll(other.m_currentKnownStructure);
+ result |= m_futurePossibleStructure.addAll(other.m_futurePossibleStructure);
if (m_value != other.m_value) {
result |= !!m_value;
m_value = JSValue();
@@ -465,8 +216,8 @@ struct AbstractValue {
mergeSpeculation(m_type, type);
if (type & SpecCell) {
- m_structure.makeTop();
- m_unclobberedStructure.makeTop();
+ m_currentKnownStructure.makeTop();
+ m_futurePossibleStructure.makeTop();
}
m_value = JSValue();
@@ -476,19 +227,21 @@ struct AbstractValue {
void filter(const StructureSet& other)
{
m_type &= other.speculationFromStructures();
- m_structure.filter(other);
- m_unclobberedStructure.filter(other);
+ m_currentKnownStructure.filter(other);
+ if (m_currentKnownStructure.isClear())
+ m_futurePossibleStructure.clear();
+ else if (m_currentKnownStructure.hasSingleton())
+ filterFuturePossibleStructure(m_currentKnownStructure.singleton());
// It's possible that prior to the above two statements we had (Foo, TOP), where
// Foo is a SpeculatedType that is disjoint with the passed StructureSet. In that
// case, we will now have (None, [someStructure]). In general, we need to make
// sure that new information gleaned from the SpeculatedType needs to be fed back
// into the information gleaned from the StructureSet.
- m_structure.filter(m_type);
- m_unclobberedStructure.filter(m_type);
+ m_currentKnownStructure.filter(m_type);
+ m_futurePossibleStructure.filter(m_type);
- if (!!m_value && !validateIgnoringValue(m_value))
- clear();
+ filterValueByType();
checkConsistency();
}
@@ -503,47 +256,38 @@ struct AbstractValue {
// the passed type is Array. At this point we'll have (None, TOP). The best way
// to ensure that the structure filtering does the right thing is to filter on
// the new type (None) rather than the one passed (Array).
- m_structure.filter(m_type);
- m_unclobberedStructure.filter(m_type);
+ m_currentKnownStructure.filter(m_type);
+ m_futurePossibleStructure.filter(m_type);
- if (!!m_value && !validateIgnoringValue(m_value))
- clear();
+ filterValueByType();
checkConsistency();
}
- bool validateIgnoringValue(JSValue value) const
+ // We could go further, and ensure that if the futurePossibleStructure contravenes
+ // the value, then we could clear both of those things. But that's unlikely to help
+ // in any realistic scenario, so we don't do it. Simpler is better.
+ void filterValueByType()
{
- if (isTop())
- return true;
-
- if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
- return false;
-
- if (value.isEmpty()) {
- ASSERT(m_type & SpecEmpty);
- return true;
- }
-
- if (m_structure.isTop())
- return true;
-
- if (!!value && value.isCell()) {
- ASSERT(m_type & SpecCell);
- return m_structure.contains(value.asCell()->structure());
+ if (!!m_type) {
+ // The type is still non-empty. This implies that regardless of what filtering
+ // was done, we either didn't have a value to begin with, or that value is still
+ // valid.
+ ASSERT(!m_value || validateType(m_value));
+ return;
}
- return true;
+ // The type has been rendered empty. That means that the value must now be invalid,
+ // as well.
+ ASSERT(!m_value || !validateType(m_value));
+ m_value = JSValue();
}
- bool validate(JSValue value) const
+ bool validateType(JSValue value) const
{
if (isTop())
return true;
- if (!!m_value && m_value != value)
- return false;
-
if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
return false;
@@ -552,18 +296,10 @@ struct AbstractValue {
return true;
}
- if (m_structure.isTop())
- return true;
-
- if (!!value && value.isCell()) {
- ASSERT(m_type & SpecCell);
- return m_structure.contains(value.asCell()->structure());
- }
-
return true;
}
- bool validateForEntry(JSValue value) const
+ bool validate(JSValue value) const
{
if (isTop())
return true;
@@ -579,12 +315,11 @@ struct AbstractValue {
return true;
}
- if (m_unclobberedStructure.isTop())
- return true;
-
if (!!value && value.isCell()) {
ASSERT(m_type & SpecCell);
- return m_unclobberedStructure.contains(value.asCell()->structure());
+ Structure* structure = value.asCell()->structure();
+ return m_currentKnownStructure.contains(structure)
+ && m_futurePossibleStructure.contains(structure);
}
return true;
@@ -593,8 +328,8 @@ struct AbstractValue {
void checkConsistency() const
{
if (!(m_type & SpecCell)) {
- ASSERT(m_structure.isClear());
- ASSERT(m_unclobberedStructure.isClear());
+ ASSERT(m_currentKnownStructure.isClear());
+ ASSERT(m_futurePossibleStructure.isClear());
}
if (isClear())
@@ -612,17 +347,102 @@ struct AbstractValue {
void dump(FILE* out) const
{
fprintf(out, "(%s, ", speculationToString(m_type));
- m_structure.dump(out);
+ m_currentKnownStructure.dump(out);
dataLog(", ");
- m_unclobberedStructure.dump(out);
+ m_futurePossibleStructure.dump(out);
if (!!m_value)
fprintf(out, ", %s", m_value.description());
fprintf(out, ")");
}
+
+ // A great way to think about the difference between m_currentKnownStructure and
+ // m_futurePossibleStructure is to consider these four examples:
+ //
+ // 1) x = foo();
+ //
+ // In this case x's m_currentKnownStructure and m_futurePossibleStructure will
+ // both be TOP, since we don't know anything about x for sure, yet.
+ //
+ // 2) x = foo();
+ // y = x.f;
+ //
+ // Where x will later have a new property added to it, 'g'. Because of the
+ // known but not-yet-executed property addition, x's currently structure will
+ // not be watchpointable; hence we have no way of statically bounding the set
+ // of possible structures that x may have if a clobbering event happens. So,
+ // x's m_currentKnownStructure will be whatever structure we check to get
+ // property 'f', and m_futurePossibleStructure will be TOP.
+ //
+ // 3) x = foo();
+ // y = x.f;
+ //
+ // Where x has a terminal structure that is still watchpointable. In this case,
+ // x's m_currentKnownStructure and m_futurePossibleStructure will both be
+ // whatever structure we checked for when getting 'f'.
+ //
+ // 4) x = foo();
+ // y = x.f;
+ // bar();
+ //
+ // Where x has a terminal structure that is still watchpointable. In this
+ // case, m_currentKnownStructure will be TOP because bar() may potentially
+ // change x's structure and we have no way of proving otherwise, but
+ // x's m_futurePossibleStructure will be whatever structure we had checked
+ // when getting property 'f'.
- StructureAbstractValue m_structure;
- StructureAbstractValue m_unclobberedStructure;
+ // This is a proven constraint on the structures that this value can have right
+ // now. The structure of the current value must belong to this set. The set may
+ // be TOP, indicating that it is the set of all possible structures, in which
+ // case the current value can have any structure. The set may be BOTTOM (empty)
+ // in which case this value cannot be a cell. This is all subject to change
+ // anytime a new value is assigned to this one, anytime there is a control flow
+ // merge, or most crucially, anytime a side-effect or structure check happens.
+ // In case of a side-effect, we typically must assume that any value may have
+ // had its structure changed, hence contravening our proof. We make the proof
+ // valid again by switching this to TOP (i.e. claiming that we have proved that
+ // this value may have any structure). Of note is that the proof represented by
+ // this field is not subject to structure transition watchpoints - even if one
+ // fires, we can be sure that this proof is still valid.
+ StructureAbstractValue m_currentKnownStructure;
+
+ // This is a proven constraint on the structures that this value can have now
+ // or any time in the future subject to the structure transition watchpoints of
+ // all members of this set not having fired. This set is impervious to side-
+ // effects; even if one happens the side-effect can only cause the value to
+ // change to at worst another structure that is also a member of this set. But,
+ // the theorem being proved by this field is predicated upon there not being
+ // any new structure transitions introduced into any members of this set. In
+ // cases where there is no way for us to guard this happening, the set must be
+ // TOP. But in cases where we can guard new structure transitions (all members
+ // of the set have still-valid structure transition watchpoints) then this set
+ // will be finite. Anytime that we make use of the finite nature of this set,
+ // we must first issue a structure transition watchpoint, which will effectively
+ // result in m_currentKnownStructure being filtered according to
+ // m_futurePossibleStructure.
+ StructureAbstractValue m_futurePossibleStructure;
+
+ // This is a proven constraint on the possible types that this value can have
+ // now or any time in the future, unless it is reassigned. This field is
+ // impervious to side-effects unless the side-effect can reassign the value
+ // (for example if we're talking about a captured variable). The relationship
+ // between this field, and the structure fields above, is as follows. The
+ // fields above constraint the structures that a cell may have, but they say
+ // nothing about whether or not the value is known to be a cell. More formally,
+ // the m_currentKnownStructure is itself an abstract value that consists of the
+ // union of the set of all non-cell values and the set of cell values that have
+ // the given structure. This abstract value is then the intersection of the
+ // m_currentKnownStructure and the set of values whose type is m_type. So, for
+ // example if m_type is SpecFinal|SpecInt32 and m_currentKnownStructure is
+ // [0x12345] then this abstract value corresponds to the set of all integers
+ // unified with the set of all objects with structure 0x12345.
SpeculatedType m_type;
+
+ // This is a proven constraint on the possible values that this value can
+ // have now or any time in the future, unless it is reassigned. Note that this
+ // implies nothing about the structure. Oddly, JSValue() (i.e. the empty value)
+ // means either BOTTOM or TOP depending on the state of m_type: if m_type is
+ // BOTTOM then JSValue() means BOTTOM; if m_type is not BOTTOM then JSValue()
+ // means TOP.
JSValue m_value;
};
diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
index 9208cde1b..000e1a938 100644
--- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
@@ -317,6 +317,17 @@ public:
// PhantomArguments and OSR exit will still do the right things.
break;
+ case CheckStructure:
+ case ForwardCheckStructure:
+ case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint:
+ // We don't care about these because if we get uses of the relevant
+ // variable then we can safely get rid of these, too. This of course
+ // relies on there not being any information transferred by the CFA
+ // from a CheckStructure on one variable to the information about the
+ // structures of another variable.
+ break;
+
default:
observeBadArgumentsUses(node);
break;
@@ -471,6 +482,19 @@ public:
break;
}
+ case CheckStructure:
+ case ForwardCheckStructure:
+ case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint: {
+ // We can just get rid of this node, if it references a phantom argument.
+ if (!isOKToOptimize(m_graph[node.child1()]))
+ break;
+ m_graph.deref(node.child1());
+ node.setOpAndDefaultFlags(Phantom);
+ node.children.setChild1(Edge());
+ break;
+ }
+
case GetByVal: {
if (!node.prediction()
|| !m_graph[node.child1()].prediction()
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index b7f48aa4b..f7536f87f 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -118,7 +118,7 @@ private:
template<PhiStackType stackType>
void processPhiStack();
- void fixVariableAccessSpeculations();
+ void fixVariableAccessPredictions();
// Add spill locations to nodes.
void allocateVirtualRegisters();
@@ -2149,6 +2149,10 @@ bool ByteCodeParser::parseBlock(unsigned limit)
NodeIndex base = get(currentInstruction[2].u.operand);
NodeIndex property = get(currentInstruction[3].u.operand);
+ ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
+ profile->computeUpdatedPrediction();
+ if (profile->hasDefiniteStructure())
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(profile->expectedStructure())), base);
NodeIndex propertyStorage = addToGraph(GetIndexedPropertyStorage, base, property);
NodeIndex getByVal = addToGraph(GetByVal, OpInfo(0), OpInfo(prediction), base, property, propertyStorage);
set(currentInstruction[1].u.operand, getByVal);
@@ -2160,6 +2164,15 @@ bool ByteCodeParser::parseBlock(unsigned limit)
NodeIndex base = get(currentInstruction[1].u.operand);
NodeIndex property = get(currentInstruction[2].u.operand);
NodeIndex value = get(currentInstruction[3].u.operand);
+
+ ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
+ profile->computeUpdatedPrediction();
+ if (profile->hasDefiniteStructure())
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(profile->expectedStructure())), base);
+
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+ dataLog("Slow case profile for bc#%u: %u\n", m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter);
+#endif
bool makeSafe =
m_inlineStackTop->m_profiledBlock->couldTakeSlowCase(m_currentIndex)
@@ -3017,7 +3030,7 @@ void ByteCodeParser::processPhiStack()
}
}
-void ByteCodeParser::fixVariableAccessSpeculations()
+void ByteCodeParser::fixVariableAccessPredictions()
{
for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
VariableAccessData* data = &m_graph.m_variableAccessData[i];
@@ -3349,7 +3362,27 @@ bool ByteCodeParser::parse()
m_graph.m_blocks[blockIndex].clear();
}
- fixVariableAccessSpeculations();
+ fixVariableAccessPredictions();
+
+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+ BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+ if (!block)
+ continue;
+ if (!block->isOSRTarget)
+ continue;
+ if (block->bytecodeBegin != m_graph.m_osrEntryBytecodeIndex)
+ continue;
+ for (size_t i = 0; i < m_graph.m_mustHandleValues.size(); ++i) {
+ NodeIndex nodeIndex = block->variablesAtHead.operand(
+ m_graph.m_mustHandleValues.operandForIndex(i));
+ if (nodeIndex == NoNode)
+ continue;
+ Node& node = m_graph[nodeIndex];
+ ASSERT(node.hasLocal());
+ node.variableAccessData()->predict(
+ speculationFromValue(m_graph.m_mustHandleValues[i]));
+ }
+ }
m_graph.m_preservedVars = m_preservedVars;
m_graph.m_localVars = m_numLocals;
diff --git a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp
index dc1632dc4..f054707e2 100644
--- a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp
@@ -613,7 +613,9 @@ private:
ASSERT(node.shouldGenerate());
Node& possibleLocalOp = m_graph[node.child1()];
- if (possibleLocalOp.hasLocal() && !possibleLocalOp.variableAccessData()->isCaptured()) {
+ if (possibleLocalOp.op() != GetLocal
+ && possibleLocalOp.hasLocal()
+ && !possibleLocalOp.variableAccessData()->isCaptured()) {
NodeIndex setLocalIndex =
firstBlock->variablesAtTail.operand(possibleLocalOp.local());
Node& setLocal = m_graph[setLocalIndex];
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index e9b1e0d8b..b78ddc89d 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -345,6 +345,7 @@ private:
break;
case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint:
if (node.child1() == child1
&& structureSet.contains(node.structure()))
return true;
@@ -418,6 +419,7 @@ private:
return false;
case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint:
if (node.structure() == structure && node.child1() == child1)
return true;
break;
@@ -843,6 +845,8 @@ private:
// At this point we will eliminate all references to this node.
m_replacements[m_compileIndex] = replacement;
+ m_changed = true;
+
return true;
}
@@ -856,6 +860,8 @@ private:
ASSERT(node.refCount() == 1);
ASSERT(node.mustGenerate());
node.setOpAndDefaultFlags(Phantom);
+
+ m_changed = true;
}
void eliminate(NodeIndex nodeIndex, NodeType phantomType = Phantom)
@@ -867,6 +873,8 @@ private:
return;
ASSERT(node.mustGenerate());
node.setOpAndDefaultFlags(phantomType);
+
+ m_changed = true;
}
void performNodeCSE(Node& node)
@@ -944,7 +952,7 @@ private:
case GetLocal: {
VariableAccessData* variableAccessData = node.variableAccessData();
- if (m_fixpointState == FixpointNotConverged && !variableAccessData->isCaptured())
+ if (!variableAccessData->isCaptured())
break;
NodeIndex relevantLocalOp;
NodeIndex possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp, variableAccessData->isCaptured());
@@ -982,7 +990,7 @@ private:
case GetLocalUnlinked: {
NodeIndex relevantLocalOpIgnored;
- m_changed |= setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored, true));
+ setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored, true));
break;
}
@@ -1117,6 +1125,7 @@ private:
break;
case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint:
if (structureTransitionWatchpointElimination(node.structure(), node.child1().index()))
eliminate();
break;
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index a8eb9ee5c..68d5534e0 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -91,7 +91,16 @@ private:
break;
}
- // FIXME: This would be a great place to remove CheckStructure's.
+ case CheckStructure:
+ case ForwardCheckStructure: {
+ AbstractValue& value = m_state.forNode(node.child1());
+ StructureAbstractValue& structureValue = value.m_futurePossibleStructure;
+ if (structureValue.isSubsetOf(node.structureSet())
+ && structureValue.hasSingleton()
+ && isCellSpeculation(value.m_type))
+ node.convertToStructureTransitionWatchpoint(structureValue.singleton());
+ break;
+ }
default:
break;
@@ -103,9 +112,7 @@ private:
}
m_state.execute(indexInBlock);
- if (!node.shouldGenerate()
- || m_state.didClobber()
- || node.hasConstant())
+ if (!node.shouldGenerate() || m_state.didClobber() || node.hasConstant())
continue;
JSValue value = m_state.forNode(nodeIndex).value();
if (!value)
diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp
index ddad4f864..ccef65208 100644
--- a/Source/JavaScriptCore/dfg/DFGDriver.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp
@@ -26,6 +26,10 @@
#include "config.h"
#include "DFGDriver.h"
+#include "JSObject.h"
+#include "JSString.h"
+#include "ScopeChain.h"
+
#if ENABLE(DFG_JIT)
#include "DFGArgumentsSimplificationPhase.h"
@@ -53,7 +57,7 @@ unsigned getNumCompilations()
}
enum CompileMode { CompileFunction, CompileOther };
-inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr* jitCodeWithArityCheck)
+inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr* jitCodeWithArityCheck, unsigned osrEntryBytecodeIndex)
{
SamplingRegion samplingRegion("DFG Compilation (Driver)");
@@ -62,6 +66,8 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo
ASSERT(codeBlock);
ASSERT(codeBlock->alternative());
ASSERT(codeBlock->alternative()->getJITType() == JITCode::BaselineJIT);
+
+ ASSERT(osrEntryBytecodeIndex != UINT_MAX);
if (!Options::useDFGJIT())
return false;
@@ -70,7 +76,30 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo
dataLog("DFG compiling code block %p(%p) for executable %p, number of instructions = %u.\n", codeBlock, codeBlock->alternative(), codeBlock->ownerExecutable(), codeBlock->instructionCount());
#endif
- Graph dfg(exec->globalData(), codeBlock);
+ // Derive our set of must-handle values. The compilation must be at least conservative
+ // enough to allow for OSR entry with these values.
+ unsigned numVarsWithValues;
+ if (osrEntryBytecodeIndex)
+ numVarsWithValues = codeBlock->m_numVars;
+ else
+ numVarsWithValues = 0;
+ Operands<JSValue> mustHandleValues(codeBlock->numParameters(), numVarsWithValues);
+ for (size_t i = 0; i < mustHandleValues.size(); ++i) {
+ int operand = mustHandleValues.operandForIndex(i);
+ if (operandIsArgument(operand)
+ && !operandToArgument(operand)
+ && compileMode == CompileFunction
+ && codeBlock->specializationKind() == CodeForConstruct) {
+ // Ugh. If we're in a constructor, the 'this' argument may hold garbage. It will
+ // also never be used. It doesn't matter what we put into the value for this,
+ // but it has to be an actual value that can be grokked by subsequent DFG passes,
+ // so we sanitize it here by turning it into Undefined.
+ mustHandleValues[i] = jsUndefined();
+ } else
+ mustHandleValues[i] = exec->uncheckedR(operand).jsValue();
+ }
+
+ Graph dfg(exec->globalData(), codeBlock, osrEntryBytecodeIndex, mustHandleValues);
if (!parse(exec, dfg))
return false;
@@ -86,6 +115,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo
validate(dfg);
performPredictionPropagation(dfg);
performFixup(dfg);
+ performStructureCheckHoisting(dfg);
unsigned cnt = 1;
for (;; ++cnt) {
#if DFG_ENABLE(DEBUG_VERBOSE)
@@ -102,10 +132,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo
dfg.resetExitStates();
performFixup(dfg);
}
- bool shouldRedoCFA = performStructureCheckHoisting(dfg);
performCSE(dfg, FixpointConverged);
- if (shouldRedoCFA)
- performCFA(dfg);
#if DFG_ENABLE(DEBUG_VERBOSE)
dataLog("DFG optimization fixpoint converged in %u iterations.\n", cnt);
#endif
@@ -135,14 +162,14 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo
return result;
}
-bool tryCompile(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode)
+bool tryCompile(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, unsigned bytecodeIndex)
{
- return compile(CompileOther, exec, codeBlock, jitCode, 0);
+ return compile(CompileOther, exec, codeBlock, jitCode, 0, bytecodeIndex);
}
-bool tryCompileFunction(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr& jitCodeWithArityCheck)
+bool tryCompileFunction(ExecState* exec, CodeBlock* codeBlock, JITCode& jitCode, MacroAssemblerCodePtr& jitCodeWithArityCheck, unsigned bytecodeIndex)
{
- return compile(CompileFunction, exec, codeBlock, jitCode, &jitCodeWithArityCheck);
+ return compile(CompileFunction, exec, codeBlock, jitCode, &jitCodeWithArityCheck, bytecodeIndex);
}
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGDriver.h b/Source/JavaScriptCore/dfg/DFGDriver.h
index a6e82fef5..1964ec34a 100644
--- a/Source/JavaScriptCore/dfg/DFGDriver.h
+++ b/Source/JavaScriptCore/dfg/DFGDriver.h
@@ -41,11 +41,11 @@ namespace DFG {
JS_EXPORT_PRIVATE unsigned getNumCompilations();
#if ENABLE(DFG_JIT)
-bool tryCompile(ExecState*, CodeBlock*, JITCode&);
-bool tryCompileFunction(ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr& jitCodeWithArityCheck);
+bool tryCompile(ExecState*, CodeBlock*, JITCode&, unsigned bytecodeIndex);
+bool tryCompileFunction(ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr& jitCodeWithArityCheck, unsigned bytecodeIndex);
#else
-inline bool tryCompile(ExecState*, CodeBlock*, JITCode&) { return false; }
-inline bool tryCompileFunction(ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr&) { return false; }
+inline bool tryCompile(ExecState*, CodeBlock*, JITCode&, unsigned) { return false; }
+inline bool tryCompileFunction(ExecState*, CodeBlock*, JITCode&, MacroAssemblerCodePtr&, unsigned) { return false; }
#endif
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 4e3cd5782..f7b10fc43 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -96,9 +96,29 @@ private:
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
dataLog(" @%u -> %s", m_compileIndex, isArray ? "GetArrayLength" : "GetStringLength");
#endif
- if (isArray)
+ if (isArray) {
node.setOp(GetArrayLength);
- else if (isArguments)
+ ASSERT(node.flags() & NodeMustGenerate);
+ node.clearFlags(NodeMustGenerate);
+ m_graph.deref(m_compileIndex);
+
+ ArrayProfile* arrayProfile =
+ m_graph.baselineCodeBlockFor(node.codeOrigin)->getArrayProfile(
+ node.codeOrigin.bytecodeIndex);
+ if (!arrayProfile)
+ break;
+ arrayProfile->computeUpdatedPrediction();
+ if (!arrayProfile->hasDefiniteStructure())
+ break;
+ m_graph.ref(node.child1());
+ Node checkStructure(CheckStructure, node.codeOrigin, OpInfo(m_graph.addStructureSet(arrayProfile->expectedStructure())), node.child1().index());
+ checkStructure.ref();
+ NodeIndex checkStructureIndex = m_graph.size();
+ m_graph.append(checkStructure);
+ m_insertionSet.append(m_indexInBlock, checkStructureIndex);
+ break;
+ }
+ if (isArguments)
node.setOp(GetArgumentsLength);
else if (isString)
node.setOp(GetStringLength);
@@ -129,10 +149,9 @@ private:
break;
}
case GetIndexedPropertyStorage: {
- SpeculatedType basePrediction = m_graph[node.child2()].prediction();
- if ((!(basePrediction & SpecInt32) && basePrediction)
- || m_graph[node.child1()].shouldSpeculateArguments()
- || !isActionableArraySpeculation(m_graph[node.child1()].prediction())) {
+ if (!m_graph[node.child1()].prediction()
+ || !m_graph[node.child2()].shouldSpeculateInteger()
+ || m_graph[node.child1()].shouldSpeculateArguments()) {
node.setOpAndDefaultFlags(Nop);
m_graph.clearAndDerefChild1(node);
m_graph.clearAndDerefChild2(node);
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index fdb78cf9b..8d164a299 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -76,11 +76,13 @@ struct ResolveGlobalData {
// Nodes that are 'dead' remain in the vector with refCount 0.
class Graph : public Vector<Node, 64> {
public:
- Graph(JSGlobalData& globalData, CodeBlock* codeBlock)
+ Graph(JSGlobalData& globalData, CodeBlock* codeBlock, unsigned osrEntryBytecodeIndex, const Operands<JSValue>& mustHandleValues)
: m_globalData(globalData)
, m_codeBlock(codeBlock)
, m_profiledBlock(codeBlock->alternative())
, m_hasArguments(false)
+ , m_osrEntryBytecodeIndex(osrEntryBytecodeIndex)
+ , m_mustHandleValues(mustHandleValues)
{
ASSERT(m_profiledBlock);
}
@@ -137,6 +139,20 @@ public:
edge = newEdge;
}
+ void compareAndSwap(Edge& edge, NodeIndex oldIndex, NodeIndex newIndex, bool changeRef)
+ {
+ if (edge.index() != oldIndex)
+ return;
+ changeIndex(edge, newIndex, changeRef);
+ }
+
+ void compareAndSwap(Edge& edge, Edge oldEdge, Edge newEdge, bool changeRef)
+ {
+ if (edge != oldEdge)
+ return;
+ changeEdge(edge, newEdge, changeRef);
+ }
+
void clearAndDerefChild1(Node& node)
{
if (!node.child1())
@@ -614,6 +630,69 @@ public:
vote(node.child3(), ballot);
}
+ template<typename T> // T = NodeIndex or Edge
+ void substitute(BasicBlock& block, unsigned startIndexInBlock, T oldThing, T newThing)
+ {
+ for (unsigned indexInBlock = startIndexInBlock; indexInBlock < block.size(); ++indexInBlock) {
+ NodeIndex nodeIndex = block[indexInBlock];
+ Node& node = at(nodeIndex);
+ if (node.flags() & NodeHasVarArgs) {
+ for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); ++childIdx)
+ compareAndSwap(m_varArgChildren[childIdx], oldThing, newThing, node.shouldGenerate());
+ continue;
+ }
+ if (!node.child1())
+ continue;
+ compareAndSwap(node.children.child1(), oldThing, newThing, node.shouldGenerate());
+ if (!node.child2())
+ continue;
+ compareAndSwap(node.children.child2(), oldThing, newThing, node.shouldGenerate());
+ if (!node.child3())
+ continue;
+ compareAndSwap(node.children.child3(), oldThing, newThing, node.shouldGenerate());
+ }
+ }
+
+ // Use this if you introduce a new GetLocal and you know that you introduced it *before*
+ // any GetLocals in the basic block.
+ // FIXME: it may be appropriate, in the future, to generalize this to handle GetLocals
+ // introduced anywhere in the basic block.
+ void substituteGetLocal(BasicBlock& block, unsigned startIndexInBlock, VariableAccessData* variableAccessData, NodeIndex newGetLocal)
+ {
+ if (variableAccessData->isCaptured()) {
+ // Let CSE worry about this one.
+ return;
+ }
+ for (unsigned indexInBlock = startIndexInBlock; indexInBlock < block.size(); ++indexInBlock) {
+ NodeIndex nodeIndex = block[indexInBlock];
+ Node& node = at(nodeIndex);
+ bool shouldContinue = true;
+ switch (node.op()) {
+ case SetLocal: {
+ if (node.local() == variableAccessData->local())
+ shouldContinue = false;
+ break;
+ }
+
+ case GetLocal: {
+ if (node.variableAccessData() != variableAccessData)
+ continue;
+ substitute(block, indexInBlock, nodeIndex, newGetLocal);
+ NodeIndex oldTailIndex = block.variablesAtTail.operand(variableAccessData->local());
+ if (oldTailIndex == nodeIndex)
+ block.variablesAtTail.operand(variableAccessData->local()) = newGetLocal;
+ shouldContinue = false;
+ break;
+ }
+
+ default:
+ break;
+ }
+ if (!shouldContinue)
+ break;
+ }
+ }
+
JSGlobalData& m_globalData;
CodeBlock* m_codeBlock;
CodeBlock* m_profiledBlock;
@@ -633,6 +712,8 @@ public:
Dominators m_dominators;
unsigned m_localVars;
unsigned m_parameterSlots;
+ unsigned m_osrEntryBytecodeIndex;
+ Operands<JSValue> m_mustHandleValues;
private:
void handleSuccessor(Vector<BlockIndex, 16>& worklist, BlockIndex blockIndex, BlockIndex successorIndex);
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 60cdd4b50..dac855be0 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -245,11 +245,19 @@ struct Node {
children.reset();
}
+ void convertToStructureTransitionWatchpoint(Structure* structure)
+ {
+ ASSERT(m_op == CheckStructure || m_op == ForwardCheckStructure);
+ m_opInfo = bitwise_cast<uintptr_t>(structure);
+ if (m_op == CheckStructure)
+ m_op = StructureTransitionWatchpoint;
+ else
+ m_op = ForwardStructureTransitionWatchpoint;
+ }
+
void convertToStructureTransitionWatchpoint()
{
- ASSERT(m_op == CheckStructure);
- m_opInfo = bitwise_cast<uintptr_t>(structureSet().singletonStructure());
- m_op = StructureTransitionWatchpoint;
+ convertToStructureTransitionWatchpoint(structureSet().singletonStructure());
}
JSCell* weakConstant()
@@ -675,7 +683,13 @@ struct Node {
bool hasStructure()
{
- return op() == StructureTransitionWatchpoint;
+ switch (op()) {
+ case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint:
+ return true;
+ default:
+ return false;
+ }
}
Structure* structure()
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 7657663a9..f0f8cb1d0 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -133,6 +133,7 @@ namespace JSC { namespace DFG {
/* the object's structure does not need to be rechecked due to side-effecting */\
/* (clobbering) operations. */\
macro(StructureTransitionWatchpoint, NodeMustGenerate) \
+ macro(ForwardStructureTransitionWatchpoint, NodeMustGenerate) \
macro(PutStructure, NodeMustGenerate) \
macro(PhantomPutStructure, NodeMustGenerate | NodeDoesNotExit) \
macro(AllocatePropertyStorage, NodeMustGenerate | NodeDoesNotExit | NodeResultStorage) \
diff --git a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp
index 97061bfb2..9a7bc96cc 100644
--- a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp
@@ -99,7 +99,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
else
value = exec->argument(argument - 1);
- if (!entry->m_expectedValues.argument(argument).validateForEntry(value)) {
+ if (!entry->m_expectedValues.argument(argument).validate(value)) {
#if ENABLE(JIT_VERBOSE_OSR)
dataLog(" OSR failed because argument %zu is %s, expected ", argument, value.description());
entry->m_expectedValues.argument(argument).dump(WTF::dataFile());
@@ -119,7 +119,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
}
continue;
}
- if (!entry->m_expectedValues.local(local).validateForEntry(exec->registers()[local].jsValue())) {
+ if (!entry->m_expectedValues.local(local).validate(exec->registers()[local].jsValue())) {
#if ENABLE(JIT_VERBOSE_OSR)
dataLog(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description());
entry->m_expectedValues.local(local).dump(WTF::dataFile());
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 94f69abc2..1247528e8 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -675,6 +675,7 @@ private:
case CheckStructure:
case ForwardCheckStructure:
case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint:
case CheckFunction:
case PutStructure:
case TearOffActivation:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index f17e2d7e4..6c6615716 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -119,11 +119,8 @@ JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpoint(ExitKind kind)
return speculationWatchpoint(kind, JSValueSource(), NoNode);
}
-void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery)
+void SpeculativeJIT::convertLastOSRExitToForward(const ValueRecovery& valueRecovery)
{
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- speculationCheck(kind, jsValueSource, nodeIndex, jumpToFail);
-
#if !ASSERT_DISABLED
if (!valueRecovery) {
// Check that the preceding node was a SetLocal with the same code origin.
@@ -155,6 +152,10 @@ void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValu
ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin);
Node* nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1));
+ if (nextNode->op() == Jump && nextNode->codeOrigin == at(m_compileIndex).codeOrigin) {
+ // We're at an inlined return. Use a backward speculation instead.
+ return;
+ }
ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin);
OSRExit& exit = m_jit.codeBlock()->lastOSRExit();
@@ -167,6 +168,28 @@ void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValu
new ValueRecoveryOverride(setLocal->local(), valueRecovery));
}
+JumpReplacementWatchpoint* SpeculativeJIT::forwardSpeculationWatchpoint(ExitKind kind)
+{
+ JumpReplacementWatchpoint* result = speculationWatchpoint(kind);
+ convertLastOSRExitToForward();
+ return result;
+}
+
+JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpointWithConditionalDirection(ExitKind kind, bool isForward)
+{
+ JumpReplacementWatchpoint* result = speculationWatchpoint(kind);
+ if (isForward)
+ convertLastOSRExitToForward();
+ return result;
+}
+
+void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& valueRecovery)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ speculationCheck(kind, jsValueSource, nodeIndex, jumpToFail);
+ convertLastOSRExitToForward(valueRecovery);
+}
+
void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery)
{
ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
@@ -246,6 +269,24 @@ void SpeculativeJIT::clearGenerationInfo()
m_fprs = RegisterBank<FPRInfo>();
}
+void SpeculativeJIT::speculateArray(Edge edge, GPRReg baseReg)
+{
+ AbstractValue& arrayValue = m_state.forNode(edge);
+ if (arrayValue.m_currentKnownStructure.hasSingleton()
+ && arrayValue.m_currentKnownStructure.singleton()->classInfo() == &JSArray::s_info)
+ return;
+
+ GPRTemporary temp(this);
+ m_jit.loadPtr(
+ MacroAssembler::Address(baseReg, JSCell::structureOffset()), temp.gpr());
+ speculationCheck(
+ Uncountable, JSValueRegs(), NoNode,
+ m_jit.branchPtr(
+ MacroAssembler::NotEqual,
+ MacroAssembler::Address(temp.gpr(), Structure::classInfoOffset()),
+ MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
+}
+
GPRReg SpeculativeJIT::fillStorage(NodeIndex nodeIndex)
{
Node& node = m_jit.graph()[nodeIndex];
@@ -1220,7 +1261,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
valueSource = ValueSource(DoubleInRegisterFile);
else if (isInt32Speculation(argumentPosition.prediction()))
valueSource = ValueSource(Int32InRegisterFile);
- else if (isArraySpeculation(argumentPosition.prediction()) || isCellSpeculation(argumentPosition.prediction()))
+ else if (isCellSpeculation(argumentPosition.prediction()))
valueSource = ValueSource(CellInRegisterFile);
else if (isBooleanSpeculation(argumentPosition.prediction()))
valueSource = ValueSource(BooleanInRegisterFile);
@@ -1343,12 +1384,7 @@ void SpeculativeJIT::checkArgumentTypes()
#if USE(JSVALUE64)
if (isInt32Speculation(predictedType))
speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::Below, JITCompiler::addressFor(virtualRegister), GPRInfo::tagTypeNumberRegister));
- else if (isArraySpeculation(predictedType)) {
- GPRTemporary temp(this);
- m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr());
- speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, temp.gpr(), GPRInfo::tagMaskRegister));
- speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
- } else if (isBooleanSpeculation(predictedType)) {
+ else if (isBooleanSpeculation(predictedType)) {
GPRTemporary temp(this);
m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), temp.gpr());
m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueFalse)), temp.gpr());
@@ -1403,13 +1439,7 @@ void SpeculativeJIT::checkArgumentTypes()
#else
if (isInt32Speculation(predictedType))
speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::Int32Tag)));
- else if (isArraySpeculation(predictedType)) {
- GPRTemporary temp(this);
- m_jit.load32(JITCompiler::tagFor(virtualRegister), temp.gpr());
- speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, temp.gpr(), TrustedImm32(JSValue::CellTag)));
- m_jit.load32(JITCompiler::payloadFor(virtualRegister), temp.gpr());
- speculationCheck(BadType, valueSource, nodeIndex, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(temp.gpr(), JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
- } else if (isBooleanSpeculation(predictedType))
+ else if (isBooleanSpeculation(predictedType))
speculationCheck(BadType, valueSource, nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::BooleanTag)));
else if (isInt8ArraySpeculation(predictedType)) {
GPRTemporary temp(this);
@@ -1591,7 +1621,6 @@ void SpeculativeJIT::compileGetByValOnString(Node& node)
GPRReg storageReg = storage.gpr();
if (!isStringSpeculation(m_state.forNode(node.child1()).m_type)) {
- ASSERT(!(at(node.child1()).prediction() & SpecString));
terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode);
noResult(m_compileIndex);
return;
@@ -3037,22 +3066,12 @@ bool SpeculativeJIT::compileStrictEq(Node& node)
void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node)
{
- if (!node.prediction() || !at(node.child1()).prediction() || !at(node.child2()).prediction()) {
- terminateSpeculativeExecution(InadequateCoverage, JSValueRegs(), NoNode);
- return;
- }
+ ASSERT(at(node.child1()).prediction());
+ ASSERT(at(node.child2()).shouldSpeculateInteger());
SpeculateCellOperand base(this, node.child1());
GPRReg baseReg = base.gpr();
- SpeculatedType basePrediction = at(node.child2()).prediction();
- if (!(basePrediction & SpecInt32) && basePrediction) {
- ASSERT_NOT_REACHED();
- terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode);
- noResult(m_compileIndex);
- return;
- }
-
GPRTemporary storage(this);
GPRReg storageReg = storage.gpr();
if (at(node.child1()).shouldSpeculateArguments()) {
@@ -3113,8 +3132,7 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node)
speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(descriptor.m_classInfo)));
m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
} else {
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
+ speculateArray(node.child1(), baseReg);
m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
}
storageResult(storageReg, m_compileIndex);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 96f2fec0a..073dbb42c 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -2181,6 +2181,10 @@ public:
// act of firing a watchpoint invalidates it. So, future recompilations will not
// attempt to set this watchpoint again.
JumpReplacementWatchpoint* speculationWatchpoint(ExitKind = UncountableWatchpoint);
+
+ // It is generally a good idea to not use this directly.
+ void convertLastOSRExitToForward(const ValueRecovery& = ValueRecovery());
+
// Note: not specifying the valueRecovery argument (leaving it as ValueRecovery()) implies
// that you've ensured that there exists a MovHint prior to your use of forwardSpeculationCheck().
void forwardSpeculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail, const ValueRecovery& = ValueRecovery());
@@ -2190,6 +2194,12 @@ public:
void terminateSpeculativeExecution(ExitKind, JSValueRegs, NodeIndex);
void terminateSpeculativeExecution(ExitKind, JSValueRegs, Edge);
void terminateSpeculativeExecutionWithConditionalDirection(ExitKind, JSValueRegs, NodeIndex, bool isForward);
+ // Issue a forward speculation watchpoint, which will exit to the next instruction rather
+ // than the current one.
+ JumpReplacementWatchpoint* forwardSpeculationWatchpoint(ExitKind = UncountableWatchpoint);
+ JumpReplacementWatchpoint* speculationWatchpointWithConditionalDirection(ExitKind, bool isForward);
+
+ void speculateArray(Edge baseEdge, GPRReg baseReg);
template<bool strict>
GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index bf3503d6d..7a9ba1e41 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -1530,7 +1530,7 @@ void SpeculativeJIT::compileObjectToObjectOrOtherEquality(
// We know that within this branch, rightChild must not be a cell. Check if that is enough to
// prove that it is either null or undefined.
- if (!isOtherSpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) {
+ if (!isOtherOrEmptySpeculation(m_state.forNode(rightChild).m_type & ~SpecCell)) {
m_jit.move(op2TagGPR, resultGPR);
m_jit.or32(TrustedImm32(1), resultGPR);
@@ -1602,7 +1602,7 @@ void SpeculativeJIT::compilePeepHoleObjectToObjectOrOtherEquality(
// We know that within this branch, rightChild must not be a cell. Check if that is enough to
// prove that it is either null or undefined.
- if (isOtherSpeculation(m_state.forNode(rightChild).m_type & ~SpecCell))
+ if (isOtherOrEmptySpeculation(m_state.forNode(rightChild).m_type & ~SpecCell))
rightNotCell.link(&m_jit);
else {
jump(notTaken, ForceJump);
@@ -1918,7 +1918,7 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- if (isArraySpeculation(value.m_type) || isCellSpeculation(value.m_type)) {
+ if (isCellSpeculation(value.m_type)) {
GPRTemporary result(this);
m_jit.load32(JITCompiler::payloadFor(node.local()), result.gpr());
@@ -2036,16 +2036,6 @@ void SpeculativeJIT::compile(Node& node)
recordSetLocal(node.local(), ValueSource(Int32InRegisterFile));
break;
}
- if (isArraySpeculation(predictedType)) {
- SpeculateCellOperand cell(this, node.child1());
- GPRReg cellGPR = cell.gpr();
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
- m_jit.storePtr(cellGPR, JITCompiler::payloadFor(node.local()));
- noResult(m_compileIndex);
- recordSetLocal(node.local(), ValueSource(CellInRegisterFile));
- break;
- }
if (isCellSpeculation(predictedType)) {
SpeculateCellOperand cell(this, node.child1());
GPRReg cellGPR = cell.gpr();
@@ -2356,7 +2346,7 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- if (!at(node.child2()).shouldSpeculateInteger() || !isActionableArraySpeculation(at(node.child1()).prediction())) {
+ if (!at(node.child2()).shouldSpeculateInteger() || (!node.child3() && !at(node.child1()).shouldSpeculateArguments())) {
SpeculateCellOperand base(this, node.child1()); // Save a register, speculate cell. We'll probably be right.
JSValueOperand property(this, node.child2());
GPRReg baseGPR = base.gpr();
@@ -2449,8 +2439,6 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- ASSERT(at(node.child1()).shouldSpeculateArray());
-
SpeculateStrictInt32Operand property(this, node.child2());
StorageOperand storage(this, node.child3());
GPRReg propertyReg = property.gpr();
@@ -2464,17 +2452,13 @@ void SpeculativeJIT::compile(Node& node)
{
SpeculateCellOperand base(this, node.child1());
GPRReg baseReg = base.gpr();
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
+ // We've already speculated that it's some kind of array, at this point.
speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
}
GPRTemporary resultTag(this);
GPRTemporary resultPayload(this);
- // FIXME: In cases where there are subsequent by_val accesses to the same base it might help to cache
- // the storage pointer - especially if there happens to be another register free right now. If we do so,
- // then we'll need to allocate a new temporary for result.
m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr());
speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag)));
m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr());
@@ -2495,7 +2479,6 @@ void SpeculativeJIT::compile(Node& node)
}
if (!at(child2).shouldSpeculateInteger()
- || !isActionableMutableArraySpeculation(at(child1).prediction())
|| at(child1).shouldSpeculateArguments()) {
SpeculateCellOperand base(this, child1); // Save a register, speculate cell. We'll probably be right.
JSValueOperand property(this, child2);
@@ -2578,27 +2561,23 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- ASSERT(at(child1).shouldSpeculateArray());
-
JSValueOperand value(this, child3);
- GPRTemporary scratch(this);
-
// Map base, property & value into registers, allocate a scratch register.
GPRReg baseReg = base.gpr();
GPRReg propertyReg = property.gpr();
GPRReg valueTagReg = value.tagGPR();
GPRReg valuePayloadReg = value.payloadGPR();
- GPRReg scratchReg = scratch.gpr();
if (!m_compileOkay)
return;
-
- writeBarrier(baseReg, valueTagReg, child3, WriteBarrierForPropertyAccess, scratchReg);
- // Check that base is an array, and that property is contained within m_vector (< m_vectorLength).
- // If we have predicted the base to be type array, we can skip the check.
- if (!isArraySpeculation(m_state.forNode(child1).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), child1, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
+ {
+ GPRTemporary scratch(this);
+ GPRReg scratchReg = scratch.gpr();
+ writeBarrier(baseReg, valueTagReg, child3, WriteBarrierForPropertyAccess, scratchReg);
+ }
+
+ speculateArray(child1, baseReg);
MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()));
if (node.op() == PutByVal)
@@ -2609,7 +2588,8 @@ void SpeculativeJIT::compile(Node& node)
value.use();
// Get the array storage.
- GPRReg storageReg = scratchReg;
+ GPRTemporary storage(this);
+ GPRReg storageReg = storage.gpr();
m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
// Check if we're writing to a hole; if so increment m_numValuesInVector.
@@ -2795,20 +2775,23 @@ void SpeculativeJIT::compile(Node& node)
case ArrayPush: {
SpeculateCellOperand base(this, node.child1());
JSValueOperand value(this, node.child2());
- GPRTemporary storage(this);
GPRTemporary storageLength(this);
GPRReg baseGPR = base.gpr();
GPRReg valueTagGPR = value.tagGPR();
GPRReg valuePayloadGPR = value.payloadGPR();
- GPRReg storageGPR = storage.gpr();
GPRReg storageLengthGPR = storageLength.gpr();
- writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, storageGPR, storageLengthGPR);
+ {
+ GPRTemporary scratch(this);
+ writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR);
+ }
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
+ speculateArray(node.child1(), baseGPR);
+ GPRTemporary storage(this);
+ GPRReg storageGPR = storage.gpr();
+
m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR);
m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR);
@@ -2844,8 +2827,7 @@ void SpeculativeJIT::compile(Node& node)
GPRReg storageGPR = storage.gpr();
GPRReg storageLengthGPR = storageLength.gpr();
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
+ speculateArray(node.child1(), baseGPR);
m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR);
m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR);
@@ -3395,8 +3377,7 @@ void SpeculativeJIT::compile(Node& node)
SpeculateCellOperand base(this, node.child1());
GPRReg baseGPR = base.gpr();
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
+ speculateArray(node.child1(), baseGPR);
GPRTemporary result(this);
GPRReg resultGPR = result.gpr();
@@ -3478,7 +3459,7 @@ void SpeculativeJIT::compile(Node& node)
case CheckStructure:
case ForwardCheckStructure: {
AbstractValue& value = m_state.forNode(node.child1());
- if (value.m_structure.isSubsetOf(node.structureSet())
+ if (value.m_currentKnownStructure.isSubsetOf(node.structureSet())
&& isCellSpeculation(value.m_type)) {
noResult(m_compileIndex);
break;
@@ -3519,9 +3500,12 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- case StructureTransitionWatchpoint: {
+ case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint: {
m_jit.addWeakReference(node.structure());
- node.structure()->addTransitionWatchpoint(speculationWatchpoint(BadCache));
+ node.structure()->addTransitionWatchpoint(
+ speculationWatchpointWithConditionalDirection(
+ BadCache, node.op() == ForwardStructureTransitionWatchpoint));
#if !ASSERT_DISABLED
SpeculateCellOperand op1(this, node.child1());
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 5541113f2..c2151088c 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -2072,16 +2072,6 @@ void SpeculativeJIT::compile(Node& node)
recordSetLocal(node.local(), ValueSource(Int32InRegisterFile));
break;
}
- if (isArraySpeculation(predictedType)) {
- SpeculateCellOperand cell(this, node.child1());
- GPRReg cellGPR = cell.gpr();
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueRegs(cellGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(cellGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
- m_jit.storePtr(cellGPR, JITCompiler::addressFor(node.local()));
- noResult(m_compileIndex);
- recordSetLocal(node.local(), ValueSource(CellInRegisterFile));
- break;
- }
if (isCellSpeculation(predictedType)) {
SpeculateCellOperand cell(this, node.child1());
GPRReg cellGPR = cell.gpr();
@@ -2389,7 +2379,7 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- if (!at(node.child2()).shouldSpeculateInteger() || !isActionableArraySpeculation(at(node.child1()).prediction())) {
+ if (!at(node.child2()).shouldSpeculateInteger() || (!node.child3() && !at(node.child1()).shouldSpeculateArguments())) {
JSValueOperand base(this, node.child1());
JSValueOperand property(this, node.child2());
GPRReg baseGPR = base.gpr();
@@ -2480,8 +2470,6 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- ASSERT(at(node.child1()).shouldSpeculateArray());
-
SpeculateCellOperand base(this, node.child1());
SpeculateStrictInt32Operand property(this, node.child2());
StorageOperand storage(this, node.child3());
@@ -2493,13 +2481,11 @@ void SpeculativeJIT::compile(Node& node)
if (!m_compileOkay)
return;
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueRegs(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
+ // We will have already speculated that the base is some kind of array,
+ // at this point.
+
speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
- // FIXME: In cases where there are subsequent by_val accesses to the same base it might help to cache
- // the storage pointer - especially if there happens to be another register free right now. If we do so,
- // then we'll need to allocate a new temporary for result.
GPRTemporary result(this);
m_jit.loadPtr(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), result.gpr());
speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));
@@ -2519,7 +2505,7 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- if (!at(child2).shouldSpeculateInteger() || !isActionableMutableArraySpeculation(at(child1).prediction())) {
+ if (!at(child2).shouldSpeculateInteger()) {
JSValueOperand arg1(this, child1);
JSValueOperand arg2(this, child2);
JSValueOperand arg3(this, child3);
@@ -2537,9 +2523,8 @@ void SpeculativeJIT::compile(Node& node)
SpeculateCellOperand base(this, child1);
SpeculateStrictInt32Operand property(this, child2);
if (at(child1).shouldSpeculateArguments()) {
+ dataLog(" in here ");
JSValueOperand value(this, child3);
- SpeculateCellOperand base(this, child1);
- SpeculateStrictInt32Operand property(this, child2);
GPRTemporary scratch(this);
GPRTemporary scratch2(this);
@@ -2656,8 +2641,6 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- ASSERT(at(child1).shouldSpeculateArray());
-
JSValueOperand value(this, child3);
GPRTemporary scratch(this);
@@ -2672,11 +2655,8 @@ void SpeculativeJIT::compile(Node& node)
writeBarrier(baseReg, value.gpr(), child3, WriteBarrierForPropertyAccess, scratchReg);
- // Check that base is an array, and that property is contained within m_vector (< m_vectorLength).
- // If we have predicted the base to be type array, we can skip the check.
- if (!isArraySpeculation(m_state.forNode(child1).m_type))
- speculationCheck(BadType, JSValueRegs(baseReg), child1, m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
-
+ speculateArray(child1, baseReg);
+
MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()));
if (node.op() == PutByVal)
speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds);
@@ -2879,9 +2859,8 @@ void SpeculativeJIT::compile(Node& node)
writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, storageGPR, storageLengthGPR);
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
-
+ speculateArray(node.child1(), baseGPR);
+
m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR);
m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR);
@@ -2917,9 +2896,8 @@ void SpeculativeJIT::compile(Node& node)
GPRReg storageGPR = storage.gpr();
GPRReg storageLengthGPR = storageLength.gpr();
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
-
+ speculateArray(node.child1(), baseGPR);
+
m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), storageGPR);
m_jit.load32(MacroAssembler::Address(storageGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), storageLengthGPR);
@@ -3415,9 +3393,8 @@ void SpeculativeJIT::compile(Node& node)
GPRReg baseGPR = base.gpr();
GPRReg resultGPR = result.gpr();
- if (!isArraySpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSArray::s_info)));
-
+ speculateArray(node.child1(), baseGPR);
+
m_jit.loadPtr(MacroAssembler::Address(baseGPR, JSArray::storageOffset()), resultGPR);
m_jit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(ArrayStorage, m_length)), resultGPR);
@@ -3493,7 +3470,7 @@ void SpeculativeJIT::compile(Node& node)
case CheckStructure:
case ForwardCheckStructure: {
AbstractValue& value = m_state.forNode(node.child1());
- if (value.m_structure.isSubsetOf(node.structureSet())
+ if (value.m_currentKnownStructure.isSubsetOf(node.structureSet())
&& isCellSpeculation(value.m_type)) {
noResult(m_compileIndex);
break;
@@ -3534,9 +3511,12 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- case StructureTransitionWatchpoint: {
+ case StructureTransitionWatchpoint:
+ case ForwardStructureTransitionWatchpoint: {
m_jit.addWeakReference(node.structure());
- node.structure()->addTransitionWatchpoint(speculationWatchpoint(BadCache));
+ node.structure()->addTransitionWatchpoint(
+ speculationWatchpointWithConditionalDirection(
+ BadCache, node.op() == ForwardStructureTransitionWatchpoint));
#if !ASSERT_DISABLED
SpeculateCellOperand op1(this, node.child1());
diff --git a/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h b/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h
new file mode 100644
index 000000000..b3082de1a
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DFGStructureAbstractValue_h
+#define DFGStructureAbstractValue_h
+
+#include <wtf/Platform.h>
+
+#if ENABLE(DFG_JIT)
+
+#include "JSCell.h"
+#include "SpeculatedType.h"
+#include "StructureSet.h"
+
+namespace JSC { namespace DFG {
+
+class StructureAbstractValue {
+public:
+ StructureAbstractValue()
+ : m_structure(0)
+ {
+ }
+
+ StructureAbstractValue(Structure* structure)
+ : m_structure(structure)
+ {
+ }
+
+ StructureAbstractValue(const StructureSet& set)
+ {
+ switch (set.size()) {
+ case 0:
+ m_structure = 0;
+ break;
+
+ case 1:
+ m_structure = set[0];
+ break;
+
+ default:
+ m_structure = topValue();
+ break;
+ }
+ }
+
+ void clear()
+ {
+ m_structure = 0;
+ }
+
+ void makeTop()
+ {
+ m_structure = topValue();
+ }
+
+ static StructureAbstractValue top()
+ {
+ StructureAbstractValue value;
+ value.makeTop();
+ return value;
+ }
+
+ void add(Structure* structure)
+ {
+ ASSERT(!contains(structure) && !isTop());
+ if (m_structure)
+ makeTop();
+ else
+ m_structure = structure;
+ }
+
+ bool addAll(const StructureSet& other)
+ {
+ if (isTop() || !other.size())
+ return false;
+ if (other.size() > 1) {
+ makeTop();
+ return true;
+ }
+ if (!m_structure) {
+ m_structure = other[0];
+ return true;
+ }
+ if (m_structure == other[0])
+ return false;
+ makeTop();
+ return true;
+ }
+
+ bool addAll(const StructureAbstractValue& other)
+ {
+ if (!other.m_structure)
+ return false;
+ if (isTop())
+ return false;
+ if (other.isTop()) {
+ makeTop();
+ return true;
+ }
+ if (m_structure) {
+ if (m_structure == other.m_structure)
+ return false;
+ makeTop();
+ return true;
+ }
+ m_structure = other.m_structure;
+ return true;
+ }
+
+ bool contains(Structure* structure) const
+ {
+ if (isTop())
+ return true;
+ if (m_structure == structure)
+ return true;
+ return false;
+ }
+
+ bool isSubsetOf(const StructureSet& other) const
+ {
+ if (isTop())
+ return false;
+ if (!m_structure)
+ return true;
+ return other.contains(m_structure);
+ }
+
+ bool doesNotContainAnyOtherThan(Structure* structure) const
+ {
+ if (isTop())
+ return false;
+ if (!m_structure)
+ return true;
+ return m_structure == structure;
+ }
+
+ bool isSupersetOf(const StructureSet& other) const
+ {
+ if (isTop())
+ return true;
+ if (!other.size())
+ return true;
+ if (other.size() > 1)
+ return false;
+ return m_structure == other[0];
+ }
+
+ bool isSubsetOf(const StructureAbstractValue& other) const
+ {
+ if (other.isTop())
+ return true;
+ if (isTop())
+ return false;
+ if (m_structure) {
+ if (other.m_structure)
+ return m_structure == other.m_structure;
+ return false;
+ }
+ return true;
+ }
+
+ bool isSupersetOf(const StructureAbstractValue& other) const
+ {
+ return other.isSubsetOf(*this);
+ }
+
+ void filter(const StructureSet& other)
+ {
+ if (!m_structure)
+ return;
+
+ if (isTop()) {
+ switch (other.size()) {
+ case 0:
+ m_structure = 0;
+ return;
+
+ case 1:
+ m_structure = other[0];
+ return;
+
+ default:
+ return;
+ }
+ }
+
+ if (other.contains(m_structure))
+ return;
+
+ m_structure = 0;
+ }
+
+ void filter(const StructureAbstractValue& other)
+ {
+ if (isTop()) {
+ m_structure = other.m_structure;
+ return;
+ }
+ if (m_structure == other.m_structure)
+ return;
+ if (other.isTop())
+ return;
+ m_structure = 0;
+ }
+
+ void filter(SpeculatedType other)
+ {
+ if (!(other & SpecCell)) {
+ clear();
+ return;
+ }
+
+ if (isClearOrTop())
+ return;
+
+ if (!(speculationFromStructure(m_structure) & other))
+ m_structure = 0;
+ }
+
+ bool isClear() const
+ {
+ return !m_structure;
+ }
+
+ bool isTop() const { return m_structure == topValue(); }
+
+ bool isClearOrTop() const { return m_structure <= topValue(); }
+ bool isNeitherClearNorTop() const { return !isClearOrTop(); }
+
+ size_t size() const
+ {
+ ASSERT(!isTop());
+ return !!m_structure;
+ }
+
+ Structure* at(size_t i) const
+ {
+ ASSERT(!isTop());
+ ASSERT(m_structure);
+ ASSERT_UNUSED(i, !i);
+ return m_structure;
+ }
+
+ Structure* operator[](size_t i) const
+ {
+ return at(i);
+ }
+
+ Structure* last() const
+ {
+ return at(0);
+ }
+
+ SpeculatedType speculationFromStructures() const
+ {
+ if (isTop())
+ return SpecCell;
+ if (isClear())
+ return SpecNone;
+ return speculationFromStructure(m_structure);
+ }
+
+ bool hasSingleton() const
+ {
+ return isNeitherClearNorTop();
+ }
+
+ Structure* singleton() const
+ {
+ ASSERT(isNeitherClearNorTop());
+ return m_structure;
+ }
+
+ bool operator==(const StructureAbstractValue& other) const
+ {
+ return m_structure == other.m_structure;
+ }
+
+ void dump(FILE* out) const
+ {
+ if (isTop()) {
+ fprintf(out, "TOP");
+ return;
+ }
+
+ fprintf(out, "[");
+ if (m_structure)
+ fprintf(out, "%p", m_structure);
+ fprintf(out, "]");
+ }
+
+private:
+ static Structure* topValue() { return reinterpret_cast<Structure*>(1); }
+
+ // This can only remember one structure at a time.
+ Structure* m_structure;
+};
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGStructureAbstractValue_h
+
+
diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
index e86c57dff..68627f95c 100644
--- a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
@@ -82,6 +82,7 @@ public:
}
case ForwardCheckStructure:
+ case ForwardStructureTransitionWatchpoint:
// We currently rely on the fact that we're the only ones who would
// insert this node.
ASSERT_NOT_REACHED();
@@ -94,6 +95,12 @@ public:
case AllocatePropertyStorage:
case ReallocatePropertyStorage:
case GetPropertyStorage:
+ case GetByVal:
+ case PutByVal:
+ case PutByValAlias:
+ case PutByValSafe:
+ case GetArrayLength:
+ case Phantom:
// Don't count these uses.
break;
@@ -215,17 +222,22 @@ public:
break;
case PutByVal:
- case PutByValAlias: {
+ case PutByValAlias:
+ case PutByValSafe: {
Edge child1 = m_graph.varArgChild(node, 0);
Edge child2 = m_graph.varArgChild(node, 1);
if (!m_graph[child1].prediction() || !m_graph[child2].prediction())
break;
- if (!m_graph[child2].shouldSpeculateInteger() || !isActionableMutableArraySpeculation(m_graph[child1].prediction())) {
+ if (!m_graph[child2].shouldSpeculateInteger()
+#if USE(JSVALUE32_64)
+ || m_graph[child1].shouldSpeculateArguments()
+#endif
+ ) {
clobber(live);
break;
}
- if (node.op() == PutByValAlias)
+ if (node.op() != PutByValSafe)
break;
if (m_graph[child1].shouldSpeculateArguments())
break;
@@ -291,7 +303,7 @@ public:
dataLog("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->first));
}
#endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-
+
// Make changes:
// 1) If a variable's live range does not span a clobber, then inject structure
// checks before the SetLocal.
@@ -348,6 +360,8 @@ public:
if (block->variablesAtTail.operand(variable->local()) == nodeIndex)
block->variablesAtTail.operand(variable->local()) = getLocalIndex;
+ m_graph.substituteGetLocal(*block, indexInBlock, variable, getLocalIndex);
+
changed = true;
break;
}