summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-08-12 09:27:39 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-08-12 09:27:39 +0200
commit3749d61e1f7a59f5ec5067e560af1eb610c82015 (patch)
tree73dc228333948738bbe02976cacca8cd382bc978 /Source/JavaScriptCore/dfg
parentb32b4dcd9a51ab8de6afc53d9e17f8707e1f7a5e (diff)
downloadqtwebkit-3749d61e1f7a59f5ec5067e560af1eb610c82015.tar.gz
Imported WebKit commit a77350243e054f3460d1137301d8b3faee3d2052 (http://svn.webkit.org/repository/webkit/trunk@125365)
New snapshot with build fixes for latest API changes in Qt and all WK1 Win MSVC fixes upstream
Diffstat (limited to 'Source/JavaScriptCore/dfg')
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractState.cpp20
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractState.h1
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractValue.h68
-rw-r--r--Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp22
-rw-r--r--Source/JavaScriptCore/dfg/DFGCSEPhase.cpp42
-rw-r--r--Source/JavaScriptCore/dfg/DFGDriver.cpp4
-rw-r--r--Source/JavaScriptCore/dfg/DFGFixupPhase.cpp3
-rw-r--r--Source/JavaScriptCore/dfg/DFGGraph.cpp6
-rw-r--r--Source/JavaScriptCore/dfg/DFGGraph.h46
-rw-r--r--Source/JavaScriptCore/dfg/DFGNode.h15
-rw-r--r--Source/JavaScriptCore/dfg/DFGNodeType.h2
-rw-r--r--Source/JavaScriptCore/dfg/DFGOSREntry.cpp4
-rw-r--r--Source/JavaScriptCore/dfg/DFGOSRExit.cpp2
-rw-r--r--Source/JavaScriptCore/dfg/DFGOperations.cpp86
-rw-r--r--Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp84
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp159
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h137
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp62
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp68
-rw-r--r--Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp490
-rw-r--r--Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.h50
-rw-r--r--Source/JavaScriptCore/dfg/DFGVariableAccessData.h37
22 files changed, 1110 insertions, 298 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index 01996f132..4f02ee793 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -950,7 +950,8 @@ bool AbstractState::execute(unsigned indexInBlock)
}
case PutByVal:
- case PutByValAlias: {
+ case PutByValAlias:
+ case PutByValSafe: {
node.setCanExit(true);
Edge child1 = m_graph.varArgChild(node, 0);
@@ -966,7 +967,7 @@ bool AbstractState::execute(unsigned indexInBlock)
|| m_graph[child1].shouldSpeculateArguments()
#endif
) {
- ASSERT(node.op() == PutByVal);
+ ASSERT(node.op() == PutByVal || node.op() == PutByValSafe);
clobberWorld(node.codeOrigin, indexInBlock);
forNode(nodeIndex).makeTop();
break;
@@ -1055,7 +1056,7 @@ bool AbstractState::execute(unsigned indexInBlock)
ASSERT(m_graph[child1].shouldSpeculateArray());
forNode(child1).filter(SpecArray);
forNode(child2).filter(SpecInt32);
- if (node.op() == PutByVal)
+ if (node.op() == PutByValSafe)
clobberWorld(node.codeOrigin, indexInBlock);
break;
}
@@ -1335,7 +1336,7 @@ bool AbstractState::execute(unsigned indexInBlock)
case PutScopedVar:
node.setCanExit(false);
- clobberStructures(indexInBlock);
+ clobberCapturedVars(node.codeOrigin);
break;
case GetById:
@@ -1415,7 +1416,8 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(nodeIndex).set(SpecInt32);
break;
- case CheckStructure: {
+ case CheckStructure:
+ case ForwardCheckStructure: {
// FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes).
AbstractValue& value = forNode(node.child1());
node.setCanExit(
@@ -1431,6 +1433,7 @@ bool AbstractState::execute(unsigned indexInBlock)
AbstractValue& value = forNode(node.child1());
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());
+ m_haveStructures = true;
node.setCanExit(true);
break;
}
@@ -1609,6 +1612,12 @@ bool AbstractState::execute(unsigned indexInBlock)
inline void AbstractState::clobberWorld(const CodeOrigin& codeOrigin, unsigned indexInBlock)
{
+ clobberCapturedVars(codeOrigin);
+ clobberStructures(indexInBlock);
+}
+
+inline void AbstractState::clobberCapturedVars(const CodeOrigin& codeOrigin)
+{
if (codeOrigin.inlineCallFrame) {
const BitVector& capturedVars = codeOrigin.inlineCallFrame->capturedVars;
for (size_t i = capturedVars.size(); i--;) {
@@ -1624,7 +1633,6 @@ inline void AbstractState::clobberWorld(const CodeOrigin& codeOrigin, unsigned i
for (size_t i = m_variables.numberOfArguments(); i--;)
m_variables.argument(i).makeTop();
}
- clobberStructures(indexInBlock);
}
inline void AbstractState::clobberStructures(unsigned indexInBlock)
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.h b/Source/JavaScriptCore/dfg/DFGAbstractState.h
index 95cadecbb..d2bc1a551 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.h
@@ -217,6 +217,7 @@ public:
private:
void clobberWorld(const CodeOrigin&, unsigned indexInBlock);
+ void clobberCapturedVars(const CodeOrigin&);
void clobberStructures(unsigned indexInBlock);
bool mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, NodeIndex);
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
index f81af4ecf..402fd0fcd 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
@@ -317,13 +317,14 @@ struct AbstractValue {
{
m_type = SpecNone;
m_structure.clear();
+ m_unclobberedStructure.clear();
m_value = JSValue();
checkConsistency();
}
bool isClear() const
{
- bool result = m_type == SpecNone && m_structure.isClear();
+ bool result = m_type == SpecNone && m_structure.isClear() && m_unclobberedStructure.isClear();
if (result)
ASSERT(!m_value);
return result;
@@ -333,6 +334,7 @@ struct AbstractValue {
{
m_type = SpecTop;
m_structure.makeTop();
+ m_unclobberedStructure.makeTop();
m_value = JSValue();
checkConsistency();
}
@@ -353,7 +355,7 @@ struct AbstractValue {
bool isTop() const
{
- return m_type == SpecTop && m_structure.isTop();
+ return m_type == SpecTop && m_structure.isTop() && m_unclobberedStructure.isTop();
}
bool valueIsTop() const
@@ -383,8 +385,11 @@ struct AbstractValue {
// it may change in the future - for example between when we compile
// the code and when we run it.
m_structure.makeTop();
- } else
+ m_unclobberedStructure.makeTop(); // FIXME: Consider not clobbering this.
+ } else {
m_structure.clear();
+ m_unclobberedStructure.clear();
+ }
m_type = speculationFromValue(value);
m_value = value;
@@ -397,6 +402,9 @@ struct AbstractValue {
m_structure.clear();
m_structure.add(structure);
+ m_unclobberedStructure.clear();
+ m_unclobberedStructure.add(structure);
+
m_type = speculationFromStructure(structure);
m_value = JSValue();
@@ -405,10 +413,13 @@ struct AbstractValue {
void set(SpeculatedType type)
{
- if (type & SpecCell)
+ if (type & SpecCell) {
m_structure.makeTop();
- else
+ m_unclobberedStructure.makeTop();
+ } else {
m_structure.clear();
+ m_unclobberedStructure.clear();
+ }
m_type = type;
m_value = JSValue();
checkConsistency();
@@ -418,6 +429,7 @@ struct AbstractValue {
{
return m_type == other.m_type
&& m_structure == other.m_structure
+ && m_unclobberedStructure == other.m_unclobberedStructure
&& m_value == other.m_value;
}
bool operator!=(const AbstractValue& other) const
@@ -437,6 +449,7 @@ struct AbstractValue {
} else {
result |= mergeSpeculation(m_type, other.m_type);
result |= m_structure.addAll(other.m_structure);
+ result |= m_unclobberedStructure.addAll(other.m_unclobberedStructure);
if (m_value != other.m_value) {
result |= !!m_value;
m_value = JSValue();
@@ -451,8 +464,10 @@ struct AbstractValue {
{
mergeSpeculation(m_type, type);
- if (type & SpecCell)
+ if (type & SpecCell) {
m_structure.makeTop();
+ m_unclobberedStructure.makeTop();
+ }
m_value = JSValue();
checkConsistency();
@@ -462,6 +477,7 @@ struct AbstractValue {
{
m_type &= other.speculationFromStructures();
m_structure.filter(other);
+ m_unclobberedStructure.filter(other);
// 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
@@ -469,6 +485,7 @@ struct AbstractValue {
// 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);
if (!!m_value && !validateIgnoringValue(m_value))
clear();
@@ -487,6 +504,7 @@ struct AbstractValue {
// 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);
if (!!m_value && !validateIgnoringValue(m_value))
clear();
@@ -523,8 +541,8 @@ struct AbstractValue {
if (isTop())
return true;
- if (!!m_value)
- return m_value == value;
+ if (!!m_value && m_value != value)
+ return false;
if (mergeSpeculations(m_type, speculationFromValue(value)) != m_type)
return false;
@@ -545,10 +563,39 @@ struct AbstractValue {
return true;
}
+ bool validateForEntry(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;
+
+ if (value.isEmpty()) {
+ ASSERT(m_type & SpecEmpty);
+ return true;
+ }
+
+ if (m_unclobberedStructure.isTop())
+ return true;
+
+ if (!!value && value.isCell()) {
+ ASSERT(m_type & SpecCell);
+ return m_unclobberedStructure.contains(value.asCell()->structure());
+ }
+
+ return true;
+ }
+
void checkConsistency() const
{
- if (!(m_type & SpecCell))
+ if (!(m_type & SpecCell)) {
ASSERT(m_structure.isClear());
+ ASSERT(m_unclobberedStructure.isClear());
+ }
if (isClear())
ASSERT(!m_value);
@@ -566,12 +613,15 @@ struct AbstractValue {
{
fprintf(out, "(%s, ", speculationToString(m_type));
m_structure.dump(out);
+ dataLog(", ");
+ m_unclobberedStructure.dump(out);
if (!!m_value)
fprintf(out, ", %s", m_value.description());
fprintf(out, ")");
}
StructureAbstractValue m_structure;
+ StructureAbstractValue m_unclobberedStructure;
SpeculatedType m_type;
JSValue m_value;
};
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index e4e94e90b..b7f48aa4b 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -257,6 +257,8 @@ private:
}
VariableAccessData* variableAccessData = newVariableAccessData(operand, isCaptured);
+ variableAccessData->mergeStructureCheckHoistingFailed(
+ m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache));
NodeIndex nodeIndex = addToGraph(SetLocal, OpInfo(variableAccessData), value);
m_currentBlock->variablesAtTail.local(operand) = nodeIndex;
}
@@ -299,7 +301,8 @@ private:
// We're getting an argument in the first basic block; link
// the GetLocal to the SetArgument.
ASSERT(nodePtr->local() == static_cast<VirtualRegister>(operand));
- nodeIndex = injectLazyOperandSpeculation(addToGraph(GetLocal, OpInfo(nodePtr->variableAccessData()), nodeIndex));
+ VariableAccessData* variable = nodePtr->variableAccessData();
+ nodeIndex = injectLazyOperandSpeculation(addToGraph(GetLocal, OpInfo(variable), nodeIndex));
m_currentBlock->variablesAtTail.argument(argument) = nodeIndex;
return nodeIndex;
}
@@ -340,6 +343,8 @@ private:
flushDirect(operand);
VariableAccessData* variableAccessData = newVariableAccessData(operand, isCaptured);
+ variableAccessData->mergeStructureCheckHoistingFailed(
+ m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache));
NodeIndex nodeIndex = addToGraph(SetLocal, OpInfo(variableAccessData), value);
m_currentBlock->variablesAtTail.argument(argument) = nodeIndex;
}
@@ -1723,7 +1728,11 @@ bool ByteCodeParser::parseBlock(unsigned limit)
if (m_currentBlock == m_graph.m_blocks[0].get() && !m_inlineStackTop->m_inlineCallFrame) {
m_graph.m_arguments.resize(m_numArguments);
for (unsigned argument = 0; argument < m_numArguments; ++argument) {
- NodeIndex setArgument = addToGraph(SetArgument, OpInfo(newVariableAccessData(argumentToOperand(argument), m_codeBlock->argumentIsCaptured(argument))));
+ VariableAccessData* variable = newVariableAccessData(
+ argumentToOperand(argument), m_codeBlock->argumentIsCaptured(argument));
+ variable->mergeStructureCheckHoistingFailed(
+ m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache));
+ NodeIndex setArgument = addToGraph(SetArgument, OpInfo(variable));
m_graph.m_arguments[argument] = setArgument;
m_currentBlock->variablesAtHead.setArgumentFirstTime(argument, setArgument);
m_currentBlock->variablesAtTail.setArgumentFirstTime(argument, setArgument);
@@ -2152,10 +2161,14 @@ bool ByteCodeParser::parseBlock(unsigned limit)
NodeIndex property = get(currentInstruction[2].u.operand);
NodeIndex value = get(currentInstruction[3].u.operand);
+ bool makeSafe =
+ m_inlineStackTop->m_profiledBlock->couldTakeSlowCase(m_currentIndex)
+ || m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, OutOfBounds);
+
addVarArgChild(base);
addVarArgChild(property);
addVarArgChild(value);
- addToGraph(Node::VarArg, PutByVal, OpInfo(0), OpInfo(0));
+ addToGraph(Node::VarArg, makeSafe ? PutByValSafe : PutByVal, OpInfo(0), OpInfo(0));
NEXT_OPCODE(op_put_by_val);
}
@@ -2222,7 +2235,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
}
case op_get_by_id:
case op_get_by_id_out_of_line: {
- SpeculatedType prediction = getPredictionWithoutOSRExit();
+ SpeculatedType prediction = getPrediction();
NodeIndex base = get(currentInstruction[2].u.operand);
unsigned identifierNumber = m_inlineStackTop->m_identifierRemap[currentInstruction[3].u.operand];
@@ -3010,6 +3023,7 @@ void ByteCodeParser::fixVariableAccessSpeculations()
VariableAccessData* data = &m_graph.m_variableAccessData[i];
data->find()->predict(data->nonUnifiedPrediction());
data->find()->mergeIsCaptured(data->isCaptured());
+ data->find()->mergeStructureCheckHoistingFailed(data->structureCheckHoistingFailed());
}
}
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 4532214ee..e9b1e0d8b 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -284,7 +284,8 @@ private:
return index;
break;
case PutByVal:
- case PutByValAlias: {
+ case PutByValAlias:
+ case PutByValSafe: {
if (!m_graph.byValIsPure(node))
return NoNode;
if (m_graph.varArgChild(node, 0) == child1 && canonicalize(m_graph.varArgChild(node, 1)) == canonicalize(child2))
@@ -337,6 +338,7 @@ private:
Node& node = m_graph[index];
switch (node.op()) {
case CheckStructure:
+ case ForwardCheckStructure:
if (node.child1() == child1
&& structureSet.isSupersetOf(node.structureSet()))
return true;
@@ -362,6 +364,7 @@ private:
case PutByVal:
case PutByValAlias:
+ case PutByValSafe:
if (m_graph.byValIsPure(node)) {
// If PutByVal speculates that it's accessing an array with an
// integer index, then it's impossible for it to cause a structure
@@ -389,6 +392,7 @@ private:
Node& node = m_graph[index];
switch (node.op()) {
case CheckStructure:
+ case ForwardCheckStructure:
if (node.child1() == child1
&& node.structureSet().containsOnly(structure))
return true;
@@ -404,6 +408,7 @@ private:
case PutByVal:
case PutByValAlias:
+ case PutByValSafe:
if (m_graph.byValIsPure(node)) {
// If PutByVal speculates that it's accessing an array with an
// integer index, then it's impossible for it to cause a structure
@@ -437,6 +442,7 @@ private:
break;
switch (node.op()) {
case CheckStructure:
+ case ForwardCheckStructure:
return NoNode;
case PhantomPutStructure:
@@ -507,6 +513,7 @@ private:
case PutByVal:
case PutByValAlias:
+ case PutByValSafe:
if (m_graph.byValIsPure(node)) {
// If PutByVal speculates that it's accessing an array with an
// integer index, then it's impossible for it to cause a structure
@@ -551,6 +558,7 @@ private:
case PutByVal:
case PutByValAlias:
case GetByVal:
+ case PutByValSafe:
if (m_graph.byValIsPure(node)) {
// If PutByVal speculates that it's accessing an array with an
// integer index, then it's impossible for it to cause a structure
@@ -603,6 +611,7 @@ private:
case PutByVal:
case PutByValAlias:
+ case PutByValSafe:
if (m_graph.byValIsPure(node)) {
// If PutByVal speculates that it's accessing an array with an
// integer index, then it's impossible for it to cause a structure
@@ -643,15 +652,6 @@ private:
// change the property storage pointer.
break;
- case PutByValAlias:
- // PutByValAlias can't change the indexed storage pointer
- break;
-
- case PutByVal:
- if (isFixedIndexedStorageObjectSpeculation(m_graph[m_graph.varArgChild(node, 0)].prediction()) && m_graph.byValIsPure(node))
- break;
- return NoNode;
-
default:
if (m_graph.clobbersWorld(index))
return NoNode;
@@ -673,7 +673,7 @@ private:
return NoNode;
}
- NodeIndex getLocalLoadElimination(VirtualRegister local, NodeIndex& relevantLocalOp)
+ NodeIndex getLocalLoadElimination(VirtualRegister local, NodeIndex& relevantLocalOp, bool careAboutClobbering)
{
relevantLocalOp = NoNode;
@@ -703,7 +703,7 @@ private:
break;
default:
- if (m_graph.clobbersWorld(index))
+ if (careAboutClobbering && m_graph.clobbersWorld(index))
return NoNode;
break;
}
@@ -944,13 +944,15 @@ private:
case GetLocal: {
VariableAccessData* variableAccessData = node.variableAccessData();
- if (!variableAccessData->isCaptured())
+ if (m_fixpointState == FixpointNotConverged && !variableAccessData->isCaptured())
break;
NodeIndex relevantLocalOp;
- NodeIndex possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp);
- ASSERT(relevantLocalOp == NoNode
- || m_graph[relevantLocalOp].op() == GetLocalUnlinked
- || m_graph[relevantLocalOp].variableAccessData() == variableAccessData);
+ NodeIndex possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp, variableAccessData->isCaptured());
+ if (relevantLocalOp == NoNode)
+ break;
+ if (m_graph[relevantLocalOp].op() != GetLocalUnlinked
+ && m_graph[relevantLocalOp].variableAccessData() != variableAccessData)
+ break;
NodeIndex phiIndex = node.child1().index();
if (!setReplacement(possibleReplacement))
break;
@@ -980,7 +982,7 @@ private:
case GetLocalUnlinked: {
NodeIndex relevantLocalOpIgnored;
- m_changed |= setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored));
+ m_changed |= setReplacement(getLocalLoadElimination(node.unlinkedLocal(), relevantLocalOpIgnored, true));
break;
}
@@ -1093,7 +1095,8 @@ private:
setReplacement(getByValLoadElimination(node.child1().index(), node.child2().index()));
break;
- case PutByVal: {
+ case PutByVal:
+ case PutByValSafe: {
Edge child1 = m_graph.varArgChild(node, 0);
Edge child2 = m_graph.varArgChild(node, 1);
if (isActionableMutableArraySpeculation(m_graph[child1].prediction())
@@ -1108,6 +1111,7 @@ private:
}
case CheckStructure:
+ case ForwardCheckStructure:
if (checkStructureLoadElimination(node.structureSet(), node.child1().index()))
eliminate();
break;
diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp
index f160b6d35..ddad4f864 100644
--- a/Source/JavaScriptCore/dfg/DFGDriver.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp
@@ -38,6 +38,7 @@
#include "DFGJITCompiler.h"
#include "DFGPredictionPropagationPhase.h"
#include "DFGRedundantPhiEliminationPhase.h"
+#include "DFGStructureCheckHoistingPhase.h"
#include "DFGValidate.h"
#include "DFGVirtualRegisterAllocationPhase.h"
#include "Options.h"
@@ -101,7 +102,10 @@ 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
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index a1954d7e0..4e3cd5782 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -314,7 +314,8 @@ private:
break;
}
- case PutByVal: {
+ case PutByVal:
+ case PutByValSafe: {
Edge child1 = m_graph.varArgChild(node, 0);
Edge child2 = m_graph.varArgChild(node, 1);
Edge child3 = m_graph.varArgChild(node, 2);
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp
index c7a4d94d2..9ae0648b8 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp
+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp
@@ -207,7 +207,7 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex)
hasPrinted = !!node.child1();
}
- if (node.flags()) {
+ if (strlen(nodeFlagsAsString(node.flags()))) {
dataLog("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags()));
hasPrinted = true;
}
@@ -287,13 +287,15 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex)
dataLog("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex());
hasPrinted = true;
}
+ dataLog("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex);
+ hasPrinted = true;
(void)hasPrinted;
dataLog(")");
if (!skipped) {
if (node.hasVariableAccessData())
- dataLog(" predicting %s, double ratio %lf%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->doubleVoteRatio(), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : "");
+ dataLog(" predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : "");
else if (node.hasHeapPrediction())
dataLog(" predicting %s", speculationToString(node.getHeapPrediction()));
}
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index 3e6539353..fdb78cf9b 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -479,6 +479,15 @@ public:
SpeculatedType prediction = at(varArgChild(node, 0)).prediction();
if (!isActionableMutableArraySpeculation(prediction))
return false;
+ return true;
+ }
+
+ case PutByValSafe: {
+ if (!at(varArgChild(node, 1)).shouldSpeculateInteger())
+ return false;
+ SpeculatedType prediction = at(varArgChild(node, 0)).prediction();
+ if (!isActionableMutableArraySpeculation(prediction))
+ return false;
if (isArraySpeculation(prediction))
return false;
return true;
@@ -524,6 +533,7 @@ public:
return !isPredictedNumerical(node);
case GetByVal:
case PutByVal:
+ case PutByValSafe:
case PutByValAlias:
return !byValIsPure(node);
default:
@@ -568,6 +578,42 @@ public:
return node.children.child(index);
}
+ void vote(Edge edge, unsigned ballot)
+ {
+ switch (at(edge).op()) {
+ case ValueToInt32:
+ case UInt32ToNumber:
+ edge = at(edge).child1();
+ break;
+ default:
+ break;
+ }
+
+ if (at(edge).op() == GetLocal)
+ at(edge).variableAccessData()->vote(ballot);
+ }
+
+ void vote(Node& node, unsigned ballot)
+ {
+ if (node.flags() & NodeHasVarArgs) {
+ for (unsigned childIdx = node.firstChild();
+ childIdx < node.firstChild() + node.numChildren();
+ childIdx++)
+ vote(m_varArgChildren[childIdx], ballot);
+ return;
+ }
+
+ if (!node.child1())
+ return;
+ vote(node.child1(), ballot);
+ if (!node.child2())
+ return;
+ vote(node.child2(), ballot);
+ if (!node.child3())
+ return;
+ vote(node.child3(), ballot);
+ }
+
JSGlobalData& m_globalData;
CodeBlock* m_codeBlock;
CodeBlock* m_profiledBlock;
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index e72bd5e36..60cdd4b50 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -245,6 +245,13 @@ struct Node {
children.reset();
}
+ void convertToStructureTransitionWatchpoint()
+ {
+ ASSERT(m_op == CheckStructure);
+ m_opInfo = bitwise_cast<uintptr_t>(structureSet().singletonStructure());
+ m_op = StructureTransitionWatchpoint;
+ }
+
JSCell* weakConstant()
{
ASSERT(op() == WeakJSConstant);
@@ -651,7 +658,13 @@ struct Node {
bool hasStructureSet()
{
- return op() == CheckStructure;
+ switch (op()) {
+ case CheckStructure:
+ case ForwardCheckStructure:
+ return true;
+ default:
+ return false;
+ }
}
StructureSet& structureSet()
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 0ca0039b9..7657663a9 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -113,12 +113,14 @@ namespace JSC { namespace DFG {
/* opcodes use VarArgs beause they may have up to 4 children. */\
macro(GetByVal, NodeResultJS | NodeMustGenerate | NodeMightClobber) \
macro(PutByVal, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \
+ macro(PutByValSafe, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \
macro(PutByValAlias, NodeMustGenerate | NodeHasVarArgs | NodeMightClobber) \
macro(GetById, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
macro(GetByIdFlush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
macro(PutById, NodeMustGenerate | NodeClobbersWorld) \
macro(PutByIdDirect, NodeMustGenerate | NodeClobbersWorld) \
macro(CheckStructure, NodeMustGenerate) \
+ macro(ForwardCheckStructure, NodeMustGenerate) \
/* Transition watchpoints are a contract between the party setting the watchpoint */\
/* and the runtime system, where the party promises that the child object once had */\
/* the structure being watched, and the runtime system in turn promises that the */\
diff --git a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp
index 9a7bc96cc..97061bfb2 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).validate(value)) {
+ if (!entry->m_expectedValues.argument(argument).validateForEntry(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).validate(exec->registers()[local].jsValue())) {
+ if (!entry->m_expectedValues.local(local).validateForEntry(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/DFGOSRExit.cpp b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp
index e9b02b2e3..b3701722e 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRExit.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSRExit.cpp
@@ -64,7 +64,7 @@ bool OSRExit::considerAddingAsFrequentExitSiteSlow(CodeBlock* dfgCodeBlock, Code
} else
exitSite = FrequentExitSite(m_codeOriginForExitProfile.bytecodeIndex, m_kind);
- return baselineCodeBlockForOriginAndBaselineCodeBlock(m_codeOrigin, profiledCodeBlock)->addFrequentExitSite(exitSite);
+ return baselineCodeBlockForOriginAndBaselineCodeBlock(m_codeOriginForExitProfile, profiledCodeBlock)->addFrequentExitSite(exitSite);
}
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index 94479d6d0..882e1cd02 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -423,12 +423,15 @@ EncodedJSValue DFG_OPERATION operationGetByIdBuildListWithReturnAddress(ExecStat
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue baseValue = JSValue::decode(base);
PropertySlot slot(baseValue);
JSValue result = baseValue.get(exec, *propertyName, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
- dfgBuildGetByIDList(exec, baseValue, *propertyName, slot, stubInfo);
+ if (accessType == static_cast<AccessType>(stubInfo.accessType))
+ dfgBuildGetByIDList(exec, baseValue, *propertyName, slot, stubInfo);
return JSValue::encode(result);
}
@@ -439,12 +442,15 @@ EncodedJSValue DFG_OPERATION operationGetByIdProtoBuildListWithReturnAddress(Exe
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue baseValue = JSValue::decode(base);
PropertySlot slot(baseValue);
JSValue result = baseValue.get(exec, *propertyName, slot);
-
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
- dfgBuildGetByIDProtoList(exec, baseValue, *propertyName, slot, stubInfo);
+
+ if (accessType == static_cast<AccessType>(stubInfo.accessType))
+ dfgBuildGetByIDProtoList(exec, baseValue, *propertyName, slot, stubInfo);
return JSValue::encode(result);
}
@@ -455,15 +461,19 @@ EncodedJSValue DFG_OPERATION operationGetByIdOptimizeWithReturnAddress(ExecState
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue baseValue = JSValue::decode(base);
PropertySlot slot(baseValue);
JSValue result = baseValue.get(exec, *propertyName, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
- if (stubInfo.seen)
- dfgRepatchGetByID(exec, baseValue, *propertyName, slot, stubInfo);
- else
- stubInfo.seen = true;
+ if (accessType == static_cast<AccessType>(stubInfo.accessType)) {
+ if (stubInfo.seen)
+ dfgRepatchGetByID(exec, baseValue, *propertyName, slot, stubInfo);
+ else
+ stubInfo.seen = true;
+ }
return JSValue::encode(result);
}
@@ -645,13 +655,18 @@ void DFG_OPERATION operationPutByIdStrictOptimizeWithReturnAddress(ExecState* ex
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue value = JSValue::decode(encodedValue);
JSValue baseValue(base);
PutPropertySlot slot(true);
baseValue.put(exec, *propertyName, value, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ if (accessType != static_cast<AccessType>(stubInfo.accessType))
+ return;
+
if (stubInfo.seen)
dfgRepatchPutByID(exec, baseValue, *propertyName, slot, stubInfo, NotDirect);
else
@@ -664,13 +679,18 @@ void DFG_OPERATION operationPutByIdNonStrictOptimizeWithReturnAddress(ExecState*
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue value = JSValue::decode(encodedValue);
JSValue baseValue(base);
PutPropertySlot slot(false);
baseValue.put(exec, *propertyName, value, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ if (accessType != static_cast<AccessType>(stubInfo.accessType))
+ return;
+
if (stubInfo.seen)
dfgRepatchPutByID(exec, baseValue, *propertyName, slot, stubInfo, NotDirect);
else
@@ -683,13 +703,18 @@ void DFG_OPERATION operationPutByIdDirectStrictOptimizeWithReturnAddress(ExecSta
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue value = JSValue::decode(encodedValue);
PutPropertySlot slot(true);
ASSERT(base->isObject());
asObject(base)->putDirect(exec->globalData(), *propertyName, value, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ if (accessType != static_cast<AccessType>(stubInfo.accessType))
+ return;
+
if (stubInfo.seen)
dfgRepatchPutByID(exec, base, *propertyName, slot, stubInfo, Direct);
else
@@ -702,13 +727,18 @@ void DFG_OPERATION operationPutByIdDirectNonStrictOptimizeWithReturnAddress(Exec
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue value = JSValue::decode(encodedValue);
PutPropertySlot slot(false);
ASSERT(base->isObject());
asObject(base)->putDirect(exec->globalData(), *propertyName, value, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ if (accessType != static_cast<AccessType>(stubInfo.accessType))
+ return;
+
if (stubInfo.seen)
dfgRepatchPutByID(exec, base, *propertyName, slot, stubInfo, Direct);
else
@@ -721,13 +751,18 @@ void DFG_OPERATION operationPutByIdStrictBuildListWithReturnAddress(ExecState* e
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue value = JSValue::decode(encodedValue);
JSValue baseValue(base);
PutPropertySlot slot(true);
baseValue.put(exec, *propertyName, value, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ if (accessType != static_cast<AccessType>(stubInfo.accessType))
+ return;
+
dfgBuildPutByIdList(exec, baseValue, *propertyName, slot, stubInfo, NotDirect);
}
@@ -737,13 +772,18 @@ void DFG_OPERATION operationPutByIdNonStrictBuildListWithReturnAddress(ExecState
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue value = JSValue::decode(encodedValue);
JSValue baseValue(base);
PutPropertySlot slot(false);
baseValue.put(exec, *propertyName, value, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ if (accessType != static_cast<AccessType>(stubInfo.accessType))
+ return;
+
dfgBuildPutByIdList(exec, baseValue, *propertyName, slot, stubInfo, NotDirect);
}
@@ -753,13 +793,18 @@ void DFG_OPERATION operationPutByIdDirectStrictBuildListWithReturnAddress(ExecSt
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue value = JSValue::decode(encodedValue);
PutPropertySlot slot(true);
ASSERT(base->isObject());
asObject(base)->putDirect(exec->globalData(), *propertyName, value, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ if (accessType != static_cast<AccessType>(stubInfo.accessType))
+ return;
+
dfgBuildPutByIdList(exec, base, *propertyName, slot, stubInfo, Direct);
}
@@ -769,13 +814,18 @@ void DFG_OPERATION operationPutByIdDirectNonStrictBuildListWithReturnAddress(Exe
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
+ StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
+
JSValue value = JSValue::decode(encodedValue);
PutPropertySlot slot(false);
ASSERT(base->isObject());
asObject(base)->putDirect(exec->globalData(), *propertyName, value, slot);
- StructureStubInfo& stubInfo = exec->codeBlock()->getStubInfo(returnAddress);
+ if (accessType != static_cast<AccessType>(stubInfo.accessType))
+ return;
+
dfgBuildPutByIdList(exec, base, *propertyName, slot, stubInfo, Direct);
}
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 2e4f8094c..94f69abc2 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -637,6 +637,7 @@ private:
}
case PutByVal:
+ case PutByValSafe:
changed |= m_graph[m_graph.varArgChild(node, 0)].mergeFlags(NodeUsedAsValue);
changed |= m_graph[m_graph.varArgChild(node, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
changed |= m_graph[m_graph.varArgChild(node, 2)].mergeFlags(NodeUsedAsValue);
@@ -672,6 +673,7 @@ private:
case ForceOSRExit:
case SetArgument:
case CheckStructure:
+ case ForwardCheckStructure:
case StructureTransitionWatchpoint:
case CheckFunction:
case PutStructure:
@@ -746,42 +748,6 @@ private:
propagate(m_graph[m_compileIndex]);
}
- void vote(Edge nodeUse, VariableAccessData::Ballot ballot)
- {
- switch (m_graph[nodeUse].op()) {
- case ValueToInt32:
- case UInt32ToNumber:
- nodeUse = m_graph[nodeUse].child1();
- break;
- default:
- break;
- }
-
- if (m_graph[nodeUse].op() == GetLocal)
- m_graph[nodeUse].variableAccessData()->vote(ballot);
- }
-
- void vote(Node& node, VariableAccessData::Ballot ballot)
- {
- if (node.flags() & NodeHasVarArgs) {
- for (unsigned childIdx = node.firstChild();
- childIdx < node.firstChild() + node.numChildren();
- childIdx++)
- vote(m_graph.m_varArgChildren[childIdx], ballot);
- return;
- }
-
- if (!node.child1())
- return;
- vote(node.child1(), ballot);
- if (!node.child2())
- return;
- vote(node.child2(), ballot);
- if (!node.child3())
- return;
- vote(node.child3(), ballot);
- }
-
void doRoundOfDoubleVoting()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
@@ -798,16 +764,16 @@ private:
SpeculatedType left = m_graph[node.child1()].prediction();
SpeculatedType right = m_graph[node.child2()].prediction();
- VariableAccessData::Ballot ballot;
+ DoubleBallot ballot;
if (isNumberSpeculation(left) && isNumberSpeculation(right)
&& !m_graph.addShouldSpeculateInteger(node))
- ballot = VariableAccessData::VoteDouble;
+ ballot = VoteDouble;
else
- ballot = VariableAccessData::VoteValue;
+ ballot = VoteValue;
- vote(node.child1(), ballot);
- vote(node.child2(), ballot);
+ m_graph.vote(node.child1(), ballot);
+ m_graph.vote(node.child2(), ballot);
break;
}
@@ -815,16 +781,16 @@ private:
SpeculatedType left = m_graph[node.child1()].prediction();
SpeculatedType right = m_graph[node.child2()].prediction();
- VariableAccessData::Ballot ballot;
+ DoubleBallot ballot;
if (isNumberSpeculation(left) && isNumberSpeculation(right)
&& !m_graph.mulShouldSpeculateInteger(node))
- ballot = VariableAccessData::VoteDouble;
+ ballot = VoteDouble;
else
- ballot = VariableAccessData::VoteValue;
+ ballot = VoteValue;
- vote(node.child1(), ballot);
- vote(node.child2(), ballot);
+ m_graph.vote(node.child1(), ballot);
+ m_graph.vote(node.child2(), ballot);
break;
}
@@ -835,46 +801,46 @@ private:
SpeculatedType left = m_graph[node.child1()].prediction();
SpeculatedType right = m_graph[node.child2()].prediction();
- VariableAccessData::Ballot ballot;
+ DoubleBallot ballot;
if (isNumberSpeculation(left) && isNumberSpeculation(right)
&& !(Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child1()])
&& node.canSpeculateInteger()))
- ballot = VariableAccessData::VoteDouble;
+ ballot = VoteDouble;
else
- ballot = VariableAccessData::VoteValue;
+ ballot = VoteValue;
- vote(node.child1(), ballot);
- vote(node.child2(), ballot);
+ m_graph.vote(node.child1(), ballot);
+ m_graph.vote(node.child2(), ballot);
break;
}
case ArithAbs:
- VariableAccessData::Ballot ballot;
+ DoubleBallot ballot;
if (!(m_graph[node.child1()].shouldSpeculateInteger()
&& node.canSpeculateInteger()))
- ballot = VariableAccessData::VoteDouble;
+ ballot = VoteDouble;
else
- ballot = VariableAccessData::VoteValue;
+ ballot = VoteValue;
- vote(node.child1(), ballot);
+ m_graph.vote(node.child1(), ballot);
break;
case ArithSqrt:
- vote(node.child1(), VariableAccessData::VoteDouble);
+ m_graph.vote(node.child1(), VoteDouble);
break;
case SetLocal: {
SpeculatedType prediction = m_graph[node.child1()].prediction();
if (isDoubleSpeculation(prediction))
- node.variableAccessData()->vote(VariableAccessData::VoteDouble);
+ node.variableAccessData()->vote(VoteDouble);
else if (!isNumberSpeculation(prediction) || isInt32Speculation(prediction))
- node.variableAccessData()->vote(VariableAccessData::VoteValue);
+ node.variableAccessData()->vote(VoteValue);
break;
}
default:
- vote(node, VariableAccessData::VoteValue);
+ m_graph.vote(node, VoteValue);
break;
}
}
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 179ad45fa..f17e2d7e4 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -56,6 +56,163 @@ SpeculativeJIT::~SpeculativeJIT()
WTF::deleteAllValues(m_slowPathGenerators);
}
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail)
+{
+ if (!m_compileOkay)
+ return;
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size()));
+}
+
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail);
+}
+
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ Vector<MacroAssembler::Jump, 16> jumpVector = jumpsToFail.jumps();
+ for (unsigned i = 0; i < jumpVector.size(); ++i)
+ speculationCheck(kind, jsValueSource, nodeIndex, jumpVector[i]);
+}
+
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::JumpList& jumpsToFail)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ speculationCheck(kind, jsValueSource, nodeUse.index(), jumpsToFail);
+}
+
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
+{
+ if (!m_compileOkay)
+ return;
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ m_jit.codeBlock()->appendSpeculationRecovery(recovery);
+ m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size(), m_jit.codeBlock()->numberOfSpeculationRecoveries()));
+}
+
+void SpeculativeJIT::speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail, recovery);
+}
+
+JumpReplacementWatchpoint* SpeculativeJIT::speculationWatchpoint(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex)
+{
+ if (!m_compileOkay)
+ return 0;
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ OSRExit& exit = m_jit.codeBlock()->osrExit(
+ m_jit.codeBlock()->appendOSRExit(
+ OSRExit(kind, jsValueSource,
+ m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex),
+ JITCompiler::Jump(), this, m_stream->size())));
+ exit.m_watchpointIndex = m_jit.codeBlock()->appendWatchpoint(
+ JumpReplacementWatchpoint(m_jit.watchpointLabel()));
+ return &m_jit.codeBlock()->watchpoint(exit.m_watchpointIndex);
+}
+
+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)
+{
+ 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.
+ Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(m_indexInBlock - 1));
+ ASSERT(setLocal->op() == SetLocal);
+ ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin);
+ }
+#endif
+
+ unsigned setLocalIndexInBlock = m_indexInBlock + 1;
+
+ Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock));
+ bool hadInt32ToDouble = false;
+
+ if (setLocal->op() == Int32ToDouble) {
+ setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock));
+ hadInt32ToDouble = true;
+ }
+ if (setLocal->op() == Flush || setLocal->op() == Phantom)
+ setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock));
+
+ if (!!valueRecovery) {
+ if (hadInt32ToDouble)
+ ASSERT(at(setLocal->child1()).child1() == m_compileIndex);
+ else
+ ASSERT(setLocal->child1() == m_compileIndex);
+ }
+ ASSERT(setLocal->op() == SetLocal);
+ ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin);
+
+ Node* nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1));
+ ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin);
+
+ OSRExit& exit = m_jit.codeBlock()->lastOSRExit();
+ exit.m_codeOrigin = nextNode->codeOrigin;
+
+ if (!valueRecovery)
+ return;
+ exit.m_lastSetOperand = setLocal->local();
+ exit.m_valueRecoveryOverride = adoptRef(
+ new ValueRecoveryOverride(setLocal->local(), valueRecovery));
+}
+
+void SpeculativeJIT::forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ Vector<MacroAssembler::Jump, 16> jumpVector = jumpsToFail.jumps();
+ for (unsigned i = 0; i < jumpVector.size(); ++i)
+ forwardSpeculationCheck(kind, jsValueSource, nodeIndex, jumpVector[i], valueRecovery);
+}
+
+void SpeculativeJIT::speculationCheckWithConditionalDirection(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, bool isForward)
+{
+ if (isForward)
+ forwardSpeculationCheck(kind, jsValueSource, nodeIndex, jumpToFail);
+ else
+ speculationCheck(kind, jsValueSource, nodeIndex, jumpToFail);
+}
+
+void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, NodeIndex nodeIndex)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+#if DFG_ENABLE(DEBUG_VERBOSE)
+ dataLog("SpeculativeJIT was terminated.\n");
+#endif
+ if (!m_compileOkay)
+ return;
+ speculationCheck(kind, jsValueRegs, nodeIndex, m_jit.jump());
+ m_compileOkay = false;
+}
+
+void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Edge nodeUse)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+ terminateSpeculativeExecution(kind, jsValueRegs, nodeUse.index());
+}
+
+void SpeculativeJIT::terminateSpeculativeExecutionWithConditionalDirection(ExitKind kind, JSValueRegs jsValueRegs, NodeIndex nodeIndex, bool isForward)
+{
+ ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
+#if DFG_ENABLE(DEBUG_VERBOSE)
+ dataLog("SpeculativeJIT was terminated.\n");
+#endif
+ if (!m_compileOkay)
+ return;
+ speculationCheckWithConditionalDirection(kind, jsValueRegs, nodeIndex, m_jit.jump(), isForward);
+ m_compileOkay = false;
+}
+
void SpeculativeJIT::addSlowPathGenerator(PassOwnPtr<SlowPathGenerator> slowPathGenerator)
{
m_slowPathGenerators.append(slowPathGenerator.leakPtr());
@@ -2902,7 +3059,7 @@ void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node)
ASSERT_NOT_REACHED();
} else if (at(node.child1()).prediction() == SpecString) {
if (!isStringSpeculation(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(&JSString::s_info)));
+ speculationCheck(BadType, JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())));
m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), storageReg);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index f43323afd..96f2fec0a 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -306,7 +306,7 @@ public:
GPRReg fillSpeculateInt(NodeIndex, DataFormat& returnFormat);
GPRReg fillSpeculateIntStrict(NodeIndex);
FPRReg fillSpeculateDouble(NodeIndex);
- GPRReg fillSpeculateCell(NodeIndex);
+ GPRReg fillSpeculateCell(NodeIndex, bool isForwardSpeculation = false);
GPRReg fillSpeculateBoolean(NodeIndex);
GeneratedOperandType checkGeneratedTypeForToInt32(NodeIndex);
@@ -2163,130 +2163,33 @@ public:
#endif
// Add a speculation check without additional recovery.
- void speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail)
- {
- if (!m_compileOkay)
- return;
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size()));
- }
- void speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail)
- {
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail);
- }
+ void speculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail);
+ void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail);
// Add a set of speculation checks without additional recovery.
- void speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail)
- {
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- Vector<MacroAssembler::Jump, 16> jumpVector = jumpsToFail.jumps();
- for (unsigned i = 0; i < jumpVector.size(); ++i)
- speculationCheck(kind, jsValueSource, nodeIndex, jumpVector[i]);
- }
- void speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::JumpList& jumpsToFail)
- {
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- speculationCheck(kind, jsValueSource, nodeUse.index(), jumpsToFail);
- }
+ void speculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::JumpList& jumpsToFail);
+ void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::JumpList& jumpsToFail);
// Add a speculation check with additional recovery.
- void speculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
- {
- if (!m_compileOkay)
- return;
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- m_jit.codeBlock()->appendSpeculationRecovery(recovery);
- m_jit.codeBlock()->appendOSRExit(OSRExit(kind, jsValueSource, m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex), jumpToFail, this, m_stream->size(), m_jit.codeBlock()->numberOfSpeculationRecoveries()));
- }
- void speculationCheck(ExitKind kind, JSValueSource jsValueSource, Edge nodeUse, MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery)
- {
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- speculationCheck(kind, jsValueSource, nodeUse.index(), jumpToFail, recovery);
- }
+ void speculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&);
+ void speculationCheck(ExitKind, JSValueSource, Edge, MacroAssembler::Jump jumpToFail, const SpeculationRecovery&);
// Use this like you would use speculationCheck(), except that you don't pass it a jump
// (because you don't have to execute a branch; that's kind of the whole point), and you
// must register the returned Watchpoint with something relevant. In general, this should
// be used with extreme care. Use speculationCheck() unless you've got an amazing reason
// not to.
- JumpReplacementWatchpoint* speculationWatchpoint(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex)
- {
- if (!m_compileOkay)
- return 0;
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- OSRExit& exit = m_jit.codeBlock()->osrExit(
- m_jit.codeBlock()->appendOSRExit(
- OSRExit(kind, jsValueSource,
- m_jit.graph().methodOfGettingAValueProfileFor(nodeIndex),
- JITCompiler::Jump(), this, m_stream->size())));
- exit.m_watchpointIndex = m_jit.codeBlock()->appendWatchpoint(
- JumpReplacementWatchpoint(m_jit.watchpointLabel()));
- return &m_jit.codeBlock()->watchpoint(exit.m_watchpointIndex);
- }
+ JumpReplacementWatchpoint* speculationWatchpoint(ExitKind, JSValueSource, NodeIndex);
// The default for speculation watchpoints is that they're uncounted, because the
// act of firing a watchpoint invalidates it. So, future recompilations will not
// attempt to set this watchpoint again.
- JumpReplacementWatchpoint* speculationWatchpoint()
- {
- return speculationWatchpoint(UncountableWatchpoint, JSValueSource(), NoNode);
- }
- void 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);
-
- unsigned setLocalIndexInBlock = m_indexInBlock + 1;
-
- Node* setLocal = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock));
- bool hadInt32ToDouble = false;
-
- if (setLocal->op() == Int32ToDouble) {
- setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock));
- hadInt32ToDouble = true;
- }
- if (setLocal->op() == Flush || setLocal->op() == Phantom)
- setLocal = &at(m_jit.graph().m_blocks[m_block]->at(++setLocalIndexInBlock));
-
- if (hadInt32ToDouble)
- ASSERT(at(setLocal->child1()).child1() == m_compileIndex);
- else
- ASSERT(setLocal->child1() == m_compileIndex);
- ASSERT(setLocal->op() == SetLocal);
- ASSERT(setLocal->codeOrigin == at(m_compileIndex).codeOrigin);
-
- Node* nextNode = &at(m_jit.graph().m_blocks[m_block]->at(setLocalIndexInBlock + 1));
- ASSERT(nextNode->codeOrigin != at(m_compileIndex).codeOrigin);
-
- OSRExit& exit = m_jit.codeBlock()->lastOSRExit();
- exit.m_codeOrigin = nextNode->codeOrigin;
- exit.m_lastSetOperand = setLocal->local();
-
- exit.m_valueRecoveryOverride = adoptRef(
- new ValueRecoveryOverride(setLocal->local(), valueRecovery));
- }
- void forwardSpeculationCheck(ExitKind kind, JSValueSource jsValueSource, NodeIndex nodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& valueRecovery)
- {
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- Vector<MacroAssembler::Jump, 16> jumpVector = jumpsToFail.jumps();
- for (unsigned i = 0; i < jumpVector.size(); ++i)
- forwardSpeculationCheck(kind, jsValueSource, nodeIndex, jumpVector[i], valueRecovery);
- }
-
+ JumpReplacementWatchpoint* speculationWatchpoint(ExitKind = UncountableWatchpoint);
+ // 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());
+ void forwardSpeculationCheck(ExitKind, JSValueSource, NodeIndex, MacroAssembler::JumpList& jumpsToFail, const ValueRecovery& = ValueRecovery());
+ void speculationCheckWithConditionalDirection(ExitKind, JSValueSource, NodeIndex, MacroAssembler::Jump jumpToFail, bool isForward);
// Called when we statically determine that a speculation will fail.
- void terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, NodeIndex nodeIndex)
- {
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
-#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpeculativeJIT was terminated.\n");
-#endif
- if (!m_compileOkay)
- return;
- speculationCheck(kind, jsValueRegs, nodeIndex, m_jit.jump());
- m_compileOkay = false;
- }
- void terminateSpeculativeExecution(ExitKind kind, JSValueRegs jsValueRegs, Edge nodeUse)
- {
- ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
- terminateSpeculativeExecution(kind, jsValueRegs, nodeUse.index());
- }
+ void terminateSpeculativeExecution(ExitKind, JSValueRegs, NodeIndex);
+ void terminateSpeculativeExecution(ExitKind, JSValueRegs, Edge);
+ void terminateSpeculativeExecutionWithConditionalDirection(ExitKind, JSValueRegs, NodeIndex, bool isForward);
template<bool strict>
GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat);
@@ -2912,10 +2815,11 @@ private:
class SpeculateCellOperand {
public:
- explicit SpeculateCellOperand(SpeculativeJIT* jit, Edge use)
+ explicit SpeculateCellOperand(SpeculativeJIT* jit, Edge use, bool isForwardSpeculation = false)
: m_jit(jit)
, m_index(use.index())
, m_gprOrInvalid(InvalidGPRReg)
+ , m_isForwardSpeculation(isForwardSpeculation)
{
ASSERT(m_jit);
ASSERT(use.useKind() != DoubleUse);
@@ -2937,7 +2841,7 @@ public:
GPRReg gpr()
{
if (m_gprOrInvalid == InvalidGPRReg)
- m_gprOrInvalid = m_jit->fillSpeculateCell(index());
+ m_gprOrInvalid = m_jit->fillSpeculateCell(index(), m_isForwardSpeculation);
return m_gprOrInvalid;
}
@@ -2950,6 +2854,7 @@ private:
SpeculativeJIT* m_jit;
NodeIndex m_index;
GPRReg m_gprOrInvalid;
+ bool m_isForwardSpeculation;
};
class SpeculateBooleanOperand {
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 6209da485..bf3503d6d 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -1284,13 +1284,13 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex)
}
}
-GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
+GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
dataLog("SpecCell@%d ", nodeIndex);
#endif
if (isKnownNotCell(nodeIndex)) {
- terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode);
+ terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation);
return allocate();
}
@@ -1314,7 +1314,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
ASSERT((info.spillFormat() & DataFormatJS) || info.spillFormat() == DataFormatCell);
if (!isCellSpeculation(type))
- speculationCheck(BadType, JSValueSource(JITCompiler::addressFor(virtualRegister)), nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::CellTag)));
+ speculationCheckWithConditionalDirection(BadType, JSValueSource(JITCompiler::addressFor(virtualRegister)), nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, JITCompiler::tagFor(virtualRegister), TrustedImm32(JSValue::CellTag)), isForwardSpeculation);
GPRReg gpr = allocate();
m_jit.load32(JITCompiler::payloadFor(virtualRegister), gpr);
m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled);
@@ -1335,7 +1335,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
m_gprs.lock(tagGPR);
m_gprs.lock(payloadGPR);
if (!isCellSpeculation(type))
- speculationCheck(BadType, JSValueRegs(tagGPR, payloadGPR), nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, tagGPR, TrustedImm32(JSValue::CellTag)));
+ speculationCheckWithConditionalDirection(BadType, JSValueRegs(tagGPR, payloadGPR), nodeIndex, m_jit.branch32(MacroAssembler::NotEqual, tagGPR, TrustedImm32(JSValue::CellTag)), isForwardSpeculation);
m_gprs.unlock(tagGPR);
m_gprs.release(tagGPR);
m_gprs.release(payloadGPR);
@@ -2483,7 +2483,8 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- case PutByVal: {
+ case PutByVal:
+ case PutByValSafe: {
Edge child1 = m_jit.graph().varArgChild(node, 0);
Edge child2 = m_jit.graph().varArgChild(node, 1);
Edge child3 = m_jit.graph().varArgChild(node, 2);
@@ -2599,12 +2600,14 @@ void SpeculativeJIT::compile(Node& node)
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)));
+ MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()));
+ if (node.op() == PutByVal)
+ speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds);
+
base.use();
property.use();
value.use();
- MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()));
-
// Get the array storage.
GPRReg storageReg = scratchReg;
m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
@@ -2626,12 +2629,14 @@ void SpeculativeJIT::compile(Node& node)
m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
- addSlowPathGenerator(
- slowPathCall(
- beyondArrayBounds, this,
- m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
- NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg));
-
+ if (node.op() == PutByValSafe) {
+ addSlowPathGenerator(
+ slowPathCall(
+ beyondArrayBounds, this,
+ m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
+ NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg));
+ }
+
noResult(m_compileIndex, UseChildrenCalledExplicitly);
break;
}
@@ -2990,7 +2995,7 @@ void SpeculativeJIT::compile(Node& node)
m_jit.move(op1PayloadGPR, resultPayloadGPR);
} else {
MacroAssembler::Jump alreadyPrimitive = m_jit.branch32(MacroAssembler::NotEqual, op1TagGPR, TrustedImm32(JSValue::CellTag));
- MacroAssembler::Jump notPrimitive = m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1PayloadGPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSString::s_info));
+ MacroAssembler::Jump notPrimitive = m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1PayloadGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()));
alreadyPrimitive.link(&m_jit);
m_jit.move(op1TagGPR, resultTagGPR);
@@ -3174,7 +3179,7 @@ void SpeculativeJIT::compile(Node& node)
GPRReg thisValueGPR = thisValue.gpr();
if (!isObjectSpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueSource::unboxedCell(thisValueGPR), node.child1(), m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(thisValueGPR, JSCell::classInfoOffset()), JITCompiler::TrustedImmPtr(&JSString::s_info)));
+ speculationCheck(BadType, JSValueSource::unboxedCell(thisValueGPR), node.child1(), m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(thisValueGPR, JSCell::structureOffset()), JITCompiler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())));
GPRTemporary result(this, thisValue);
GPRReg resultGPR = result.gpr();
@@ -3418,7 +3423,7 @@ void SpeculativeJIT::compile(Node& node)
GPRReg resultGPR = result.gpr();
if (!isStringSpeculation(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(&JSString::s_info)));
+ speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())));
m_jit.load32(MacroAssembler::Address(baseGPR, JSString::offsetOfLength()), resultGPR);
@@ -3470,7 +3475,8 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- case CheckStructure: {
+ case CheckStructure:
+ case ForwardCheckStructure: {
AbstractValue& value = m_state.forNode(node.child1());
if (value.m_structure.isSubsetOf(node.structureSet())
&& isCellSpeculation(value.m_type)) {
@@ -3478,13 +3484,19 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- SpeculateCellOperand base(this, node.child1());
+ SpeculateCellOperand base(this, node.child1(), node.op() == ForwardCheckStructure);
ASSERT(node.structureSet().size());
- if (node.structureSet().size() == 1)
- speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, JITCompiler::Address(base.gpr(), JSCell::structureOffset()), node.structureSet()[0]));
- else {
+ if (node.structureSet().size() == 1) {
+ speculationCheckWithConditionalDirection(
+ BadCache, JSValueRegs(), NoNode,
+ m_jit.branchWeakPtr(
+ JITCompiler::NotEqual,
+ JITCompiler::Address(base.gpr(), JSCell::structureOffset()),
+ node.structureSet()[0]),
+ node.op() == ForwardCheckStructure);
+ } else {
GPRTemporary structure(this);
m_jit.loadPtr(JITCompiler::Address(base.gpr(), JSCell::structureOffset()), structure.gpr());
@@ -3494,7 +3506,11 @@ void SpeculativeJIT::compile(Node& node)
for (size_t i = 0; i < node.structureSet().size() - 1; ++i)
done.append(m_jit.branchWeakPtr(JITCompiler::Equal, structure.gpr(), node.structureSet()[i]));
- speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, structure.gpr(), node.structureSet().last()));
+ speculationCheckWithConditionalDirection(
+ BadCache, JSValueRegs(), NoNode,
+ m_jit.branchWeakPtr(
+ JITCompiler::NotEqual, structure.gpr(), node.structureSet().last()),
+ node.op() == ForwardCheckStructure);
done.link(&m_jit);
}
@@ -3505,7 +3521,7 @@ void SpeculativeJIT::compile(Node& node)
case StructureTransitionWatchpoint: {
m_jit.addWeakReference(node.structure());
- node.structure()->addTransitionWatchpoint(speculationWatchpoint());
+ node.structure()->addTransitionWatchpoint(speculationWatchpoint(BadCache));
#if !ASSERT_DISABLED
SpeculateCellOperand op1(this, node.child1());
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 39f4a0740..5541113f2 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -1326,7 +1326,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex)
}
}
-GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
+GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
dataLog("SpecCell@%d ", nodeIndex);
@@ -1339,7 +1339,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
switch (info.registerFormat()) {
case DataFormatNone: {
if (info.spillFormat() == DataFormatInteger || info.spillFormat() == DataFormatDouble) {
- terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode);
+ terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation);
return allocate();
}
@@ -1353,7 +1353,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
info.fillJSValue(*m_stream, gpr, DataFormatJSCell);
return gpr;
}
- terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode);
+ terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation);
return gpr;
}
ASSERT(info.spillFormat() & DataFormatJS);
@@ -1362,7 +1362,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
info.fillJSValue(*m_stream, gpr, DataFormatJS);
if (!isCellSpeculation(type))
- speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister));
+ speculationCheckWithConditionalDirection(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister), isForwardSpeculation);
info.fillJSValue(*m_stream, gpr, DataFormatJSCell);
return gpr;
}
@@ -1378,7 +1378,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
GPRReg gpr = info.gpr();
m_gprs.lock(gpr);
if (!isCellSpeculation(type))
- speculationCheck(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister));
+ speculationCheckWithConditionalDirection(BadType, JSValueRegs(gpr), nodeIndex, m_jit.branchTestPtr(MacroAssembler::NonZero, gpr, GPRInfo::tagMaskRegister), isForwardSpeculation);
info.fillJSValue(*m_stream, gpr, DataFormatJSCell);
return gpr;
}
@@ -1389,7 +1389,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
case DataFormatDouble:
case DataFormatJSBoolean:
case DataFormatBoolean: {
- terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode);
+ terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation);
return allocate();
}
@@ -1578,7 +1578,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(op2GPR, resultGPR);
m_jit.andPtr(MacroAssembler::TrustedImm32(~TagBitUndefined), resultGPR);
@@ -1649,7 +1649,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);
@@ -2508,7 +2508,8 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- case PutByVal: {
+ case PutByVal:
+ case PutByValSafe: {
Edge child1 = m_jit.graph().varArgChild(node, 0);
Edge child2 = m_jit.graph().varArgChild(node, 1);
Edge child3 = m_jit.graph().varArgChild(node, 2);
@@ -2675,13 +2676,15 @@ void SpeculativeJIT::compile(Node& node)
// 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)));
+
+ MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()));
+ if (node.op() == PutByVal)
+ speculationCheck(OutOfBounds, JSValueRegs(), NoNode, beyondArrayBounds);
base.use();
property.use();
value.use();
- MacroAssembler::Jump beyondArrayBounds = m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset()));
-
// Get the array storage.
GPRReg storageReg = scratchReg;
m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
@@ -2702,11 +2705,13 @@ void SpeculativeJIT::compile(Node& node)
// Store the value to the array.
m_jit.storePtr(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
- addSlowPathGenerator(
- slowPathCall(
- beyondArrayBounds, this,
- m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
- NoResult, baseReg, propertyReg, valueReg));
+ if (node.op() == PutByValSafe) {
+ addSlowPathGenerator(
+ slowPathCall(
+ beyondArrayBounds, this,
+ m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
+ NoResult, baseReg, propertyReg, valueReg));
+ }
noResult(m_compileIndex, UseChildrenCalledExplicitly);
break;
@@ -3043,7 +3048,7 @@ void SpeculativeJIT::compile(Node& node)
m_jit.move(op1GPR, resultGPR);
else {
MacroAssembler::Jump alreadyPrimitive = m_jit.branchTestPtr(MacroAssembler::NonZero, op1GPR, GPRInfo::tagMaskRegister);
- MacroAssembler::Jump notPrimitive = m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1GPR, JSCell::classInfoOffset()), MacroAssembler::TrustedImmPtr(&JSString::s_info));
+ MacroAssembler::Jump notPrimitive = m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(op1GPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get()));
alreadyPrimitive.link(&m_jit);
m_jit.move(op1GPR, resultGPR);
@@ -3208,7 +3213,7 @@ void SpeculativeJIT::compile(Node& node)
GPRReg resultGPR = result.gpr();
if (!isObjectSpeculation(m_state.forNode(node.child1()).m_type))
- speculationCheck(BadType, JSValueRegs(thisValueGPR), node.child1(), m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(thisValueGPR, JSCell::classInfoOffset()), JITCompiler::TrustedImmPtr(&JSString::s_info)));
+ speculationCheck(BadType, JSValueRegs(thisValueGPR), node.child1(), m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(thisValueGPR, JSCell::structureOffset()), JITCompiler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())));
m_jit.move(thisValueGPR, resultGPR);
@@ -3435,7 +3440,7 @@ void SpeculativeJIT::compile(Node& node)
GPRReg resultGPR = result.gpr();
if (!isStringSpeculation(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(&JSString::s_info)));
+ speculationCheck(BadType, JSValueRegs(baseGPR), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseGPR, JSCell::structureOffset()), MacroAssembler::TrustedImmPtr(m_jit.globalData()->stringStructure.get())));
m_jit.load32(MacroAssembler::Address(baseGPR, JSString::offsetOfLength()), resultGPR);
@@ -3485,7 +3490,8 @@ void SpeculativeJIT::compile(Node& node)
noResult(m_compileIndex);
break;
}
- case CheckStructure: {
+ case CheckStructure:
+ case ForwardCheckStructure: {
AbstractValue& value = m_state.forNode(node.child1());
if (value.m_structure.isSubsetOf(node.structureSet())
&& isCellSpeculation(value.m_type)) {
@@ -3493,13 +3499,19 @@ void SpeculativeJIT::compile(Node& node)
break;
}
- SpeculateCellOperand base(this, node.child1());
+ SpeculateCellOperand base(this, node.child1(), node.op() == ForwardCheckStructure);
ASSERT(node.structureSet().size());
- if (node.structureSet().size() == 1)
- speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, JITCompiler::Address(base.gpr(), JSCell::structureOffset()), node.structureSet()[0]));
- else {
+ if (node.structureSet().size() == 1) {
+ speculationCheckWithConditionalDirection(
+ BadCache, JSValueRegs(), NoNode,
+ m_jit.branchWeakPtr(
+ JITCompiler::NotEqual,
+ JITCompiler::Address(base.gpr(), JSCell::structureOffset()),
+ node.structureSet()[0]),
+ node.op() == ForwardCheckStructure);
+ } else {
GPRTemporary structure(this);
m_jit.loadPtr(JITCompiler::Address(base.gpr(), JSCell::structureOffset()), structure.gpr());
@@ -3509,7 +3521,11 @@ void SpeculativeJIT::compile(Node& node)
for (size_t i = 0; i < node.structureSet().size() - 1; ++i)
done.append(m_jit.branchWeakPtr(JITCompiler::Equal, structure.gpr(), node.structureSet()[i]));
- speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, structure.gpr(), node.structureSet().last()));
+ speculationCheckWithConditionalDirection(
+ BadCache, JSValueRegs(), NoNode,
+ m_jit.branchWeakPtr(
+ JITCompiler::NotEqual, structure.gpr(), node.structureSet().last()),
+ node.op() == ForwardCheckStructure);
done.link(&m_jit);
}
@@ -3520,7 +3536,7 @@ void SpeculativeJIT::compile(Node& node)
case StructureTransitionWatchpoint: {
m_jit.addWeakReference(node.structure());
- node.structure()->addTransitionWatchpoint(speculationWatchpoint());
+ node.structure()->addTransitionWatchpoint(speculationWatchpoint(BadCache));
#if !ASSERT_DISABLED
SpeculateCellOperand op1(this, node.child1());
diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
new file mode 100644
index 000000000..e86c57dff
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "config.h"
+#include "DFGStructureCheckHoistingPhase.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGBasicBlock.h"
+#include "DFGGraph.h"
+#include "DFGInsertionSet.h"
+#include "DFGPhase.h"
+#include <wtf/HashMap.h>
+
+namespace JSC { namespace DFG {
+
+enum CheckBallot { VoteOther, VoteStructureCheck };
+
+class StructureCheckHoistingPhase : public Phase {
+public:
+ StructureCheckHoistingPhase(Graph& graph)
+ : Phase(graph, "structure check hoisting")
+ {
+ }
+
+ bool run()
+ {
+ for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
+ VariableAccessData* variable = &m_graph.m_variableAccessData[i];
+ if (!variable->isRoot())
+ continue;
+ variable->clearVotes();
+ }
+
+ // Identify the set of variables that are always subject to the same structure
+ // checks. For now, only consider monomorphic structure checks (one structure).
+
+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+ BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+ if (!block)
+ continue;
+ for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
+ NodeIndex nodeIndex = block->at(indexInBlock);
+ Node& node = m_graph[nodeIndex];
+ if (!node.shouldGenerate())
+ continue;
+ switch (node.op()) {
+ case CheckStructure: {
+ Node& child = m_graph[node.child1()];
+ if (child.op() != GetLocal)
+ break;
+ VariableAccessData* variable = child.variableAccessData();
+ variable->vote(VoteStructureCheck);
+ if (variable->isCaptured() || variable->structureCheckHoistingFailed())
+ break;
+ if (!isCellSpeculation(variable->prediction()))
+ break;
+ noticeStructureCheck(variable, node.structureSet());
+ break;
+ }
+
+ case ForwardCheckStructure:
+ // We currently rely on the fact that we're the only ones who would
+ // insert this node.
+ ASSERT_NOT_REACHED();
+ break;
+
+ case GetByOffset:
+ case PutByOffset:
+ case PutStructure:
+ case StructureTransitionWatchpoint:
+ case AllocatePropertyStorage:
+ case ReallocatePropertyStorage:
+ case GetPropertyStorage:
+ // Don't count these uses.
+ break;
+
+ default:
+ m_graph.vote(node, VoteOther);
+ break;
+ }
+ }
+ }
+
+ // Disable structure hoisting on variables that appear to mostly be used in
+ // contexts where it doesn't make sense.
+
+ for (unsigned i = m_graph.m_variableAccessData.size(); i--;) {
+ VariableAccessData* variable = &m_graph.m_variableAccessData[i];
+ if (!variable->isRoot())
+ continue;
+ if (variable->voteRatio() >= Options::structureCheckVoteRatioForHoisting())
+ continue;
+ HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
+ if (iter == m_map.end())
+ continue;
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+ dataLog("Zeroing the structure to hoist for %s because the ratio is %lf.\n",
+ m_graph.nameOfVariableAccessData(variable), variable->voteRatio());
+#endif
+ iter->second.m_structure = 0;
+ }
+
+ // Identify the set of variables that are live across a structure clobber.
+
+ Operands<VariableAccessData*> live(
+ m_graph.m_blocks[0]->variablesAtTail.numberOfArguments(),
+ m_graph.m_blocks[0]->variablesAtTail.numberOfLocals());
+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+ BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+ if (!block)
+ continue;
+ ASSERT(live.numberOfArguments() == block->variablesAtTail.numberOfArguments());
+ ASSERT(live.numberOfLocals() == block->variablesAtTail.numberOfLocals());
+ for (unsigned i = live.size(); i--;) {
+ NodeIndex indexAtTail = block->variablesAtTail[i];
+ VariableAccessData* variable;
+ if (indexAtTail == NoNode)
+ variable = 0;
+ else
+ variable = m_graph[indexAtTail].variableAccessData();
+ live[i] = variable;
+ }
+ for (unsigned indexInBlock = block->size(); indexInBlock--;) {
+ NodeIndex nodeIndex = block->at(indexInBlock);
+ Node& node = m_graph[nodeIndex];
+ if (!node.shouldGenerate())
+ continue;
+ switch (node.op()) {
+ case GetLocal:
+ case Flush:
+ // This is a birth.
+ live.operand(node.local()) = node.variableAccessData();
+ break;
+
+ case SetLocal:
+ case SetArgument:
+ ASSERT(live.operand(node.local())); // Must be live.
+ ASSERT(live.operand(node.local()) == node.variableAccessData()); // Must have the variable we expected.
+ // This is a death.
+ live.operand(node.local()) = 0;
+ break;
+
+ // Use the CFA's notion of what clobbers the world.
+ case ValueAdd:
+ if (m_graph.addShouldSpeculateInteger(node))
+ break;
+ if (Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()]))
+ break;
+ clobber(live);
+ break;
+
+ case CompareLess:
+ case CompareLessEq:
+ case CompareGreater:
+ case CompareGreaterEq:
+ case CompareEq: {
+ Node& left = m_graph[node.child1()];
+ Node& right = m_graph[node.child2()];
+ if (Node::shouldSpeculateInteger(left, right))
+ break;
+ if (Node::shouldSpeculateNumber(left, right))
+ break;
+ if (node.op() == CompareEq) {
+ if ((m_graph.isConstant(node.child1().index())
+ && m_graph.valueOfJSConstant(node.child1().index()).isNull())
+ || (m_graph.isConstant(node.child2().index())
+ && m_graph.valueOfJSConstant(node.child2().index()).isNull()))
+ break;
+
+ if (Node::shouldSpeculateFinalObject(left, right))
+ break;
+ if (Node::shouldSpeculateArray(left, right))
+ break;
+ if (left.shouldSpeculateFinalObject() && right.shouldSpeculateFinalObjectOrOther())
+ break;
+ if (right.shouldSpeculateFinalObject() && left.shouldSpeculateFinalObjectOrOther())
+ break;
+ if (left.shouldSpeculateArray() && right.shouldSpeculateArrayOrOther())
+ break;
+ if (right.shouldSpeculateArray() && left.shouldSpeculateArrayOrOther())
+ break;
+ }
+ clobber(live);
+ break;
+ }
+
+ case GetByVal:
+ if (!node.prediction() || !m_graph[node.child1()].prediction() || !m_graph[node.child2()].prediction())
+ break;
+ if (!isActionableArraySpeculation(m_graph[node.child1()].prediction()) || !m_graph[node.child2()].shouldSpeculateInteger())
+ clobber(live);
+ break;
+
+ case PutByVal:
+ case PutByValAlias: {
+ 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())) {
+ clobber(live);
+ break;
+ }
+ if (node.op() == PutByValAlias)
+ break;
+ if (m_graph[child1].shouldSpeculateArguments())
+ break;
+ if (m_graph[child1].shouldSpeculateInt8Array())
+ break;
+ if (m_graph[child1].shouldSpeculateInt16Array())
+ break;
+ if (m_graph[child1].shouldSpeculateInt32Array())
+ break;
+ if (m_graph[child1].shouldSpeculateUint8Array())
+ break;
+ if (m_graph[child1].shouldSpeculateUint8ClampedArray())
+ break;
+ if (m_graph[child1].shouldSpeculateUint16Array())
+ break;
+ if (m_graph[child1].shouldSpeculateUint32Array())
+ break;
+ if (m_graph[child1].shouldSpeculateFloat32Array())
+ break;
+ if (m_graph[child1].shouldSpeculateFloat64Array())
+ break;
+ clobber(live);
+ break;
+ }
+
+ case GetMyArgumentsLengthSafe:
+ case GetMyArgumentByValSafe:
+ case GetById:
+ case GetByIdFlush:
+ case PutStructure:
+ case PhantomPutStructure:
+ case PutById:
+ case PutByIdDirect:
+ case Call:
+ case Construct:
+ case Resolve:
+ case ResolveBase:
+ case ResolveBaseStrictPut:
+ case ResolveGlobal:
+ clobber(live);
+ break;
+
+ default:
+ ASSERT(node.op() != Phi);
+ break;
+ }
+ }
+ }
+
+ bool changed = false;
+
+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
+ for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin();
+ it != m_map.end(); ++it) {
+ if (!it->second.m_structure) {
+ dataLog("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->first));
+ continue;
+ }
+ if (it->second.m_isClobbered && !it->second.m_structure->transitionWatchpointSetIsStillValid()) {
+ dataLog("Not hoisting checks for %s because the structure is clobbered and has an invalid watchpoint set.\n", m_graph.nameOfVariableAccessData(it->first));
+ continue;
+ }
+ 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.
+ // 2) If a variable's live range spans a clobber but is watchpointable, then
+ // inject structure checks before the SetLocal and replace all other structure
+ // checks on that variable with structure transition watchpoints.
+
+ InsertionSet<NodeIndex> insertionSet;
+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.m_blocks.size(); ++blockIndex) {
+ BasicBlock* block = m_graph.m_blocks[blockIndex].get();
+ if (!block)
+ continue;
+ for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) {
+ NodeIndex nodeIndex = block->at(indexInBlock);
+ Node& node = m_graph[nodeIndex];
+ // Be careful not to use 'node' after appending to the graph. In those switch
+ // cases where we need to append, we first carefully extract everything we need
+ // from the node, before doing any appending.
+ if (!node.shouldGenerate())
+ continue;
+ switch (node.op()) {
+ case SetArgument: {
+ ASSERT(!blockIndex);
+ // Insert a GetLocal and a CheckStructure immediately following this
+ // SetArgument, if the variable was a candidate for structure hoisting.
+ // If the basic block previously only had the SetArgument as its
+ // variable-at-tail, then replace it with this GetLocal.
+ VariableAccessData* variable = node.variableAccessData();
+ HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
+ if (iter == m_map.end())
+ break;
+ if (!iter->second.m_structure)
+ break;
+ if (iter->second.m_isClobbered && !iter->second.m_structure->transitionWatchpointSetIsStillValid())
+ break;
+
+ node.ref();
+
+ CodeOrigin codeOrigin = node.codeOrigin;
+
+ Node getLocal(GetLocal, codeOrigin, OpInfo(variable), nodeIndex);
+ getLocal.predict(variable->prediction());
+ getLocal.ref();
+ NodeIndex getLocalIndex = m_graph.size();
+ m_graph.append(getLocal);
+ insertionSet.append(indexInBlock + 1, getLocalIndex);
+
+ Node checkStructure(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->second.m_structure)), getLocalIndex);
+ checkStructure.ref();
+ NodeIndex checkStructureIndex = m_graph.size();
+ m_graph.append(checkStructure);
+ insertionSet.append(indexInBlock + 1, checkStructureIndex);
+
+ if (block->variablesAtTail.operand(variable->local()) == nodeIndex)
+ block->variablesAtTail.operand(variable->local()) = getLocalIndex;
+
+ changed = true;
+ break;
+ }
+
+ case SetLocal: {
+ VariableAccessData* variable = node.variableAccessData();
+ HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(variable);
+ if (iter == m_map.end())
+ break;
+ if (!iter->second.m_structure)
+ break;
+ if (iter->second.m_isClobbered && !iter->second.m_structure->transitionWatchpointSetIsStillValid())
+ break;
+
+ // First insert a dead SetLocal to tell OSR that the child's value should
+ // be dropped into this bytecode variable if the CheckStructure decides
+ // to exit.
+
+ CodeOrigin codeOrigin = node.codeOrigin;
+ NodeIndex child1 = node.child1().index();
+
+ Node setLocal(SetLocal, codeOrigin, OpInfo(variable), child1);
+ NodeIndex setLocalIndex = m_graph.size();
+ m_graph.append(setLocal);
+ insertionSet.append(indexInBlock, setLocalIndex);
+ m_graph[child1].ref();
+ // Use a ForwardCheckStructure to indicate that we should exit to the
+ // next bytecode instruction rather than reexecuting the current one.
+ Node checkStructure(ForwardCheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(iter->second.m_structure)), child1);
+ checkStructure.ref();
+ NodeIndex checkStructureIndex = m_graph.size();
+ m_graph.append(checkStructure);
+ insertionSet.append(indexInBlock, checkStructureIndex);
+ changed = true;
+ break;
+ }
+
+ case CheckStructure: {
+ Node& child = m_graph[node.child1()];
+ if (child.op() != GetLocal)
+ break;
+ HashMap<VariableAccessData*, CheckData>::iterator iter = m_map.find(child.variableAccessData());
+ if (iter == m_map.end())
+ break;
+ if (!iter->second.m_structure)
+ break;
+ if (!iter->second.m_isClobbered) {
+ node.setOpAndDefaultFlags(Phantom);
+ ASSERT(node.refCount() == 1);
+ break;
+ }
+ if (!iter->second.m_structure->transitionWatchpointSetIsStillValid())
+ break;
+ ASSERT(iter->second.m_structure == node.structureSet().singletonStructure());
+ node.convertToStructureTransitionWatchpoint();
+ changed = true;
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ insertionSet.execute(*block);
+ }
+
+ return changed;
+ }
+
+private:
+ void noticeStructureCheck(VariableAccessData* variable, Structure* structure)
+ {
+ HashMap<VariableAccessData*, CheckData>::AddResult result =
+ m_map.add(variable, CheckData(structure, false));
+ if (result.isNewEntry)
+ return;
+ if (result.iterator->second.m_structure == structure)
+ return;
+ result.iterator->second.m_structure = 0;
+ }
+
+ void noticeStructureCheck(VariableAccessData* variable, const StructureSet& set)
+ {
+ if (set.size() != 1) {
+ noticeStructureCheck(variable, 0);
+ return;
+ }
+ noticeStructureCheck(variable, set.singletonStructure());
+ }
+
+ void noticeClobber(VariableAccessData* variable)
+ {
+ HashMap<VariableAccessData*, CheckData>::iterator iter =
+ m_map.find(variable);
+ if (iter == m_map.end())
+ return;
+ iter->second.m_isClobbered = true;
+ }
+
+ void clobber(const Operands<VariableAccessData*>& live)
+ {
+ for (size_t i = live.size(); i--;) {
+ VariableAccessData* variable = live[i];
+ if (!variable)
+ continue;
+ noticeClobber(variable);
+ }
+ }
+
+ struct CheckData {
+ Structure* m_structure;
+ bool m_isClobbered;
+
+ CheckData()
+ : m_structure(0)
+ , m_isClobbered(false)
+ {
+ }
+
+ CheckData(Structure* structure, bool isClobbered)
+ : m_structure(structure)
+ , m_isClobbered(isClobbered)
+ {
+ }
+ };
+
+ HashMap<VariableAccessData*, CheckData> m_map;
+};
+
+bool performStructureCheckHoisting(Graph& graph)
+{
+ SamplingRegion samplingRegion("DFG Structure Check Hoisting Phase");
+ return runPhase<StructureCheckHoistingPhase>(graph);
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+
diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.h b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.h
new file mode 100644
index 000000000..1e6462a92
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 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 DFGStructureCheckHoistingPhase_h
+#define DFGStructureCheckHoistingPhase_h
+
+#include <wtf/Platform.h>
+
+#if ENABLE(DFG_JIT)
+
+namespace JSC { namespace DFG {
+
+class Graph;
+
+// Hoists CheckStructure on variables to assignments to those variables, if either of
+// the following is true:
+// A) The structure's transition watchpoint set is valid.
+// B) The span of code within which the variable is live has no effects that might
+// clobber the structure.
+
+bool performStructureCheckHoisting(Graph&);
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGStructureCheckHoistingPhase_h
+
diff --git a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
index e734e6387..6d8e89799 100644
--- a/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
+++ b/Source/JavaScriptCore/dfg/DFGVariableAccessData.h
@@ -37,10 +37,10 @@
namespace JSC { namespace DFG {
+enum DoubleBallot { VoteValue, VoteDouble };
+
class VariableAccessData : public UnionFind<VariableAccessData> {
public:
- enum Ballot { VoteValue, VoteDouble };
-
VariableAccessData()
: m_local(static_cast<VirtualRegister>(std::numeric_limits<int>::min()))
, m_prediction(SpecNone)
@@ -49,6 +49,7 @@ public:
, m_doubleFormatState(EmptyDoubleFormatState)
, m_isCaptured(false)
, m_isArgumentsAlias(false)
+ , m_structureCheckHoistingFailed(false)
{
clearVotes();
}
@@ -61,6 +62,7 @@ public:
, m_doubleFormatState(EmptyDoubleFormatState)
, m_isCaptured(isCaptured)
, m_isArgumentsAlias(false)
+ , m_structureCheckHoistingFailed(false)
{
clearVotes();
}
@@ -90,6 +92,20 @@ public:
return m_isCaptured;
}
+ bool mergeStructureCheckHoistingFailed(bool failed)
+ {
+ bool newFailed = m_structureCheckHoistingFailed | failed;
+ if (newFailed == m_structureCheckHoistingFailed)
+ return false;
+ m_structureCheckHoistingFailed = newFailed;
+ return true;
+ }
+
+ bool structureCheckHoistingFailed()
+ {
+ return m_structureCheckHoistingFailed;
+ }
+
bool mergeIsArgumentsAlias(bool isArgumentsAlias)
{
bool newIsArgumentsAlias = m_isArgumentsAlias | isArgumentsAlias;
@@ -136,20 +152,20 @@ public:
void clearVotes()
{
ASSERT(find() == this);
- m_votes[VoteValue] = 0;
- m_votes[VoteDouble] = 0;
+ m_votes[0] = 0;
+ m_votes[1] = 0;
}
- void vote(Ballot ballot)
+ void vote(unsigned ballot)
{
- ASSERT(static_cast<unsigned>(ballot) < 2);
+ ASSERT(ballot < 2);
m_votes[ballot]++;
}
- double doubleVoteRatio()
+ double voteRatio()
{
ASSERT(find() == this);
- return static_cast<double>(m_votes[VoteDouble]) / m_votes[VoteValue];
+ return static_cast<double>(m_votes[1]) / m_votes[0];
}
bool shouldUseDoubleFormatAccordingToVote()
@@ -176,7 +192,7 @@ public:
// If the variable has been voted to become a double, then make it a
// double.
- if (doubleVoteRatio() >= Options::doubleVoteRatioForDoubleFormat())
+ if (voteRatio() >= Options::doubleVoteRatioForDoubleFormat())
return true;
return false;
@@ -250,11 +266,12 @@ private:
SpeculatedType m_argumentAwarePrediction;
NodeFlags m_flags;
- float m_votes[2];
+ float m_votes[2]; // Used primarily for double voting but may be reused for other purposes.
DoubleFormatState m_doubleFormatState;
bool m_isCaptured;
bool m_isArgumentsAlias;
+ bool m_structureCheckHoistingFailed;
};
} } // namespace JSC::DFG