summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/dfg
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2012-11-22 09:09:45 +0100
committerSimon Hausmann <simon.hausmann@digia.com>2012-11-22 09:10:13 +0100
commit470286ecfe79d59df14944e5b5d34630fc739391 (patch)
tree43983212872e06cebefd2ae474418fa2908ca54c /Source/JavaScriptCore/dfg
parent23037105e948c2065da5a937d3a2396b0ff45c1e (diff)
downloadqtwebkit-470286ecfe79d59df14944e5b5d34630fc739391.tar.gz
Imported WebKit commit e89504fa9195b2063b2530961d4b73dd08de3242 (http://svn.webkit.org/repository/webkit/trunk@135485)
Change-Id: I03774e5ac79721c13ffa30d152537a74d0b12e66 Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'Source/JavaScriptCore/dfg')
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractState.cpp311
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractState.h50
-rw-r--r--Source/JavaScriptCore/dfg/DFGAbstractValue.h26
-rw-r--r--Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp30
-rw-r--r--Source/JavaScriptCore/dfg/DFGArrayMode.cpp295
-rw-r--r--Source/JavaScriptCore/dfg/DFGArrayMode.h78
-rw-r--r--Source/JavaScriptCore/dfg/DFGBasicBlock.h3
-rw-r--r--Source/JavaScriptCore/dfg/DFGBranchDirection.h88
-rw-r--r--Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp191
-rw-r--r--Source/JavaScriptCore/dfg/DFGCCallHelpers.h105
-rw-r--r--Source/JavaScriptCore/dfg/DFGCFAPhase.cpp22
-rw-r--r--Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp50
-rw-r--r--Source/JavaScriptCore/dfg/DFGCSEPhase.cpp16
-rw-r--r--Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h4
-rw-r--r--Source/JavaScriptCore/dfg/DFGCapabilities.cpp6
-rw-r--r--Source/JavaScriptCore/dfg/DFGCapabilities.h1
-rw-r--r--Source/JavaScriptCore/dfg/DFGCommon.h15
-rw-r--r--Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp237
-rw-r--r--Source/JavaScriptCore/dfg/DFGDisassembler.cpp11
-rw-r--r--Source/JavaScriptCore/dfg/DFGDriver.cpp8
-rw-r--r--Source/JavaScriptCore/dfg/DFGFixupPhase.cpp108
-rw-r--r--Source/JavaScriptCore/dfg/DFGGraph.cpp149
-rw-r--r--Source/JavaScriptCore/dfg/DFGGraph.h17
-rw-r--r--Source/JavaScriptCore/dfg/DFGJITCompiler.cpp5
-rw-r--r--Source/JavaScriptCore/dfg/DFGJITCompiler.h8
-rw-r--r--Source/JavaScriptCore/dfg/DFGNode.h111
-rw-r--r--Source/JavaScriptCore/dfg/DFGNodeFlags.cpp6
-rw-r--r--Source/JavaScriptCore/dfg/DFGNodeFlags.h9
-rw-r--r--Source/JavaScriptCore/dfg/DFGNodeType.h6
-rw-r--r--Source/JavaScriptCore/dfg/DFGOSREntry.cpp24
-rw-r--r--Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp2
-rw-r--r--Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp10
-rw-r--r--Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp32
-rw-r--r--Source/JavaScriptCore/dfg/DFGOperations.cpp131
-rw-r--r--Source/JavaScriptCore/dfg/DFGOperations.h12
-rw-r--r--Source/JavaScriptCore/dfg/DFGPhase.cpp4
-rw-r--r--Source/JavaScriptCore/dfg/DFGPhase.h2
-rw-r--r--Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp188
-rw-r--r--Source/JavaScriptCore/dfg/DFGRegisterBank.h34
-rw-r--r--Source/JavaScriptCore/dfg/DFGRepatch.cpp172
-rw-r--r--Source/JavaScriptCore/dfg/DFGRepatch.h2
-rw-r--r--Source/JavaScriptCore/dfg/DFGScoreBoard.h20
-rw-r--r--Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h2
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp260
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h56
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp558
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp390
-rw-r--r--Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp10
-rw-r--r--Source/JavaScriptCore/dfg/DFGThunks.cpp12
-rw-r--r--Source/JavaScriptCore/dfg/DFGThunks.h2
-rw-r--r--Source/JavaScriptCore/dfg/DFGValidate.cpp32
-rw-r--r--Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp6
-rw-r--r--Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp14
53 files changed, 3040 insertions, 901 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index e518c24a8..23b84cedf 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -30,6 +30,8 @@
#include "CodeBlock.h"
#include "DFGBasicBlock.h"
+#include "GetByIdStatus.h"
+#include "PutByIdStatus.h"
namespace JSC { namespace DFG {
@@ -150,16 +152,16 @@ void AbstractState::initialize(Graph& graph)
int operand = graph.m_mustHandleValues.operandForIndex(i);
block->valuesAtHead.operand(operand).merge(value);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Initializing Block #%u, operand r%d, to ", blockIndex, operand);
+ dataLogF(" Initializing Block #%u, operand r%d, to ", blockIndex, operand);
block->valuesAtHead.operand(operand).dump(WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
}
block->cfaShouldRevisit = true;
}
}
-bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDirectionPtr)
+bool AbstractState::endBasicBlock(MergeMode mergeMode)
{
ASSERT(m_block);
@@ -167,6 +169,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi
block->cfaFoundConstants = m_foundConstants;
block->cfaDidFinish = m_isValid;
+ block->cfaBranchDirection = m_branchDirection;
if (!m_isValid) {
reset();
@@ -178,7 +181,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi
if (mergeMode != DontMerge || !ASSERT_DISABLED) {
for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Merging state for argument %zu.\n", argument);
+ dataLogF(" Merging state for argument %zu.\n", argument);
#endif
AbstractValue& destination = block->valuesAtTail.argument(argument);
changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument));
@@ -186,7 +189,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi
for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Merging state for local %zu.\n", local);
+ dataLogF(" Merging state for local %zu.\n", local);
#endif
AbstractValue& destination = block->valuesAtTail.local(local);
changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local));
@@ -195,12 +198,8 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi
ASSERT(mergeMode != DontMerge || !changed);
- BranchDirection branchDirection = m_branchDirection;
- if (branchDirectionPtr)
- *branchDirectionPtr = branchDirection;
-
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Branch direction = %s\n", branchDirectionToString(branchDirection));
+ dataLogF(" Branch direction = %s\n", branchDirectionToString(m_branchDirection));
#endif
reset();
@@ -208,7 +207,7 @@ bool AbstractState::endBasicBlock(MergeMode mergeMode, BranchDirection* branchDi
if (mergeMode != MergeToSuccessors)
return changed;
- return mergeToSuccessors(m_graph, block, branchDirection);
+ return mergeToSuccessors(m_graph, block);
}
void AbstractState::reset()
@@ -218,6 +217,27 @@ void AbstractState::reset()
m_branchDirection = InvalidBranchDirection;
}
+AbstractState::BooleanResult AbstractState::booleanResult(Node& node, AbstractValue& value)
+{
+ JSValue childConst = value.value();
+ if (childConst) {
+ if (childConst.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec()))
+ return DefinitelyTrue;
+ return DefinitelyFalse;
+ }
+
+ // Next check if we can fold because we know that the source is an object or string and does not equal undefined.
+ if (isCellSpeculation(value.m_type)
+ && value.m_currentKnownStructure.hasSingleton()) {
+ Structure* structure = value.m_currentKnownStructure.singleton();
+ if (!structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node.codeOrigin))
+ && structure->typeInfo().type() != StringType)
+ return DefinitelyTrue;
+ }
+
+ return UnknownBooleanResult;
+}
+
bool AbstractState::execute(unsigned indexInBlock)
{
ASSERT(m_block);
@@ -239,6 +259,12 @@ bool AbstractState::execute(unsigned indexInBlock)
node.setCanExit(false);
break;
}
+
+ case Identity: {
+ forNode(nodeIndex) = forNode(node.child1());
+ node.setCanExit(false);
+ break;
+ }
case GetLocal: {
VariableAccessData* variableAccessData = node.variableAccessData();
@@ -424,7 +450,10 @@ bool AbstractState::execute(unsigned indexInBlock)
break;
}
speculateNumberUnary(node);
- forNode(nodeIndex).set(SpecDouble);
+ if (isInt32Speculation(forNode(node.child1()).m_type))
+ forNode(nodeIndex).set(SpecDoubleReal);
+ else
+ forNode(nodeIndex).set(SpecDouble);
break;
}
@@ -448,9 +477,13 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(nodeIndex).set(SpecInt32);
break;
}
- if (Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()])) {
+ if (Node::shouldSpeculateNumberExpectingDefined(m_graph[node.child1()], m_graph[node.child2()])) {
speculateNumberBinary(node);
- forNode(nodeIndex).set(SpecDouble);
+ if (isRealNumberSpeculation(forNode(node.child1()).m_type)
+ && isRealNumberSpeculation(forNode(node.child2()).m_type))
+ forNode(nodeIndex).set(SpecDoubleReal);
+ else
+ forNode(nodeIndex).set(SpecDouble);
break;
}
if (node.op() == ValueAdd) {
@@ -522,7 +555,11 @@ bool AbstractState::execute(unsigned indexInBlock)
break;
}
speculateNumberBinary(node);
- forNode(nodeIndex).set(SpecDouble);
+ if (isRealNumberSpeculation(forNode(node.child1()).m_type)
+ || isRealNumberSpeculation(forNode(node.child2()).m_type))
+ forNode(nodeIndex).set(SpecDoubleReal);
+ else
+ forNode(nodeIndex).set(SpecDouble);
break;
}
@@ -560,7 +597,7 @@ bool AbstractState::execute(unsigned indexInBlock)
break;
}
}
- if (Node::shouldSpeculateInteger(
+ if (Node::shouldSpeculateIntegerForArithmetic(
m_graph[node.child1()], m_graph[node.child2()])
&& node.canSpeculateInteger()) {
speculateInt32Binary(node, true); // forcing can-exit, which is a bit on the conservative side.
@@ -580,7 +617,7 @@ bool AbstractState::execute(unsigned indexInBlock)
node.setCanExit(false);
break;
}
- if (m_graph[node.child1()].shouldSpeculateInteger()
+ if (m_graph[node.child1()].shouldSpeculateIntegerForArithmetic()
&& node.canSpeculateInteger()) {
speculateInt32Unary(node, true);
forNode(nodeIndex).set(SpecInt32);
@@ -605,8 +642,18 @@ bool AbstractState::execute(unsigned indexInBlock)
}
case LogicalNot: {
- JSValue childConst = forNode(node.child1()).value();
- if (childConst && trySetConstant(nodeIndex, jsBoolean(!childConst.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec())))) {
+ bool didSetConstant = false;
+ switch (booleanResult(node, forNode(node.child1()))) {
+ case DefinitelyTrue:
+ didSetConstant = trySetConstant(nodeIndex, jsBoolean(false));
+ break;
+ case DefinitelyFalse:
+ didSetConstant = trySetConstant(nodeIndex, jsBoolean(true));
+ break;
+ default:
+ break;
+ }
+ if (didSetConstant) {
m_foundConstants = true;
node.setCanExit(false);
break;
@@ -678,12 +725,13 @@ bool AbstractState::execute(unsigned indexInBlock)
case CompareGreater:
case CompareGreaterEq:
case CompareEq: {
+ bool constantWasSet = false;
+
JSValue leftConst = forNode(node.child1()).value();
JSValue rightConst = forNode(node.child2()).value();
if (leftConst && rightConst && leftConst.isNumber() && rightConst.isNumber()) {
double a = leftConst.asNumber();
double b = rightConst.asNumber();
- bool constantWasSet;
switch (node.op()) {
case CompareLess:
constantWasSet = trySetConstant(nodeIndex, jsBoolean(a < b));
@@ -705,11 +753,20 @@ bool AbstractState::execute(unsigned indexInBlock)
constantWasSet = false;
break;
}
- if (constantWasSet) {
- m_foundConstants = true;
- node.setCanExit(false);
- break;
- }
+ }
+
+ if (!constantWasSet && node.op() == CompareEq) {
+ SpeculatedType leftType = forNode(node.child1()).m_type;
+ SpeculatedType rightType = forNode(node.child2()).m_type;
+ if ((isInt32Speculation(leftType) && isOtherSpeculation(rightType))
+ || (isOtherSpeculation(leftType) && isInt32Speculation(rightType)))
+ constantWasSet = trySetConstant(nodeIndex, jsBoolean(false));
+ }
+
+ if (constantWasSet) {
+ m_foundConstants = true;
+ node.setCanExit(false);
+ break;
}
forNode(nodeIndex).set(SpecBoolean);
@@ -842,6 +899,7 @@ bool AbstractState::execute(unsigned indexInBlock)
switch (node.arrayMode().type()) {
case Array::SelectUsingPredictions:
case Array::Unprofiled:
+ case Array::Undecided:
ASSERT_NOT_REACHED();
break;
case Array::ForceExit:
@@ -859,6 +917,24 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(node.child2()).filter(SpecInt32);
forNode(nodeIndex).makeTop();
break;
+ case Array::Int32:
+ forNode(node.child2()).filter(SpecInt32);
+ if (node.arrayMode().isOutOfBounds()) {
+ clobberWorld(node.codeOrigin, indexInBlock);
+ forNode(nodeIndex).makeTop();
+ } else
+ forNode(nodeIndex).set(SpecInt32);
+ break;
+ case Array::Double:
+ forNode(node.child2()).filter(SpecInt32);
+ if (node.arrayMode().isOutOfBounds()) {
+ clobberWorld(node.codeOrigin, indexInBlock);
+ forNode(nodeIndex).makeTop();
+ } else if (node.arrayMode().isSaneChain())
+ forNode(nodeIndex).set(SpecDouble);
+ else
+ forNode(nodeIndex).set(SpecDoubleReal);
+ break;
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
@@ -926,6 +1002,20 @@ bool AbstractState::execute(unsigned indexInBlock)
case Array::Generic:
clobberWorld(node.codeOrigin, indexInBlock);
break;
+ case Array::Int32:
+ forNode(child1).filter(SpecCell);
+ forNode(child2).filter(SpecInt32);
+ forNode(child3).filter(SpecInt32);
+ if (node.arrayMode().isOutOfBounds())
+ clobberWorld(node.codeOrigin, indexInBlock);
+ break;
+ case Array::Double:
+ forNode(child1).filter(SpecCell);
+ forNode(child2).filter(SpecInt32);
+ forNode(child3).filter(SpecRealNumber);
+ if (node.arrayMode().isOutOfBounds())
+ clobberWorld(node.codeOrigin, indexInBlock);
+ break;
case Array::Contiguous:
case Array::ArrayStorage:
forNode(child1).filter(SpecCell);
@@ -1018,6 +1108,16 @@ bool AbstractState::execute(unsigned indexInBlock)
case ArrayPush:
node.setCanExit(true);
+ switch (node.arrayMode().type()) {
+ case Array::Int32:
+ forNode(node.child2()).filter(SpecInt32);
+ break;
+ case Array::Double:
+ forNode(node.child2()).filter(SpecRealNumber);
+ break;
+ default:
+ break;
+ }
clobberWorld(node.codeOrigin, indexInBlock);
forNode(nodeIndex).set(SpecNumber);
break;
@@ -1043,23 +1143,21 @@ bool AbstractState::execute(unsigned indexInBlock)
break;
case Branch: {
- JSValue value = forNode(node.child1()).value();
- if (value) {
- bool booleanValue = value.toBoolean(m_codeBlock->globalObjectFor(node.codeOrigin)->globalExec());
- if (booleanValue)
- m_branchDirection = TakeTrue;
- else
- m_branchDirection = TakeFalse;
+ BooleanResult result = booleanResult(node, forNode(node.child1()));
+ if (result == DefinitelyTrue) {
+ m_branchDirection = TakeTrue;
+ node.setCanExit(false);
+ break;
+ }
+ if (result == DefinitelyFalse) {
+ m_branchDirection = TakeFalse;
node.setCanExit(false);
break;
}
// FIXME: The above handles the trivial cases of sparse conditional
// constant propagation, but we can do better:
- // 1) If the abstract value does not have a concrete value but describes
- // something that is known to evaluate true (or false) then we ought
- // to sparse conditional that.
- // 2) We can specialize the source variable's value on each direction of
- // the branch.
+ // We can specialize the source variable's value on each direction of
+ // the branch.
Node& child = m_graph[node.child1()];
if (child.shouldSpeculateBoolean())
speculateBooleanUnary(node);
@@ -1122,13 +1220,13 @@ bool AbstractState::execute(unsigned indexInBlock)
case NewArray:
node.setCanExit(true);
- forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructure());
+ forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()));
m_haveStructures = true;
break;
case NewArrayBuffer:
node.setCanExit(true);
- forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructure());
+ forNode(nodeIndex).set(m_graph.globalObjectFor(node.codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()));
m_haveStructures = true;
break;
@@ -1156,6 +1254,7 @@ bool AbstractState::execute(unsigned indexInBlock)
// be hit, but then again, you never know.
destination = source;
node.setCanExit(false);
+ m_foundConstants = true; // Tell the constant folder to turn this into Identity.
break;
}
@@ -1188,10 +1287,14 @@ bool AbstractState::execute(unsigned indexInBlock)
destination.set(SpecFinalObject);
break;
}
+
+ case InheritorIDWatchpoint:
+ node.setCanExit(true);
+ break;
case NewObject:
node.setCanExit(false);
- forNode(nodeIndex).set(m_codeBlock->globalObjectFor(node.codeOrigin)->emptyObjectStructure());
+ forNode(nodeIndex).set(node.structure());
m_haveStructures = true;
break;
@@ -1308,8 +1411,30 @@ bool AbstractState::execute(unsigned indexInBlock)
m_isValid = false;
break;
}
- if (isCellSpeculation(m_graph[node.child1()].prediction()))
+ if (isCellSpeculation(m_graph[node.child1()].prediction())) {
forNode(node.child1()).filter(SpecCell);
+
+ if (Structure* structure = forNode(node.child1()).bestProvenStructure()) {
+ GetByIdStatus status = GetByIdStatus::computeFor(
+ m_graph.m_globalData, structure,
+ m_graph.m_codeBlock->identifier(node.identifierNumber()));
+ if (status.isSimple()) {
+ // Assert things that we can't handle and that the computeFor() method
+ // above won't be able to return.
+ ASSERT(status.structureSet().size() == 1);
+ ASSERT(status.chain().isEmpty());
+
+ if (status.specificValue())
+ forNode(nodeIndex).set(status.specificValue());
+ else
+ forNode(nodeIndex).makeTop();
+ forNode(node.child1()).filter(status.structureSet());
+
+ m_foundConstants = true;
+ break;
+ }
+ }
+ }
clobberWorld(node.codeOrigin, indexInBlock);
forNode(nodeIndex).makeTop();
break;
@@ -1374,7 +1499,7 @@ bool AbstractState::execute(unsigned indexInBlock)
forNode(nodeIndex).clear(); // The result is not a JS value.
break;
case CheckArray: {
- if (node.arrayMode().alreadyChecked(forNode(node.child1()))) {
+ if (node.arrayMode().alreadyChecked(m_graph, node, forNode(node.child1()))) {
m_foundConstants = true;
node.setCanExit(false);
break;
@@ -1384,11 +1509,11 @@ bool AbstractState::execute(unsigned indexInBlock)
case Array::String:
forNode(node.child1()).filter(SpecString);
break;
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
- // This doesn't filter anything meaningful right now. We may want to add
- // CFA tracking of array mode speculations, but we don't have that, yet.
forNode(node.child1()).filter(SpecCell);
break;
case Array::Arguments:
@@ -1430,7 +1555,7 @@ bool AbstractState::execute(unsigned indexInBlock)
break;
}
case Arrayify: {
- if (node.arrayMode().alreadyChecked(forNode(node.child1()))) {
+ if (node.arrayMode().alreadyChecked(m_graph, node, forNode(node.child1()))) {
m_foundConstants = true;
node.setCanExit(false);
break;
@@ -1472,25 +1597,65 @@ bool AbstractState::execute(unsigned indexInBlock)
break;
}
case GetByOffset:
- node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
- forNode(node.child1()).filter(SpecCell);
+ if (!m_graph[node.child1()].hasStorageResult()) {
+ node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
+ forNode(node.child1()).filter(SpecCell);
+ }
forNode(nodeIndex).makeTop();
break;
- case PutByOffset:
- node.setCanExit(!isCellSpeculation(forNode(node.child1()).m_type));
- forNode(node.child1()).filter(SpecCell);
+ case PutByOffset: {
+ bool canExit = false;
+ if (!m_graph[node.child1()].hasStorageResult()) {
+ canExit |= !isCellSpeculation(forNode(node.child1()).m_type);
+ forNode(node.child1()).filter(SpecCell);
+ }
+ canExit |= !isCellSpeculation(forNode(node.child2()).m_type);
+ forNode(node.child2()).filter(SpecCell);
+ node.setCanExit(canExit);
break;
+ }
- case CheckFunction:
+ case CheckFunction: {
+ JSValue value = forNode(node.child1()).value();
+ if (value == node.function()) {
+ m_foundConstants = true;
+ ASSERT(value);
+ node.setCanExit(false);
+ break;
+ }
+
node.setCanExit(true); // Lies! We can do better.
- forNode(node.child1()).filter(SpecFunction);
- // FIXME: Should be able to propagate the fact that we know what the function is.
+ if (!forNode(node.child1()).filterByValue(node.function())) {
+ m_isValid = false;
+ break;
+ }
break;
+ }
case PutById:
case PutByIdDirect:
node.setCanExit(true);
+ if (Structure* structure = forNode(node.child1()).bestProvenStructure()) {
+ PutByIdStatus status = PutByIdStatus::computeFor(
+ m_graph.m_globalData,
+ m_graph.globalObjectFor(node.codeOrigin),
+ structure,
+ m_graph.m_codeBlock->identifier(node.identifierNumber()),
+ node.op() == PutByIdDirect);
+ if (status.isSimpleReplace()) {
+ forNode(node.child1()).filter(structure);
+ m_foundConstants = true;
+ break;
+ }
+ if (status.isSimpleTransition()) {
+ clobberStructures(indexInBlock);
+ forNode(node.child1()).set(status.newStructure());
+ m_haveStructures = true;
+ m_foundConstants = true;
+ break;
+ }
+ }
forNode(node.child1()).filter(SpecCell);
clobberWorld(node.codeOrigin, indexInBlock);
break;
@@ -1622,15 +1787,15 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract
return false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" It's live, node @%u.\n", nodeIndex);
+ dataLogF(" It's live, node @%u.\n", nodeIndex);
#endif
if (node.variableAccessData()->isCaptured()) {
source = inVariable;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Transfering ");
+ dataLogF(" Transfering ");
source.dump(WTF::dataFile());
- dataLog(" from last access due to captured variable.\n");
+ dataLogF(" from last access due to captured variable.\n");
#endif
} else {
switch (node.op()) {
@@ -1640,9 +1805,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract
// The block transfers the value from head to tail.
source = inVariable;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Transfering ");
+ dataLogF(" Transfering ");
source.dump(WTF::dataFile());
- dataLog(" from head to tail.\n");
+ dataLogF(" from head to tail.\n");
#endif
break;
@@ -1650,9 +1815,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract
// The block refines the value with additional speculations.
source = forNode(nodeIndex);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Refining to ");
+ dataLogF(" Refining to ");
source.dump(WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
break;
@@ -1665,9 +1830,9 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract
} else
source = forNode(node.child1());
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Setting to ");
+ dataLogF(" Setting to ");
source.dump(WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
break;
@@ -1681,7 +1846,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract
// Abstract execution did not change the output value of the variable, for this
// basic block, on this iteration.
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Not changed!\n");
+ dataLogF(" Not changed!\n");
#endif
return false;
}
@@ -1691,7 +1856,7 @@ inline bool AbstractState::mergeStateAtTail(AbstractValue& destination, Abstract
// true to indicate that the fixpoint must go on!
destination = source;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Changed!\n");
+ dataLogF(" Changed!\n");
#endif
return true;
}
@@ -1722,7 +1887,7 @@ inline bool AbstractState::merge(BasicBlock* from, BasicBlock* to)
}
inline bool AbstractState::mergeToSuccessors(
- Graph& graph, BasicBlock* basicBlock, BranchDirection branchDirection)
+ Graph& graph, BasicBlock* basicBlock)
{
Node& terminal = graph[basicBlock->last()];
@@ -1730,25 +1895,25 @@ inline bool AbstractState::mergeToSuccessors(
switch (terminal.op()) {
case Jump: {
- ASSERT(branchDirection == InvalidBranchDirection);
+ ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Merging to block #%u.\n", terminal.takenBlockIndex());
+ dataLogF(" Merging to block #%u.\n", terminal.takenBlockIndex());
#endif
return merge(basicBlock, graph.m_blocks[terminal.takenBlockIndex()].get());
}
case Branch: {
- ASSERT(branchDirection != InvalidBranchDirection);
+ ASSERT(basicBlock->cfaBranchDirection != InvalidBranchDirection);
bool changed = false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Merging to block #%u.\n", terminal.takenBlockIndex());
+ dataLogF(" Merging to block #%u.\n", terminal.takenBlockIndex());
#endif
- if (branchDirection != TakeFalse)
+ if (basicBlock->cfaBranchDirection != TakeFalse)
changed |= merge(basicBlock, graph.m_blocks[terminal.takenBlockIndex()].get());
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Merging to block #%u.\n", terminal.notTakenBlockIndex());
+ dataLogF(" Merging to block #%u.\n", terminal.notTakenBlockIndex());
#endif
- if (branchDirection != TakeTrue)
+ if (basicBlock->cfaBranchDirection != TakeTrue)
changed |= merge(basicBlock, graph.m_blocks[terminal.notTakenBlockIndex()].get());
return changed;
}
@@ -1756,7 +1921,7 @@ inline bool AbstractState::mergeToSuccessors(
case Return:
case Throw:
case ThrowReferenceError:
- ASSERT(branchDirection == InvalidBranchDirection);
+ ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection);
return false;
default:
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.h b/Source/JavaScriptCore/dfg/DFGAbstractState.h
index ec1a06231..230cd836c 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,6 +31,7 @@
#if ENABLE(DFG_JIT)
#include "DFGAbstractValue.h"
+#include "DFGBranchDirection.h"
#include "DFGGraph.h"
#include "DFGNode.h"
#include <wtf/Vector.h>
@@ -92,36 +93,6 @@ public:
MergeToSuccessors
};
- enum BranchDirection {
- // This is not a branch and so there is no branch direction, or
- // the branch direction has yet to be set.
- InvalidBranchDirection,
-
- // The branch takes the true case.
- TakeTrue,
-
- // The branch takes the false case.
- TakeFalse,
-
- // For all we know, the branch could go either direction, so we
- // have to assume the worst.
- TakeBoth
- };
-
- static const char* branchDirectionToString(BranchDirection branchDirection)
- {
- switch (branchDirection) {
- case InvalidBranchDirection:
- return "Invalid";
- case TakeTrue:
- return "TakeTrue";
- case TakeFalse:
- return "TakeFalse";
- case TakeBoth:
- return "TakeBoth";
- }
- }
-
AbstractState(Graph&);
~AbstractState();
@@ -174,11 +145,7 @@ public:
// A true return means that you must revisit (at least) the successor
// blocks. This also sets cfaShouldRevisit to true for basic blocks
// that must be visited next.
- //
- // If you'd like to know what direction the branch at the end of the
- // basic block is thought to have taken, you can pass a non-0 pointer
- // for BranchDirection.
- bool endBasicBlock(MergeMode, BranchDirection* = 0);
+ bool endBasicBlock(MergeMode);
// Reset the AbstractState. This throws away any results, and at this point
// you can safely call beginBasicBlock() on any basic block.
@@ -211,8 +178,8 @@ public:
// successors. Returns true if any of the successors' states changed. Note
// that this is automatically called in endBasicBlock() if MergeMode is
// MergeToSuccessors.
- bool mergeToSuccessors(Graph&, BasicBlock*, BranchDirection);
-
+ bool mergeToSuccessors(Graph&, BasicBlock*);
+
void dump(FILE* out);
private:
@@ -268,6 +235,13 @@ private:
childValue2.filter(SpecNumber);
}
+ enum BooleanResult {
+ UnknownBooleanResult,
+ DefinitelyFalse,
+ DefinitelyTrue
+ };
+ BooleanResult booleanResult(Node&, AbstractValue&);
+
bool trySetConstant(NodeIndex nodeIndex, JSValue value)
{
// Make sure we don't constant fold something that will produce values that contravene
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractValue.h b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
index c198b5e52..c60b792f6 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractValue.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractValue.h
@@ -284,6 +284,21 @@ struct AbstractValue {
checkConsistency();
}
+ bool filterByValue(JSValue value)
+ {
+ if (!validate(value))
+ return false;
+
+ if (!!value && value.isCell())
+ filter(StructureSet(value.asCell()->structure()));
+ else
+ filter(speculationFromValue(value));
+
+ m_value = value;
+
+ return true;
+ }
+
bool validateType(JSValue value) const
{
if (isTop())
@@ -327,6 +342,15 @@ struct AbstractValue {
return true;
}
+ Structure* bestProvenStructure() const
+ {
+ if (m_currentKnownStructure.hasSingleton())
+ return m_currentKnownStructure.singleton();
+ if (m_futurePossibleStructure.hasSingleton())
+ return m_futurePossibleStructure.singleton();
+ return 0;
+ }
+
void checkConsistency() const
{
if (!(m_type & SpecCell)) {
@@ -351,7 +375,7 @@ struct AbstractValue {
{
fprintf(out, "(%s, %s, ", speculationToString(m_type), arrayModesToString(m_arrayModes));
m_currentKnownStructure.dump(out);
- dataLog(", ");
+ dataLogF(", ");
m_futurePossibleStructure.dump(out);
if (!!m_value)
fprintf(out, ", %s", m_value.description());
diff --git a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
index 00b1109f6..b02e0112c 100644
--- a/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp
@@ -359,49 +359,49 @@ public:
}
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Arguments aliasing states:\n");
+ dataLogF("Arguments aliasing states:\n");
for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i) {
VariableAccessData* variableAccessData = &m_graph.m_variableAccessData[i];
if (!variableAccessData->isRoot())
continue;
- dataLog(" r%d(%s): ", variableAccessData->local(), m_graph.nameOfVariableAccessData(variableAccessData));
+ dataLogF(" r%d(%s): ", variableAccessData->local(), m_graph.nameOfVariableAccessData(variableAccessData));
if (variableAccessData->isCaptured())
- dataLog("Captured");
+ dataLogF("Captured");
else {
ArgumentsAliasingData& data =
m_argumentsAliasing.find(variableAccessData)->value;
bool first = true;
if (data.callContextIsValid()) {
if (!first)
- dataLog(", ");
- dataLog("Have Call Context: %p", data.callContext);
+ dataLogF(", ");
+ dataLogF("Have Call Context: %p", data.callContext);
first = false;
if (!m_createsArguments.contains(data.callContext))
- dataLog(" (Does Not Create Arguments)");
+ dataLogF(" (Does Not Create Arguments)");
}
if (data.argumentsAssignmentIsValid()) {
if (!first)
- dataLog(", ");
- dataLog("Arguments Assignment Is Valid");
+ dataLogF(", ");
+ dataLogF("Arguments Assignment Is Valid");
first = false;
}
if (!data.escapes) {
if (!first)
- dataLog(", ");
- dataLog("Does Not Escape");
+ dataLogF(", ");
+ dataLogF("Does Not Escape");
first = false;
}
if (!first)
- dataLog(", ");
+ dataLogF(", ");
if (data.isValid()) {
if (m_createsArguments.contains(data.callContext))
- dataLog("VALID");
+ dataLogF("VALID");
else
- dataLog("INVALID (due to argument creation)");
+ dataLogF("INVALID (due to argument creation)");
} else
- dataLog("INVALID (due to bad variable use)");
+ dataLogF("INVALID (due to bad variable use)");
}
- dataLog("\n");
+ dataLogF("\n");
}
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp
index 699902a16..3bfb6a43e 100644
--- a/Source/JavaScriptCore/dfg/DFGArrayMode.cpp
+++ b/Source/JavaScriptCore/dfg/DFGArrayMode.cpp
@@ -29,24 +29,52 @@
#if ENABLE(DFG_JIT)
#include "DFGAbstractValue.h"
+#include "DFGGraph.h"
namespace JSC { namespace DFG {
ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, bool makeSafe)
{
- switch (profile->observedArrayModes()) {
+ ArrayModes observed = profile->observedArrayModes();
+ switch (observed) {
case 0:
return ArrayMode(Array::Unprofiled);
case asArrayModes(NonArray):
if (action == Array::Write && !profile->mayInterceptIndexedAccesses())
- return ArrayMode(Array::Contiguous, Array::NonArray, Array::OutOfBounds, Array::Convert); // FIXME: we don't know whether to go to contiguous or array storage. We're making a static guess here. In future we should use exit profiling for this.
+ return ArrayMode(Array::Undecided, Array::NonArray, Array::OutOfBounds, Array::Convert);
return ArrayMode(Array::SelectUsingPredictions);
+
+ case asArrayModes(ArrayWithUndecided):
+ if (action == Array::Write)
+ return ArrayMode(Array::Undecided, Array::Array, Array::OutOfBounds, Array::Convert);
+ return ArrayMode(Array::Generic);
+
+ case asArrayModes(NonArray) | asArrayModes(ArrayWithUndecided):
+ if (action == Array::Write && !profile->mayInterceptIndexedAccesses())
+ return ArrayMode(Array::Undecided, Array::PossiblyArray, Array::OutOfBounds, Array::Convert);
+ return ArrayMode(Array::SelectUsingPredictions);
+
+ case asArrayModes(NonArrayWithInt32):
+ return ArrayMode(Array::Int32, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
+ case asArrayModes(ArrayWithInt32):
+ return ArrayMode(Array::Int32, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
+ case asArrayModes(NonArrayWithInt32) | asArrayModes(ArrayWithInt32):
+ return ArrayMode(Array::Int32, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
+
+ case asArrayModes(NonArrayWithDouble):
+ return ArrayMode(Array::Double, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
+ case asArrayModes(ArrayWithDouble):
+ return ArrayMode(Array::Double, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
+ case asArrayModes(NonArrayWithDouble) | asArrayModes(ArrayWithDouble):
+ return ArrayMode(Array::Double, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
+
case asArrayModes(NonArrayWithContiguous):
return ArrayMode(Array::Contiguous, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(ArrayWithContiguous):
return ArrayMode(Array::Contiguous, Array::Array, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous):
return ArrayMode(Array::Contiguous, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
+
case asArrayModes(NonArrayWithArrayStorage):
return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::AsIs).withProfile(profile, makeSafe);
case asArrayModes(NonArrayWithSlowPutArrayStorage):
@@ -62,36 +90,39 @@ ArrayMode ArrayMode::fromObserved(ArrayProfile* profile, Array::Action action, b
case asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(profile, makeSafe);
- case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage):
- return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::Convert).withProfile(profile, makeSafe);
- case asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage):
- return ArrayMode(Array::ArrayStorage, Array::Array, Array::Convert).withProfile(profile, makeSafe);
- case asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage):
- return ArrayMode(Array::ArrayStorage, Array::PossiblyArray, Array::Convert).withProfile(profile, makeSafe);
- case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous):
- if (action == Array::Write && !profile->mayInterceptIndexedAccesses())
- return ArrayMode(Array::Contiguous, Array::NonArray, Array::OutOfBounds, Array::Convert);
- return ArrayMode(Array::SelectUsingPredictions);
- case asArrayModes(NonArray) | asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage):
- case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage):
- if (action == Array::Write && !profile->mayInterceptIndexedAccesses())
- return ArrayMode(Array::ArrayStorage, Array::NonArray, Array::OutOfBounds, Array::Convert);
- return ArrayMode(Array::SelectUsingPredictions);
- case asArrayModes(NonArray) | asArrayModes(NonArrayWithSlowPutArrayStorage):
- case asArrayModes(NonArray) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage):
- if (action == Array::Write && !profile->mayInterceptIndexedAccesses())
- return ArrayMode(Array::SlowPutArrayStorage, Array::NonArray, Array::OutOfBounds, Array::Convert);
- return ArrayMode(Array::SelectUsingPredictions);
+
default:
- // We know that this is possibly a kind of array for which, though there is no
- // useful data in the array profile, we may be able to extract useful data from
- // the value profiles of the inputs. Hence, we leave it as undecided, and let
- // the predictions propagator decide later.
- return ArrayMode(Array::SelectUsingPredictions);
+ if ((observed & asArrayModes(NonArray)) && profile->mayInterceptIndexedAccesses())
+ return ArrayMode(Array::SelectUsingPredictions);
+
+ Array::Type type;
+ Array::Class arrayClass;
+
+ if (shouldUseSlowPutArrayStorage(observed))
+ type = Array::SlowPutArrayStorage;
+ else if (shouldUseFastArrayStorage(observed))
+ type = Array::ArrayStorage;
+ else if (shouldUseContiguous(observed))
+ type = Array::Contiguous;
+ else if (shouldUseDouble(observed))
+ type = Array::Double;
+ else if (shouldUseInt32(observed))
+ type = Array::Int32;
+ else
+ type = Array::Undecided;
+
+ if (observed & (asArrayModes(ArrayWithUndecided) | asArrayModes(ArrayWithInt32) | asArrayModes(ArrayWithDouble) | asArrayModes(ArrayWithContiguous) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage)))
+ arrayClass = Array::Array;
+ else if (observed & (asArrayModes(NonArray) | asArrayModes(NonArrayWithInt32) | asArrayModes(NonArrayWithDouble) | asArrayModes(NonArrayWithContiguous) | asArrayModes(NonArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage)))
+ arrayClass = Array::NonArray;
+ else
+ arrayClass = Array::PossiblyArray;
+
+ return ArrayMode(type, arrayClass, Array::Convert).withProfile(profile, makeSafe);
}
}
-ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index) const
+ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index, SpeculatedType value) const
{
if (!base || !index) {
// It can be that we had a legitimate arrayMode but no incoming predictions. That'll
@@ -104,52 +135,124 @@ ArrayMode ArrayMode::refine(SpeculatedType base, SpeculatedType index) const
if (!isInt32Speculation(index) || !isCellSpeculation(base))
return ArrayMode(Array::Generic);
- if (type() == Array::Unprofiled) {
- // If the indexing type wasn't recorded in the array profile but the values are
- // base=cell property=int, then we know that this access didn't execute.
+ switch (type()) {
+ case Array::Unprofiled:
return ArrayMode(Array::ForceExit);
- }
-
- if (type() != Array::SelectUsingPredictions)
+
+ case Array::Undecided:
+ if (!value)
+ return withType(Array::ForceExit);
+ if (isInt32Speculation(value))
+ return withTypeAndConversion(Array::Int32, Array::Convert);
+ if (isNumberSpeculation(value))
+ return withTypeAndConversion(Array::Double, Array::Convert);
+ return withTypeAndConversion(Array::Contiguous, Array::Convert);
+
+ case Array::Int32:
+ if (!value || isInt32Speculation(value))
+ return *this;
+ if (isNumberSpeculation(value))
+ return withTypeAndConversion(Array::Double, Array::Convert);
+ return withTypeAndConversion(Array::Contiguous, Array::Convert);
+
+ case Array::Double:
+ if (!value || isNumberSpeculation(value))
+ return *this;
+ return withTypeAndConversion(Array::Contiguous, Array::Convert);
+
+ case Array::SelectUsingPredictions:
+ if (isStringSpeculation(base))
+ return ArrayMode(Array::String);
+
+ if (isArgumentsSpeculation(base))
+ return ArrayMode(Array::Arguments);
+
+ if (isInt8ArraySpeculation(base))
+ return ArrayMode(Array::Int8Array);
+
+ if (isInt16ArraySpeculation(base))
+ return ArrayMode(Array::Int16Array);
+
+ if (isInt32ArraySpeculation(base))
+ return ArrayMode(Array::Int32Array);
+
+ if (isUint8ArraySpeculation(base))
+ return ArrayMode(Array::Uint8Array);
+
+ if (isUint8ClampedArraySpeculation(base))
+ return ArrayMode(Array::Uint8ClampedArray);
+
+ if (isUint16ArraySpeculation(base))
+ return ArrayMode(Array::Uint16Array);
+
+ if (isUint32ArraySpeculation(base))
+ return ArrayMode(Array::Uint32Array);
+
+ if (isFloat32ArraySpeculation(base))
+ return ArrayMode(Array::Float32Array);
+
+ if (isFloat64ArraySpeculation(base))
+ return ArrayMode(Array::Float64Array);
+
+ return ArrayMode(Array::Generic);
+
+ default:
return *this;
+ }
+}
+
+Structure* ArrayMode::originalArrayStructure(Graph& graph, const CodeOrigin& codeOrigin) const
+{
+ if (!isJSArrayWithOriginalStructure())
+ return 0;
- if (isStringSpeculation(base))
- return ArrayMode(Array::String);
-
- if (isArgumentsSpeculation(base))
- return ArrayMode(Array::Arguments);
-
- if (isInt8ArraySpeculation(base))
- return ArrayMode(Array::Int8Array);
-
- if (isInt16ArraySpeculation(base))
- return ArrayMode(Array::Int16Array);
-
- if (isInt32ArraySpeculation(base))
- return ArrayMode(Array::Int32Array);
-
- if (isUint8ArraySpeculation(base))
- return ArrayMode(Array::Uint8Array);
-
- if (isUint8ClampedArraySpeculation(base))
- return ArrayMode(Array::Uint8ClampedArray);
-
- if (isUint16ArraySpeculation(base))
- return ArrayMode(Array::Uint16Array);
-
- if (isUint32ArraySpeculation(base))
- return ArrayMode(Array::Uint32Array);
-
- if (isFloat32ArraySpeculation(base))
- return ArrayMode(Array::Float32Array);
-
- if (isFloat64ArraySpeculation(base))
- return ArrayMode(Array::Float64Array);
+ JSGlobalObject* globalObject = graph.globalObjectFor(codeOrigin);
- return ArrayMode(Array::Generic);
+ switch (type()) {
+ case Array::Int32:
+ return globalObject->originalArrayStructureForIndexingType(ArrayWithInt32);
+ case Array::Double:
+ return globalObject->originalArrayStructureForIndexingType(ArrayWithDouble);
+ case Array::Contiguous:
+ return globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous);
+ case Array::ArrayStorage:
+ return globalObject->originalArrayStructureForIndexingType(ArrayWithArrayStorage);
+ default:
+ CRASH();
+ return 0;
+ }
+}
+
+Structure* ArrayMode::originalArrayStructure(Graph& graph, Node& node) const
+{
+ return originalArrayStructure(graph, node.codeOrigin);
+}
+
+bool ArrayMode::alreadyChecked(Graph& graph, Node& node, AbstractValue& value, IndexingType shape) const
+{
+ switch (arrayClass()) {
+ case Array::OriginalArray:
+ return value.m_currentKnownStructure.hasSingleton()
+ && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape
+ && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray)
+ && graph.globalObjectFor(node.codeOrigin)->isOriginalArrayStructure(value.m_currentKnownStructure.singleton());
+
+ case Array::Array:
+ if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape | IsArray)))
+ return true;
+ return value.m_currentKnownStructure.hasSingleton()
+ && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape
+ && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray);
+
+ default:
+ if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape) | asArrayModes(shape | IsArray)))
+ return true;
+ return value.m_currentKnownStructure.hasSingleton()
+ && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape;
+ }
}
-bool ArrayMode::alreadyChecked(AbstractValue& value) const
+bool ArrayMode::alreadyChecked(Graph& graph, Node& node, AbstractValue& value) const
{
switch (type()) {
case Array::Generic:
@@ -161,44 +264,37 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const
case Array::String:
return speculationChecked(value.m_type, SpecString);
+ case Array::Int32:
+ return alreadyChecked(graph, node, value, Int32Shape);
+
+ case Array::Double:
+ return alreadyChecked(graph, node, value, DoubleShape);
+
case Array::Contiguous:
- if (isJSArray()) {
- if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithContiguous)))
- return true;
- return value.m_currentKnownStructure.hasSingleton()
- && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType())
- && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray);
- }
- if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithContiguous) | asArrayModes(ArrayWithContiguous)))
- return true;
- return value.m_currentKnownStructure.hasSingleton()
- && hasContiguous(value.m_currentKnownStructure.singleton()->indexingType());
+ return alreadyChecked(graph, node, value, ContiguousShape);
case Array::ArrayStorage:
- if (isJSArray()) {
- if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage)))
- return true;
- return value.m_currentKnownStructure.hasSingleton()
- && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType())
- && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray);
- }
- if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage)))
- return true;
- return value.m_currentKnownStructure.hasSingleton()
- && hasFastArrayStorage(value.m_currentKnownStructure.singleton()->indexingType());
+ return alreadyChecked(graph, node, value, ArrayStorageShape);
case Array::SlowPutArrayStorage:
- if (isJSArray()) {
+ switch (arrayClass()) {
+ case Array::OriginalArray:
+ CRASH();
+ return false;
+
+ case Array::Array:
if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage)))
return true;
return value.m_currentKnownStructure.hasSingleton()
&& hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType())
&& (value.m_currentKnownStructure.singleton()->indexingType() & IsArray);
+
+ default:
+ if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage)))
+ return true;
+ return value.m_currentKnownStructure.hasSingleton()
+ && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType());
}
- if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage)))
- return true;
- return value.m_currentKnownStructure.hasSingleton()
- && hasArrayStorage(value.m_currentKnownStructure.singleton()->indexingType());
case Array::Arguments:
return speculationChecked(value.m_type, SpecArguments);
@@ -232,6 +328,7 @@ bool ArrayMode::alreadyChecked(AbstractValue& value) const
case Array::SelectUsingPredictions:
case Array::Unprofiled:
+ case Array::Undecided:
break;
}
@@ -252,6 +349,12 @@ const char* arrayTypeToString(Array::Type type)
return "ForceExit";
case Array::String:
return "String";
+ case Array::Undecided:
+ return "Undecided";
+ case Array::Int32:
+ return "Int32";
+ case Array::Double:
+ return "Double";
case Array::Contiguous:
return "Contiguous";
case Array::ArrayStorage:
@@ -306,6 +409,8 @@ const char* arrayClassToString(Array::Class arrayClass)
const char* arraySpeculationToString(Array::Speculation speculation)
{
switch (speculation) {
+ case Array::SaneChain:
+ return "SaneChain";
case Array::InBounds:
return "InBounds";
case Array::ToHole:
diff --git a/Source/JavaScriptCore/dfg/DFGArrayMode.h b/Source/JavaScriptCore/dfg/DFGArrayMode.h
index 615965c92..0799868d6 100644
--- a/Source/JavaScriptCore/dfg/DFGArrayMode.h
+++ b/Source/JavaScriptCore/dfg/DFGArrayMode.h
@@ -33,9 +33,15 @@
#include "ArrayProfile.h"
#include "SpeculatedType.h"
-namespace JSC { namespace DFG {
+namespace JSC {
+struct CodeOrigin;
+
+namespace DFG {
+
+class Graph;
struct AbstractValue;
+struct Node;
// Use a namespace + enum instead of enum alone to avoid the namespace collision
// that would otherwise occur, since we say things like "Int8Array" and "JSArray"
@@ -52,7 +58,10 @@ enum Type {
ForceExit, // Implies that we have no idea how to execute this operation, so we should just give up.
Generic,
String,
-
+
+ Undecided,
+ Int32,
+ Double,
Contiguous,
ArrayStorage,
SlowPutArrayStorage,
@@ -77,11 +86,11 @@ enum Class {
};
enum Speculation {
- InBounds,
- ToHole,
- OutOfBounds
+ SaneChain, // In bounds and the array prototype chain is still intact, i.e. loading a hole doesn't require special treatment.
+ InBounds, // In bounds and not loading a hole.
+ ToHole, // Potentially storing to a hole.
+ OutOfBounds // Out-of-bounds access and anything can happen.
};
-
enum Conversion {
AsIs,
Convert
@@ -159,7 +168,7 @@ public:
mySpeculation = Array::InBounds;
if (isJSArray()) {
- if (profile->usesOriginalArrayStructures())
+ if (profile->usesOriginalArrayStructures() && benefitsFromOriginalArray())
myArrayClass = Array::OriginalArray;
else
myArrayClass = Array::Array;
@@ -169,15 +178,27 @@ public:
return ArrayMode(type(), myArrayClass, mySpeculation, conversion());
}
- ArrayMode refine(SpeculatedType base, SpeculatedType index) const;
+ ArrayMode withType(Array::Type type) const
+ {
+ return ArrayMode(type, arrayClass(), speculation(), conversion());
+ }
- bool alreadyChecked(AbstractValue&) const;
+ ArrayMode withTypeAndConversion(Array::Type type, Array::Conversion conversion) const
+ {
+ return ArrayMode(type, arrayClass(), speculation(), conversion);
+ }
+
+ ArrayMode refine(SpeculatedType base, SpeculatedType index, SpeculatedType value = SpecNone) const;
+
+ bool alreadyChecked(Graph&, Node&, AbstractValue&) const;
const char* toString() const;
bool usesButterfly() const
{
switch (type()) {
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
@@ -203,9 +224,20 @@ public:
return arrayClass() == Array::OriginalArray;
}
+ bool isSaneChain() const
+ {
+ return speculation() == Array::SaneChain;
+ }
+
bool isInBounds() const
{
- return speculation() == Array::InBounds;
+ switch (speculation()) {
+ case Array::SaneChain:
+ case Array::InBounds:
+ return true;
+ default:
+ return false;
+ }
}
bool mayStoreToHole() const
@@ -263,6 +295,7 @@ public:
case Array::Unprofiled:
case Array::ForceExit:
case Array::Generic:
+ case Array::Undecided:
return false;
default:
return true;
@@ -277,6 +310,8 @@ public:
case Array::ForceExit:
case Array::Generic:
return false;
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage:
@@ -286,6 +321,23 @@ public:
}
}
+ bool benefitsFromOriginalArray() const
+ {
+ switch (type()) {
+ case Array::Int32:
+ case Array::Double:
+ case Array::Contiguous:
+ case Array::ArrayStorage:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ // Returns 0 if this is not OriginalArray.
+ Structure* originalArrayStructure(Graph&, const CodeOrigin&) const;
+ Structure* originalArrayStructure(Graph&, Node&) const;
+
bool benefitsFromStructureCheck() const
{
switch (type()) {
@@ -309,6 +361,10 @@ public:
switch (type()) {
case Array::Generic:
return ALL_ARRAY_MODES;
+ case Array::Int32:
+ return arrayModesWithIndexingShape(Int32Shape);
+ case Array::Double:
+ return arrayModesWithIndexingShape(DoubleShape);
case Array::Contiguous:
return arrayModesWithIndexingShape(ContiguousShape);
case Array::ArrayStorage:
@@ -354,6 +410,8 @@ private:
}
}
+ bool alreadyChecked(Graph&, Node&, AbstractValue&, IndexingType shape) const;
+
union {
struct {
uint8_t type;
diff --git a/Source/JavaScriptCore/dfg/DFGBasicBlock.h b/Source/JavaScriptCore/dfg/DFGBasicBlock.h
index 441e2e75e..6f348f2e1 100644
--- a/Source/JavaScriptCore/dfg/DFGBasicBlock.h
+++ b/Source/JavaScriptCore/dfg/DFGBasicBlock.h
@@ -29,6 +29,7 @@
#if ENABLE(DFG_JIT)
#include "DFGAbstractValue.h"
+#include "DFGBranchDirection.h"
#include "DFGNode.h"
#include "Operands.h"
#include <wtf/OwnPtr.h>
@@ -46,6 +47,7 @@ struct BasicBlock : Vector<NodeIndex, 8> {
, cfaShouldRevisit(false)
, cfaFoundConstants(false)
, cfaDidFinish(true)
+ , cfaBranchDirection(InvalidBranchDirection)
#if !ASSERT_DISABLED
, isLinked(false)
#endif
@@ -105,6 +107,7 @@ struct BasicBlock : Vector<NodeIndex, 8> {
bool cfaShouldRevisit;
bool cfaFoundConstants;
bool cfaDidFinish;
+ BranchDirection cfaBranchDirection;
#if !ASSERT_DISABLED
bool isLinked;
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGBranchDirection.h b/Source/JavaScriptCore/dfg/DFGBranchDirection.h
new file mode 100644
index 000000000..8bbe3c635
--- /dev/null
+++ b/Source/JavaScriptCore/dfg/DFGBranchDirection.h
@@ -0,0 +1,88 @@
+/*
+ * 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 DFGBranchDirection_h
+#define DFGBranchDirection_h
+
+#include <wtf/Platform.h>
+
+#if ENABLE(DFG_JIT)
+
+namespace JSC { namespace DFG {
+
+enum BranchDirection {
+ // This is not a branch and so there is no branch direction, or
+ // the branch direction has yet to be set.
+ InvalidBranchDirection,
+
+ // The branch takes the true case.
+ TakeTrue,
+
+ // The branch takes the false case.
+ TakeFalse,
+
+ // For all we know, the branch could go either direction, so we
+ // have to assume the worst.
+ TakeBoth
+};
+
+static inline const char* branchDirectionToString(BranchDirection branchDirection)
+{
+ switch (branchDirection) {
+ case InvalidBranchDirection:
+ return "Invalid";
+ case TakeTrue:
+ return "TakeTrue";
+ case TakeFalse:
+ return "TakeFalse";
+ case TakeBoth:
+ return "TakeBoth";
+ }
+}
+
+static inline bool isKnownDirection(BranchDirection branchDirection)
+{
+ switch (branchDirection) {
+ case TakeTrue:
+ case TakeFalse:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool branchCondition(BranchDirection branchDirection)
+{
+ if (branchDirection == TakeTrue)
+ return true;
+ ASSERT(branchDirection == TakeFalse);
+ return false;
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGBranchDirection_h
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 70aa2b637..9b879b9e3 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -218,7 +218,7 @@ private:
if (operand == JSStack::Callee)
return getCallee();
-
+
// Is this an argument?
if (operandIsArgument(operand))
return getArgument(operand);
@@ -256,7 +256,7 @@ private:
m_inlineStackTop->m_lazyOperands.prediction(
LazyOperandValueProfileKey(m_currentIndex, node.local()));
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Lazy operand [@%u, bc#%u, r%d] prediction: %s\n",
+ dataLogF("Lazy operand [@%u, bc#%u, r%d] prediction: %s\n",
nodeIndex, m_currentIndex, node.local(), speculationToString(prediction));
#endif
node.variableAccessData()->predict(prediction);
@@ -876,7 +876,7 @@ private:
SpeculatedType prediction = m_inlineStackTop->m_profiledBlock->valueProfilePredictionForBytecodeOffset(bytecodeIndex);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Dynamic [@%u, bc#%u] prediction: %s\n", nodeIndex, bytecodeIndex, speculationToString(prediction));
+ dataLogF("Dynamic [@%u, bc#%u] prediction: %s\n", nodeIndex, bytecodeIndex, speculationToString(prediction));
#endif
return prediction;
@@ -905,10 +905,15 @@ private:
return getPrediction(m_graph.size(), m_currentProfilingIndex);
}
- ArrayMode getArrayMode(ArrayProfile* profile)
+ ArrayMode getArrayMode(ArrayProfile* profile, Array::Action action)
{
profile->computeUpdatedPrediction(m_inlineStackTop->m_codeBlock);
- return ArrayMode::fromObserved(profile, Array::Read, false);
+ return ArrayMode::fromObserved(profile, action, false);
+ }
+
+ ArrayMode getArrayMode(ArrayProfile* profile)
+ {
+ return getArrayMode(profile, Array::Read);
}
ArrayMode getArrayModeAndEmitChecks(ArrayProfile* profile, Array::Action action, NodeIndex base)
@@ -917,8 +922,8 @@ private:
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
if (m_inlineStackTop->m_profiledBlock->numberOfRareCaseProfiles())
- dataLog("Slow case profile for bc#%u: %u\n", m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter);
- dataLog("Array profile for bc#%u: %p%s%s, %u\n", m_currentIndex, profile->expectedStructure(), profile->structureIsPolymorphic() ? " (polymorphic)" : "", profile->mayInterceptIndexedAccesses() ? " (may intercept)" : "", profile->observedArrayModes());
+ dataLogF("Slow case profile for bc#%u: %u\n", m_currentIndex, m_inlineStackTop->m_profiledBlock->rareCaseProfileForBytecodeOffset(m_currentIndex)->m_counter);
+ dataLogF("Array profile for bc#%u: %p%s%s, %u\n", m_currentIndex, profile->expectedStructure(), profile->structureIsPolymorphic() ? " (polymorphic)" : "", profile->mayInterceptIndexedAccesses() ? " (may intercept)" : "", profile->observedArrayModes());
#endif
bool makeSafe =
@@ -962,13 +967,13 @@ private:
if (m_inlineStackTop->m_profiledBlock->likelyToTakeDeepestSlowCase(m_currentIndex)
|| m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow)) {
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Making ArithMul @%u take deepest slow case.\n", nodeIndex);
+ dataLogF("Making ArithMul @%u take deepest slow case.\n", nodeIndex);
#endif
m_graph[nodeIndex].mergeFlags(NodeMayOverflow | NodeMayNegZero);
} else if (m_inlineStackTop->m_profiledBlock->likelyToTakeSlowCase(m_currentIndex)
|| m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero)) {
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Making ArithMul @%u take faster slow case.\n", nodeIndex);
+ dataLogF("Making ArithMul @%u take faster slow case.\n", nodeIndex);
#endif
m_graph[nodeIndex].mergeFlags(NodeMayNegZero);
}
@@ -998,7 +1003,7 @@ private:
return nodeIndex;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Making %s @%u safe at bc#%u because special fast-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(m_graph[nodeIndex].op()), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->specialFastCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero));
+ dataLogF("Making %s @%u safe at bc#%u because special fast-case counter is at %u and exit profiles say %d, %d\n", Graph::opName(m_graph[nodeIndex].op()), nodeIndex, m_currentIndex, m_inlineStackTop->m_profiledBlock->specialFastCaseProfileForBytecodeOffset(m_currentIndex)->m_counter, m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, Overflow), m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, NegativeZero));
#endif
// FIXME: It might be possible to make this more granular. The DFG certainly can
@@ -1272,19 +1277,19 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn
m_inlineStackTop->m_profiledBlock, m_currentIndex);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("For call at @%lu bc#%u: ", m_graph.size(), m_currentIndex);
+ dataLogF("For call at @%lu bc#%u: ", m_graph.size(), m_currentIndex);
if (callLinkStatus.isSet()) {
if (callLinkStatus.couldTakeSlowPath())
- dataLog("could take slow path, ");
- dataLog("target = %p\n", callLinkStatus.callTarget());
+ dataLogF("could take slow path, ");
+ dataLogF("target = %p\n", callLinkStatus.callTarget());
} else
- dataLog("not set.\n");
+ dataLogF("not set.\n");
#endif
if (m_graph.isFunctionConstant(callTarget)) {
callType = ConstantFunction;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Call at [@%lu, bc#%u] has a function constant: %p, exec %p.\n",
+ dataLogF("Call at [@%lu, bc#%u] has a function constant: %p, exec %p.\n",
m_graph.size(), m_currentIndex,
m_graph.valueOfFunctionConstant(callTarget),
m_graph.valueOfFunctionConstant(callTarget)->executable());
@@ -1292,7 +1297,7 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn
} else if (m_graph.isInternalFunctionConstant(callTarget)) {
callType = ConstantInternalFunction;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Call at [@%lu, bc#%u] has an internal function constant: %p.\n",
+ dataLogF("Call at [@%lu, bc#%u] has an internal function constant: %p.\n",
m_graph.size(), m_currentIndex,
m_graph.valueOfInternalFunctionConstant(callTarget));
#endif
@@ -1300,14 +1305,14 @@ void ByteCodeParser::handleCall(Interpreter* interpreter, Instruction* currentIn
&& !m_inlineStackTop->m_exitProfile.hasExitSite(m_currentIndex, BadCache)) {
callType = LinkedFunction;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Call at [@%lu, bc#%u] is linked to: %p, exec %p.\n",
+ dataLogF("Call at [@%lu, bc#%u] is linked to: %p, exec %p.\n",
m_graph.size(), m_currentIndex, callLinkStatus.callTarget(),
callLinkStatus.callTarget()->executable());
#endif
} else {
callType = UnknownFunction;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Call at [@%lu, bc#%u] is has an unknown or ambiguous target.\n",
+ dataLogF("Call at [@%lu, bc#%u] is has an unknown or ambiguous target.\n",
m_graph.size(), m_currentIndex);
#endif
}
@@ -1432,7 +1437,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c
ASSERT(canInlineFunctionFor(codeBlock, kind));
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Inlining executable %p.\n", executable);
+ dataLogF("Inlining executable %p.\n", executable);
#endif
// Now we know without a doubt that we are committed to inlining. So begin the process
@@ -1517,7 +1522,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c
// caller. It doesn't need to be linked to, but it needs outgoing links.
if (!inlineStackEntry.m_unlinkedBlocks.isEmpty()) {
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Reascribing bytecode index of block %p from bc#%u to bc#%u (inline return case).\n", lastBlock, lastBlock->bytecodeBegin, m_currentIndex);
+ dataLogF("Reascribing bytecode index of block %p from bc#%u to bc#%u (inline return case).\n", lastBlock, lastBlock->bytecodeBegin, m_currentIndex);
#endif
// For debugging purposes, set the bytecodeBegin. Note that this doesn't matter
// for release builds because this block will never serve as a potential target
@@ -1529,7 +1534,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c
m_currentBlock = m_graph.m_blocks.last().get();
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Done inlining executable %p, continuing code generation at epilogue.\n", executable);
+ dataLogF("Done inlining executable %p, continuing code generation at epilogue.\n", executable);
#endif
return true;
}
@@ -1556,7 +1561,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c
// Need to create a new basic block for the continuation at the caller.
OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(nextOffset, m_numArguments, m_numLocals));
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Creating inline epilogue basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame));
+ dataLogF("Creating inline epilogue basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame));
#endif
m_currentBlock = block.get();
ASSERT(m_inlineStackTop->m_caller->m_blockLinkingTargets.isEmpty() || m_graph.m_blocks[m_inlineStackTop->m_caller->m_blockLinkingTargets.last()]->bytecodeBegin < nextOffset);
@@ -1568,7 +1573,7 @@ bool ByteCodeParser::handleInlining(bool usesResult, int callTarget, NodeIndex c
// At this point we return and continue to generate code for the caller, but
// in the new basic block.
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Done inlining executable %p, continuing code generation in new block.\n", executable);
+ dataLogF("Done inlining executable %p, continuing code generation in new block.\n", executable);
#endif
return true;
}
@@ -1649,7 +1654,12 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins
return false;
ArrayMode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile);
+ if (!arrayMode.isJSArray())
+ return false;
switch (arrayMode.type()) {
+ case Array::Undecided:
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage: {
NodeIndex arrayPush = addToGraph(ArrayPush, OpInfo(arrayMode.asWord()), OpInfo(prediction), get(registerOffset + argumentToOperand(0)), get(registerOffset + argumentToOperand(1)));
@@ -1669,7 +1679,11 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins
return false;
ArrayMode arrayMode = getArrayMode(m_currentInstruction[5].u.arrayProfile);
+ if (!arrayMode.isJSArray())
+ return false;
switch (arrayMode.type()) {
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage: {
NodeIndex arrayPop = addToGraph(ArrayPop, OpInfo(arrayMode.asWord()), OpInfo(prediction), get(registerOffset + argumentToOperand(0)));
@@ -1689,7 +1703,7 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins
int thisOperand = registerOffset + argumentToOperand(0);
int indexOperand = registerOffset + argumentToOperand(1);
- NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand));
+ NodeIndex charCode = addToGraph(StringCharCodeAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), getToInt32(indexOperand));
if (usesResult)
set(resultOperand, charCode);
@@ -1702,7 +1716,7 @@ bool ByteCodeParser::handleIntrinsic(bool usesResult, int resultOperand, Intrins
int thisOperand = registerOffset + argumentToOperand(0);
int indexOperand = registerOffset + argumentToOperand(1);
- NodeIndex charCode = addToGraph(StringCharAt, OpInfo(Array::String), get(thisOperand), getToInt32(indexOperand));
+ NodeIndex charCode = addToGraph(StringCharAt, OpInfo(ArrayMode(Array::String).asWord()), get(thisOperand), getToInt32(indexOperand));
if (usesResult)
set(resultOperand, charCode);
@@ -1754,7 +1768,7 @@ bool ByteCodeParser::handleConstantInternalFunction(
if (argumentCountIncludingThis == 2) {
setIntrinsicResult(
usesResult, resultOperand,
- addToGraph(NewArrayWithSize, get(registerOffset + argumentToOperand(1))));
+ addToGraph(NewArrayWithSize, OpInfo(ArrayWithUndecided), get(registerOffset + argumentToOperand(1))));
return true;
}
@@ -1762,7 +1776,7 @@ bool ByteCodeParser::handleConstantInternalFunction(
addVarArgChild(get(registerOffset + argumentToOperand(i)));
setIntrinsicResult(
usesResult, resultOperand,
- addToGraph(Node::VarArg, NewArray, OpInfo(0), OpInfo(0)));
+ addToGraph(Node::VarArg, NewArray, OpInfo(ArrayWithUndecided), OpInfo(0)));
return true;
}
@@ -2063,7 +2077,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
addToGraph(Jump, OpInfo(m_currentIndex));
else {
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Refusing to plant jump at limit %u because block %p is empty.\n", limit, m_currentBlock);
+ dataLogF("Refusing to plant jump at limit %u because block %p is empty.\n", limit, m_currentBlock);
#endif
}
return shouldContinueParsing;
@@ -2090,9 +2104,9 @@ bool ByteCodeParser::parseBlock(unsigned limit)
m_inlineStackTop->m_profiledBlock->valueProfileForBytecodeOffset(m_currentProfilingIndex);
profile->computeUpdatedPrediction();
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("[@%lu bc#%u]: profile %p: ", m_graph.size(), m_currentProfilingIndex, profile);
+ dataLogF("[@%lu bc#%u]: profile %p: ", m_graph.size(), m_currentProfilingIndex, profile);
profile->dump(WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
if (profile->m_singletonValueIsTop
|| !profile->m_singletonValue
@@ -2110,36 +2124,65 @@ bool ByteCodeParser::parseBlock(unsigned limit)
}
case op_create_this: {
- set(currentInstruction[1].u.operand, addToGraph(CreateThis, get(JSStack::Callee)));
+ int calleeOperand = currentInstruction[2].u.operand;
+ NodeIndex callee = get(calleeOperand);
+ bool alreadyEmitted = false;
+ if (m_graph[callee].op() == WeakJSConstant) {
+ JSCell* cell = m_graph[callee].weakConstant();
+ ASSERT(cell->inherits(&JSFunction::s_info));
+
+ JSFunction* function = jsCast<JSFunction*>(cell);
+ Structure* inheritorID = function->tryGetKnownInheritorID();
+ if (inheritorID) {
+ addToGraph(InheritorIDWatchpoint, OpInfo(function));
+ set(currentInstruction[1].u.operand, addToGraph(NewObject, OpInfo(inheritorID)));
+ alreadyEmitted = true;
+ }
+ }
+ if (!alreadyEmitted)
+ set(currentInstruction[1].u.operand, addToGraph(CreateThis, callee));
NEXT_OPCODE(op_create_this);
}
case op_new_object: {
- set(currentInstruction[1].u.operand, addToGraph(NewObject));
+ set(currentInstruction[1].u.operand, addToGraph(NewObject, OpInfo(m_inlineStackTop->m_codeBlock->globalObject()->emptyObjectStructure())));
NEXT_OPCODE(op_new_object);
}
case op_new_array: {
int startOperand = currentInstruction[2].u.operand;
int numOperands = currentInstruction[3].u.operand;
+ ArrayAllocationProfile* profile = currentInstruction[4].u.arrayAllocationProfile;
for (int operandIdx = startOperand; operandIdx < startOperand + numOperands; ++operandIdx)
addVarArgChild(get(operandIdx));
- set(currentInstruction[1].u.operand, addToGraph(Node::VarArg, NewArray, OpInfo(0), OpInfo(0)));
+ set(currentInstruction[1].u.operand, addToGraph(Node::VarArg, NewArray, OpInfo(profile->selectIndexingType()), OpInfo(0)));
NEXT_OPCODE(op_new_array);
}
case op_new_array_with_size: {
int lengthOperand = currentInstruction[2].u.operand;
- set(currentInstruction[1].u.operand, addToGraph(NewArrayWithSize, get(lengthOperand)));
+ ArrayAllocationProfile* profile = currentInstruction[3].u.arrayAllocationProfile;
+ set(currentInstruction[1].u.operand, addToGraph(NewArrayWithSize, OpInfo(profile->selectIndexingType()), get(lengthOperand)));
NEXT_OPCODE(op_new_array_with_size);
}
case op_new_array_buffer: {
int startConstant = currentInstruction[2].u.operand;
int numConstants = currentInstruction[3].u.operand;
+ ArrayAllocationProfile* profile = currentInstruction[4].u.arrayAllocationProfile;
NewArrayBufferData data;
data.startConstant = m_inlineStackTop->m_constantBufferRemap[startConstant];
data.numConstants = numConstants;
+ data.indexingType = profile->selectIndexingType();
+
+ // If this statement has never executed, we'll have the wrong indexing type in the profile.
+ for (int i = 0; i < numConstants; ++i) {
+ data.indexingType =
+ leastUpperBoundOfIndexingTypeAndValue(
+ data.indexingType,
+ m_codeBlock->constantBuffer(data.startConstant)[i]);
+ }
+
m_graph.m_newArrayBufferData.append(data);
set(currentInstruction[1].u.operand, addToGraph(NewArrayBuffer, OpInfo(&m_graph.m_newArrayBufferData.last())));
NEXT_OPCODE(op_new_array_buffer);
@@ -2150,6 +2193,22 @@ bool ByteCodeParser::parseBlock(unsigned limit)
NEXT_OPCODE(op_new_regexp);
}
+ case op_get_callee: {
+ ValueProfile* profile = currentInstruction[2].u.profile;
+ profile->computeUpdatedPrediction();
+ if (profile->m_singletonValueIsTop
+ || !profile->m_singletonValue
+ || !profile->m_singletonValue.isCell())
+ set(currentInstruction[1].u.operand, get(JSStack::Callee));
+ else {
+ ASSERT(profile->m_singletonValue.asCell()->inherits(&JSFunction::s_info));
+ NodeIndex actualCallee = get(JSStack::Callee);
+ addToGraph(CheckFunction, OpInfo(profile->m_singletonValue.asCell()), actualCallee);
+ set(currentInstruction[1].u.operand, addToGraph(WeakJSConstant, OpInfo(profile->m_singletonValue.asCell())));
+ }
+ NEXT_OPCODE(op_get_callee);
+ }
+
// === Bitwise operations ===
case op_bitand: {
@@ -3215,12 +3274,12 @@ void ByteCodeParser::processPhiStack()
VariableAccessData* dataForPhi = m_graph[entry.m_phi].variableAccessData();
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Handling phi entry for var %u, phi @%u.\n", entry.m_varNo, entry.m_phi);
+ dataLogF(" Handling phi entry for var %u, phi @%u.\n", entry.m_varNo, entry.m_phi);
#endif
for (size_t i = 0; i < predecessors.size(); ++i) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Dealing with predecessor block %u.\n", predecessors[i]);
+ dataLogF(" Dealing with predecessor block %u.\n", predecessors[i]);
#endif
BasicBlock* predecessorBlock = m_graph.m_blocks[predecessors[i]].get();
@@ -3230,7 +3289,7 @@ void ByteCodeParser::processPhiStack()
NodeIndex valueInPredecessor = var;
if (valueInPredecessor == NoNode) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Did not find node, adding phi.\n");
+ dataLogF(" Did not find node, adding phi.\n");
#endif
valueInPredecessor = insertPhiNode(OpInfo(newVariableAccessData(stackType == ArgumentPhiStack ? argumentToOperand(varNo) : static_cast<int>(varNo), false)), predecessorBlock);
@@ -3242,7 +3301,7 @@ void ByteCodeParser::processPhiStack()
phiStack.append(PhiStackEntry(predecessorBlock, valueInPredecessor, varNo));
} else if (m_graph[valueInPredecessor].op() == GetLocal) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Found GetLocal @%u.\n", valueInPredecessor);
+ dataLogF(" Found GetLocal @%u.\n", valueInPredecessor);
#endif
// We want to ensure that the VariableAccessDatas are identical between the
@@ -3254,7 +3313,7 @@ void ByteCodeParser::processPhiStack()
valueInPredecessor = m_graph[valueInPredecessor].child1().index();
} else {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Found @%u.\n", valueInPredecessor);
+ dataLogF(" Found @%u.\n", valueInPredecessor);
#endif
}
ASSERT(m_graph[valueInPredecessor].op() == SetLocal
@@ -3269,48 +3328,48 @@ void ByteCodeParser::processPhiStack()
Node* phiNode = &m_graph[entry.m_phi];
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Ref count of @%u = %u.\n", entry.m_phi, phiNode->refCount());
+ dataLogF(" Ref count of @%u = %u.\n", entry.m_phi, phiNode->refCount());
#endif
if (phiNode->refCount()) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Reffing @%u.\n", valueInPredecessor);
+ dataLogF(" Reffing @%u.\n", valueInPredecessor);
#endif
m_graph.ref(valueInPredecessor);
}
if (!phiNode->child1()) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Setting @%u->child1 = @%u.\n", entry.m_phi, valueInPredecessor);
+ dataLogF(" Setting @%u->child1 = @%u.\n", entry.m_phi, valueInPredecessor);
#endif
phiNode->children.setChild1(Edge(valueInPredecessor));
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Children of @%u: ", entry.m_phi);
+ dataLogF(" Children of @%u: ", entry.m_phi);
phiNode->dumpChildren(WTF::dataFile());
- dataLog(".\n");
+ dataLogF(".\n");
#endif
continue;
}
if (!phiNode->child2()) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Setting @%u->child2 = @%u.\n", entry.m_phi, valueInPredecessor);
+ dataLogF(" Setting @%u->child2 = @%u.\n", entry.m_phi, valueInPredecessor);
#endif
phiNode->children.setChild2(Edge(valueInPredecessor));
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Children of @%u: ", entry.m_phi);
+ dataLogF(" Children of @%u: ", entry.m_phi);
phiNode->dumpChildren(WTF::dataFile());
- dataLog(".\n");
+ dataLogF(".\n");
#endif
continue;
}
if (!phiNode->child3()) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Setting @%u->child3 = @%u.\n", entry.m_phi, valueInPredecessor);
+ dataLogF(" Setting @%u->child3 = @%u.\n", entry.m_phi, valueInPredecessor);
#endif
phiNode->children.setChild3(Edge(valueInPredecessor));
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Children of @%u: ", entry.m_phi);
+ dataLogF(" Children of @%u: ", entry.m_phi);
phiNode->dumpChildren(WTF::dataFile());
- dataLog(".\n");
+ dataLogF(".\n");
#endif
continue;
}
@@ -3318,7 +3377,7 @@ void ByteCodeParser::processPhiStack()
NodeIndex newPhi = insertPhiNode(OpInfo(dataForPhi), entry.m_block);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Splitting @%u, created @%u.\n", entry.m_phi, newPhi);
+ dataLogF(" Splitting @%u, created @%u.\n", entry.m_phi, newPhi);
#endif
phiNode = &m_graph[entry.m_phi]; // reload after vector resize
@@ -3329,17 +3388,17 @@ void ByteCodeParser::processPhiStack()
newPhiNode.children = phiNode->children;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Children of @%u: ", newPhi);
+ dataLogF(" Children of @%u: ", newPhi);
newPhiNode.dumpChildren(WTF::dataFile());
- dataLog(".\n");
+ dataLogF(".\n");
#endif
phiNode->children.initialize(newPhi, valueInPredecessor, NoNode);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Children of @%u: ", entry.m_phi);
+ dataLogF(" Children of @%u: ", entry.m_phi);
phiNode->dumpChildren(WTF::dataFile());
- dataLog(".\n");
+ dataLogF(".\n");
#endif
}
}
@@ -3366,7 +3425,7 @@ void ByteCodeParser::linkBlock(BasicBlock* block, Vector<BlockIndex>& possibleTa
case Jump:
node.setTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.takenBytecodeOffsetDuringParsing()));
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Linked basic block %p to %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex());
+ dataLogF("Linked basic block %p to %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex());
#endif
break;
@@ -3374,13 +3433,13 @@ void ByteCodeParser::linkBlock(BasicBlock* block, Vector<BlockIndex>& possibleTa
node.setTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.takenBytecodeOffsetDuringParsing()));
node.setNotTakenBlockIndex(m_graph.blockIndexForBytecodeOffset(possibleTargets, node.notTakenBytecodeOffsetDuringParsing()));
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Linked basic block %p to %p, #%u and %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex(), m_graph.m_blocks[node.notTakenBlockIndex()].get(), node.notTakenBlockIndex());
+ dataLogF("Linked basic block %p to %p, #%u and %p, #%u.\n", block, m_graph.m_blocks[node.takenBlockIndex()].get(), node.takenBlockIndex(), m_graph.m_blocks[node.notTakenBlockIndex()].get(), node.notTakenBlockIndex());
#endif
break;
default:
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Marking basic block %p as linked.\n", block);
+ dataLogF("Marking basic block %p as linked.\n", block);
#endif
break;
}
@@ -3489,9 +3548,9 @@ ByteCodeParser::InlineStackEntry::InlineStackEntry(
}
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Current captured variables: ");
+ dataLogF("Current captured variables: ");
inlineCallFrame.capturedVars.dump(WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
byteCodeParser->m_codeBlock->inlineCallFrames().append(inlineCallFrame);
@@ -3598,7 +3657,7 @@ void ByteCodeParser::parseCodeBlock()
CodeBlock* codeBlock = m_inlineStackTop->m_codeBlock;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Parsing code block %p. codeType = %s, captureCount = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n",
+ dataLogF("Parsing code block %p. codeType = %s, captureCount = %u, needsFullScopeChain = %s, needsActivation = %s, isStrictMode = %s\n",
codeBlock,
codeTypeToString(codeBlock->codeType()),
codeBlock->symbolTable() ? codeBlock->symbolTable()->captureCount() : 0,
@@ -3612,7 +3671,7 @@ void ByteCodeParser::parseCodeBlock()
// The maximum bytecode offset to go into the current basicblock is either the next jump target, or the end of the instructions.
unsigned limit = jumpTargetIndex < codeBlock->numberOfJumpTargets() ? codeBlock->jumpTarget(jumpTargetIndex) : codeBlock->instructions().size();
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Parsing bytecode with limit %p bc#%u at inline depth %u.\n", m_inlineStackTop->executable(), limit, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame));
+ dataLogF("Parsing bytecode with limit %p bc#%u at inline depth %u.\n", m_inlineStackTop->executable(), limit, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame));
#endif
ASSERT(m_currentIndex < limit);
@@ -3634,13 +3693,13 @@ void ByteCodeParser::parseCodeBlock()
// Change its bytecode begin and continue.
m_currentBlock = m_graph.m_blocks.last().get();
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Reascribing bytecode index of block %p from bc#%u to bc#%u (peephole case).\n", m_currentBlock, m_currentBlock->bytecodeBegin, m_currentIndex);
+ dataLogF("Reascribing bytecode index of block %p from bc#%u to bc#%u (peephole case).\n", m_currentBlock, m_currentBlock->bytecodeBegin, m_currentIndex);
#endif
m_currentBlock->bytecodeBegin = m_currentIndex;
} else {
OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(m_currentIndex, m_numArguments, m_numLocals));
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Creating basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame));
+ dataLogF("Creating basic block %p, #%zu for %p bc#%u at inline depth %u.\n", block.get(), m_graph.m_blocks.size(), m_inlineStackTop->executable(), m_currentIndex, CodeOrigin::inlineDepthForCallFrame(m_inlineStackTop->m_inlineCallFrame));
#endif
m_currentBlock = block.get();
ASSERT(m_inlineStackTop->m_unlinkedBlocks.isEmpty() || m_graph.m_blocks[m_inlineStackTop->m_unlinkedBlocks.last().m_blockIndex]->bytecodeBegin < m_currentIndex);
@@ -3697,14 +3756,14 @@ bool ByteCodeParser::parse()
linkBlocks(inlineStackEntry.m_unlinkedBlocks, inlineStackEntry.m_blockLinkingTargets);
m_graph.determineReachability();
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Processing local variable phis.\n");
+ dataLogF("Processing local variable phis.\n");
#endif
m_currentProfilingIndex = m_currentIndex;
processPhiStack<LocalPhiStack>();
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Processing argument phis.\n");
+ dataLogF("Processing argument phis.\n");
#endif
processPhiStack<ArgumentPhiStack>();
diff --git a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h
index a2570b7ea..8adde0598 100644
--- a/Source/JavaScriptCore/dfg/DFGCCallHelpers.h
+++ b/Source/JavaScriptCore/dfg/DFGCCallHelpers.h
@@ -210,6 +210,15 @@ public:
addCallArgument(arg3);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, GPRReg arg3)
+ {
+ resetCallArguments();
+ addCallArgument(GPRInfo::callFrameRegister);
+ addCallArgument(arg1);
+ addCallArgument(arg2);
+ addCallArgument(arg3);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, TrustedImmPtr arg3)
{
resetCallArguments();
@@ -268,6 +277,16 @@ public:
addCallArgument(arg4);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, TrustedImm32 arg4)
+ {
+ resetCallArguments();
+ addCallArgument(GPRInfo::callFrameRegister);
+ addCallArgument(arg1);
+ addCallArgument(arg2);
+ addCallArgument(arg3);
+ addCallArgument(arg4);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, TrustedImmPtr arg2, GPRReg arg3)
{
resetCallArguments();
@@ -317,6 +336,23 @@ public:
addCallArgument(arg4);
addCallArgument(arg5);
}
+
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2)
+ {
+ resetCallArguments();
+ addCallArgument(GPRInfo::callFrameRegister);
+ addCallArgument(arg1);
+ addCallArgument(arg2);
+ }
+
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
+ {
+ resetCallArguments();
+ addCallArgument(GPRInfo::callFrameRegister);
+ addCallArgument(arg1);
+ addCallArgument(arg2);
+ addCallArgument(arg3);
+ }
#endif // !NUMBER_OF_ARGUMENT_REGISTERS
// These methods are suitable for any calling convention that provides for
// at least 4 argument registers, e.g. X86_64, ARMv7.
@@ -463,6 +499,20 @@ public:
{
setupTwoStubArgs<FPRInfo::argumentFPR0, FPRInfo::argumentFPR1>(arg1, arg2);
}
+
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2)
+ {
+ moveDouble(arg1, FPRInfo::argumentFPR0);
+ move(arg2, GPRInfo::argumentGPR1);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
+ {
+ moveDouble(arg3, FPRInfo::argumentFPR0);
+ setupStubArguments(arg1, arg2);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
#elif CPU(ARM)
#if CPU(ARM_HARDFP)
ALWAYS_INLINE void setupArguments(FPRReg arg1)
@@ -485,6 +535,20 @@ public:
moveDouble(ARMRegisters::d2, FPRInfo::argumentFPR1);
}
}
+
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2)
+ {
+ moveDouble(arg1, FPRInfo::argumentFPR0);
+ move(arg2, GPRInfo::argumentGPR1);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
+ {
+ moveDouble(arg3, FPRInfo::argumentFPR0);
+ setupStubArguments(arg1, arg2);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
#else
ALWAYS_INLINE void setupArguments(FPRReg arg1)
{
@@ -496,6 +560,21 @@ public:
assembler().vmov(GPRInfo::argumentGPR0, GPRInfo::argumentGPR1, arg1);
assembler().vmov(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3, arg2);
}
+
+ ALWAYS_INLINE void setupArgumentsWithExecState(FPRReg arg1, GPRReg arg2)
+ {
+ move(arg2, GPRInfo::argumentGPR3);
+ assembler().vmov(GPRInfo::argumentGPR1, GPRInfo::argumentGPR2, arg1);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, FPRReg arg3)
+ {
+ setupStubArguments(arg1, arg2);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ assembler().vmov(GPRInfo::argumentGPR3, GPRInfo::nonArgGPR0, arg3);
+ poke(GPRInfo::nonArgGPR0);
+ }
#endif // CPU(ARM_HARDFP)
#else
#error "DFG JIT not supported on this platform."
@@ -635,6 +714,13 @@ public:
move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, GPRReg arg3)
+ {
+ setupTwoStubArgs<GPRInfo::argumentGPR1, GPRInfo::argumentGPR3>(arg1, arg3);
+ move(arg2, GPRInfo::argumentGPR2);
+ move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImm32 arg2, TrustedImmPtr arg3)
{
move(arg1, GPRInfo::argumentGPR1);
@@ -723,6 +809,12 @@ public:
setupArgumentsWithExecState(arg1, arg2, arg3);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, GPRReg arg3, TrustedImm32 arg4)
+ {
+ poke(arg4);
+ setupArgumentsWithExecState(arg1, arg2, arg3);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, TrustedImmPtr arg2, TrustedImm32 arg3, GPRReg arg4)
{
poke(arg4);
@@ -779,6 +871,12 @@ public:
setupArgumentsWithExecState(arg1, arg2, arg3);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4)
+ {
+ poke(arg4);
+ setupArgumentsWithExecState(arg1, arg2, arg3);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4, GPRReg arg5)
{
poke(arg5, 1);
@@ -786,6 +884,13 @@ public:
setupArgumentsWithExecState(arg1, arg2, arg3);
}
+ ALWAYS_INLINE void setupArgumentsWithExecState(GPRReg arg1, GPRReg arg2, TrustedImm32 arg3, GPRReg arg4, TrustedImm32 arg5)
+ {
+ poke(arg5, 1);
+ poke(arg4);
+ setupArgumentsWithExecState(arg1, arg2, arg3);
+ }
+
ALWAYS_INLINE void setupArgumentsWithExecState(TrustedImm32 arg1, GPRReg arg2, GPRReg arg3, GPRReg arg4, TrustedImmPtr arg5)
{
poke(arg5, 1);
diff --git a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp
index 24ea0b36f..1a88066d1 100644
--- a/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCFAPhase.cpp
@@ -78,47 +78,47 @@ private:
if (!block->cfaShouldRevisit)
return;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Block #%u (bc#%u):\n", blockIndex, block->bytecodeBegin);
+ dataLogF(" Block #%u (bc#%u):\n", blockIndex, block->bytecodeBegin);
#endif
m_state.beginBasicBlock(block);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" head vars: ");
+ dataLogF(" head vars: ");
dumpOperands(block->valuesAtHead, WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
for (unsigned i = 0; i < block->size(); ++i) {
NodeIndex nodeIndex = block->at(i);
if (!m_graph[nodeIndex].shouldGenerate())
continue;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" %s @%u: ", Graph::opName(m_graph[nodeIndex].op()), nodeIndex);
+ dataLogF(" %s @%u: ", Graph::opName(m_graph[nodeIndex].op()), nodeIndex);
m_state.dump(WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
if (!m_state.execute(i)) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Expect OSR exit.\n");
+ dataLogF(" Expect OSR exit.\n");
#endif
break;
}
}
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" tail regs: ");
+ dataLogF(" tail regs: ");
m_state.dump(WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
m_changed |= m_state.endBasicBlock(AbstractState::MergeToSuccessors);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" tail vars: ");
+ dataLogF(" tail vars: ");
dumpOperands(block->valuesAtTail, WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
}
void performForwardCFA()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("CFA [%u]\n", ++m_count);
+ dataLogF("CFA [%u]\n", ++m_count);
#endif
for (BlockIndex block = 0; block < m_graph.m_blocks.size(); ++block)
diff --git a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp
index e0d973992..d9ae4a274 100644
--- a/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp
@@ -66,7 +66,7 @@ public:
ASSERT(m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[0]
== blockIndex);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("CFGSimplify: Jump merge on Block #%u to Block #%u.\n",
+ dataLogF("CFGSimplify: Jump merge on Block #%u to Block #%u.\n",
blockIndex, m_graph.successor(block, 0));
#endif
if (extremeLogging)
@@ -76,14 +76,14 @@ public:
break;
} else {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Not jump merging on Block #%u to Block #%u because predecessors = ",
+ dataLogF("Not jump merging on Block #%u to Block #%u because predecessors = ",
blockIndex, m_graph.successor(block, 0));
for (unsigned i = 0; i < m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors.size(); ++i) {
if (i)
- dataLog(", ");
- dataLog("#%u", m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[i]);
+ dataLogF(", ");
+ dataLogF("#%u", m_graph.m_blocks[m_graph.successor(block, 0)]->m_predecessors[i]);
}
- dataLog(".\n");
+ dataLogF(".\n");
#endif
}
@@ -99,14 +99,13 @@ public:
case Branch: {
// Branch on constant -> jettison the not-taken block and merge.
- if (m_graph[m_graph[block->last()].child1()].hasConstant()) {
- bool condition =
- m_graph.valueOfJSConstant(m_graph[block->last()].child1().index()).toBoolean(m_graph.globalObjectFor(m_graph[block->last()].codeOrigin)->globalExec());
+ if (isKnownDirection(block->cfaBranchDirection)) {
+ bool condition = branchCondition(block->cfaBranchDirection);
BasicBlock* targetBlock = m_graph.m_blocks[
m_graph.successorForCondition(block, condition)].get();
if (targetBlock->m_predecessors.size() == 1) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("CFGSimplify: Known condition (%s) branch merge on Block #%u to Block #%u, jettisoning Block #%u.\n",
+ dataLogF("CFGSimplify: Known condition (%s) branch merge on Block #%u to Block #%u, jettisoning Block #%u.\n",
condition ? "true" : "false",
blockIndex, m_graph.successorForCondition(block, condition),
m_graph.successorForCondition(block, !condition));
@@ -119,7 +118,7 @@ public:
m_graph.successorForCondition(block, !condition));
} else {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("CFGSimplify: Known condition (%s) branch->jump conversion on Block #%u to Block #%u, jettisoning Block #%u.\n",
+ dataLogF("CFGSimplify: Known condition (%s) branch->jump conversion on Block #%u to Block #%u, jettisoning Block #%u.\n",
condition ? "true" : "false",
blockIndex, m_graph.successorForCondition(block, condition),
m_graph.successorForCondition(block, !condition));
@@ -153,13 +152,13 @@ public:
ASSERT(targetBlock->isReachable);
if (targetBlock->m_predecessors.size() == 1) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("CFGSimplify: Branch to same successor merge on Block #%u to Block #%u.\n",
+ dataLogF("CFGSimplify: Branch to same successor merge on Block #%u to Block #%u.\n",
blockIndex, targetBlockIndex);
#endif
mergeBlocks(blockIndex, targetBlockIndex, NoBlock);
} else {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("CFGSimplify: Branch->jump conversion to same successor on Block #%u to Block #%u.\n",
+ dataLogF("CFGSimplify: Branch->jump conversion to same successor on Block #%u to Block #%u.\n",
blockIndex, targetBlockIndex);
#endif
ASSERT(m_graph[block->last()].isTerminal());
@@ -180,7 +179,7 @@ public:
}
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Not branch simplifying on Block #%u because the successors differ and the condition is not known.\n",
+ dataLogF("Not branch simplifying on Block #%u because the successors differ and the condition is not known.\n",
blockIndex);
#endif
@@ -288,22 +287,22 @@ private:
if (child.op() != GetLocal)
return;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Considering GetLocal at @%u, local r%d.\n", edge.index(), child.local());
+ dataLogF(" Considering GetLocal at @%u, local r%d.\n", edge.index(), child.local());
#endif
if (child.variableAccessData()->isCaptured()) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" It's captured.\n");
+ dataLogF(" It's captured.\n");
#endif
return;
}
NodeIndex originalNodeIndex = block->variablesAtTail.operand(child.local());
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Dealing with original @%u.\n", originalNodeIndex);
+ dataLogF(" Dealing with original @%u.\n", originalNodeIndex);
#endif
ASSERT(originalNodeIndex != NoNode);
Node* originalNode = &m_graph[originalNodeIndex];
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Original has local r%d.\n", originalNode->local());
+ dataLogF(" Original has local r%d.\n", originalNode->local());
#endif
ASSERT(child.local() == originalNode->local());
if (changeRef)
@@ -328,14 +327,14 @@ private:
switch (originalNode->op()) {
case SetLocal: {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" It's a SetLocal.\n");
+ dataLogF(" It's a SetLocal.\n");
#endif
m_graph.changeIndex(edge, originalNode->child1().index(), changeRef);
break;
}
case GetLocal: {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" It's a GetLocal.\n");
+ dataLogF(" It's a GetLocal.\n");
#endif
m_graph.changeIndex(edge, originalNodeIndex, changeRef);
break;
@@ -343,7 +342,7 @@ private:
case Phi:
case SetArgument: {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" It's Phi/SetArgument.\n");
+ dataLogF(" It's Phi/SetArgument.\n");
#endif
// Keep the GetLocal!
break;
@@ -381,7 +380,7 @@ private:
Node& phiNode = m_graph[phiNodeIndex];
NodeIndex myNodeIndex = sourceBlock->variablesAtTail.operand(phiNode.local());
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Considering removing reference from phi @%u to @%u on local r%d:",
+ dataLogF("Considering removing reference from phi @%u to @%u on local r%d:",
phiNodeIndex, myNodeIndex, phiNode.local());
#endif
if (myNodeIndex == NoNode) {
@@ -395,7 +394,7 @@ private:
for (unsigned j = 0; j < AdjacencyList::Size; ++j)
removePotentiallyDeadPhiReference(myNodeIndex, phiNode, j, sourceBlock->isReachable);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("\n");
+ dataLogF("\n");
#endif
}
}
@@ -403,7 +402,7 @@ private:
void fixJettisonedPredecessors(BlockIndex blockIndex, BlockIndex jettisonedBlockIndex)
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Fixing predecessors and phis due to jettison of Block #%u from Block #%u.\n",
+ dataLogF("Fixing predecessors and phis due to jettison of Block #%u from Block #%u.\n",
jettisonedBlockIndex, blockIndex);
#endif
BasicBlock* jettisonedBlock = m_graph.m_blocks[jettisonedBlockIndex].get();
@@ -423,7 +422,7 @@ private:
if (phiNode.children.child(edgeIndex).indexUnchecked() != myNodeIndex)
return;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Removing reference at child %u.", edgeIndex);
+ dataLogF(" Removing reference at child %u.", edgeIndex);
#endif
if (changeRef && phiNode.shouldGenerate())
m_graph.deref(myNodeIndex);
@@ -711,7 +710,7 @@ private:
bool changeRef = phiNode.shouldGenerate();
OperandSubstitution substitution = substitutions.operand(phiNode.local());
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Performing operand substitution @%u -> @%u.\n",
+ dataLogF(" Performing operand substitution @%u -> @%u.\n",
substitution.oldChild, substitution.newChild);
#endif
if (!phiNode.child1())
@@ -730,6 +729,7 @@ private:
}
firstBlock->valuesAtTail = secondBlock->valuesAtTail;
+ firstBlock->cfaBranchDirection = secondBlock->cfaBranchDirection;
m_graph.m_blocks[secondBlockIndex].clear();
}
diff --git a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
index 19051c174..36acb2c21 100644
--- a/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp
@@ -79,7 +79,7 @@ private:
result++;
ASSERT(result <= m_indexInBlock);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" limit %u: ", result);
+ dataLogF(" limit %u: ", result);
#endif
return result;
}
@@ -372,7 +372,7 @@ private:
return NoNode;
}
- bool checkFunctionElimination(JSFunction* function, NodeIndex child1)
+ bool checkFunctionElimination(JSCell* function, NodeIndex child1)
{
for (unsigned i = endIndexForPureCSE(); i--;) {
NodeIndex index = m_currentBlock->at(i);
@@ -970,7 +970,7 @@ private:
return false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Replacing @%u -> @%u", m_compileIndex, replacement);
+ dataLogF(" Replacing @%u -> @%u", m_compileIndex, replacement);
#endif
Node& node = m_graph[m_compileIndex];
@@ -988,7 +988,7 @@ private:
void eliminate()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Eliminating @%u", m_compileIndex);
+ dataLogF(" Eliminating @%u", m_compileIndex);
#endif
Node& node = m_graph[m_compileIndex];
@@ -1029,7 +1029,7 @@ private:
return;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" %s @%u: ", Graph::opName(m_graph[m_compileIndex].op()), m_compileIndex);
+ dataLogF(" %s @%u: ", Graph::opName(m_graph[m_compileIndex].op()), m_compileIndex);
#endif
// NOTE: there are some nodes that we deliberately don't CSE even though we
@@ -1043,6 +1043,10 @@ private:
switch (node.op()) {
+ case Identity:
+ setReplacement(node.child1().index());
+ break;
+
// Handle the pure nodes. These nodes never have any side-effects.
case BitAnd:
case BitOr:
@@ -1313,7 +1317,7 @@ private:
m_lastSeen[node.op()] = m_indexInBlock;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("\n");
+ dataLogF("\n");
#endif
}
diff --git a/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h b/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h
index 46d5f44cb..03713b6c5 100644
--- a/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h
+++ b/Source/JavaScriptCore/dfg/DFGCallArrayAllocatorSlowPathGenerator.h
@@ -61,7 +61,7 @@ protected:
jit->silentSpill(m_plans[i]);
jit->callOperation(m_function, m_resultGPR, m_structure, m_size);
GPRReg canTrample = SpeculativeJIT::pickCanTrample(m_resultGPR);
- for (unsigned i = 0; i < m_plans.size(); ++i)
+ for (unsigned i = m_plans.size(); i--;)
jit->silentFill(m_plans[i], canTrample);
jit->m_jit.loadPtr(MacroAssembler::Address(m_resultGPR, JSObject::butterflyOffset()), m_storageGPR);
jumpTo(jit);
@@ -106,7 +106,7 @@ protected:
done.link(&jit->m_jit);
jit->callOperation(m_function, m_resultGPR, scratchGPR, m_sizeGPR);
GPRReg canTrample = SpeculativeJIT::pickCanTrample(m_resultGPR);
- for (unsigned i = 0; i < m_plans.size(); ++i)
+ for (unsigned i = m_plans.size(); i--;)
jit->silentFill(m_plans[i], canTrample);
jumpTo(jit);
}
diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
index 910c3d986..869751372 100644
--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
@@ -38,7 +38,7 @@ static inline void debugFail(CodeBlock* codeBlock, OpcodeID opcodeID, bool resul
{
ASSERT_UNUSED(result, !result);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]);
+ dataLogF("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]);
#else
UNUSED_PARAM(codeBlock);
UNUSED_PARAM(opcodeID);
@@ -51,10 +51,10 @@ static inline void debugFail(CodeBlock* codeBlock, OpcodeID opcodeID, Capability
ASSERT(result != CanCompile);
#if DFG_ENABLE(DEBUG_VERBOSE)
if (result == CannotCompile)
- dataLog("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]);
+ dataLogF("Cannot handle code block %p because of opcode %s.\n", codeBlock, opcodeNames[opcodeID]);
else {
ASSERT(result == ShouldProfile);
- dataLog("Cannot compile code block %p because of opcode %s, but inlining might be possible.\n", codeBlock, opcodeNames[opcodeID]);
+ dataLogF("Cannot compile code block %p because of opcode %s, but inlining might be possible.\n", codeBlock, opcodeNames[opcodeID]);
}
#else
UNUSED_PARAM(codeBlock);
diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.h b/Source/JavaScriptCore/dfg/DFGCapabilities.h
index 1f9778efe..a89c697f6 100644
--- a/Source/JavaScriptCore/dfg/DFGCapabilities.h
+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.h
@@ -116,6 +116,7 @@ inline CapabilityLevel canCompileOpcode(OpcodeID opcodeID, CodeBlock*, Instructi
case op_enter:
case op_convert_this:
case op_create_this:
+ case op_get_callee:
case op_bitand:
case op_bitor:
case op_bitxor:
diff --git a/Source/JavaScriptCore/dfg/DFGCommon.h b/Source/JavaScriptCore/dfg/DFGCommon.h
index c3726ed85..2c0556d60 100644
--- a/Source/JavaScriptCore/dfg/DFGCommon.h
+++ b/Source/JavaScriptCore/dfg/DFGCommon.h
@@ -135,11 +135,6 @@ enum NoResultTag { NoResult };
enum OptimizationFixpointState { BeforeFixpoint, FixpointNotConverged, FixpointConverged };
-inline bool shouldShowDisassembly()
-{
- return Options::showDisassembly() || Options::showDFGDisassembly();
-}
-
} } // namespace JSC::DFG
#endif // ENABLE(DFG_JIT)
@@ -150,6 +145,16 @@ namespace JSC { namespace DFG {
enum CapabilityLevel { CannotCompile, ShouldProfile, CanCompile, CapabilityLevelNotSet };
+// Unconditionally disable DFG disassembly support if the DFG is not compiled in.
+inline bool shouldShowDisassembly()
+{
+#if ENABLE(DFG_JIT)
+ return Options::showDisassembly() || Options::showDFGDisassembly();
+#else
+ return false;
+#endif
+}
+
} } // namespace JSC::DFG
#endif // DFGCommon_h
diff --git a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
index 43aa2c007..2221954b5 100644
--- a/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
@@ -33,6 +33,8 @@
#include "DFGGraph.h"
#include "DFGInsertionSet.h"
#include "DFGPhase.h"
+#include "GetByIdStatus.h"
+#include "PutByIdStatus.h"
namespace JSC { namespace DFG {
@@ -65,7 +67,7 @@ private:
bool foldConstants(BlockIndex blockIndex)
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Constant folding considering Block #%u.\n", blockIndex);
+ dataLogF("Constant folding considering Block #%u.\n", blockIndex);
#endif
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
bool changed = false;
@@ -109,14 +111,16 @@ private:
StructureAbstractValue& structureValue = value.m_futurePossibleStructure;
if (structureValue.isSubsetOf(set)
&& structureValue.hasSingleton()
- && isCellSpeculation(value.m_type))
+ && isCellSpeculation(value.m_type)) {
node.convertToStructureTransitionWatchpoint(structureValue.singleton());
+ changed = true;
+ }
break;
}
case CheckArray:
case Arrayify: {
- if (!node.arrayMode().alreadyChecked(m_state.forNode(node.child1())))
+ if (!node.arrayMode().alreadyChecked(m_graph, node, m_state.forNode(node.child1())))
break;
ASSERT(node.refCount() == 1);
node.setOpAndDefaultFlags(Phantom);
@@ -124,6 +128,206 @@ private:
break;
}
+ case CheckFunction: {
+ if (m_state.forNode(node.child1()).value() != node.function())
+ break;
+ node.setOpAndDefaultFlags(Phantom);
+ eliminated = true;
+ break;
+ }
+
+ case ConvertThis: {
+ if (!isObjectSpeculation(m_state.forNode(node.child1()).m_type))
+ break;
+ node.setOpAndDefaultFlags(Identity);
+ changed = true;
+ break;
+ }
+
+ case GetById:
+ case GetByIdFlush: {
+ CodeOrigin codeOrigin = node.codeOrigin;
+ NodeIndex child = node.child1().index();
+ unsigned identifierNumber = node.identifierNumber();
+
+ if (!isCellSpeculation(m_graph[child].prediction()))
+ break;
+
+ Structure* structure = m_state.forNode(child).bestProvenStructure();
+ if (!structure)
+ break;
+
+ bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
+
+ GetByIdStatus status = GetByIdStatus::computeFor(
+ globalData(), structure, codeBlock()->identifier(identifierNumber));
+
+ if (!status.isSimple())
+ break;
+
+ ASSERT(status.structureSet().size() == 1);
+ ASSERT(status.chain().isEmpty());
+ ASSERT(status.structureSet().singletonStructure() == structure);
+
+ // Now before we do anything else, push the CFA forward over the GetById
+ // and make sure we signal to the loop that it should continue and not
+ // do any eliminations.
+ m_state.execute(indexInBlock);
+ eliminated = true;
+
+ if (needsWatchpoint) {
+ ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
+ m_graph[child].ref();
+ Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(structure), child);
+ watchpoint.ref();
+ NodeIndex watchpointIndex = m_graph.size();
+ m_graph.append(watchpoint);
+ m_insertionSet.append(indexInBlock, watchpointIndex);
+ }
+
+ NodeIndex propertyStorageIndex;
+
+ m_graph[child].ref();
+ if (isInlineOffset(status.offset()))
+ propertyStorageIndex = child;
+ else {
+ Node getButterfly(GetButterfly, codeOrigin, child);
+ getButterfly.ref();
+ propertyStorageIndex = m_graph.size();
+ m_graph.append(getButterfly);
+ m_insertionSet.append(indexInBlock, propertyStorageIndex);
+ }
+
+ m_graph[nodeIndex].convertToGetByOffset(m_graph.m_storageAccessData.size(), propertyStorageIndex);
+
+ StorageAccessData storageAccessData;
+ storageAccessData.offset = indexRelativeToBase(status.offset());
+ storageAccessData.identifierNumber = identifierNumber;
+ m_graph.m_storageAccessData.append(storageAccessData);
+ break;
+ }
+
+ case PutById:
+ case PutByIdDirect: {
+ CodeOrigin codeOrigin = node.codeOrigin;
+ NodeIndex child = node.child1().index();
+ unsigned identifierNumber = node.identifierNumber();
+
+ Structure* structure = m_state.forNode(child).bestProvenStructure();
+ if (!structure)
+ break;
+
+ bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton();
+
+ PutByIdStatus status = PutByIdStatus::computeFor(
+ globalData(),
+ m_graph.globalObjectFor(codeOrigin),
+ structure,
+ codeBlock()->identifier(identifierNumber),
+ node.op() == PutByIdDirect);
+
+ if (!status.isSimpleReplace() && !status.isSimpleTransition())
+ break;
+
+ ASSERT(status.oldStructure() == structure);
+
+ // Now before we do anything else, push the CFA forward over the PutById
+ // and make sure we signal to the loop that it should continue and not
+ // do any eliminations.
+ m_state.execute(indexInBlock);
+ eliminated = true;
+
+ if (needsWatchpoint) {
+ ASSERT(m_state.forNode(child).m_futurePossibleStructure.isSubsetOf(StructureSet(structure)));
+ m_graph[child].ref();
+ Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(structure), child);
+ watchpoint.ref();
+ NodeIndex watchpointIndex = m_graph.size();
+ m_graph.append(watchpoint);
+ m_insertionSet.append(indexInBlock, watchpointIndex);
+ }
+
+ StructureTransitionData* transitionData = 0;
+ if (status.isSimpleTransition()) {
+ transitionData = m_graph.addStructureTransitionData(
+ StructureTransitionData(structure, status.newStructure()));
+
+ if (node.op() == PutById) {
+ if (!structure->storedPrototype().isNull()) {
+ addStructureTransitionCheck(
+ codeOrigin, indexInBlock,
+ structure->storedPrototype().asCell());
+ }
+
+ for (WriteBarrier<Structure>* it = status.structureChain()->head(); *it; ++it) {
+ JSValue prototype = (*it)->storedPrototype();
+ if (prototype.isNull())
+ continue;
+ ASSERT(prototype.isCell());
+ addStructureTransitionCheck(
+ codeOrigin, indexInBlock, prototype.asCell());
+ }
+ }
+ }
+
+ NodeIndex propertyStorageIndex;
+
+ m_graph[child].ref();
+ if (isInlineOffset(status.offset()))
+ propertyStorageIndex = child;
+ else if (status.isSimpleReplace() || structure->outOfLineCapacity() == status.newStructure()->outOfLineCapacity()) {
+ Node getButterfly(GetButterfly, codeOrigin, child);
+ getButterfly.ref();
+ propertyStorageIndex = m_graph.size();
+ m_graph.append(getButterfly);
+ m_insertionSet.append(indexInBlock, propertyStorageIndex);
+ } else if (!structure->outOfLineCapacity()) {
+ ASSERT(status.newStructure()->outOfLineCapacity());
+ ASSERT(!isInlineOffset(status.offset()));
+ Node allocateStorage(AllocatePropertyStorage, codeOrigin, OpInfo(transitionData), child);
+ allocateStorage.ref(); // Once for the use.
+ allocateStorage.ref(); // Twice because it's must-generate.
+ propertyStorageIndex = m_graph.size();
+ m_graph.append(allocateStorage);
+ m_insertionSet.append(indexInBlock, propertyStorageIndex);
+ } else {
+ ASSERT(structure->outOfLineCapacity());
+ ASSERT(status.newStructure()->outOfLineCapacity() > structure->outOfLineCapacity());
+ ASSERT(!isInlineOffset(status.offset()));
+
+ Node getButterfly(GetButterfly, codeOrigin, child);
+ getButterfly.ref();
+ NodeIndex getButterflyIndex = m_graph.size();
+ m_graph.append(getButterfly);
+ m_insertionSet.append(indexInBlock, getButterflyIndex);
+
+ m_graph[child].ref();
+ Node reallocateStorage(ReallocatePropertyStorage, codeOrigin, OpInfo(transitionData), child, getButterflyIndex);
+ reallocateStorage.ref(); // Once for the use.
+ reallocateStorage.ref(); // Twice because it's must-generate.
+ propertyStorageIndex = m_graph.size();
+ m_graph.append(reallocateStorage);
+ m_insertionSet.append(indexInBlock, propertyStorageIndex);
+ }
+
+ if (status.isSimpleTransition()) {
+ m_graph[child].ref();
+ Node putStructure(PutStructure, codeOrigin, OpInfo(transitionData), child);
+ putStructure.ref();
+ NodeIndex putStructureIndex = m_graph.size();
+ m_graph.append(putStructure);
+ m_insertionSet.append(indexInBlock, putStructureIndex);
+ }
+
+ m_graph[nodeIndex].convertToPutByOffset(m_graph.m_storageAccessData.size(), propertyStorageIndex);
+
+ StorageAccessData storageAccessData;
+ storageAccessData.offset = indexRelativeToBase(status.offset());
+ storageAccessData.identifierNumber = identifierNumber;
+ m_graph.m_storageAccessData.append(storageAccessData);
+ break;
+ }
+
default:
break;
}
@@ -213,6 +417,31 @@ private:
return changed;
}
+ void addStructureTransitionCheck(CodeOrigin codeOrigin, unsigned indexInBlock, JSCell* cell)
+ {
+ Node weakConstant(WeakJSConstant, codeOrigin, OpInfo(cell));
+ weakConstant.ref();
+ weakConstant.predict(speculationFromValue(cell));
+ NodeIndex weakConstantIndex = m_graph.size();
+ m_graph.append(weakConstant);
+ m_insertionSet.append(indexInBlock, weakConstantIndex);
+
+ if (cell->structure()->transitionWatchpointSetIsStillValid()) {
+ Node watchpoint(StructureTransitionWatchpoint, codeOrigin, OpInfo(cell->structure()), weakConstantIndex);
+ watchpoint.ref();
+ NodeIndex watchpointIndex = m_graph.size();
+ m_graph.append(watchpoint);
+ m_insertionSet.append(indexInBlock, watchpointIndex);
+ return;
+ }
+
+ Node check(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(cell->structure())), weakConstantIndex);
+ check.ref();
+ NodeIndex checkIndex = m_graph.size();
+ m_graph.append(check);
+ m_insertionSet.append(indexInBlock, checkIndex);
+ }
+
// This is necessary because the CFA may reach conclusions about constants based on its
// assumption that certain code must exit, but then those constants may lead future
// reexecutions of the CFA to believe that the same code will now no longer exit. Thus
@@ -225,7 +454,7 @@ private:
bool changed = false;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Painting unreachable code in Block #%u.\n", blockIndex);
+ dataLogF("Painting unreachable code in Block #%u.\n", blockIndex);
#endif
BasicBlock* block = m_graph.m_blocks[blockIndex].get();
m_state.beginBasicBlock(block);
diff --git a/Source/JavaScriptCore/dfg/DFGDisassembler.cpp b/Source/JavaScriptCore/dfg/DFGDisassembler.cpp
index cfbb936b8..654824196 100644
--- a/Source/JavaScriptCore/dfg/DFGDisassembler.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDisassembler.cpp
@@ -43,8 +43,8 @@ void Disassembler::dump(LinkBuffer& linkBuffer)
{
m_graph.m_dominators.computeIfNecessary(m_graph);
- dataLog("Generated JIT code for DFG CodeBlock %p, instruction count = %u:\n", m_graph.m_codeBlock, m_graph.m_codeBlock->instructionCount());
- dataLog(" Code at [%p, %p):\n", linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize());
+ dataLogF("Generated JIT code for DFG CodeBlock %p, instruction count = %u:\n", m_graph.m_codeBlock, m_graph.m_codeBlock->instructionCount());
+ dataLogF(" Code at [%p, %p):\n", linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize());
const char* prefix = " ";
const char* disassemblyPrefix = " ";
@@ -82,7 +82,7 @@ void Disassembler::dump(LinkBuffer& linkBuffer)
}
}
dumpDisassembly(disassemblyPrefix, linkBuffer, previousLabel, m_endOfMainPath, lastNodeIndex);
- dataLog("%s(End Of Main Path)\n", prefix);
+ dataLogF("%s(End Of Main Path)\n", prefix);
dumpDisassembly(disassemblyPrefix, linkBuffer, previousLabel, m_endOfCode, NoNode);
}
@@ -104,10 +104,7 @@ void Disassembler::dumpDisassembly(const char* prefix, LinkBuffer& linkBuffer, M
CodeLocationLabel end = linkBuffer.locationOf(currentLabel);
previousLabel = currentLabel;
ASSERT(bitwise_cast<uintptr_t>(end.executableAddress()) >= bitwise_cast<uintptr_t>(start.executableAddress()));
- if (tryToDisassemble(start, bitwise_cast<uintptr_t>(end.executableAddress()) - bitwise_cast<uintptr_t>(start.executableAddress()), prefixBuffer.get(), WTF::dataFile()))
- return;
-
- dataLog("%s disassembly not available for range %p...%p\n", prefixBuffer.get(), start.executableAddress(), end.executableAddress());
+ disassemble(start, bitwise_cast<uintptr_t>(end.executableAddress()) - bitwise_cast<uintptr_t>(start.executableAddress()), prefixBuffer.get(), WTF::dataFile());
}
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGDriver.cpp b/Source/JavaScriptCore/dfg/DFGDriver.cpp
index eb68fa344..8645c6dce 100644
--- a/Source/JavaScriptCore/dfg/DFGDriver.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDriver.cpp
@@ -72,7 +72,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo
return false;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("DFG compiling code block %p(%p) for executable %p, number of instructions = %u.\n", codeBlock, codeBlock->alternative(), codeBlock->ownerExecutable(), codeBlock->instructionCount());
+ dataLogF("DFG compiling code block %p(%p) for executable %p, number of instructions = %u.\n", codeBlock, codeBlock->alternative(), codeBlock->ownerExecutable(), codeBlock->instructionCount());
#endif
// Derive our set of must-handle values. The compilation must be at least conservative
@@ -119,7 +119,7 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo
dfg.m_fixpointState = FixpointNotConverged;
for (;; ++cnt) {
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("DFG beginning optimization fixpoint iteration #%u.\n", cnt);
+ dataLogF("DFG beginning optimization fixpoint iteration #%u.\n", cnt);
#endif
bool changed = false;
performCFA(dfg);
@@ -135,13 +135,13 @@ inline bool compile(CompileMode compileMode, ExecState* exec, CodeBlock* codeBlo
dfg.m_fixpointState = FixpointConverged;
performCSE(dfg);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("DFG optimization fixpoint converged in %u iterations.\n", cnt);
+ dataLogF("DFG optimization fixpoint converged in %u iterations.\n", cnt);
#endif
performVirtualRegisterAllocation(dfg);
GraphDumpMode modeForFinalValidate = DumpGraph;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Graph after optimization:\n");
+ dataLogF("Graph after optimization:\n");
dfg.dump();
modeForFinalValidate = DontDumpGraph;
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 5a76aa8df..1ba40def3 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -69,7 +69,7 @@ private:
NodeType op = node.op();
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" %s @%u: ", Graph::opName(op), m_compileIndex);
+ dataLogF(" %s @%u: ", Graph::opName(op), m_compileIndex);
#endif
switch (op) {
@@ -134,6 +134,17 @@ private:
m_graph[node.child2()].prediction()));
blessArrayOperation(node.child1(), node.child2(), 2);
+
+ Node* nodePtr = &m_graph[m_compileIndex];
+ ArrayMode arrayMode = nodePtr->arrayMode();
+ if (arrayMode.type() == Array::Double
+ && arrayMode.arrayClass() == Array::OriginalArray
+ && arrayMode.speculation() == Array::InBounds
+ && arrayMode.conversion() == Array::AsIs
+ && m_graph.globalObjectFor(nodePtr->codeOrigin)->arrayPrototypeChainIsSane()
+ && !(nodePtr->flags() & NodeUsedAsOther))
+ nodePtr->setArrayMode(arrayMode.withSpeculation(Array::SaneChain));
+
break;
}
case StringCharAt:
@@ -145,7 +156,30 @@ private:
}
case ArrayPush: {
+ // May need to refine the array mode in case the value prediction contravenes
+ // the array prediction. For example, we may have evidence showing that the
+ // array is in Int32 mode, but the value we're storing is likely to be a double.
+ // Then we should turn this into a conversion to Double array followed by the
+ // push. On the other hand, we absolutely don't want to refine based on the
+ // base prediction. If it has non-cell garbage in it, then we want that to be
+ // ignored. That's because ArrayPush can't handle any array modes that aren't
+ // array-related - so if refine() turned this into a "Generic" ArrayPush then
+ // that would break things.
+ node.setArrayMode(
+ node.arrayMode().refine(
+ m_graph[node.child1()].prediction() & SpecCell,
+ SpecInt32,
+ m_graph[node.child2()].prediction()));
blessArrayOperation(node.child1(), node.child2(), 2);
+
+ Node* nodePtr = &m_graph[m_compileIndex];
+ switch (nodePtr->arrayMode().type()) {
+ case Array::Double:
+ fixDoubleEdge(1);
+ break;
+ default:
+ break;
+ }
break;
}
@@ -236,7 +270,7 @@ private:
case ValueAdd: {
if (m_graph.addShouldSpeculateInteger(node))
break;
- if (!Node::shouldSpeculateNumber(m_graph[node.child1()], m_graph[node.child2()]))
+ if (!Node::shouldSpeculateNumberExpectingDefined(m_graph[node.child1()], m_graph[node.child2()]))
break;
fixDoubleEdge(0);
fixDoubleEdge(1);
@@ -262,7 +296,7 @@ private:
case ArithMin:
case ArithMax:
case ArithMod: {
- if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()])
+ if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()])
&& node.canSpeculateInteger())
break;
fixDoubleEdge(0);
@@ -279,7 +313,7 @@ private:
}
case ArithDiv: {
- if (Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child2()])
+ if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()])
&& node.canSpeculateInteger()) {
if (isX86())
break;
@@ -307,7 +341,7 @@ private:
}
case ArithAbs: {
- if (m_graph[node.child1()].shouldSpeculateInteger()
+ if (m_graph[node.child1()].shouldSpeculateIntegerForArithmetic()
&& node.canSpeculateInteger())
break;
fixDoubleEdge(0);
@@ -328,13 +362,17 @@ private:
node.setArrayMode(
node.arrayMode().refine(
m_graph[child1].prediction(),
- m_graph[child2].prediction()));
+ m_graph[child2].prediction(),
+ m_graph[child3].prediction()));
blessArrayOperation(child1, child2, 3);
Node* nodePtr = &m_graph[m_compileIndex];
switch (nodePtr->arrayMode().modeForPut().type()) {
+ case Array::Double:
+ fixDoubleEdge(2);
+ break;
case Array::Int8Array:
case Array::Int16Array:
case Array::Int32Array:
@@ -355,16 +393,29 @@ private:
break;
}
+ case NewArray: {
+ for (unsigned i = m_graph.varArgNumChildren(node); i--;) {
+ node.setIndexingType(
+ leastUpperBoundOfIndexingTypeAndType(
+ node.indexingType(), m_graph[m_graph.varArgChild(node, i)].prediction()));
+ }
+ if (node.indexingType() == ArrayWithDouble) {
+ for (unsigned i = m_graph.varArgNumChildren(node); i--;)
+ fixDoubleEdge(i);
+ }
+ break;
+ }
+
default:
break;
}
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
if (!(node.flags() & NodeHasVarArgs)) {
- dataLog("new children: ");
+ dataLogF("new children: ");
node.dumpChildren(WTF::dataFile());
}
- dataLog("\n");
+ dataLogF("\n");
#endif
}
@@ -384,29 +435,12 @@ private:
m_graph.ref(array);
+ Structure* structure = arrayMode.originalArrayStructure(m_graph, codeOrigin);
+
if (arrayMode.doesConversion()) {
if (index != NoNode)
m_graph.ref(index);
- Structure* structure = 0;
- if (arrayMode.isJSArrayWithOriginalStructure()) {
- JSGlobalObject* globalObject = m_graph.baselineCodeBlockFor(codeOrigin)->globalObject();
- switch (arrayMode.type()) {
- case Array::Contiguous:
- structure = globalObject->arrayStructure();
- if (structure->indexingType() != ArrayWithContiguous)
- structure = 0;
- break;
- case Array::ArrayStorage:
- structure = globalObject->arrayStructureWithArrayStorage();
- if (structure->indexingType() != ArrayWithArrayStorage)
- structure = 0;
- break;
- default:
- break;
- }
- }
-
if (structure) {
Node arrayify(ArrayifyToStructure, codeOrigin, OpInfo(structure), OpInfo(arrayMode.asWord()), array, index);
arrayify.ref();
@@ -421,11 +455,19 @@ private:
m_insertionSet.append(m_indexInBlock, arrayifyIndex);
}
} else {
- Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode.asWord()), array);
- checkArray.ref();
- NodeIndex checkArrayIndex = m_graph.size();
- m_graph.append(checkArray);
- m_insertionSet.append(m_indexInBlock, checkArrayIndex);
+ if (structure) {
+ Node checkStructure(CheckStructure, codeOrigin, OpInfo(m_graph.addStructureSet(structure)), array);
+ checkStructure.ref();
+ NodeIndex checkStructureIndex = m_graph.size();
+ m_graph.append(checkStructure);
+ m_insertionSet.append(m_indexInBlock, checkStructureIndex);
+ } else {
+ Node checkArray(CheckArray, codeOrigin, OpInfo(arrayMode.asWord()), array);
+ checkArray.ref();
+ NodeIndex checkArrayIndex = m_graph.size();
+ m_graph.append(checkArray);
+ m_insertionSet.append(m_indexInBlock, checkArrayIndex);
+ }
}
if (!storageCheck(arrayMode))
@@ -506,7 +548,7 @@ private:
NodeIndex resultIndex = (NodeIndex)m_graph.size();
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("(replacing @%u->@%u with @%u->@%u) ",
+ dataLogF("(replacing @%u->@%u with @%u->@%u) ",
m_compileIndex, edge.index(), m_compileIndex, resultIndex);
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp
index 8e8817f81..19587ba64 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp
+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp
@@ -98,7 +98,7 @@ const char* Graph::nameOfVariableAccessData(VariableAccessData* variableAccessDa
static void printWhiteSpace(unsigned amount)
{
while (amount-- > 0)
- dataLog(" ");
+ dataLogF(" ");
}
void Graph::dumpCodeOrigin(const char* prefix, NodeIndex prevNodeIndex, NodeIndex nodeIndex)
@@ -124,16 +124,16 @@ void Graph::dumpCodeOrigin(const char* prefix, NodeIndex prevNodeIndex, NodeInde
// Print the pops.
for (unsigned i = previousInlineStack.size(); i-- > indexOfDivergence;) {
- dataLog("%s", prefix);
+ dataLogF("%s", prefix);
printWhiteSpace(i * 2);
- dataLog("<-- %p\n", previousInlineStack[i].inlineCallFrame->executable.get());
+ dataLogF("<-- %p\n", previousInlineStack[i].inlineCallFrame->executable.get());
}
// Print the pushes.
for (unsigned i = indexOfDivergence; i < currentInlineStack.size(); ++i) {
- dataLog("%s", prefix);
+ dataLogF("%s", prefix);
printWhiteSpace(i * 2);
- dataLog("--> %p\n", currentInlineStack[i].inlineCallFrame->executable.get());
+ dataLogF("--> %p\n", currentInlineStack[i].inlineCallFrame->executable.get());
}
}
@@ -158,7 +158,7 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex)
if (mustGenerate)
--refCount;
- dataLog("%s", prefix);
+ dataLogF("%s", prefix);
printNodeWhiteSpace(node);
// Example/explanation of dataflow dump output
@@ -178,22 +178,22 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex)
// $# - the index in the CodeBlock of a constant { for numeric constants the value is displayed | for integers, in both decimal and hex }.
// id# - the index in the CodeBlock of an identifier { if codeBlock is passed to dump(), the string representation is displayed }.
// var# - the index of a var on the global object, used by GetGlobalVar/PutGlobalVar operations.
- dataLog("% 4d:%s<%c%u:", (int)nodeIndex, skipped ? " skipped " : " ", mustGenerate ? '!' : ' ', refCount);
+ dataLogF("% 4d:%s<%c%u:", (int)nodeIndex, skipped ? " skipped " : " ", mustGenerate ? '!' : ' ', refCount);
if (node.hasResult() && !skipped && node.hasVirtualRegister())
- dataLog("%u", node.virtualRegister());
+ dataLogF("%u", node.virtualRegister());
else
- dataLog("-");
- dataLog(">\t%s(", opName(op));
+ dataLogF("-");
+ dataLogF(">\t%s(", opName(op));
bool hasPrinted = false;
if (node.flags() & NodeHasVarArgs) {
for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++) {
if (hasPrinted)
- dataLog(", ");
+ dataLogF(", ");
else
hasPrinted = true;
if (!m_varArgChildren[childIdx])
continue;
- dataLog("%s@%u%s",
+ dataLogF("%s@%u%s",
useKindToString(m_varArgChildren[childIdx].useKind()),
m_varArgChildren[childIdx].index(),
speculationToAbbreviatedString(
@@ -201,19 +201,19 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex)
}
} else {
if (!!node.child1()) {
- dataLog("%s@%u%s",
+ dataLogF("%s@%u%s",
useKindToString(node.child1().useKind()),
node.child1().index(),
speculationToAbbreviatedString(at(node.child1()).prediction()));
}
if (!!node.child2()) {
- dataLog(", %s@%u%s",
+ dataLogF(", %s@%u%s",
useKindToString(node.child2().useKind()),
node.child2().index(),
speculationToAbbreviatedString(at(node.child2()).prediction()));
}
if (!!node.child3()) {
- dataLog(", %s@%u%s",
+ dataLogF(", %s@%u%s",
useKindToString(node.child3().useKind()),
node.child3().index(),
speculationToAbbreviatedString(at(node.child3()).prediction()));
@@ -222,47 +222,51 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex)
}
if (strlen(nodeFlagsAsString(node.flags()))) {
- dataLog("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags()));
+ dataLogF("%s%s", hasPrinted ? ", " : "", nodeFlagsAsString(node.flags()));
hasPrinted = true;
}
if (node.hasArrayMode()) {
- dataLog("%s%s", hasPrinted ? ", " : "", node.arrayMode().toString());
+ dataLogF("%s%s", hasPrinted ? ", " : "", node.arrayMode().toString());
hasPrinted = true;
}
if (node.hasVarNumber()) {
- dataLog("%svar%u", hasPrinted ? ", " : "", node.varNumber());
+ dataLogF("%svar%u", hasPrinted ? ", " : "", node.varNumber());
hasPrinted = true;
}
if (node.hasRegisterPointer()) {
- dataLog(
+ dataLogF(
"%sglobal%u(%p)", hasPrinted ? ", " : "",
globalObjectFor(node.codeOrigin)->findRegisterIndex(node.registerPointer()),
node.registerPointer());
hasPrinted = true;
}
if (node.hasIdentifier()) {
- dataLog("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).string().utf8().data());
+ dataLogF("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), m_codeBlock->identifier(node.identifierNumber()).string().utf8().data());
hasPrinted = true;
}
if (node.hasStructureSet()) {
for (size_t i = 0; i < node.structureSet().size(); ++i) {
- dataLog("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structureSet()[i], indexingTypeToString(node.structureSet()[i]->indexingType()));
+ dataLogF("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structureSet()[i], indexingTypeToString(node.structureSet()[i]->indexingType()));
hasPrinted = true;
}
}
if (node.hasStructure()) {
- dataLog("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structure(), indexingTypeToString(node.structure()->indexingType()));
+ dataLogF("%sstruct(%p: %s)", hasPrinted ? ", " : "", node.structure(), indexingTypeToString(node.structure()->indexingType()));
hasPrinted = true;
}
if (node.hasStructureTransitionData()) {
- dataLog("%sstruct(%p -> %p)", hasPrinted ? ", " : "", node.structureTransitionData().previousStructure, node.structureTransitionData().newStructure);
+ dataLogF("%sstruct(%p -> %p)", hasPrinted ? ", " : "", node.structureTransitionData().previousStructure, node.structureTransitionData().newStructure);
+ hasPrinted = true;
+ }
+ if (node.hasFunction()) {
+ dataLogF("%s%p", hasPrinted ? ", " : "", node.function());
hasPrinted = true;
}
if (node.hasStorageAccessData()) {
StorageAccessData& storageAccessData = m_storageAccessData[node.storageAccessDataIndex()];
- dataLog("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).string().utf8().data());
+ dataLogF("%sid%u{%s}", hasPrinted ? ", " : "", storageAccessData.identifierNumber, m_codeBlock->identifier(storageAccessData.identifierNumber).string().utf8().data());
- dataLog(", %lu", static_cast<unsigned long>(storageAccessData.offset));
+ dataLogF(", %lu", static_cast<unsigned long>(storageAccessData.offset));
hasPrinted = true;
}
ASSERT(node.hasVariableAccessData() == node.hasLocal());
@@ -270,100 +274,105 @@ void Graph::dump(const char* prefix, NodeIndex nodeIndex)
VariableAccessData* variableAccessData = node.variableAccessData();
int operand = variableAccessData->operand();
if (operandIsArgument(operand))
- dataLog("%sarg%u(%s)", hasPrinted ? ", " : "", operandToArgument(operand), nameOfVariableAccessData(variableAccessData));
+ dataLogF("%sarg%u(%s)", hasPrinted ? ", " : "", operandToArgument(operand), nameOfVariableAccessData(variableAccessData));
else
- dataLog("%sr%u(%s)", hasPrinted ? ", " : "", operand, nameOfVariableAccessData(variableAccessData));
+ dataLogF("%sr%u(%s)", hasPrinted ? ", " : "", operand, nameOfVariableAccessData(variableAccessData));
hasPrinted = true;
}
if (node.hasConstantBuffer()) {
if (hasPrinted)
- dataLog(", ");
- dataLog("%u:[", node.startConstant());
+ dataLogF(", ");
+ dataLogF("%u:[", node.startConstant());
for (unsigned i = 0; i < node.numConstants(); ++i) {
if (i)
- dataLog(", ");
- dataLog("%s", m_codeBlock->constantBuffer(node.startConstant())[i].description());
+ dataLogF(", ");
+ dataLogF("%s", m_codeBlock->constantBuffer(node.startConstant())[i].description());
}
- dataLog("]");
+ dataLogF("]");
hasPrinted = true;
}
+ if (node.hasIndexingType()) {
+ if (hasPrinted)
+ dataLogF(", ");
+ dataLogF("%s", indexingTypeToString(node.indexingType()));
+ }
if (op == JSConstant) {
- dataLog("%s$%u", hasPrinted ? ", " : "", node.constantNumber());
+ dataLogF("%s$%u", hasPrinted ? ", " : "", node.constantNumber());
JSValue value = valueOfJSConstant(nodeIndex);
- dataLog(" = %s", value.description());
+ dataLogF(" = %s", value.description());
hasPrinted = true;
}
if (op == WeakJSConstant) {
- dataLog("%s%p", hasPrinted ? ", " : "", node.weakConstant());
+ dataLogF("%s%p", hasPrinted ? ", " : "", node.weakConstant());
hasPrinted = true;
}
if (node.isBranch() || node.isJump()) {
- dataLog("%sT:#%u", hasPrinted ? ", " : "", node.takenBlockIndex());
+ dataLogF("%sT:#%u", hasPrinted ? ", " : "", node.takenBlockIndex());
hasPrinted = true;
}
if (node.isBranch()) {
- dataLog("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex());
+ dataLogF("%sF:#%u", hasPrinted ? ", " : "", node.notTakenBlockIndex());
hasPrinted = true;
}
- dataLog("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex);
+ dataLogF("%sbc#%u", hasPrinted ? ", " : "", node.codeOrigin.bytecodeIndex);
hasPrinted = true;
(void)hasPrinted;
- dataLog(")");
+ dataLogF(")");
if (!skipped) {
if (node.hasVariableAccessData())
- dataLog(" predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : "");
+ dataLogF(" predicting %s%s", speculationToString(node.variableAccessData()->prediction()), node.variableAccessData()->shouldUseDoubleFormat() ? ", forcing double" : "");
else if (node.hasHeapPrediction())
- dataLog(" predicting %s", speculationToString(node.getHeapPrediction()));
+ dataLogF(" predicting %s", speculationToString(node.getHeapPrediction()));
}
- dataLog("\n");
+ dataLogF("\n");
}
void Graph::dumpBlockHeader(const char* prefix, BlockIndex blockIndex, PhiNodeDumpMode phiNodeDumpMode)
{
BasicBlock* block = m_blocks[blockIndex].get();
- dataLog("%sBlock #%u (bc#%u): %s%s\n", prefix, (int)blockIndex, block->bytecodeBegin, block->isReachable ? "" : " (skipped)", block->isOSRTarget ? " (OSR target)" : "");
- dataLog("%s Predecessors:", prefix);
+ dataLogF("%sBlock #%u (bc#%u): %s%s\n", prefix, (int)blockIndex, block->bytecodeBegin, block->isReachable ? "" : " (skipped)", block->isOSRTarget ? " (OSR target)" : "");
+ dataLogF("%s Predecessors:", prefix);
for (size_t i = 0; i < block->m_predecessors.size(); ++i)
- dataLog(" #%u", block->m_predecessors[i]);
- dataLog("\n");
+ dataLogF(" #%u", block->m_predecessors[i]);
+ dataLogF("\n");
if (m_dominators.isValid()) {
- dataLog("%s Dominated by:", prefix);
+ dataLogF("%s Dominated by:", prefix);
for (size_t i = 0; i < m_blocks.size(); ++i) {
if (!m_dominators.dominates(i, blockIndex))
continue;
- dataLog(" #%lu", static_cast<unsigned long>(i));
+ dataLogF(" #%lu", static_cast<unsigned long>(i));
}
- dataLog("\n");
- dataLog("%s Dominates:", prefix);
+ dataLogF("\n");
+ dataLogF("%s Dominates:", prefix);
for (size_t i = 0; i < m_blocks.size(); ++i) {
if (!m_dominators.dominates(blockIndex, i))
continue;
- dataLog(" #%lu", static_cast<unsigned long>(i));
+ dataLogF(" #%lu", static_cast<unsigned long>(i));
}
- dataLog("\n");
+ dataLogF("\n");
}
- dataLog("%s Phi Nodes:", prefix);
+ dataLogF("%s Phi Nodes:", prefix);
for (size_t i = 0; i < block->phis.size(); ++i) {
NodeIndex phiNodeIndex = block->phis[i];
Node& phiNode = at(phiNodeIndex);
if (!phiNode.shouldGenerate() && phiNodeDumpMode == DumpLivePhisOnly)
continue;
- dataLog(" @%u->(", phiNodeIndex);
+ dataLogF(" @%u->(", phiNodeIndex);
if (phiNode.child1()) {
- dataLog("@%u", phiNode.child1().index());
+ dataLogF("@%u", phiNode.child1().index());
if (phiNode.child2()) {
- dataLog(", @%u", phiNode.child2().index());
+ dataLogF(", @%u", phiNode.child2().index());
if (phiNode.child3())
- dataLog(", @%u", phiNode.child3().index());
+ dataLogF(", @%u", phiNode.child3().index());
}
}
- dataLog(")%s", i + 1 < block->phis.size() ? "," : "");
+ dataLogF(")%s", i + 1 < block->phis.size() ? "," : "");
}
- dataLog("\n");
+ dataLogF("\n");
}
void Graph::dump()
@@ -374,29 +383,29 @@ void Graph::dump()
if (!block)
continue;
dumpBlockHeader("", b, DumpAllPhis);
- dataLog(" vars before: ");
+ dataLogF(" vars before: ");
if (block->cfaHasVisited)
dumpOperands(block->valuesAtHead, WTF::dataFile());
else
- dataLog("<empty>");
- dataLog("\n");
- dataLog(" var links: ");
+ dataLogF("<empty>");
+ dataLogF("\n");
+ dataLogF(" var links: ");
dumpOperands(block->variablesAtHead, WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
for (size_t i = 0; i < block->size(); ++i) {
dumpCodeOrigin("", lastNodeIndex, block->at(i));
dump("", block->at(i));
lastNodeIndex = block->at(i);
}
- dataLog(" vars after: ");
+ dataLogF(" vars after: ");
if (block->cfaHasVisited)
dumpOperands(block->valuesAtTail, WTF::dataFile());
else
- dataLog("<empty>");
- dataLog("\n");
- dataLog(" var links: ");
+ dataLogF("<empty>");
+ dataLogF("\n");
+ dataLogF(" var links: ");
dumpOperands(block->variablesAtTail, WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
}
}
@@ -451,7 +460,7 @@ void Graph::predictArgumentTypes()
at(m_arguments[arg]).variableAccessData()->predict(profile->computeUpdatedPrediction());
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Argument [%zu] prediction: %s\n", arg, speculationToString(at(m_arguments[arg]).variableAccessData()->prediction()));
+ dataLogF("Argument [%zu] prediction: %s\n", arg, speculationToString(at(m_arguments[arg]).variableAccessData()->prediction()));
#endif
}
}
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index 9fbb2df07..0c77b2959 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -220,7 +220,7 @@ public:
if (right.hasConstant())
return addImmediateShouldSpeculateInteger(add, left, right);
- return Node::shouldSpeculateInteger(left, right) && add.canSpeculateInteger();
+ return Node::shouldSpeculateIntegerExpectingDefined(left, right) && add.canSpeculateInteger();
}
bool mulShouldSpeculateInteger(Node& mul)
@@ -230,18 +230,13 @@ public:
Node& left = at(mul.child1());
Node& right = at(mul.child2());
- if (left.hasConstant())
- return mulImmediateShouldSpeculateInteger(mul, right, left);
- if (right.hasConstant())
- return mulImmediateShouldSpeculateInteger(mul, left, right);
-
- return Node::shouldSpeculateInteger(left, right) && mul.canSpeculateInteger() && !nodeMayOverflow(mul.arithNodeFlags());
+ return Node::shouldSpeculateIntegerForArithmetic(left, right) && mul.canSpeculateInteger();
}
bool negateShouldSpeculateInteger(Node& negate)
{
ASSERT(negate.op() == ArithNegate);
- return at(negate.child1()).shouldSpeculateInteger() && negate.canSpeculateInteger();
+ return at(negate.child1()).shouldSpeculateIntegerForArithmetic() && negate.canSpeculateInteger();
}
bool addShouldSpeculateInteger(NodeIndex nodeIndex)
@@ -493,6 +488,8 @@ public:
switch (node.arrayMode().type()) {
case Array::Generic:
return false;
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage:
return !node.arrayMode().isOutOfBounds();
@@ -712,7 +709,7 @@ private:
if (!immediateValue.isNumber())
return false;
- if (!variable.shouldSpeculateInteger())
+ if (!variable.shouldSpeculateIntegerExpectingDefined())
return false;
if (immediateValue.isInt32())
@@ -734,7 +731,7 @@ private:
if (!immediateValue.isInt32())
return false;
- if (!variable.shouldSpeculateInteger())
+ if (!variable.shouldSpeculateIntegerForArithmetic())
return false;
int32_t intImmediate = immediateValue.asInt32();
diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
index c7f941a7a..191aa7fe5 100644
--- a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
+++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
@@ -128,7 +128,7 @@ void JITCompiler::link(LinkBuffer& linkBuffer)
{
// Link the code, populate data in CodeBlock data structures.
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("JIT code for %p start at [%p, %p). Size = %zu.\n", m_codeBlock, linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize(), linkBuffer.debugSize());
+ dataLogF("JIT code for %p start at [%p, %p). Size = %zu.\n", m_codeBlock, linkBuffer.debugAddress(), static_cast<char*>(linkBuffer.debugAddress()) + linkBuffer.debugSize(), linkBuffer.debugSize());
#endif
// Link all calls out from the JIT code to their respective functions.
@@ -188,11 +188,12 @@ void JITCompiler::link(LinkBuffer& linkBuffer)
CallLinkInfo& info = m_codeBlock->callLinkInfo(i);
info.callType = m_jsCalls[i].m_callType;
info.isDFG = true;
- info.bytecodeIndex = m_jsCalls[i].m_codeOrigin.bytecodeIndex;
+ info.codeOrigin = m_jsCalls[i].m_codeOrigin;
linkBuffer.link(m_jsCalls[i].m_slowCall, FunctionPtr((m_globalData->getCTIStub(info.callType == CallLinkInfo::Construct ? linkConstructThunkGenerator : linkCallThunkGenerator)).code().executableAddress()));
info.callReturnLocation = linkBuffer.locationOfNearCall(m_jsCalls[i].m_slowCall);
info.hotPathBegin = linkBuffer.locationOf(m_jsCalls[i].m_targetToCheck);
info.hotPathOther = linkBuffer.locationOfNearCall(m_jsCalls[i].m_fastCall);
+ info.calleeGPR = static_cast<unsigned>(m_jsCalls[i].m_callee);
}
MacroAssemblerCodeRef osrExitThunk = globalData()->getCTIStub(osrExitGenerationThunkGenerator);
diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.h b/Source/JavaScriptCore/dfg/DFGJITCompiler.h
index c73934832..0bd88b788 100644
--- a/Source/JavaScriptCore/dfg/DFGJITCompiler.h
+++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.h
@@ -357,9 +357,9 @@ public:
m_propertyAccesses.append(record);
}
- void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin)
+ void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin)
{
- m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, codeOrigin));
+ m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, callee, codeOrigin));
}
void addWeakReference(JSCell* target)
@@ -440,11 +440,12 @@ private:
Vector<CallExceptionRecord> m_exceptionChecks;
struct JSCallRecord {
- JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin)
+ JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin)
: m_fastCall(fastCall)
, m_slowCall(slowCall)
, m_targetToCheck(targetToCheck)
, m_callType(callType)
+ , m_callee(callee)
, m_codeOrigin(codeOrigin)
{
}
@@ -453,6 +454,7 @@ private:
Call m_slowCall;
DataLabelPtr m_targetToCheck;
CallLinkInfo::CallType m_callType;
+ GPRReg m_callee;
CodeOrigin m_codeOrigin;
};
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index e66629ec4..18c8ce16f 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -62,6 +62,7 @@ struct StructureTransitionData {
struct NewArrayBufferData {
unsigned startConstant;
unsigned numConstants;
+ IndexingType indexingType;
};
// This type used in passing an immediate argument to Node constructor;
@@ -268,6 +269,26 @@ struct Node {
convertToStructureTransitionWatchpoint(structureSet().singletonStructure());
}
+ void convertToGetByOffset(unsigned storageAccessDataIndex, NodeIndex storage)
+ {
+ ASSERT(m_op == GetById || m_op == GetByIdFlush);
+ m_opInfo = storageAccessDataIndex;
+ children.setChild1(Edge(storage));
+ m_op = GetByOffset;
+ m_flags &= ~NodeClobbersWorld;
+ }
+
+ void convertToPutByOffset(unsigned storageAccessDataIndex, NodeIndex storage)
+ {
+ ASSERT(m_op == PutById || m_op == PutByIdDirect);
+ m_opInfo = storageAccessDataIndex;
+ children.setChild3(children.child2());
+ children.setChild2(children.child1());
+ children.setChild1(Edge(storage));
+ m_op = PutByOffset;
+ m_flags &= ~NodeClobbersWorld;
+ }
+
JSCell* weakConstant()
{
ASSERT(op() == WeakJSConstant);
@@ -433,6 +454,32 @@ struct Node {
return newArrayBufferData()->numConstants;
}
+ bool hasIndexingType()
+ {
+ switch (op()) {
+ case NewArray:
+ case NewArrayWithSize:
+ case NewArrayBuffer:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ IndexingType indexingType()
+ {
+ ASSERT(hasIndexingType());
+ if (op() == NewArrayBuffer)
+ return newArrayBufferData()->indexingType;
+ return m_opInfo;
+ }
+
+ void setIndexingType(IndexingType indexingType)
+ {
+ ASSERT(hasIndexingType());
+ m_opInfo = indexingType;
+ }
+
bool hasRegexpIndex()
{
return op() == NewRegexp;
@@ -518,6 +565,11 @@ struct Node {
return (m_flags & NodeResultMask) == NodeResultBoolean;
}
+ bool hasStorageResult()
+ {
+ return (m_flags & NodeResultMask) == NodeResultStorage;
+ }
+
bool isJump()
{
return op() == Jump;
@@ -649,15 +701,23 @@ struct Node {
return mergeSpeculation(m_opInfo2, prediction);
}
- bool hasFunctionCheckData()
+ bool hasFunction()
{
- return op() == CheckFunction;
+ switch (op()) {
+ case CheckFunction:
+ case InheritorIDWatchpoint:
+ return true;
+ default:
+ return false;
+ }
}
- JSFunction* function()
+ JSCell* function()
{
- ASSERT(hasFunctionCheckData());
- return reinterpret_cast<JSFunction*>(m_opInfo);
+ ASSERT(hasFunction());
+ JSCell* result = reinterpret_cast<JSFunction*>(m_opInfo);
+ ASSERT(JSValue(result).isFunction());
+ return result;
}
bool hasStructureTransitionData()
@@ -702,6 +762,7 @@ struct Node {
case StructureTransitionWatchpoint:
case ForwardStructureTransitionWatchpoint:
case ArrayifyToStructure:
+ case NewObject:
return true;
default:
return false;
@@ -922,16 +983,36 @@ struct Node {
return isInt32Speculation(prediction());
}
+ bool shouldSpeculateIntegerForArithmetic()
+ {
+ return isInt32SpeculationForArithmetic(prediction());
+ }
+
+ bool shouldSpeculateIntegerExpectingDefined()
+ {
+ return isInt32SpeculationExpectingDefined(prediction());
+ }
+
bool shouldSpeculateDouble()
{
return isDoubleSpeculation(prediction());
}
+ bool shouldSpeculateDoubleForArithmetic()
+ {
+ return isDoubleSpeculationForArithmetic(prediction());
+ }
+
bool shouldSpeculateNumber()
{
return isNumberSpeculation(prediction());
}
+ bool shouldSpeculateNumberExpectingDefined()
+ {
+ return isNumberSpeculationExpectingDefined(prediction());
+ }
+
bool shouldSpeculateBoolean()
{
return isBooleanSpeculation(prediction());
@@ -1037,11 +1118,31 @@ struct Node {
return op1.shouldSpeculateInteger() && op2.shouldSpeculateInteger();
}
+ static bool shouldSpeculateIntegerForArithmetic(Node& op1, Node& op2)
+ {
+ return op1.shouldSpeculateIntegerForArithmetic() && op2.shouldSpeculateIntegerForArithmetic();
+ }
+
+ static bool shouldSpeculateIntegerExpectingDefined(Node& op1, Node& op2)
+ {
+ return op1.shouldSpeculateIntegerExpectingDefined() && op2.shouldSpeculateIntegerExpectingDefined();
+ }
+
+ static bool shouldSpeculateDoubleForArithmetic(Node& op1, Node& op2)
+ {
+ return op1.shouldSpeculateDoubleForArithmetic() && op2.shouldSpeculateDoubleForArithmetic();
+ }
+
static bool shouldSpeculateNumber(Node& op1, Node& op2)
{
return op1.shouldSpeculateNumber() && op2.shouldSpeculateNumber();
}
+ static bool shouldSpeculateNumberExpectingDefined(Node& op1, Node& op2)
+ {
+ return op1.shouldSpeculateNumberExpectingDefined() && op2.shouldSpeculateNumberExpectingDefined();
+ }
+
static bool shouldSpeculateFinalObject(Node& op1, Node& op2)
{
return op1.shouldSpeculateFinalObject() && op2.shouldSpeculateFinalObject();
diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
index 480a7dab9..fb83c5a71 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.cpp
@@ -112,6 +112,12 @@ const char* nodeFlagsAsString(NodeFlags flags)
ptr.strcat("PureNum");
hasPrinted = true;
}
+ if (flags & NodeUsedAsOther) {
+ if (hasPrinted)
+ ptr.strcat("|");
+ ptr.strcat("UseAsOther");
+ hasPrinted = true;
+ }
}
if (flags & NodeMayOverflow) {
diff --git a/Source/JavaScriptCore/dfg/DFGNodeFlags.h b/Source/JavaScriptCore/dfg/DFGNodeFlags.h
index a897d0c4f..5e41bfc6b 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeFlags.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeFlags.h
@@ -52,14 +52,15 @@ namespace JSC { namespace DFG {
#define NodeMayOverflow 0x100
#define NodeMayNegZero 0x200
-#define NodeBackPropMask 0x1C00
+#define NodeBackPropMask 0x3C00
#define NodeUseBottom 0x000
#define NodeUsedAsNumber 0x400 // The result of this computation may be used in a context that observes fractional results.
#define NodeNeedsNegZero 0x800 // The result of this computation may be used in a context that observes -0.
-#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero)
-#define NodeUsedAsInt 0x1000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
+#define NodeUsedAsOther 0x1000 // The result of this computation may be used in a context that distinguishes between NaN and other things (like undefined).
+#define NodeUsedAsValue (NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther)
+#define NodeUsedAsInt 0x2000 // The result of this computation is known to be used in a context that prefers, but does not require, integer values.
-#define NodeDoesNotExit 0x2000 // This flag is negated to make it natural for the default to be that a node does exit.
+#define NodeDoesNotExit 0x4000 // This flag is negated to make it natural for the default to be that a node does exit.
typedef uint16_t NodeFlags;
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 624b1ae75..cbcdde660 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -43,6 +43,11 @@ namespace JSC { namespace DFG {
/* code block. */\
macro(WeakJSConstant, NodeResultJS | NodeDoesNotExit) \
\
+ /* Marker to indicate that an operation was optimized entirely and all that is left */\
+ /* is to make one node alias another. CSE will later usually eliminate this node, */\
+ /* though it may choose not to if it would corrupt predictions (very rare). */\
+ macro(Identity, NodeResultJS | NodeDoesNotExit) \
+ \
/* Nodes for handling functions (both as call and as construct). */\
macro(ConvertThis, NodeResultJS) \
macro(CreateThis, NodeResultJS) /* Note this is not MustGenerate since we're returning it anyway. */ \
@@ -154,6 +159,7 @@ namespace JSC { namespace DFG {
macro(GlobalVarWatchpoint, NodeMustGenerate) \
macro(PutGlobalVarCheck, NodeMustGenerate) \
macro(CheckFunction, NodeMustGenerate) \
+ macro(InheritorIDWatchpoint, NodeMustGenerate) \
\
/* Optimizations for array mutation. */\
macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
diff --git a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp
index b838c4fb4..ed13ed5b5 100644
--- a/Source/JavaScriptCore/dfg/DFGOSREntry.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSREntry.cpp
@@ -44,7 +44,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
ASSERT(!codeBlock->jitCodeMap());
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog("OSR in %p(%p) from bc#%u\n", codeBlock, codeBlock->alternative(), bytecodeIndex);
+ dataLogF("OSR in %p(%p) from bc#%u\n", codeBlock, codeBlock->alternative(), bytecodeIndex);
#endif
JSGlobalData* globalData = &exec->globalData();
@@ -52,7 +52,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
if (!entry) {
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog(" OSR failed because the entrypoint was optimized out.\n");
+ dataLogF(" OSR failed because the entrypoint was optimized out.\n");
#endif
return 0;
}
@@ -86,9 +86,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
for (size_t argument = 0; argument < entry->m_expectedValues.numberOfArguments(); ++argument) {
if (argument >= exec->argumentCountIncludingThis()) {
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog(" OSR failed because argument %zu was not passed, expected ", argument);
+ dataLogF(" OSR failed because argument %zu was not passed, expected ", argument);
entry->m_expectedValues.argument(argument).dump(WTF::dataFile());
- dataLog(".\n");
+ dataLogF(".\n");
#endif
return 0;
}
@@ -101,9 +101,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
if (!entry->m_expectedValues.argument(argument).validate(value)) {
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog(" OSR failed because argument %zu is %s, expected ", argument, value.description());
+ dataLogF(" OSR failed because argument %zu is %s, expected ", argument, value.description());
entry->m_expectedValues.argument(argument).dump(WTF::dataFile());
- dataLog(".\n");
+ dataLogF(".\n");
#endif
return 0;
}
@@ -113,7 +113,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
if (entry->m_localsForcedDouble.get(local)) {
if (!exec->registers()[local].jsValue().isNumber()) {
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog(" OSR failed because variable %zu is %s, expected number.\n", local, exec->registers()[local].jsValue().description());
+ dataLogF(" OSR failed because variable %zu is %s, expected number.\n", local, exec->registers()[local].jsValue().description());
#endif
return 0;
}
@@ -121,9 +121,9 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
}
if (!entry->m_expectedValues.local(local).validate(exec->registers()[local].jsValue())) {
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description());
+ dataLogF(" OSR failed because variable %zu is %s, expected ", local, exec->registers()[local].jsValue().description());
entry->m_expectedValues.local(local).dump(WTF::dataFile());
- dataLog(".\n");
+ dataLogF(".\n");
#endif
return 0;
}
@@ -138,13 +138,13 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
if (!globalData->interpreter->stack().grow(&exec->registers()[codeBlock->m_numCalleeRegisters])) {
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog(" OSR failed because stack growth failed.\n");
+ dataLogF(" OSR failed because stack growth failed.\n");
#endif
return 0;
}
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog(" OSR should succeed.\n");
+ dataLogF(" OSR should succeed.\n");
#endif
// 3) Perform data format conversions.
@@ -162,7 +162,7 @@ void* prepareOSREntry(ExecState* exec, CodeBlock* codeBlock, unsigned bytecodeIn
void* result = codeBlock->getJITCode().executableAddressAtOffset(entry->m_machineCodeOffset);
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog(" OSR returning machine code address %p.\n", result);
+ dataLogF(" OSR returning machine code address %p.\n", result);
#endif
return result;
diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp
index 2ce1c887b..c65443e29 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler.cpp
@@ -81,7 +81,7 @@ void compileOSRExit(ExecState* exec)
recovery = &codeBlock->speculationRecovery(exit.m_recoveryIndex - 1);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Generating OSR exit #%u (seq#%u, bc#%u, @%u, %s) for code block %p.\n", exitIndex, exit.m_streamIndex, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, exitKindToString(exit.m_kind), codeBlock);
+ dataLogF("Generating OSR exit #%u (seq#%u, bc#%u, @%u, %s) for code block %p.\n", exitIndex, exit.m_streamIndex, exit.m_codeOrigin.bytecodeIndex, exit.m_nodeIndex, exitKindToString(exit.m_kind), codeBlock);
#endif
{
diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
index df4f3c905..732e67c30 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler32_64.cpp
@@ -37,14 +37,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
{
// 1) Pro-forma stuff.
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("OSR exit for Node @%d (", (int)exit.m_nodeIndex);
+ dataLogF("OSR exit for Node @%d (", (int)exit.m_nodeIndex);
for (CodeOrigin codeOrigin = exit.m_codeOrigin; ; codeOrigin = codeOrigin.inlineCallFrame->caller) {
- dataLog("bc#%u", codeOrigin.bytecodeIndex);
+ dataLogF("bc#%u", codeOrigin.bytecodeIndex);
if (!codeOrigin.inlineCallFrame)
break;
- dataLog(" -> %p ", codeOrigin.inlineCallFrame->executable.get());
+ dataLogF(" -> %p ", codeOrigin.inlineCallFrame->executable.get());
}
- dataLog(") at JIT offset 0x%x ", m_jit.debugOffset());
+ dataLogF(") at JIT offset 0x%x ", m_jit.debugOffset());
dumpOperands(operands, WTF::dataFile());
#endif
#if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE)
@@ -762,7 +762,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
m_jit.jump(GPRInfo::regT2);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog(" -> %p\n", jumpTarget);
+ dataLogF(" -> %p\n", jumpTarget);
#endif
}
diff --git a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
index b278997ab..b83c0b3f5 100644
--- a/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
@@ -37,14 +37,14 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
{
// 1) Pro-forma stuff.
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("OSR exit for Node @%d (", (int)exit.m_nodeIndex);
+ dataLogF("OSR exit for Node @%d (", (int)exit.m_nodeIndex);
for (CodeOrigin codeOrigin = exit.m_codeOrigin; ; codeOrigin = codeOrigin.inlineCallFrame->caller) {
- dataLog("bc#%u", codeOrigin.bytecodeIndex);
+ dataLogF("bc#%u", codeOrigin.bytecodeIndex);
if (!codeOrigin.inlineCallFrame)
break;
- dataLog(" -> %p ", codeOrigin.inlineCallFrame->executable.get());
+ dataLogF(" -> %p ", codeOrigin.inlineCallFrame->executable.get());
}
- dataLog(") ");
+ dataLogF(") ");
dumpOperands(operands, WTF::dataFile());
#endif
#if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE)
@@ -138,7 +138,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
EncodedJSValue* bucket = exit.m_valueProfile.getSpecFailBucket(0);
#if DFG_ENABLE(VERBOSE_SPECULATION_FAILURE)
- dataLog(" (have exit profile, bucket %p) ", bucket);
+ dataLogF(" (have exit profile, bucket %p) ", bucket);
#endif
if (exit.m_jsValueSource.isAddress()) {
@@ -243,24 +243,24 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
}
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog(" ");
+ dataLogF(" ");
if (numberOfPoisonedVirtualRegisters)
- dataLog("Poisoned=%u ", numberOfPoisonedVirtualRegisters);
+ dataLogF("Poisoned=%u ", numberOfPoisonedVirtualRegisters);
if (numberOfDisplacedVirtualRegisters)
- dataLog("Displaced=%u ", numberOfDisplacedVirtualRegisters);
+ dataLogF("Displaced=%u ", numberOfDisplacedVirtualRegisters);
if (haveUnboxedInt32s)
- dataLog("UnboxedInt32 ");
+ dataLogF("UnboxedInt32 ");
if (haveUnboxedDoubles)
- dataLog("UnboxedDoubles ");
+ dataLogF("UnboxedDoubles ");
if (haveUInt32s)
- dataLog("UInt32 ");
+ dataLogF("UInt32 ");
if (haveFPRs)
- dataLog("FPR ");
+ dataLogF("FPR ");
if (haveConstants)
- dataLog("Constants ");
+ dataLogF("Constants ");
if (haveUndefined)
- dataLog("Undefined ");
- dataLog(" ");
+ dataLogF("Undefined ");
+ dataLogF(" ");
#endif
ScratchBuffer* scratchBuffer = m_jit.globalData()->scratchBufferForSize(sizeof(EncodedJSValue) * std::max(haveUInt32s ? 2u : 0u, numberOfPoisonedVirtualRegisters + (numberOfDisplacedVirtualRegisters <= GPRInfo::numberOfRegisters ? 0 : numberOfDisplacedVirtualRegisters)));
@@ -710,7 +710,7 @@ void OSRExitCompiler::compileExit(const OSRExit& exit, const Operands<ValueRecov
m_jit.jump(GPRInfo::regT1);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("-> %p\n", jumpTarget);
+ dataLogF("-> %p\n", jumpTarget);
#endif
}
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index 0e45e230c..909c657a1 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -27,9 +27,9 @@
#include "DFGOperations.h"
#include "Arguments.h"
-#include "ButterflyInlineMethods.h"
+#include "ButterflyInlines.h"
#include "CodeBlock.h"
-#include "CopiedSpaceInlineMethods.h"
+#include "CopiedSpaceInlines.h"
#include "DFGOSRExit.h"
#include "DFGRepatch.h"
#include "DFGThunks.h"
@@ -302,12 +302,12 @@ JSCell* DFG_OPERATION operationCreateThis(ExecState* exec, JSCell* constructor)
return constructEmptyObject(exec, jsCast<JSFunction*>(constructor)->cachedInheritorID(exec));
}
-JSCell* DFG_OPERATION operationNewObject(ExecState* exec)
+JSCell* DFG_OPERATION operationNewObject(ExecState* exec, Structure* structure)
{
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
- return constructEmptyObject(exec);
+ return constructEmptyObject(exec, structure);
}
EncodedJSValue DFG_OPERATION operationValueAdd(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
@@ -580,6 +580,40 @@ void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState* exec,
array, exec, Identifier::from(exec, index), JSValue::decode(encodedValue), slot);
}
+void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsStrict(ExecState* exec, JSObject* array, int32_t index, double value)
+{
+ JSGlobalData* globalData = &exec->globalData();
+ NativeCallFrameTracer tracer(globalData, exec);
+
+ JSValue jsValue = JSValue(JSValue::EncodeAsDouble, value);
+
+ if (index >= 0) {
+ array->putByIndexInline(exec, index, jsValue, true);
+ return;
+ }
+
+ PutPropertySlot slot(true);
+ array->methodTable()->put(
+ array, exec, Identifier::from(exec, index), jsValue, slot);
+}
+
+void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsNonStrict(ExecState* exec, JSObject* array, int32_t index, double value)
+{
+ JSGlobalData* globalData = &exec->globalData();
+ NativeCallFrameTracer tracer(globalData, exec);
+
+ JSValue jsValue = JSValue(JSValue::EncodeAsDouble, value);
+
+ if (index >= 0) {
+ array->putByIndexInline(exec, index, jsValue, false);
+ return;
+ }
+
+ PutPropertySlot slot(false);
+ array->methodTable()->put(
+ array, exec, Identifier::from(exec, index), jsValue, slot);
+}
+
EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue encodedValue, JSArray* array)
{
JSGlobalData* globalData = &exec->globalData();
@@ -589,6 +623,15 @@ EncodedJSValue DFG_OPERATION operationArrayPush(ExecState* exec, EncodedJSValue
return JSValue::encode(jsNumber(array->length()));
}
+EncodedJSValue DFG_OPERATION operationArrayPushDouble(ExecState* exec, double value, JSArray* array)
+{
+ JSGlobalData* globalData = &exec->globalData();
+ NativeCallFrameTracer tracer(globalData, exec);
+
+ array->push(exec, JSValue(JSValue::EncodeAsDouble, value));
+ return JSValue::encode(jsNumber(array->length()));
+}
+
EncodedJSValue DFG_OPERATION operationArrayPop(ExecState* exec, JSArray* array)
{
JSGlobalData* globalData = &exec->globalData();
@@ -1019,14 +1062,14 @@ char* DFG_OPERATION operationLinkConstruct(ExecState* execCallee)
return linkFor(execCallee, CodeForConstruct);
}
-inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind)
+inline char* virtualForWithFunction(ExecState* execCallee, CodeSpecializationKind kind, JSCell*& calleeAsFunctionCell)
{
ExecState* exec = execCallee->callerFrame();
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
JSValue calleeAsValue = execCallee->calleeAsValue();
- JSCell* calleeAsFunctionCell = getJSFunction(calleeAsValue);
+ calleeAsFunctionCell = getJSFunction(calleeAsValue);
if (UNLIKELY(!calleeAsFunctionCell))
return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind));
@@ -1044,6 +1087,56 @@ inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind)
return reinterpret_cast<char*>(executable->generatedJITCodeWithArityCheckFor(kind).executableAddress());
}
+inline char* virtualFor(ExecState* execCallee, CodeSpecializationKind kind)
+{
+ JSCell* calleeAsFunctionCellIgnored;
+ return virtualForWithFunction(execCallee, kind, calleeAsFunctionCellIgnored);
+}
+
+static bool attemptToOptimizeClosureCall(ExecState* execCallee, JSCell* calleeAsFunctionCell, CallLinkInfo& callLinkInfo)
+{
+ if (!calleeAsFunctionCell)
+ return false;
+
+ JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell);
+ JSFunction* oldCallee = callLinkInfo.callee.get();
+
+ if (!oldCallee
+ || oldCallee->structure() != callee->structure()
+ || oldCallee->executable() != callee->executable())
+ return false;
+
+ ASSERT(callee->executable()->hasJITCodeForCall());
+ MacroAssemblerCodePtr codePtr = callee->executable()->generatedJITCodeForCall().addressForCall();
+
+ CodeBlock* codeBlock;
+ if (callee->executable()->isHostFunction())
+ codeBlock = 0;
+ else {
+ codeBlock = &jsCast<FunctionExecutable*>(callee->executable())->generatedBytecodeForCall();
+ if (execCallee->argumentCountIncludingThis() < static_cast<size_t>(codeBlock->numParameters()))
+ return false;
+ }
+
+ dfgLinkClosureCall(
+ execCallee, callLinkInfo, codeBlock,
+ callee->structure(), callee->executable(), codePtr);
+
+ return true;
+}
+
+char* DFG_OPERATION operationLinkClosureCall(ExecState* execCallee)
+{
+ JSCell* calleeAsFunctionCell;
+ char* result = virtualForWithFunction(execCallee, CodeForCall, calleeAsFunctionCell);
+ CallLinkInfo& callLinkInfo = execCallee->callerFrame()->codeBlock()->getCallLinkInfo(execCallee->returnPC());
+
+ if (!attemptToOptimizeClosureCall(execCallee, calleeAsFunctionCell, callLinkInfo))
+ dfgLinkSlowFor(execCallee, callLinkInfo, CodeForCall);
+
+ return result;
+}
+
char* DFG_OPERATION operationVirtualCall(ExecState* execCallee)
{
return virtualFor(execCallee, CodeForCall);
@@ -1327,30 +1420,36 @@ char* DFG_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState*
return reinterpret_cast<char*>(result);
}
-char* DFG_OPERATION operationEnsureContiguous(ExecState* exec, JSObject* object)
+char* DFG_OPERATION operationEnsureInt32(ExecState* exec, JSObject* object)
{
JSGlobalData& globalData = exec->globalData();
NativeCallFrameTracer tracer(&globalData, exec);
- return reinterpret_cast<char*>(object->ensureContiguous(globalData));
+ return reinterpret_cast<char*>(object->ensureInt32(globalData));
}
-char* DFG_OPERATION operationEnsureArrayStorage(ExecState* exec, JSObject* object)
+char* DFG_OPERATION operationEnsureDouble(ExecState* exec, JSObject* object)
{
JSGlobalData& globalData = exec->globalData();
NativeCallFrameTracer tracer(&globalData, exec);
+
+ return reinterpret_cast<char*>(object->ensureDouble(globalData));
+}
- return reinterpret_cast<char*>(object->ensureArrayStorage(globalData));
+char* DFG_OPERATION operationEnsureContiguous(ExecState* exec, JSObject* object)
+{
+ JSGlobalData& globalData = exec->globalData();
+ NativeCallFrameTracer tracer(&globalData, exec);
+
+ return reinterpret_cast<char*>(object->ensureContiguous(globalData));
}
-char* DFG_OPERATION operationEnsureContiguousOrArrayStorage(ExecState* exec, JSObject* object, int32_t index)
+char* DFG_OPERATION operationEnsureArrayStorage(ExecState* exec, JSObject* object)
{
JSGlobalData& globalData = exec->globalData();
NativeCallFrameTracer tracer(&globalData, exec);
- if (static_cast<unsigned>(index) >= MIN_SPARSE_ARRAY_INDEX)
- return reinterpret_cast<char*>(object->ensureArrayStorage(globalData));
- return reinterpret_cast<char*>(object->ensureIndexedStorage(globalData));
+ return reinterpret_cast<char*>(object->ensureArrayStorage(globalData));
}
double DFG_OPERATION operationFModOnInts(int32_t a, int32_t b)
@@ -1423,7 +1522,7 @@ void DFG_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void*
SpeculationFailureDebugInfo* debugInfo = static_cast<SpeculationFailureDebugInfo*>(debugInfoRaw);
CodeBlock* codeBlock = debugInfo->codeBlock;
CodeBlock* alternative = codeBlock->alternative();
- dataLog("Speculation failure in %p at @%u with executeCounter = %s, "
+ dataLogF("Speculation failure in %p at @%u with executeCounter = %s, "
"reoptimizationRetryCounter = %u, optimizationDelayCounter = %u, "
"osrExitCounter = %u\n",
codeBlock,
@@ -1438,7 +1537,7 @@ void DFG_OPERATION debugOperationPrintSpeculationFailure(ExecState* exec, void*
extern "C" void DFG_OPERATION triggerReoptimizationNow(CodeBlock* codeBlock)
{
#if ENABLE(JIT_VERBOSE_OSR)
- dataLog("%p: Entered reoptimize\n", codeBlock);
+ dataLogF("%p: Entered reoptimize\n", codeBlock);
#endif
// We must be called with the baseline code block.
ASSERT(JITCode::isBaselineCode(codeBlock->getJITType()));
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index 8d2beacec..00e6b07b7 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -64,6 +64,7 @@ typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EAZ)(ExecState*, JSArray*,
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECI)(ExecState*, JSCell*, Identifier*);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_ECJ)(ExecState*, JSCell*, EncodedJSValue);
+typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EDA)(ExecState*, double, JSArray*);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EGriJsgI)(ExecState*, ResolveOperation*, JSGlobalObject*, Identifier*);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EI)(ExecState*, Identifier*);
typedef EncodedJSValue DFG_OPERATION (*J_DFGOperation_EIRo)(ExecState*, Identifier*, ResolveOperations*);
@@ -84,6 +85,7 @@ typedef JSCell* DFG_OPERATION (*C_DFGOperation_E)(ExecState*);
typedef JSCell* DFG_OPERATION (*C_DFGOperation_EC)(ExecState*, JSCell*);
typedef JSCell* DFG_OPERATION (*C_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*);
typedef JSCell* DFG_OPERATION (*C_DFGOperation_EIcf)(ExecState*, InlineCallFrame*);
+typedef JSCell* DFG_OPERATION (*C_DFGOperation_ESt)(ExecState*, Structure*);
typedef double DFG_OPERATION (*D_DFGOperation_DD)(double, double);
typedef double DFG_OPERATION (*D_DFGOperation_ZZ)(int32_t, int32_t);
typedef double DFG_OPERATION (*D_DFGOperation_EJ)(ExecState*, EncodedJSValue);
@@ -92,6 +94,7 @@ typedef size_t DFG_OPERATION (*S_DFGOperation_ECC)(ExecState*, JSCell*, JSCell*)
typedef size_t DFG_OPERATION (*S_DFGOperation_EJ)(ExecState*, EncodedJSValue);
typedef size_t DFG_OPERATION (*S_DFGOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue);
typedef size_t DFG_OPERATION (*S_DFGOperation_J)(EncodedJSValue);
+typedef void DFG_OPERATION (*V_DFGOperation_EOZD)(ExecState*, JSObject*, int32_t, double);
typedef void DFG_OPERATION (*V_DFGOperation_EOZJ)(ExecState*, JSObject*, int32_t, EncodedJSValue);
typedef void DFG_OPERATION (*V_DFGOperation_EC)(ExecState*, JSCell*);
typedef void DFG_OPERATION (*V_DFGOperation_ECIcf)(ExecState*, JSCell*, InlineCallFrame*);
@@ -116,7 +119,7 @@ typedef char* DFG_OPERATION (*P_DFGOperation_EStSS)(ExecState*, Structure*, size
typedef char* DFG_OPERATION (*P_DFGOperation_EStZ)(ExecState*, Structure*, int32_t);
// These routines are provide callbacks out to C++ implementations of operations too complex to JIT.
-JSCell* DFG_OPERATION operationNewObject(ExecState*) WTF_INTERNAL;
+JSCell* DFG_OPERATION operationNewObject(ExecState*, Structure*) WTF_INTERNAL;
JSCell* DFG_OPERATION operationCreateThis(ExecState*, JSCell* constructor) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationConvertThis(ExecState*, EncodedJSValue encodedOp1) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationValueAdd(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
@@ -148,7 +151,10 @@ void DFG_OPERATION operationPutByValCellStrict(ExecState*, JSCell*, EncodedJSVal
void DFG_OPERATION operationPutByValCellNonStrict(ExecState*, JSCell*, EncodedJSValue encodedProperty, EncodedJSValue encodedValue) WTF_INTERNAL;
void DFG_OPERATION operationPutByValBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL;
void DFG_OPERATION operationPutByValBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, EncodedJSValue encodedValue) WTF_INTERNAL;
+void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsStrict(ExecState*, JSObject*, int32_t index, double value) WTF_INTERNAL;
+void DFG_OPERATION operationPutDoubleByValBeyondArrayBoundsNonStrict(ExecState*, JSObject*, int32_t index, double value) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationArrayPush(ExecState*, EncodedJSValue encodedValue, JSArray*) WTF_INTERNAL;
+EncodedJSValue DFG_OPERATION operationArrayPushDouble(ExecState*, double value, JSArray*) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationArrayPop(ExecState*, JSArray*) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationArrayPopAndRecoverLength(ExecState*, JSArray*) WTF_INTERNAL;
EncodedJSValue DFG_OPERATION operationRegExpExec(ExecState*, JSCell*, JSCell*) WTF_INTERNAL;
@@ -175,6 +181,7 @@ size_t DFG_OPERATION operationCompareStrictEqCell(ExecState*, EncodedJSValue enc
size_t DFG_OPERATION operationCompareStrictEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) WTF_INTERNAL;
char* DFG_OPERATION operationVirtualCall(ExecState*) WTF_INTERNAL;
char* DFG_OPERATION operationLinkCall(ExecState*) WTF_INTERNAL;
+char* DFG_OPERATION operationLinkClosureCall(ExecState*) WTF_INTERNAL;
char* DFG_OPERATION operationVirtualConstruct(ExecState*) WTF_INTERNAL;
char* DFG_OPERATION operationLinkConstruct(ExecState*) WTF_INTERNAL;
JSCell* DFG_OPERATION operationCreateActivation(ExecState*) WTF_INTERNAL;
@@ -195,9 +202,10 @@ char* DFG_OPERATION operationAllocatePropertyStorageWithInitialCapacity(ExecStat
char* DFG_OPERATION operationAllocatePropertyStorage(ExecState*, size_t newSize) WTF_INTERNAL;
char* DFG_OPERATION operationReallocateButterflyToHavePropertyStorageWithInitialCapacity(ExecState*, JSObject*) WTF_INTERNAL;
char* DFG_OPERATION operationReallocateButterflyToGrowPropertyStorage(ExecState*, JSObject*, size_t newSize) WTF_INTERNAL;
+char* DFG_OPERATION operationEnsureInt32(ExecState*, JSObject*);
+char* DFG_OPERATION operationEnsureDouble(ExecState*, JSObject*);
char* DFG_OPERATION operationEnsureContiguous(ExecState*, JSObject*);
char* DFG_OPERATION operationEnsureArrayStorage(ExecState*, JSObject*);
-char* DFG_OPERATION operationEnsureContiguousOrArrayStorage(ExecState*, JSObject*, int32_t);
// This method is used to lookup an exception hander, keyed by faultLocation, which is
// the return location from one of the calls out to one of the helper operations above.
diff --git a/Source/JavaScriptCore/dfg/DFGPhase.cpp b/Source/JavaScriptCore/dfg/DFGPhase.cpp
index f97c49e31..20301e814 100644
--- a/Source/JavaScriptCore/dfg/DFGPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPhase.cpp
@@ -35,8 +35,8 @@ namespace JSC { namespace DFG {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
void Phase::beginPhase()
{
- dataLog("Beginning DFG phase %s.\n", m_name);
- dataLog("Graph before %s:\n", m_name);
+ dataLogF("Beginning DFG phase %s.\n", m_name);
+ dataLogF("Graph before %s:\n", m_name);
m_graph.dump();
}
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGPhase.h b/Source/JavaScriptCore/dfg/DFGPhase.h
index a73d26baf..939e199e0 100644
--- a/Source/JavaScriptCore/dfg/DFGPhase.h
+++ b/Source/JavaScriptCore/dfg/DFGPhase.h
@@ -83,7 +83,7 @@ bool runAndLog(PhaseType& phase)
bool result = phase.run();
#if DFG_ENABLE(DEBUG_VERBOSE)
if (result)
- dataLog("Phase %s changed the IR.\n", phase.name());
+ dataLogF("Phase %s changed the IR.\n", phase.name());
#endif
return result;
}
diff --git a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
index 3e8ead5c6..4b8a17285 100644
--- a/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
@@ -116,6 +116,52 @@ private:
return false;
return !!m_graph.valueOfNumberConstant(nodeIndex);
}
+
+ bool isWithinPowerOfTwoForConstant(Node& node, int power)
+ {
+ JSValue immediateValue = node.valueOfJSConstant(codeBlock());
+ if (!immediateValue.isInt32())
+ return false;
+ int32_t intImmediate = immediateValue.asInt32();
+ return intImmediate > -(1 << power) && intImmediate < (1 << power);
+ }
+
+ bool isWithinPowerOfTwoNonRecursive(NodeIndex nodeIndex, int power)
+ {
+ Node& node = m_graph[nodeIndex];
+ if (node.op() != JSConstant)
+ return false;
+ return isWithinPowerOfTwoForConstant(node, power);
+ }
+
+ bool isWithinPowerOfTwo(NodeIndex nodeIndex, int power)
+ {
+ Node& node = m_graph[nodeIndex];
+ switch (node.op()) {
+ case JSConstant: {
+ return isWithinPowerOfTwoForConstant(node, power);
+ }
+
+ case BitAnd: {
+ return isWithinPowerOfTwoNonRecursive(node.child1().index(), power)
+ || isWithinPowerOfTwoNonRecursive(node.child2().index(), power);
+ }
+
+ case BitRShift:
+ case BitURShift: {
+ Node& shiftAmount = m_graph[node.child2()];
+ if (shiftAmount.op() != JSConstant)
+ return false;
+ JSValue immediateValue = shiftAmount.valueOfJSConstant(codeBlock());
+ if (!immediateValue.isInt32())
+ return false;
+ return immediateValue > 32 - power;
+ }
+
+ default:
+ return false;
+ }
+ }
SpeculatedType speculatedDoubleTypeForPrediction(SpeculatedType value)
{
@@ -140,7 +186,7 @@ private:
NodeFlags flags = node.flags() & NodeBackPropMask;
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" %s @%u: %s ", Graph::opName(op), m_compileIndex, nodeFlagsAsString(flags));
+ dataLogF(" %s @%u: %s ", Graph::opName(op), m_compileIndex, nodeFlagsAsString(flags));
#endif
bool changed = false;
@@ -184,7 +230,7 @@ private:
case BitURShift: {
changed |= setPrediction(SpecInt32);
flags |= NodeUsedAsInt;
- flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero);
+ flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther);
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -193,7 +239,7 @@ private:
case ValueToInt32: {
changed |= setPrediction(SpecInt32);
flags |= NodeUsedAsInt;
- flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero);
+ flags &= ~(NodeUsedAsNumber | NodeNeedsNegZero | NodeUsedAsOther);
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
}
@@ -221,28 +267,10 @@ private:
case StringCharCodeAt: {
changed |= mergePrediction(SpecInt32);
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
break;
}
- case ArithMod: {
- SpeculatedType left = m_graph[node.child1()].prediction();
- SpeculatedType right = m_graph[node.child2()].prediction();
-
- if (left && right) {
- if (isInt32Speculation(mergeSpeculations(left, right))
- && nodeCanSpeculateInteger(node.arithNodeFlags()))
- changed |= mergePrediction(SpecInt32);
- else
- changed |= mergePrediction(SpecDouble);
- }
-
- flags |= NodeUsedAsValue;
- changed |= m_graph[node.child1()].mergeFlags(flags);
- changed |= m_graph[node.child2()].mergeFlags(flags);
- break;
- }
-
case UInt32ToNumber: {
if (nodeCanSpeculateInteger(node.arithNodeFlags()))
changed |= mergePrediction(SpecInt32);
@@ -258,7 +286,7 @@ private:
SpeculatedType right = m_graph[node.child2()].prediction();
if (left && right) {
- if (isNumberSpeculation(left) && isNumberSpeculation(right)) {
+ if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right)) {
if (m_graph.addShouldSpeculateInteger(node))
changed |= mergePrediction(SpecInt32);
else
@@ -272,6 +300,8 @@ private:
if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index()))
flags &= ~NodeNeedsNegZero;
+ if (m_graph[node.child1()].hasNumberResult() || m_graph[node.child2()].hasNumberResult())
+ flags &= ~NodeUsedAsOther;
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
@@ -291,6 +321,7 @@ private:
if (isNotNegZero(node.child1().index()) || isNotNegZero(node.child2().index()))
flags &= ~NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
@@ -310,6 +341,7 @@ private:
if (isNotZero(node.child1().index()) || isNotZero(node.child2().index()))
flags &= ~NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
@@ -324,6 +356,8 @@ private:
changed |= mergePrediction(speculatedDoubleTypeForPrediction(m_graph[node.child1()].prediction()));
}
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
@@ -333,7 +367,7 @@ private:
SpeculatedType right = m_graph[node.child2()].prediction();
if (left && right) {
- if (isInt32Speculation(mergeSpeculations(left, right))
+ if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()])
&& nodeCanSpeculateInteger(node.arithNodeFlags()))
changed |= mergePrediction(SpecInt32);
else
@@ -341,6 +375,8 @@ private:
}
flags |= NodeUsedAsNumber;
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -359,10 +395,20 @@ private:
// As soon as a multiply happens, we can easily end up in the part
// of the double domain where the point at which you do truncation
- // can change the outcome. So, ArithMul always checks for overflow
- // no matter what, and always forces its inputs to check as well.
+ // can change the outcome. So, ArithMul always forces its inputs to
+ // check for overflow. Additionally, it will have to check for overflow
+ // itself unless we can prove that there is no way for the values
+ // produced to cause double rounding.
+
+ if (!isWithinPowerOfTwo(node.child1().index(), 22)
+ && !isWithinPowerOfTwo(node.child2().index(), 22))
+ flags |= NodeUsedAsNumber;
+
+ changed |= node.mergeFlags(flags);
flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -373,7 +419,7 @@ private:
SpeculatedType right = m_graph[node.child2()].prediction();
if (left && right) {
- if (isInt32Speculation(mergeSpeculations(left, right))
+ if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()])
&& nodeCanSpeculateInteger(node.arithNodeFlags()))
changed |= mergePrediction(SpecInt32);
else
@@ -382,10 +428,32 @@ private:
// As soon as a multiply happens, we can easily end up in the part
// of the double domain where the point at which you do truncation
- // can change the outcome. So, ArithMul always checks for overflow
+ // can change the outcome. So, ArithDiv always checks for overflow
// no matter what, and always forces its inputs to check as well.
flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
+
+ changed |= m_graph[node.child1()].mergeFlags(flags);
+ changed |= m_graph[node.child2()].mergeFlags(flags);
+ break;
+ }
+
+ case ArithMod: {
+ SpeculatedType left = m_graph[node.child1()].prediction();
+ SpeculatedType right = m_graph[node.child2()].prediction();
+
+ if (left && right) {
+ if (Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child2()])
+ && nodeCanSpeculateInteger(node.arithNodeFlags()))
+ changed |= mergePrediction(SpecInt32);
+ else
+ changed |= mergePrediction(SpecDouble);
+ }
+
+ flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
+
changed |= m_graph[node.child1()].mergeFlags(flags);
changed |= m_graph[node.child2()].mergeFlags(flags);
break;
@@ -393,18 +461,20 @@ private:
case ArithSqrt: {
changed |= setPrediction(SpecDouble);
- changed |= m_graph[node.child1()].mergeFlags(flags | NodeUsedAsValue);
+ flags |= NodeUsedAsNumber | NodeNeedsNegZero;
+ flags &= ~NodeUsedAsOther;
+ changed |= m_graph[node.child1()].mergeFlags(flags);
break;
}
case ArithAbs: {
SpeculatedType child = m_graph[node.child1()].prediction();
- if (nodeCanSpeculateInteger(node.arithNodeFlags()))
- changed |= mergePrediction(child);
+ if (isInt32SpeculationForArithmetic(child)
+ && nodeCanSpeculateInteger(node.arithNodeFlags()))
+ changed |= mergePrediction(SpecInt32);
else
- changed |= setPrediction(speculatedDoubleTypeForPrediction(child));
+ changed |= mergePrediction(speculatedDoubleTypeForPrediction(child));
- flags &= ~NodeNeedsNegZero;
changed |= m_graph[node.child1()].mergeFlags(flags);
break;
}
@@ -447,13 +517,13 @@ private:
changed |= mergePrediction(node.getHeapPrediction());
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
break;
}
case GetMyArgumentByValSafe: {
changed |= mergePrediction(node.getHeapPrediction());
- changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
break;
}
@@ -554,7 +624,7 @@ private:
case NewArrayWithSize: {
changed |= setPrediction(SpecArray);
- changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue | NodeUsedAsInt);
break;
}
@@ -571,7 +641,7 @@ private:
case StringCharAt: {
changed |= setPrediction(SpecString);
changed |= m_graph[node.child1()].mergeFlags(NodeUsedAsValue);
- changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsInt);
+ changed |= m_graph[node.child2()].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
break;
}
@@ -580,7 +650,7 @@ private:
for (unsigned childIdx = node.firstChild();
childIdx < node.firstChild() + node.numChildren();
++childIdx)
- changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber);
+ changed |= m_graph[m_graph.m_varArgChildren[childIdx]].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther);
break;
}
@@ -636,16 +706,17 @@ private:
case PhantomArguments:
case CheckArray:
case Arrayify:
- case ArrayifyToStructure: {
+ case ArrayifyToStructure:
+ case Identity: {
// This node should never be visible at this stage of compilation. It is
// inserted by fixup(), which follows this phase.
- ASSERT_NOT_REACHED();
+ CRASH();
break;
}
case PutByVal:
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, 1)].mergeFlags(NodeUsedAsNumber | NodeUsedAsOther | NodeUsedAsInt);
changed |= m_graph[m_graph.varArgChild(node, 2)].mergeFlags(NodeUsedAsValue);
break;
@@ -690,6 +761,7 @@ private:
case CheckArgumentsNotCreated:
case GlobalVarWatchpoint:
case GarbageValue:
+ case InheritorIDWatchpoint:
changed |= mergeDefaultFlags(node);
break;
@@ -700,7 +772,7 @@ private:
break;
case LastNodeType:
- ASSERT_NOT_REACHED();
+ CRASH();
break;
#else
default:
@@ -710,7 +782,7 @@ private:
}
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("%s\n", speculationToString(m_graph[m_compileIndex].prediction()));
+ dataLogF("%s\n", speculationToString(m_graph[m_compileIndex].prediction()));
#endif
m_changed |= changed;
@@ -743,7 +815,7 @@ private:
void propagateForward()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Propagating predictions forward [%u]\n", ++m_count);
+ dataLogF("Propagating predictions forward [%u]\n", ++m_count);
#endif
for (m_compileIndex = 0; m_compileIndex < m_graph.size(); ++m_compileIndex)
propagate(m_graph[m_compileIndex]);
@@ -752,7 +824,7 @@ private:
void propagateBackward()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Propagating predictions backward [%u]\n", ++m_count);
+ dataLogF("Propagating predictions backward [%u]\n", ++m_count);
#endif
for (m_compileIndex = m_graph.size(); m_compileIndex-- > 0;)
propagate(m_graph[m_compileIndex]);
@@ -761,7 +833,7 @@ private:
void doRoundOfDoubleVoting()
{
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Voting on double uses of locals [%u]\n", m_count);
+ dataLogF("Voting on double uses of locals [%u]\n", m_count);
#endif
for (unsigned i = 0; i < m_graph.m_variableAccessData.size(); ++i)
m_graph.m_variableAccessData[i].find()->clearVotes();
@@ -776,7 +848,7 @@ private:
DoubleBallot ballot;
- if (isNumberSpeculation(left) && isNumberSpeculation(right)
+ if (isNumberSpeculationExpectingDefined(left) && isNumberSpeculationExpectingDefined(right)
&& !m_graph.addShouldSpeculateInteger(node))
ballot = VoteDouble;
else
@@ -814,7 +886,7 @@ private:
DoubleBallot ballot;
if (isNumberSpeculation(left) && isNumberSpeculation(right)
- && !(Node::shouldSpeculateInteger(m_graph[node.child1()], m_graph[node.child1()])
+ && !(Node::shouldSpeculateIntegerForArithmetic(m_graph[node.child1()], m_graph[node.child1()])
&& node.canSpeculateInteger()))
ballot = VoteDouble;
else
@@ -827,7 +899,7 @@ private:
case ArithAbs:
DoubleBallot ballot;
- if (!(m_graph[node.child1()].shouldSpeculateInteger()
+ if (!(m_graph[node.child1()].shouldSpeculateIntegerForArithmetic()
&& node.canSpeculateInteger()))
ballot = VoteDouble;
else
@@ -849,6 +921,24 @@ private:
break;
}
+ case PutByVal:
+ case PutByValAlias: {
+ Edge child1 = m_graph.varArgChild(node, 0);
+ Edge child2 = m_graph.varArgChild(node, 1);
+ Edge child3 = m_graph.varArgChild(node, 2);
+ m_graph.vote(child1, VoteValue);
+ m_graph.vote(child2, VoteValue);
+ switch (node.arrayMode().type()) {
+ case Array::Double:
+ m_graph.vote(child3, VoteDouble);
+ break;
+ default:
+ m_graph.vote(child3, VoteValue);
+ break;
+ }
+ break;
+ }
+
default:
m_graph.vote(node, VoteValue);
break;
diff --git a/Source/JavaScriptCore/dfg/DFGRegisterBank.h b/Source/JavaScriptCore/dfg/DFGRegisterBank.h
index 1d1d6fa52..3dbd1fe91 100644
--- a/Source/JavaScriptCore/dfg/DFGRegisterBank.h
+++ b/Source/JavaScriptCore/dfg/DFGRegisterBank.h
@@ -75,7 +75,6 @@ class RegisterBank {
public:
RegisterBank()
- : m_lastAllocated(NUM_REGS - 1)
{
}
@@ -86,12 +85,7 @@ public:
{
VirtualRegister ignored;
- for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) {
- if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister)
- return allocateInternal(i, ignored);
- }
- // Loop over the remaining entries.
- for (uint32_t i = 0; i <= m_lastAllocated; ++i) {
+ for (uint32_t i = 0; i < NUM_REGS; ++i) {
if (!m_data[i].lockCount && m_data[i].name == InvalidVirtualRegister)
return allocateInternal(i, ignored);
}
@@ -115,9 +109,6 @@ public:
uint32_t currentLowest = NUM_REGS;
SpillHint currentSpillOrder = SpillHintInvalid;
- // Scan through all register, starting at the last allocated & looping around.
- ASSERT(m_lastAllocated < NUM_REGS);
-
// This loop is broken into two halves, looping from the last allocated
// register (the register returned last time this method was called) to
// the maximum register value, then from 0 to the last allocated.
@@ -125,7 +116,7 @@ public:
// thrash, and minimize time spent scanning locked registers in allocation.
// If a unlocked and unnamed register is found return it immediately.
// Otherwise, find the first unlocked register with the lowest spillOrder.
- for (uint32_t i = m_lastAllocated + 1; i < NUM_REGS; ++i) {
+ for (uint32_t i = 0 ; i < NUM_REGS; ++i) {
// (1) If the current register is locked, it is not a candidate.
if (m_data[i].lockCount)
continue;
@@ -140,18 +131,6 @@ public:
currentLowest = i;
}
}
- // Loop over the remaining entries.
- for (uint32_t i = 0; i <= m_lastAllocated; ++i) {
- if (m_data[i].lockCount)
- continue;
- SpillHint spillOrder = m_data[i].spillOrder;
- if (spillOrder == SpillHintInvalid)
- return allocateInternal(i, spillMe);
- if (spillOrder < currentSpillOrder) {
- currentSpillOrder = spillOrder;
- currentLowest = i;
- }
- }
// Deadlock check - this could only occur is all registers are locked!
ASSERT(currentLowest != NUM_REGS && currentSpillOrder != SpillHintInvalid);
@@ -237,11 +216,11 @@ public:
// For each register, print the VirtualRegister 'name'.
for (uint32_t i =0; i < NUM_REGS; ++i) {
if (m_data[i].name != InvalidVirtualRegister)
- dataLog("[%02d]", m_data[i].name);
+ dataLogF("[%02d]", m_data[i].name);
else
- dataLog("[--]");
+ dataLogF("[--]");
}
- dataLog("\n");
+ dataLogF("\n");
}
#endif
@@ -354,7 +333,6 @@ private:
// Mark the register as locked (with a lock count of 1).
m_data[i].lockCount = 1;
- m_lastAllocated = i;
return BankInfo::toRegister(i);
}
@@ -378,8 +356,6 @@ private:
// Holds the current status of all registers.
MapEntry m_data[NUM_REGS];
- // Used to to implement a simple round-robin like allocation scheme.
- uint32_t m_lastAllocated;
};
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.cpp b/Source/JavaScriptCore/dfg/DFGRepatch.cpp
index 7c15ef33e..a20eb544a 100644
--- a/Source/JavaScriptCore/dfg/DFGRepatch.cpp
+++ b/Source/JavaScriptCore/dfg/DFGRepatch.cpp
@@ -114,6 +114,23 @@ static void addStructureTransitionCheck(
failureCases, scratchGPR);
}
+static void replaceWithJump(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo, const MacroAssemblerCodePtr target)
+{
+ if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) {
+ repatchBuffer.replaceWithJump(
+ RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress(
+ stubInfo.callReturnLocation.dataLabelPtrAtOffset(
+ -(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall)),
+ CodeLocationLabel(target));
+ return;
+ }
+
+ repatchBuffer.relink(
+ stubInfo.callReturnLocation.jumpAtOffset(
+ stubInfo.patch.dfg.deltaCallToStructCheck),
+ CodeLocationLabel(target));
+}
+
static void emitRestoreScratch(MacroAssembler& stubJit, bool needToRestoreScratch, GPRReg scratchGPR, MacroAssembler::Jump& success, MacroAssembler::Jump& fail, MacroAssembler::JumpList failureCases)
{
if (needToRestoreScratch) {
@@ -284,7 +301,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier
stubInfo.patch.dfg.deltaCallToDone).executableAddress()));
RepatchBuffer repatchBuffer(codeBlock);
- repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code()));
+ replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code());
repatchBuffer.relink(stubInfo.callReturnLocation, operationGetById);
return true;
@@ -334,7 +351,7 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier
generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.dfg.deltaCallToSlowCase), stubInfo.stubRoutine);
RepatchBuffer repatchBuffer(codeBlock);
- repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code()));
+ replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code());
repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdProtoBuildList);
stubInfo.initGetByIdChain(*globalData, codeBlock->ownerExecutable(), structure, prototypeChain, count, true);
@@ -518,9 +535,11 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi
polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure, isDirect);
- CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck);
RepatchBuffer repatchBuffer(codeBlock);
- repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code()));
+ repatchBuffer.relink(
+ stubInfo.callReturnLocation.jumpAtOffset(
+ stubInfo.patch.dfg.deltaCallToStructCheck),
+ CodeLocationLabel(stubRoutine->code().code()));
if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1))
return true;
@@ -584,9 +603,8 @@ static bool tryBuildGetByIDProtoList(ExecState* exec, JSValue baseValue, const I
polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure, true);
- CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck);
RepatchBuffer repatchBuffer(codeBlock);
- repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code()));
+ replaceWithJump(repatchBuffer, stubInfo, stubRoutine->code().code());
if (listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1))
return true;
@@ -976,7 +994,10 @@ static bool tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier
stubInfo.stubRoutine);
RepatchBuffer repatchBuffer(codeBlock);
- repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.dfg.deltaCallToStructCheck), CodeLocationLabel(stubInfo.stubRoutine->code().code()));
+ repatchBuffer.relink(
+ stubInfo.callReturnLocation.jumpAtOffset(
+ stubInfo.patch.dfg.deltaCallToStructCheck),
+ CodeLocationLabel(stubInfo.stubRoutine->code().code()));
repatchBuffer.relink(stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind));
stubInfo.initPutByIdTransition(*globalData, codeBlock->ownerExecutable(), oldStructure, structure, prototypeChain, putKind == Direct);
@@ -1092,8 +1113,20 @@ void dfgBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identifier& p
dfgRepatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
}
+static void linkSlowFor(RepatchBuffer& repatchBuffer, JSGlobalData* globalData, CallLinkInfo& callLinkInfo, CodeSpecializationKind kind)
+{
+ if (kind == CodeForCall) {
+ repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualCallThunkGenerator).code());
+ return;
+ }
+ ASSERT(kind == CodeForConstruct);
+ repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualConstructThunkGenerator).code());
+}
+
void dfgLinkFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock, JSFunction* callee, MacroAssemblerCodePtr codePtr, CodeSpecializationKind kind)
{
+ ASSERT(!callLinkInfo.stub);
+
CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
JSGlobalData* globalData = callerCodeBlock->globalData();
@@ -1108,17 +1141,125 @@ void dfgLinkFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCo
calleeCodeBlock->linkIncomingCall(&callLinkInfo);
if (kind == CodeForCall) {
- repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualCallThunkGenerator).code());
+ repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(linkClosureCallThunkGenerator).code());
return;
}
+
ASSERT(kind == CodeForConstruct);
- repatchBuffer.relink(callLinkInfo.callReturnLocation, globalData->getCTIStub(virtualConstructThunkGenerator).code());
+ linkSlowFor(repatchBuffer, globalData, callLinkInfo, CodeForConstruct);
+}
+
+void dfgLinkSlowFor(ExecState* exec, CallLinkInfo& callLinkInfo, CodeSpecializationKind kind)
+{
+ CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
+ JSGlobalData* globalData = callerCodeBlock->globalData();
+
+ RepatchBuffer repatchBuffer(callerCodeBlock);
+
+ linkSlowFor(repatchBuffer, globalData, callLinkInfo, kind);
+}
+
+void dfgLinkClosureCall(ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock, Structure* structure, ExecutableBase* executable, MacroAssemblerCodePtr codePtr)
+{
+ ASSERT(!callLinkInfo.stub);
+
+ CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
+ JSGlobalData* globalData = callerCodeBlock->globalData();
+
+ GPRReg calleeGPR = static_cast<GPRReg>(callLinkInfo.calleeGPR);
+
+ CCallHelpers stubJit(globalData, callerCodeBlock);
+
+ CCallHelpers::JumpList slowPath;
+
+#if USE(JSVALUE64)
+ slowPath.append(
+ stubJit.branchTest64(
+ CCallHelpers::NonZero, calleeGPR, GPRInfo::tagMaskRegister));
+#else
+ // We would have already checked that the callee is a cell.
+#endif
+
+ slowPath.append(
+ stubJit.branchPtr(
+ CCallHelpers::NotEqual,
+ CCallHelpers::Address(calleeGPR, JSCell::structureOffset()),
+ CCallHelpers::TrustedImmPtr(structure)));
+
+ slowPath.append(
+ stubJit.branchPtr(
+ CCallHelpers::NotEqual,
+ CCallHelpers::Address(calleeGPR, JSFunction::offsetOfExecutable()),
+ CCallHelpers::TrustedImmPtr(executable)));
+
+ stubJit.loadPtr(
+ CCallHelpers::Address(calleeGPR, JSFunction::offsetOfScopeChain()),
+ GPRInfo::returnValueGPR);
+
+#if USE(JSVALUE64)
+ stubJit.store64(
+ GPRInfo::returnValueGPR,
+ CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain)));
+#else
+ stubJit.storePtr(
+ GPRInfo::returnValueGPR,
+ CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
+ stubJit.store32(
+ CCallHelpers::TrustedImm32(JSValue::CellTag),
+ CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
+#endif
+
+ JITCompiler::Call call = stubJit.nearCall();
+ JITCompiler::Jump done = stubJit.jump();
+
+ slowPath.link(&stubJit);
+ stubJit.move(CCallHelpers::TrustedImmPtr(callLinkInfo.callReturnLocation.executableAddress()), GPRInfo::nonArgGPR2);
+ stubJit.restoreReturnAddressBeforeReturn(GPRInfo::nonArgGPR2);
+ stubJit.move(calleeGPR, GPRInfo::nonArgGPR0);
+#if USE(JSVALUE32_64)
+ stubJit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), GPRInfo::nonArgGPR1);
+#endif
+ JITCompiler::Jump slow = stubJit.jump();
+
+ LinkBuffer patchBuffer(*globalData, &stubJit, callerCodeBlock);
+
+ patchBuffer.link(call, FunctionPtr(codePtr.executableAddress()));
+ patchBuffer.link(done, callLinkInfo.callReturnLocation.labelAtOffset(0));
+ patchBuffer.link(slow, CodeLocationLabel(globalData->getCTIStub(virtualCallThunkGenerator).code()));
+
+ RefPtr<ClosureCallStubRoutine> stubRoutine = adoptRef(new ClosureCallStubRoutine(
+ FINALIZE_DFG_CODE(
+ patchBuffer,
+ ("DFG closure call stub for CodeBlock %p, return point %p, target %p (CodeBlock %p)",
+ callerCodeBlock, callLinkInfo.callReturnLocation.labelAtOffset(0).executableAddress(),
+ codePtr.executableAddress(), calleeCodeBlock)),
+ *globalData, callerCodeBlock->ownerExecutable(), structure, executable, callLinkInfo.codeOrigin));
+
+ RepatchBuffer repatchBuffer(callerCodeBlock);
+
+ repatchBuffer.replaceWithJump(
+ RepatchBuffer::startOfBranchPtrWithPatchOnRegister(callLinkInfo.hotPathBegin),
+ CodeLocationLabel(stubRoutine->code().code()));
+ linkSlowFor(repatchBuffer, globalData, callLinkInfo, CodeForCall);
+
+ callLinkInfo.stub = stubRoutine.release();
+
+ ASSERT(!calleeCodeBlock || calleeCodeBlock->isIncomingCallAlreadyLinked(&callLinkInfo));
}
void dfgResetGetByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo)
{
repatchBuffer.relink(stubInfo.callReturnLocation, operationGetByIdOptimize);
- repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall), reinterpret_cast<void*>(-1));
+ CodeLocationDataLabelPtr structureLabel = stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall);
+ if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) {
+ repatchBuffer.revertJumpReplacementToPatchableBranchPtrWithPatch(
+ RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress(structureLabel),
+ MacroAssembler::Address(
+ static_cast<MacroAssembler::RegisterID>(stubInfo.patch.dfg.baseGPR),
+ JSCell::structureOffset()),
+ reinterpret_cast<void*>(-1));
+ }
+ repatchBuffer.repatch(structureLabel, reinterpret_cast<void*>(-1));
#if USE(JSVALUE64)
repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelCompactAtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), 0);
#else
@@ -1143,7 +1284,16 @@ void dfgResetPutByID(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo)
optimizedFunction = operationPutByIdDirectNonStrictOptimize;
}
repatchBuffer.relink(stubInfo.callReturnLocation, optimizedFunction);
- repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall), reinterpret_cast<void*>(-1));
+ CodeLocationDataLabelPtr structureLabel = stubInfo.callReturnLocation.dataLabelPtrAtOffset(-(intptr_t)stubInfo.patch.dfg.deltaCheckImmToCall);
+ if (MacroAssembler::canJumpReplacePatchableBranchPtrWithPatch()) {
+ repatchBuffer.revertJumpReplacementToPatchableBranchPtrWithPatch(
+ RepatchBuffer::startOfPatchableBranchPtrWithPatchOnAddress(structureLabel),
+ MacroAssembler::Address(
+ static_cast<MacroAssembler::RegisterID>(stubInfo.patch.dfg.baseGPR),
+ JSCell::structureOffset()),
+ reinterpret_cast<void*>(-1));
+ }
+ repatchBuffer.repatch(structureLabel, reinterpret_cast<void*>(-1));
#if USE(JSVALUE64)
repatchBuffer.repatch(stubInfo.callReturnLocation.dataLabel32AtOffset(stubInfo.patch.dfg.deltaCallToLoadOrStore), 0);
#else
diff --git a/Source/JavaScriptCore/dfg/DFGRepatch.h b/Source/JavaScriptCore/dfg/DFGRepatch.h
index 83d4e976d..97d26aab2 100644
--- a/Source/JavaScriptCore/dfg/DFGRepatch.h
+++ b/Source/JavaScriptCore/dfg/DFGRepatch.h
@@ -41,6 +41,8 @@ void dfgBuildGetByIDProtoList(ExecState*, JSValue, const Identifier&, const Prop
void dfgRepatchPutByID(ExecState*, JSValue, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind);
void dfgBuildPutByIdList(ExecState*, JSValue, const Identifier&, const PutPropertySlot&, StructureStubInfo&, PutKind);
void dfgLinkFor(ExecState*, CallLinkInfo&, CodeBlock*, JSFunction* callee, MacroAssemblerCodePtr, CodeSpecializationKind);
+void dfgLinkSlowFor(ExecState*, CallLinkInfo&, CodeSpecializationKind);
+void dfgLinkClosureCall(ExecState*, CallLinkInfo&, CodeBlock*, Structure*, ExecutableBase*, MacroAssemblerCodePtr);
void dfgResetGetByID(RepatchBuffer&, StructureStubInfo&);
void dfgResetPutByID(RepatchBuffer&, StructureStubInfo&);
diff --git a/Source/JavaScriptCore/dfg/DFGScoreBoard.h b/Source/JavaScriptCore/dfg/DFGScoreBoard.h
index 430bdf552..9b509fe2a 100644
--- a/Source/JavaScriptCore/dfg/DFGScoreBoard.h
+++ b/Source/JavaScriptCore/dfg/DFGScoreBoard.h
@@ -113,7 +113,7 @@ public:
ASSERT(m_used[index] != max());
if (node.refCount() == ++m_used[index]) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Freeing virtual register %u.", index);
+ dataLogF(" Freeing virtual register %u.", index);
#endif
// If the use count in the scoreboard reaches the use count for the node,
// then this was its last use; the virtual register is now free.
@@ -122,7 +122,7 @@ public:
m_free.append(index);
} else {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Virtual register %u is at %u/%u uses.", index, m_used[index], node.refCount());
+ dataLogF(" Virtual register %u is at %u/%u uses.", index, m_used[index], node.refCount());
#endif
}
}
@@ -148,26 +148,26 @@ public:
#ifndef NDEBUG
void dump()
{
- dataLog(" USED: [ ");
+ dataLogF(" USED: [ ");
for (unsigned i = 0; i < m_used.size(); ++i) {
if (!m_free.contains(i)) {
- dataLog("%d:", i);
+ dataLogF("%d:", i);
if (m_used[i] == max())
- dataLog("local ");
+ dataLogF("local ");
else
- dataLog("%d ", m_used[i]);
+ dataLogF("%d ", m_used[i]);
}
}
- dataLog("]\n");
+ dataLogF("]\n");
- dataLog(" FREE: [ ");
+ dataLogF(" FREE: [ ");
for (unsigned i = 0; i < m_used.size(); ++i) {
if (m_free.contains(i) && m_used[i] != max()) {
ASSERT(!m_used[i]);
- dataLog("%d ", i);
+ dataLogF("%d ", i);
}
}
- dataLog("]\n");
+ dataLogF("]\n");
}
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h b/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h
index fa1f888e0..4acd8690a 100644
--- a/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h
+++ b/Source/JavaScriptCore/dfg/DFGSlowPathGenerator.h
@@ -49,7 +49,7 @@ public:
void generate(SpeculativeJIT* jit)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Generating slow path %p at offset 0x%x\n", this, jit->m_jit.debugOffset());
+ dataLogF("Generating slow path %p at offset 0x%x\n", this, jit->m_jit.debugOffset());
#endif
m_label = jit->m_jit.label();
jit->m_compileIndex = m_compileIndex;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 6bedd6d68..41276d233 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -59,7 +59,7 @@ SpeculativeJIT::~SpeculativeJIT()
void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR, GPRReg storageGPR, unsigned numElements)
{
- ASSERT(hasContiguous(structure->indexingType()));
+ ASSERT(hasUndecided(structure->indexingType()) || hasInt32(structure->indexingType()) || hasDouble(structure->indexingType()) || hasContiguous(structure->indexingType()));
GPRTemporary scratch(this);
GPRReg scratchGPR = scratch.gpr();
@@ -67,6 +67,7 @@ void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR,
unsigned vectorLength = std::max(BASE_VECTOR_LEN, numElements);
JITCompiler::JumpList slowCases;
+
slowCases.append(
emitAllocateBasicStorage(TrustedImm32(vectorLength * sizeof(JSValue) + sizeof(IndexingHeader)), storageGPR));
m_jit.subPtr(TrustedImm32(vectorLength * sizeof(JSValue)), storageGPR);
@@ -79,6 +80,21 @@ void SpeculativeJIT::emitAllocateJSArray(Structure* structure, GPRReg resultGPR,
m_jit.store32(TrustedImm32(numElements), MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
m_jit.store32(TrustedImm32(vectorLength), MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
+ if (hasDouble(structure->indexingType()) && numElements < vectorLength) {
+#if USE(JSVALUE64)
+ m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR);
+ for (unsigned i = numElements; i < vectorLength; ++i)
+ m_jit.store64(scratchGPR, MacroAssembler::Address(storageGPR, sizeof(double) * i));
+#else
+ EncodedValueDescriptor value;
+ value.asInt64 = JSValue::encode(JSValue(JSValue::EncodeAsDouble, QNaN));
+ for (unsigned i = numElements; i < vectorLength; ++i) {
+ m_jit.store32(TrustedImm32(value.asBits.tag), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(TrustedImm32(value.asBits.payload), MacroAssembler::Address(storageGPR, sizeof(double) * i + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+ }
+#endif
+ }
+
// I want a slow path that also loads out the storage pointer, and that's
// what this custom CallArrayAllocatorSlowPathGenerator gives me. It's a lot
// of work for a very small piece of functionality. :-/
@@ -258,7 +274,7 @@ void SpeculativeJIT::terminateSpeculativeExecution(ExitKind kind, JSValueRegs js
{
ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpeculativeJIT was terminated.\n");
+ dataLogF("SpeculativeJIT was terminated.\n");
#endif
if (!m_compileOkay)
return;
@@ -276,7 +292,7 @@ void SpeculativeJIT::terminateSpeculativeExecutionWithConditionalDirection(ExitK
{
ASSERT(at(m_compileIndex).canExit() || m_isCheckingArgumentTypes);
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpeculativeJIT was terminated.\n");
+ dataLogF("SpeculativeJIT was terminated.\n");
#endif
if (!m_compileOkay)
return;
@@ -292,7 +308,7 @@ void SpeculativeJIT::addSlowPathGenerator(PassOwnPtr<SlowPathGenerator> slowPath
void SpeculativeJIT::runSlowPathGenerators()
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Running %lu slow path generators.\n", m_slowPathGenerators.size());
+ dataLogF("Running %lu slow path generators.\n", m_slowPathGenerators.size());
#endif
for (unsigned i = 0; i < m_slowPathGenerators.size(); ++i)
m_slowPathGenerators[i]->generate(this);
@@ -343,32 +359,49 @@ const TypedArrayDescriptor* SpeculativeJIT::typedArrayDescriptor(ArrayMode array
}
}
+JITCompiler::Jump SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode, IndexingType shape, bool invert)
+{
+ switch (arrayMode.arrayClass()) {
+ case Array::OriginalArray: {
+ CRASH();
+ JITCompiler::Jump result; // I already know that VC++ takes unkindly to the expression "return Jump()", so I'm doing it this way in anticipation of someone eventually using VC++ to compile the DFG.
+ return result;
+ }
+
+ case Array::Array:
+ m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR);
+ return m_jit.branch32(
+ invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | shape));
+
+ default:
+ m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR);
+ return m_jit.branch32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(shape));
+ }
+}
+
JITCompiler::JumpList SpeculativeJIT::jumpSlowForUnwantedArrayMode(GPRReg tempGPR, ArrayMode arrayMode, bool invert)
{
JITCompiler::JumpList result;
switch (arrayMode.type()) {
- case Array::Contiguous: {
- if (arrayMode.isJSArray()) {
- m_jit.and32(TrustedImm32(IsArray | IndexingShapeMask), tempGPR);
- result.append(
- m_jit.branch32(
- invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(IsArray | ContiguousShape)));
- break;
- }
- m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR);
- result.append(
- m_jit.branch32(invert ? MacroAssembler::Equal : MacroAssembler::NotEqual, tempGPR, TrustedImm32(ContiguousShape)));
- break;
- }
+ case Array::Int32:
+ return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, Int32Shape, invert);
+
+ case Array::Double:
+ return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, DoubleShape, invert);
+
+ case Array::Contiguous:
+ return jumpSlowForUnwantedArrayMode(tempGPR, arrayMode, ContiguousShape, invert);
+
case Array::ArrayStorage:
case Array::SlowPutArrayStorage: {
+ ASSERT(!arrayMode.isJSArrayWithOriginalStructure());
+
if (arrayMode.isJSArray()) {
if (arrayMode.isSlowPut()) {
if (invert) {
- JITCompiler::Jump slow =
- m_jit.branchTest32(
- MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray));
+ JITCompiler::Jump slow = m_jit.branchTest32(
+ MacroAssembler::Zero, tempGPR, MacroAssembler::TrustedImm32(IsArray));
m_jit.and32(TrustedImm32(IndexingShapeMask), tempGPR);
m_jit.sub32(TrustedImm32(ArrayStorageShape), tempGPR);
result.append(
@@ -426,7 +459,7 @@ void SpeculativeJIT::checkArray(Node& node)
const TypedArrayDescriptor* result = typedArrayDescriptor(node.arrayMode());
- if (node.arrayMode().alreadyChecked(m_state.forNode(node.child1()))) {
+ if (node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1()))) {
noResult(m_compileIndex);
return;
}
@@ -437,6 +470,8 @@ void SpeculativeJIT::checkArray(Node& node)
case Array::String:
expectedClassInfo = &JSString::s_info;
break;
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous:
case Array::ArrayStorage:
case Array::SlowPutArrayStorage: {
@@ -528,16 +563,30 @@ void SpeculativeJIT::arrayify(Node& node, GPRReg baseReg, GPRReg propertyReg)
// If we're allegedly creating contiguous storage and the index is bogus, then
// just don't.
- if (node.arrayMode().type() == Array::Contiguous && propertyReg != InvalidGPRReg) {
- speculationCheck(
- Uncountable, JSValueRegs(), NoNode,
- m_jit.branch32(
- MacroAssembler::AboveOrEqual, propertyReg, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)));
+ if (propertyReg != InvalidGPRReg) {
+ switch (node.arrayMode().type()) {
+ case Array::Int32:
+ case Array::Double:
+ case Array::Contiguous:
+ speculationCheck(
+ Uncountable, JSValueRegs(), NoNode,
+ m_jit.branch32(
+ MacroAssembler::AboveOrEqual, propertyReg, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)));
+ break;
+ default:
+ break;
+ }
}
// Now call out to create the array storage.
silentSpillAllRegisters(tempGPR);
switch (node.arrayMode().type()) {
+ case Array::Int32:
+ callOperation(operationEnsureInt32, tempGPR, baseReg);
+ break;
+ case Array::Double:
+ callOperation(operationEnsureDouble, tempGPR, baseReg);
+ break;
case Array::Contiguous:
callOperation(operationEnsureContiguous, tempGPR, baseReg);
break;
@@ -956,33 +1005,33 @@ static const char* dataFormatString(DataFormat format)
void SpeculativeJIT::dump(const char* label)
{
if (label)
- dataLog("<%s>\n", label);
+ dataLogF("<%s>\n", label);
- dataLog(" gprs:\n");
+ dataLogF(" gprs:\n");
m_gprs.dump();
- dataLog(" fprs:\n");
+ dataLogF(" fprs:\n");
m_fprs.dump();
- dataLog(" VirtualRegisters:\n");
+ dataLogF(" VirtualRegisters:\n");
for (unsigned i = 0; i < m_generationInfo.size(); ++i) {
GenerationInfo& info = m_generationInfo[i];
if (info.alive())
- dataLog(" % 3d:%s%s", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat()));
+ dataLogF(" % 3d:%s%s", i, dataFormatString(info.registerFormat()), dataFormatString(info.spillFormat()));
else
- dataLog(" % 3d:[__][__]", i);
+ dataLogF(" % 3d:[__][__]", i);
if (info.registerFormat() == DataFormatDouble)
- dataLog(":fpr%d\n", info.fpr());
+ dataLogF(":fpr%d\n", info.fpr());
else if (info.registerFormat() != DataFormatNone
#if USE(JSVALUE32_64)
&& !(info.registerFormat() & DataFormatJS)
#endif
) {
ASSERT(info.gpr() != InvalidGPRReg);
- dataLog(":%s\n", GPRInfo::debugName(info.gpr()));
+ dataLogF(":%s\n", GPRInfo::debugName(info.gpr()));
} else
- dataLog("\n");
+ dataLogF("\n");
}
if (label)
- dataLog("</%s>\n", label);
+ dataLogF("</%s>\n", label);
}
#endif
@@ -994,13 +1043,13 @@ void SpeculativeJIT::checkConsistency()
for (gpr_iterator iter = m_gprs.begin(); iter != m_gprs.end(); ++iter) {
if (iter.isLocked()) {
- dataLog("DFG_CONSISTENCY_CHECK failed: gpr %s is locked.\n", iter.debugName());
+ dataLogF("DFG_CONSISTENCY_CHECK failed: gpr %s is locked.\n", iter.debugName());
failed = true;
}
}
for (fpr_iterator iter = m_fprs.begin(); iter != m_fprs.end(); ++iter) {
if (iter.isLocked()) {
- dataLog("DFG_CONSISTENCY_CHECK failed: fpr %s is locked.\n", iter.debugName());
+ dataLogF("DFG_CONSISTENCY_CHECK failed: fpr %s is locked.\n", iter.debugName());
failed = true;
}
}
@@ -1028,7 +1077,7 @@ void SpeculativeJIT::checkConsistency()
GPRReg gpr = info.gpr();
ASSERT(gpr != InvalidGPRReg);
if (m_gprs.name(gpr) != virtualRegister) {
- dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (gpr %s).\n", virtualRegister, GPRInfo::debugName(gpr));
+ dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (gpr %s).\n", virtualRegister, GPRInfo::debugName(gpr));
failed = true;
}
break;
@@ -1037,7 +1086,7 @@ void SpeculativeJIT::checkConsistency()
FPRReg fpr = info.fpr();
ASSERT(fpr != InvalidFPRReg);
if (m_fprs.name(fpr) != virtualRegister) {
- dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (fpr %s).\n", virtualRegister, FPRInfo::debugName(fpr));
+ dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for virtual register %d (fpr %s).\n", virtualRegister, FPRInfo::debugName(fpr));
failed = true;
}
break;
@@ -1053,18 +1102,18 @@ void SpeculativeJIT::checkConsistency()
GenerationInfo& info = m_generationInfo[virtualRegister];
#if USE(JSVALUE64)
if (iter.regID() != info.gpr()) {
- dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister);
+ dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister);
failed = true;
}
#else
if (!(info.registerFormat() & DataFormatJS)) {
if (iter.regID() != info.gpr()) {
- dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister);
+ dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister);
failed = true;
}
} else {
if (iter.regID() != info.tagGPR() && iter.regID() != info.payloadGPR()) {
- dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister);
+ dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for gpr %s (virtual register %d).\n", iter.debugName(), virtualRegister);
failed = true;
}
}
@@ -1078,7 +1127,7 @@ void SpeculativeJIT::checkConsistency()
GenerationInfo& info = m_generationInfo[virtualRegister];
if (iter.regID() != info.fpr()) {
- dataLog("DFG_CONSISTENCY_CHECK failed: name mismatch for fpr %s (virtual register %d).\n", iter.debugName(), virtualRegister);
+ dataLogF("DFG_CONSISTENCY_CHECK failed: name mismatch for fpr %s (virtual register %d).\n", iter.debugName(), virtualRegister);
failed = true;
}
}
@@ -1496,7 +1545,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
#endif
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Setting up state for block #%u: ", m_block);
+ dataLogF("Setting up state for block #%u: ", m_block);
#endif
m_stream->appendAndLog(VariableEvent::reset());
@@ -1544,7 +1593,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
}
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("\n");
+ dataLogF("\n");
#endif
for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) {
@@ -1554,7 +1603,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
m_codeOriginForOSR = node.codeOrigin;
if (!node.shouldGenerate()) {
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset());
+ dataLogF("SpeculativeJIT skipping Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset());
#endif
switch (node.op()) {
case JSConstant:
@@ -1601,7 +1650,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
// The exception is the this argument, which we don't really need to be
// able to recover.
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("\nRecovery for argument %d: ", i);
+ dataLogF("\nRecovery for argument %d: ", i);
recovery.dump(WTF::dataFile());
#endif
inlineCallFrame->arguments[i] = recovery;
@@ -1617,7 +1666,7 @@ void SpeculativeJIT::compile(BasicBlock& block)
} else {
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset());
+ dataLogF("SpeculativeJIT generating Node @%d (bc#%u) at JIT offset 0x%x ", (int)m_compileIndex, node.codeOrigin.bytecodeIndex, m_jit.debugOffset());
#endif
#if DFG_ENABLE(JIT_BREAK_ON_EVERY_NODE)
m_jit.breakpoint();
@@ -1642,25 +1691,25 @@ void SpeculativeJIT::compile(BasicBlock& block)
#if DFG_ENABLE(DEBUG_VERBOSE)
if (node.hasResult()) {
GenerationInfo& info = m_generationInfo[node.virtualRegister()];
- dataLog("-> %s, vr#%d", dataFormatToString(info.registerFormat()), (int)node.virtualRegister());
+ dataLogF("-> %s, vr#%d", dataFormatToString(info.registerFormat()), (int)node.virtualRegister());
if (info.registerFormat() != DataFormatNone) {
if (info.registerFormat() == DataFormatDouble)
- dataLog(", %s", FPRInfo::debugName(info.fpr()));
+ dataLogF(", %s", FPRInfo::debugName(info.fpr()));
#if USE(JSVALUE32_64)
else if (info.registerFormat() & DataFormatJS)
- dataLog(", %s %s", GPRInfo::debugName(info.tagGPR()), GPRInfo::debugName(info.payloadGPR()));
+ dataLogF(", %s %s", GPRInfo::debugName(info.tagGPR()), GPRInfo::debugName(info.payloadGPR()));
#endif
else
- dataLog(", %s", GPRInfo::debugName(info.gpr()));
+ dataLogF(", %s", GPRInfo::debugName(info.gpr()));
}
- dataLog(" ");
+ dataLogF(" ");
} else
- dataLog(" ");
+ dataLogF(" ");
#endif
}
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("\n");
+ dataLogF("\n");
#endif
// Make sure that the abstract state is rematerialized for the next node.
@@ -1797,6 +1846,85 @@ ValueRecovery SpeculativeJIT::computeValueRecoveryFor(const ValueSource& valueSo
return ValueRecovery();
}
+void SpeculativeJIT::compileDoublePutByVal(Node& node, SpeculateCellOperand& base, SpeculateStrictInt32Operand& property)
+{
+ Edge child3 = m_jit.graph().varArgChild(node, 2);
+ Edge child4 = m_jit.graph().varArgChild(node, 3);
+
+ ArrayMode arrayMode = node.arrayMode();
+
+ GPRReg baseReg = base.gpr();
+ GPRReg propertyReg = property.gpr();
+
+ SpeculateDoubleOperand value(this, child3);
+
+ FPRReg valueReg = value.fpr();
+
+ if (!isRealNumberSpeculation(m_state.forNode(child3).m_type)) {
+ // FIXME: We need a way of profiling these, and we need to hoist them into
+ // SpeculateDoubleOperand.
+ speculationCheck(
+ BadType, JSValueRegs(), NoNode,
+ m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueReg, valueReg));
+ }
+
+ if (!m_compileOkay)
+ return;
+
+ StorageOperand storage(this, child4);
+ GPRReg storageReg = storage.gpr();
+
+ if (node.op() == PutByValAlias) {
+ // Store the value to the array.
+ GPRReg propertyReg = property.gpr();
+ FPRReg valueReg = value.fpr();
+ m_jit.storeDouble(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight));
+
+ noResult(m_compileIndex);
+ return;
+ }
+
+ GPRTemporary temporary;
+ GPRReg temporaryReg = temporaryRegisterForPutByVal(temporary, node);
+
+ MacroAssembler::JumpList slowCases;
+
+ if (arrayMode.isInBounds()) {
+ speculationCheck(
+ Uncountable, JSValueRegs(), NoNode,
+ m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
+ } else {
+ MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
+
+ slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength())));
+
+ if (!arrayMode.isOutOfBounds())
+ speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases);
+
+ m_jit.add32(TrustedImm32(1), propertyReg, temporaryReg);
+ m_jit.store32(temporaryReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
+
+ inBounds.link(&m_jit);
+ }
+
+ m_jit.storeDouble(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight));
+
+ base.use();
+ property.use();
+ value.use();
+ storage.use();
+
+ if (arrayMode.isOutOfBounds()) {
+ addSlowPathGenerator(
+ slowPathCall(
+ slowCases, this,
+ m_jit.codeBlock()->isStrictMode() ? operationPutDoubleByValBeyondArrayBoundsStrict : operationPutDoubleByValBeyondArrayBoundsNonStrict,
+ NoResult, baseReg, propertyReg, valueReg));
+ }
+
+ noResult(m_compileIndex, UseChildrenCalledExplicitly);
+}
+
void SpeculativeJIT::compileGetCharCodeAt(Node& node)
{
SpeculateCellOperand string(this, node.child1());
@@ -1841,7 +1969,7 @@ void SpeculativeJIT::compileGetByValOnString(Node& node)
GPRReg propertyReg = property.gpr();
GPRReg storageReg = storage.gpr();
- ASSERT(ArrayMode(Array::String).alreadyChecked(m_state.forNode(node.child1())));
+ ASSERT(ArrayMode(Array::String).alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1())));
// unsigned comparison so we can filter out negative indices and indices that are too large
speculationCheck(Uncountable, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSString::offsetOfLength())));
@@ -1878,7 +2006,7 @@ void SpeculativeJIT::compileGetByValOnString(Node& node)
GeneratedOperandType SpeculativeJIT::checkGeneratedTypeForToInt32(NodeIndex nodeIndex)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("checkGeneratedTypeForToInt32@%d ", nodeIndex);
+ dataLogF("checkGeneratedTypeForToInt32@%d ", nodeIndex);
#endif
Node& node = at(nodeIndex);
VirtualRegister virtualRegister = node.virtualRegister();
@@ -2250,7 +2378,7 @@ void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor&
GPRTemporary result(this);
GPRReg resultReg = result.gpr();
- ASSERT(node.arrayMode().alreadyChecked(m_state.forNode(node.child1())));
+ ASSERT(node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1())));
speculationCheck(
Uncountable, JSValueRegs(), NoNode,
@@ -2400,7 +2528,7 @@ void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor
GPRReg propertyReg = property.gpr();
GPRReg storageReg = storage.gpr();
- ASSERT(node.arrayMode().alreadyChecked(m_state.forNode(node.child1())));
+ ASSERT(node.arrayMode().alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1())));
FPRTemporary result(this);
FPRReg resultReg = result.fpr();
@@ -2437,7 +2565,7 @@ void SpeculativeJIT::compilePutByValForFloatTypedArray(const TypedArrayDescripto
SpeculateDoubleOperand valueOp(this, valueUse);
- ASSERT_UNUSED(baseUse, node.arrayMode().alreadyChecked(m_state.forNode(baseUse)));
+ ASSERT_UNUSED(baseUse, node.arrayMode().alreadyChecked(m_jit.graph(), m_jit.graph()[m_compileIndex], m_state.forNode(baseUse)));
GPRTemporary result(this);
@@ -2763,7 +2891,7 @@ void SpeculativeJIT::compileAdd(Node& node)
return;
}
- if (Node::shouldSpeculateNumber(at(node.child1()), at(node.child2()))) {
+ if (Node::shouldSpeculateNumberExpectingDefined(at(node.child1()), at(node.child2()))) {
SpeculateDoubleOperand op1(this, node.child1());
SpeculateDoubleOperand op2(this, node.child2());
FPRTemporary result(this, op1, op2);
@@ -3001,7 +3129,7 @@ void SpeculativeJIT::compileIntegerArithDivForX86(Node& node)
void SpeculativeJIT::compileArithMod(Node& node)
{
- if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2()))
+ if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2()))
&& node.canSpeculateInteger()) {
compileSoftModulo(node);
return;
@@ -3260,7 +3388,7 @@ void SpeculativeJIT::compileGetByValOnArguments(Node& node)
if (!m_compileOkay)
return;
- ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_state.forNode(node.child1())));
+ ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1())));
// Two really lame checks.
speculationCheck(
@@ -3317,7 +3445,7 @@ void SpeculativeJIT::compileGetArgumentsLength(Node& node)
if (!m_compileOkay)
return;
- ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_state.forNode(node.child1())));
+ ASSERT(ArrayMode(Array::Arguments).alreadyChecked(m_jit.graph(), node, m_state.forNode(node.child1())));
speculationCheck(
Uncountable, JSValueSource(), NoNode,
@@ -3336,6 +3464,8 @@ void SpeculativeJIT::compileGetArrayLength(Node& node)
const TypedArrayDescriptor* descriptor = typedArrayDescriptor(node.arrayMode());
switch (node.arrayMode().type()) {
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous: {
StorageOperand storage(this, node.child2());
GPRTemporary result(this, storage);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 446ea7dbe..f1384e269 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -1308,6 +1308,11 @@ public:
m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(identifier));
return appendCallWithExceptionCheckSetResult(operation, result);
}
+ JITCompiler::Call callOperation(J_DFGOperation_EDA operation, GPRReg result, FPRReg arg1, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallWithExceptionCheckSetResult(operation, result);
+ }
JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg result, GPRReg arg1, GPRReg arg2)
{
m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -1363,6 +1368,11 @@ public:
m_jit.setupArgumentsWithExecState(TrustedImmPtr(inlineCallFrame));
return appendCallWithExceptionCheckSetResult(operation, result);
}
+ JITCompiler::Call callOperation(C_DFGOperation_ESt operation, GPRReg result, Structure* structure)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure));
+ return appendCallWithExceptionCheckSetResult(operation, result);
+ }
JITCompiler::Call callOperation(S_DFGOperation_J operation, GPRReg result, GPRReg arg1)
{
m_jit.setupArguments(arg1);
@@ -1453,6 +1463,11 @@ public:
m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
return appendCallWithExceptionCheck(operation);
}
+ JITCompiler::Call callOperation(V_DFGOperation_EOZD operation, GPRReg arg1, GPRReg arg2, FPRReg arg3)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+ return appendCallWithExceptionCheck(operation);
+ }
JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3)
{
m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
@@ -1661,11 +1676,21 @@ public:
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, TrustedImm32(arg1Tag), TrustedImmPtr(identifier));
return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
}
+ JITCompiler::Call callOperation(J_DFGOperation_EDA operation, GPRReg resultTag, GPRReg resultPayload, FPRReg arg1, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2);
+ return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
+ }
JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload, GPRReg arg2)
{
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2);
return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
}
+ JITCompiler::Call callOperation(J_DFGOperation_EJA operation, GPRReg resultTag, GPRReg resultPayload, TrustedImm32 arg1Tag, GPRReg arg1Payload, GPRReg arg2)
+ {
+ m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag, arg2);
+ return appendCallWithExceptionCheckSetResult(operation, resultPayload, resultTag);
+ }
JITCompiler::Call callOperation(J_DFGOperation_EJ operation, GPRReg resultTag, GPRReg resultPayload, GPRReg arg1Tag, GPRReg arg1Payload)
{
m_jit.setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG arg1Payload, arg1Tag);
@@ -1716,6 +1741,11 @@ public:
m_jit.setupArgumentsWithExecState(TrustedImmPtr(inlineCallFrame));
return appendCallWithExceptionCheckSetResult(operation, result);
}
+ JITCompiler::Call callOperation(C_DFGOperation_ESt operation, GPRReg result, Structure* structure)
+ {
+ m_jit.setupArgumentsWithExecState(TrustedImmPtr(structure));
+ return appendCallWithExceptionCheckSetResult(operation, result);
+ }
JITCompiler::Call callOperation(S_DFGOperation_J operation, GPRReg result, GPRReg arg1Tag, GPRReg arg1Payload)
{
m_jit.setupArguments(arg1Payload, arg1Tag);
@@ -1819,11 +1849,21 @@ public:
m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag);
return appendCallWithExceptionCheck(operation);
}
+ JITCompiler::Call callOperation(V_DFGOperation_EOZD operation, GPRReg arg1, GPRReg arg2, FPRReg arg3)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2, arg3);
+ return appendCallWithExceptionCheck(operation);
+ }
JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3Tag, GPRReg arg3Payload)
{
m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag);
return appendCallWithExceptionCheck(operation);
}
+ JITCompiler::Call callOperation(V_DFGOperation_EOZJ operation, GPRReg arg1, GPRReg arg2, TrustedImm32 arg3Tag, GPRReg arg3Payload)
+ {
+ m_jit.setupArgumentsWithExecState(arg1, arg2, EABI_32BIT_DUMMY_ARG arg3Payload, arg3Tag);
+ return appendCallWithExceptionCheck(operation);
+ }
JITCompiler::Call callOperation(V_DFGOperation_W operation, WatchpointSet* watchpointSet)
{
m_jit.setupArguments(TrustedImmPtr(watchpointSet));
@@ -2270,6 +2310,11 @@ public:
void compileAllocatePropertyStorage(Node&);
void compileReallocatePropertyStorage(Node&);
+#if USE(JSVALUE32_64)
+ template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType>
+ void compileContiguousPutByVal(Node&, BaseOperandType&, PropertyOperandType&, ValueOperandType&, GPRReg valuePayloadReg, TagType valueTag);
+#endif
+ void compileDoublePutByVal(Node&, SpeculateCellOperand& base, SpeculateStrictInt32Operand& property);
bool putByValWillNeedExtraRegister(ArrayMode arrayMode)
{
return arrayMode.mayStoreToHole();
@@ -2415,6 +2460,7 @@ public:
const TypedArrayDescriptor* typedArrayDescriptor(ArrayMode);
+ JITCompiler::Jump jumpSlowForUnwantedArrayMode(GPRReg tempWithIndexingTypeReg, ArrayMode, IndexingType, bool invert);
JITCompiler::JumpList jumpSlowForUnwantedArrayMode(GPRReg tempWithIndexingTypeReg, ArrayMode, bool invert = false);
void checkArray(Node&);
void arrayify(Node&, GPRReg baseReg, GPRReg propertyReg);
@@ -2955,6 +3001,11 @@ public:
m_gprOrInvalid = m_jit->fillSpeculateInt(index(), m_format);
return m_gprOrInvalid;
}
+
+ void use()
+ {
+ m_jit->use(m_index);
+ }
private:
SpeculativeJIT* m_jit;
@@ -3035,6 +3086,11 @@ public:
m_fprOrInvalid = m_jit->fillSpeculateDouble(index());
return m_fprOrInvalid;
}
+
+ void use()
+ {
+ m_jit->use(m_index);
+ }
private:
SpeculativeJIT* m_jit;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 65fdf5593..05af6962e 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -29,9 +29,11 @@
#if ENABLE(DFG_JIT)
+#include "ArrayPrototype.h"
#include "DFGCallArrayAllocatorSlowPathGenerator.h"
#include "DFGSlowPathGenerator.h"
#include "JSActivation.h"
+#include "ObjectPrototype.h"
namespace JSC { namespace DFG {
@@ -996,7 +998,6 @@ void SpeculativeJIT::nonSpeculativeNonPeepholeStrictEq(Node& node, bool invert)
void SpeculativeJIT::emitCall(Node& node)
{
-
if (node.op() != Call)
ASSERT(node.op() == Construct);
@@ -1047,8 +1048,8 @@ void SpeculativeJIT::emitCall(Node& node)
m_jit.addPtr(TrustedImm32(m_jit.codeBlock()->m_numCalleeRegisters * sizeof(Register)), GPRInfo::callFrameRegister);
- slowPath.append(m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleePayloadGPR, targetToCheck));
slowPath.append(m_jit.branch32(MacroAssembler::NotEqual, calleeTagGPR, TrustedImm32(JSValue::CellTag)));
+ slowPath.append(m_jit.branchPtrWithPatch(MacroAssembler::NotEqual, calleePayloadGPR, targetToCheck));
m_jit.loadPtr(MacroAssembler::Address(calleePayloadGPR, OBJECT_OFFSETOF(JSFunction, m_scope)), resultPayloadGPR);
m_jit.storePtr(resultPayloadGPR, MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
m_jit.store32(MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register)) * JSStack::ScopeChain + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
@@ -1082,14 +1083,14 @@ void SpeculativeJIT::emitCall(Node& node)
jsValueResult(resultTagGPR, resultPayloadGPR, m_compileIndex, DataFormatJS, UseChildrenCalledExplicitly);
- m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, at(m_compileIndex).codeOrigin);
+ m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, calleePayloadGPR, at(m_compileIndex).codeOrigin);
}
template<bool strict>
GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& returnFormat)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpecInt@%d ", nodeIndex);
+ dataLogF("SpecInt@%d ", nodeIndex);
#endif
if (isKnownNotInteger(nodeIndex)) {
terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode);
@@ -1187,7 +1188,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntStrict(NodeIndex nodeIndex)
FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpecDouble@%d ", nodeIndex);
+ dataLogF("SpecDouble@%d ", nodeIndex);
#endif
if (isKnownNotNumber(nodeIndex)) {
terminateSpeculativeExecution(Uncountable, JSValueRegs(), NoNode);
@@ -1322,7 +1323,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex)
GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpecCell@%d ", nodeIndex);
+ dataLogF("SpecCell@%d ", nodeIndex);
#endif
if (isKnownNotCell(nodeIndex)) {
terminateSpeculativeExecutionWithConditionalDirection(Uncountable, JSValueRegs(), NoNode, isForwardSpeculation);
@@ -1397,7 +1398,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpec
GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpecBool@%d ", nodeIndex);
+ dataLogF("SpecBool@%d ", nodeIndex);
#endif
SpeculatedType type = m_state.forNode(nodeIndex).m_type;
Node& node = m_jit.graph()[nodeIndex];
@@ -2046,6 +2047,69 @@ void SpeculativeJIT::emitBranch(Node& node)
}
}
+template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType>
+void SpeculativeJIT::compileContiguousPutByVal(Node& node, BaseOperandType& base, PropertyOperandType& property, ValueOperandType& value, GPRReg valuePayloadReg, TagType valueTag)
+{
+ Edge child4 = m_jit.graph().varArgChild(node, 3);
+
+ ArrayMode arrayMode = node.arrayMode();
+
+ GPRReg baseReg = base.gpr();
+ GPRReg propertyReg = property.gpr();
+
+ StorageOperand storage(this, child4);
+ GPRReg storageReg = storage.gpr();
+
+ if (node.op() == PutByValAlias) {
+ // Store the value to the array.
+ GPRReg propertyReg = property.gpr();
+ m_jit.store32(valueTag, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+
+ noResult(m_compileIndex);
+ return;
+ }
+
+ MacroAssembler::JumpList slowCases;
+
+ if (arrayMode.isInBounds()) {
+ speculationCheck(
+ Uncountable, JSValueRegs(), NoNode,
+ m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
+ } else {
+ MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
+
+ slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength())));
+
+ if (!arrayMode.isOutOfBounds())
+ speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases);
+
+ m_jit.add32(TrustedImm32(1), propertyReg);
+ m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
+ m_jit.sub32(TrustedImm32(1), propertyReg);
+
+ inBounds.link(&m_jit);
+ }
+
+ m_jit.store32(valueTag, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+
+ base.use();
+ property.use();
+ value.use();
+ storage.use();
+
+ if (arrayMode.isOutOfBounds()) {
+ addSlowPathGenerator(
+ slowPathCall(
+ slowCases, this,
+ m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
+ NoResult, baseReg, propertyReg, valueTag, valuePayloadReg));
+ }
+
+ noResult(m_compileIndex, UseChildrenCalledExplicitly);
+}
+
void SpeculativeJIT::compile(Node& node)
{
NodeType op = node.op();
@@ -2064,6 +2128,18 @@ void SpeculativeJIT::compile(Node& node)
initConstantInfo(m_compileIndex);
break;
+ case Identity: {
+ // This could be done a lot better. We take the cheap way out because Identity
+ // is only going to stick around after CSE if we had prediction weirdness.
+ JSValueOperand operand(this, node.child1());
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ m_jit.move(operand.tagGPR(), resultTag.gpr());
+ m_jit.move(operand.payloadGPR(), resultPayload.gpr());
+ jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex);
+ break;
+ }
+
case GetLocal: {
SpeculatedType prediction = node.variableAccessData()->prediction();
AbstractValue& value = block()->valuesAtHead.operand(node.local());
@@ -2366,7 +2442,8 @@ void SpeculativeJIT::compile(Node& node)
break;
case ArithDiv: {
- if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) {
+ if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2()))
+ && node.canSpeculateInteger()) {
#if CPU(X86)
compileIntegerArithDivForX86(node);
#else // CPU(X86) -> so non-X86 code follows
@@ -2393,7 +2470,8 @@ void SpeculativeJIT::compile(Node& node)
}
case ArithAbs: {
- if (at(node.child1()).shouldSpeculateInteger() && node.canSpeculateInteger()) {
+ if (at(node.child1()).shouldSpeculateIntegerForArithmetic()
+ && node.canSpeculateInteger()) {
SpeculateIntegerOperand op1(this, node.child1());
GPRTemporary result(this, op1);
GPRTemporary scratch(this);
@@ -2417,7 +2495,8 @@ void SpeculativeJIT::compile(Node& node)
case ArithMin:
case ArithMax: {
- if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) {
+ if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2()))
+ && node.canSpeculateInteger()) {
SpeculateStrictInt32Operand op1(this, node.child1());
SpeculateStrictInt32Operand op2(this, node.child2());
GPRTemporary result(this, op1);
@@ -2567,6 +2646,7 @@ void SpeculativeJIT::compile(Node& node)
jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex);
break;
}
+ case Array::Int32:
case Array::Contiguous: {
if (node.arrayMode().isInBounds()) {
SpeculateStrictInt32Operand property(this, node.child2());
@@ -2580,8 +2660,20 @@ void SpeculativeJIT::compile(Node& node)
speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
- GPRTemporary resultTag(this);
GPRTemporary resultPayload(this);
+ if (node.arrayMode().type() == Array::Int32) {
+ speculationCheck(
+ OutOfBounds, JSValueRegs(), NoNode,
+ m_jit.branch32(
+ MacroAssembler::Equal,
+ MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)),
+ TrustedImm32(JSValue::EmptyValueTag)));
+ m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr());
+ integerResult(resultPayload.gpr(), m_compileIndex);
+ break;
+ }
+
+ GPRTemporary resultTag(this);
m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr());
speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag)));
m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr());
@@ -2621,6 +2713,68 @@ void SpeculativeJIT::compile(Node& node)
jsValueResult(resultTagReg, resultPayloadReg, m_compileIndex);
break;
}
+ case Array::Double: {
+ if (node.arrayMode().isInBounds()) {
+ if (node.arrayMode().isSaneChain()) {
+ JSGlobalObject* globalObject = m_jit.globalObjectFor(node.codeOrigin);
+ ASSERT(globalObject->arrayPrototypeChainIsSane());
+ globalObject->arrayPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint());
+ globalObject->objectPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint());
+ }
+
+ SpeculateStrictInt32Operand property(this, node.child2());
+ StorageOperand storage(this, node.child3());
+
+ GPRReg propertyReg = property.gpr();
+ GPRReg storageReg = storage.gpr();
+
+ if (!m_compileOkay)
+ return;
+
+ speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
+
+ FPRTemporary result(this);
+ m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.fpr());
+ if (!node.arrayMode().isSaneChain())
+ speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr()));
+ doubleResult(result.fpr(), m_compileIndex);
+ break;
+ }
+
+ SpeculateCellOperand base(this, node.child1());
+ SpeculateStrictInt32Operand property(this, node.child2());
+ StorageOperand storage(this, node.child3());
+
+ GPRReg baseReg = base.gpr();
+ GPRReg propertyReg = property.gpr();
+ GPRReg storageReg = storage.gpr();
+
+ if (!m_compileOkay)
+ return;
+
+ GPRTemporary resultTag(this);
+ GPRTemporary resultPayload(this);
+ FPRTemporary temp(this);
+ GPRReg resultTagReg = resultTag.gpr();
+ GPRReg resultPayloadReg = resultPayload.gpr();
+ FPRReg tempReg = temp.fpr();
+
+ MacroAssembler::JumpList slowCases;
+
+ slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
+
+ m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), tempReg);
+ slowCases.append(m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempReg, tempReg));
+ boxDouble(tempReg, resultTagReg, resultPayloadReg);
+
+ addSlowPathGenerator(
+ slowPathCall(
+ slowCases, this, operationGetByValArrayInt,
+ JSValueRegs(resultTagReg, resultPayloadReg), baseReg, propertyReg));
+
+ jsValueResult(resultTagReg, resultPayloadReg, m_compileIndex);
+ break;
+ }
case Array::ArrayStorage:
case Array::SlowPutArrayStorage: {
if (node.arrayMode().isInBounds()) {
@@ -2771,6 +2925,17 @@ void SpeculativeJIT::compile(Node& node)
GPRReg propertyReg = property.gpr();
switch (arrayMode.type()) {
+ case Array::Int32: {
+ SpeculateIntegerOperand value(this, child3);
+
+ GPRReg valuePayloadReg = value.gpr();
+
+ if (!m_compileOkay)
+ return;
+
+ compileContiguousPutByVal(node, base, property, value, valuePayloadReg, TrustedImm32(JSValue::Int32Tag));
+ break;
+ }
case Array::Contiguous: {
JSValueOperand value(this, child3);
@@ -2784,61 +2949,14 @@ void SpeculativeJIT::compile(Node& node)
GPRTemporary scratch(this);
writeBarrier(baseReg, valueTagReg, child3, WriteBarrierForPropertyAccess, scratch.gpr());
}
-
- StorageOperand storage(this, child4);
- GPRReg storageReg = storage.gpr();
-
- if (node.op() == PutByValAlias) {
- // Store the value to the array.
- GPRReg propertyReg = property.gpr();
- m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
- m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
-
- noResult(m_compileIndex);
- break;
- }
-
- MacroAssembler::JumpList slowCases;
-
- if (arrayMode.isInBounds()) {
- speculationCheck(
- Uncountable, JSValueRegs(), NoNode,
- m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
- } else {
- MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
-
- slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfVectorLength())));
-
- if (!arrayMode.isOutOfBounds())
- speculationCheck(Uncountable, JSValueRegs(), NoNode, slowCases);
-
- m_jit.add32(TrustedImm32(1), propertyReg);
- m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength()));
- m_jit.sub32(TrustedImm32(1), propertyReg);
-
- inBounds.link(&m_jit);
- }
-
- m_jit.store32(valueTagReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
- m_jit.store32(valuePayloadReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
- base.use();
- property.use();
- value.use();
- storage.use();
-
- if (arrayMode.isOutOfBounds()) {
- addSlowPathGenerator(
- slowPathCall(
- slowCases, this,
- m_jit.codeBlock()->isStrictMode() ? operationPutByValBeyondArrayBoundsStrict : operationPutByValBeyondArrayBoundsNonStrict,
- NoResult, baseReg, propertyReg, valueTagReg, valuePayloadReg));
- }
-
- noResult(m_compileIndex, UseChildrenCalledExplicitly);
+ compileContiguousPutByVal(node, base, property, value, valuePayloadReg, valueTagReg);
+ break;
+ }
+ case Array::Double: {
+ compileDoublePutByVal(node, base, property);
break;
}
-
case Array::ArrayStorage:
case Array::SlowPutArrayStorage: {
JSValueOperand value(this, child3);
@@ -3028,24 +3146,47 @@ void SpeculativeJIT::compile(Node& node)
ASSERT(node.arrayMode().isJSArray());
SpeculateCellOperand base(this, node.child1());
- JSValueOperand value(this, node.child2());
GPRTemporary storageLength(this);
GPRReg baseGPR = base.gpr();
- GPRReg valueTagGPR = value.tagGPR();
- GPRReg valuePayloadGPR = value.payloadGPR();
GPRReg storageLengthGPR = storageLength.gpr();
- if (Heap::isWriteBarrierEnabled()) {
- GPRTemporary scratch(this);
- writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR);
- }
-
StorageOperand storage(this, node.child3());
GPRReg storageGPR = storage.gpr();
switch (node.arrayMode().type()) {
+ case Array::Int32: {
+ SpeculateIntegerOperand value(this, node.child2());
+ GPRReg valuePayloadGPR = value.gpr();
+
+ m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR);
+ MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
+ m_jit.store32(TrustedImm32(JSValue::Int32Tag), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(valuePayloadGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+ m_jit.add32(TrustedImm32(1), storageLengthGPR);
+ m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
+ m_jit.move(TrustedImm32(JSValue::Int32Tag), storageGPR);
+
+ addSlowPathGenerator(
+ slowPathCall(
+ slowPath, this, operationArrayPush,
+ JSValueRegs(storageGPR, storageLengthGPR),
+ TrustedImm32(JSValue::Int32Tag), valuePayloadGPR, baseGPR));
+
+ jsValueResult(storageGPR, storageLengthGPR, m_compileIndex);
+ break;
+ }
+
case Array::Contiguous: {
+ JSValueOperand value(this, node.child2());
+ GPRReg valueTagGPR = value.tagGPR();
+ GPRReg valuePayloadGPR = value.payloadGPR();
+
+ if (Heap::isWriteBarrierEnabled()) {
+ GPRTemporary scratch(this);
+ writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR);
+ }
+
m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR);
MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
m_jit.store32(valueTagGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
@@ -3064,7 +3205,45 @@ void SpeculativeJIT::compile(Node& node)
break;
}
+ case Array::Double: {
+ SpeculateDoubleOperand value(this, node.child2());
+ FPRReg valueFPR = value.fpr();
+
+ if (!isRealNumberSpeculation(m_state.forNode(node.child2()).m_type)) {
+ // FIXME: We need a way of profiling these, and we need to hoist them into
+ // SpeculateDoubleOperand.
+ speculationCheck(
+ BadType, JSValueRegs(), NoNode,
+ m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueFPR, valueFPR));
+ }
+
+ m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR);
+ MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
+ m_jit.storeDouble(valueFPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight));
+ m_jit.add32(TrustedImm32(1), storageLengthGPR);
+ m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
+ m_jit.move(TrustedImm32(JSValue::Int32Tag), storageGPR);
+
+ addSlowPathGenerator(
+ slowPathCall(
+ slowPath, this, operationArrayPushDouble,
+ JSValueRegs(storageGPR, storageLengthGPR),
+ valueFPR, baseGPR));
+
+ jsValueResult(storageGPR, storageLengthGPR, m_compileIndex);
+ break;
+ }
+
case Array::ArrayStorage: {
+ JSValueOperand value(this, node.child2());
+ GPRReg valueTagGPR = value.tagGPR();
+ GPRReg valuePayloadGPR = value.payloadGPR();
+
+ if (Heap::isWriteBarrierEnabled()) {
+ GPRTemporary scratch(this);
+ writeBarrier(baseGPR, valueTagGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR);
+ }
+
m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR);
// Refuse to handle bizarre lengths.
@@ -3107,6 +3286,7 @@ void SpeculativeJIT::compile(Node& node)
GPRReg storageGPR = storage.gpr();
switch (node.arrayMode().type()) {
+ case Array::Int32:
case Array::Contiguous: {
m_jit.load32(
MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), valuePayloadGPR);
@@ -3140,6 +3320,44 @@ void SpeculativeJIT::compile(Node& node)
break;
}
+ case Array::Double: {
+ FPRTemporary temp(this);
+ FPRReg tempFPR = temp.fpr();
+
+ m_jit.load32(
+ MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), valuePayloadGPR);
+ MacroAssembler::Jump undefinedCase =
+ m_jit.branchTest32(MacroAssembler::Zero, valuePayloadGPR);
+ m_jit.sub32(TrustedImm32(1), valuePayloadGPR);
+ m_jit.store32(
+ valuePayloadGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
+ m_jit.loadDouble(
+ MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight),
+ tempFPR);
+ MacroAssembler::Jump slowCase = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempFPR, tempFPR);
+ JSValue nan = JSValue(JSValue::EncodeAsDouble, QNaN);
+ m_jit.store32(
+ MacroAssembler::TrustedImm32(nan.u.asBits.tag),
+ MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(
+ MacroAssembler::TrustedImm32(nan.u.asBits.payload),
+ MacroAssembler::BaseIndex(storageGPR, valuePayloadGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+ boxDouble(tempFPR, valueTagGPR, valuePayloadGPR);
+
+ addSlowPathGenerator(
+ slowPathMove(
+ undefinedCase, this,
+ MacroAssembler::TrustedImm32(jsUndefined().tag()), valueTagGPR,
+ MacroAssembler::TrustedImm32(jsUndefined().payload()), valuePayloadGPR));
+ addSlowPathGenerator(
+ slowPathCall(
+ slowCase, this, operationArrayPopAndRecoverLength,
+ JSValueRegs(valueTagGPR, valuePayloadGPR), baseGPR));
+
+ jsValueResult(valueTagGPR, valuePayloadGPR, m_compileIndex);
+ break;
+ }
+
case Array::ArrayStorage: {
GPRTemporary storageLength(this);
GPRReg storageLengthGPR = storageLength.gpr();
@@ -3358,11 +3576,17 @@ void SpeculativeJIT::compile(Node& node)
case NewArray: {
JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin);
- if (!globalObject->isHavingABadTime()) {
+ if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) {
globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint());
- ASSERT(hasContiguous(globalObject->arrayStructure()->indexingType()));
-
+ Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType());
+ ASSERT(structure->indexingType() == node.indexingType());
+ ASSERT(
+ hasUndecided(structure->indexingType())
+ || hasInt32(structure->indexingType())
+ || hasDouble(structure->indexingType())
+ || hasContiguous(structure->indexingType()));
+
unsigned numElements = node.numChildren();
GPRTemporary result(this);
@@ -3371,17 +3595,52 @@ void SpeculativeJIT::compile(Node& node)
GPRReg resultGPR = result.gpr();
GPRReg storageGPR = storage.gpr();
- emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements);
+ emitAllocateJSArray(structure, resultGPR, storageGPR, numElements);
// At this point, one way or another, resultGPR and storageGPR have pointers to
// the JSArray and the Butterfly, respectively.
+ ASSERT(!hasUndecided(structure->indexingType()) || !node.numChildren());
+
for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) {
- JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]);
- GPRReg opTagGPR = operand.tagGPR();
- GPRReg opPayloadGPR = operand.payloadGPR();
- m_jit.store32(opTagGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
- m_jit.store32(opPayloadGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+ Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx];
+ switch (node.indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ CRASH();
+ break;
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ SpeculateDoubleOperand operand(this, use);
+ FPRReg opFPR = operand.fpr();
+ if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) {
+ // FIXME: We need a way of profiling these, and we need to hoist them into
+ // SpeculateDoubleOperand.
+ speculationCheck(
+ BadType, JSValueRegs(), NoNode,
+ m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR));
+ }
+
+ m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIdx));
+ break;
+ }
+ case ALL_INT32_INDEXING_TYPES: {
+ SpeculateIntegerOperand operand(this, use);
+ m_jit.store32(TrustedImm32(JSValue::Int32Tag), MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(operand.gpr(), MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+ break;
+ }
+ case ALL_CONTIGUOUS_INDEXING_TYPES: {
+ JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]);
+ GPRReg opTagGPR = operand.tagGPR();
+ GPRReg opPayloadGPR = operand.payloadGPR();
+ m_jit.store32(opTagGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(opPayloadGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx + OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+ break;
+ }
+ default:
+ CRASH();
+ break;
+ }
}
// Yuck, we should *really* have a way of also returning the storageGPR. But
@@ -3399,7 +3658,7 @@ void SpeculativeJIT::compile(Node& node)
flushRegisters();
GPRResult result(this);
callOperation(
- operationNewEmptyArray, result.gpr(), globalObject->arrayStructure());
+ operationNewEmptyArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()));
cellResult(result.gpr(), m_compileIndex);
break;
}
@@ -3409,13 +3668,61 @@ void SpeculativeJIT::compile(Node& node)
EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) {
- JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]);
- GPRReg opTagGPR = operand.tagGPR();
- GPRReg opPayloadGPR = operand.payloadGPR();
- operand.use();
-
- m_jit.store32(opTagGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
- m_jit.store32(opPayloadGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
+ // Need to perform the speculations that this node promises to perform. If we're
+ // emitting code here and the indexing type is not array storage then there is
+ // probably something hilarious going on and we're already failing at all the
+ // things, but at least we're going to be sound.
+ Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx];
+ switch (node.indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ CRASH();
+ break;
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ SpeculateDoubleOperand operand(this, use);
+ FPRReg opFPR = operand.fpr();
+ if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) {
+ // FIXME: We need a way of profiling these, and we need to hoist them into
+ // SpeculateDoubleOperand.
+ speculationCheck(
+ BadType, JSValueRegs(), NoNode,
+ m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR));
+ }
+
+ m_jit.storeDouble(opFPR, reinterpret_cast<char*>(buffer + operandIdx));
+ break;
+ }
+ case ALL_INT32_INDEXING_TYPES: {
+ SpeculateIntegerOperand operand(this, use);
+ GPRReg opGPR = operand.gpr();
+ m_jit.store32(TrustedImm32(JSValue::Int32Tag), reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
+ m_jit.store32(opGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
+ break;
+ }
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
+ JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]);
+ GPRReg opTagGPR = operand.tagGPR();
+ GPRReg opPayloadGPR = operand.payloadGPR();
+
+ m_jit.store32(opTagGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
+ m_jit.store32(opPayloadGPR, reinterpret_cast<char*>(buffer + operandIdx) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
+ operand.use();
+ break;
+ }
+ default:
+ CRASH();
+ break;
+ }
+ }
+
+ switch (node.indexingType()) {
+ case ALL_DOUBLE_INDEXING_TYPES:
+ case ALL_INT32_INDEXING_TYPES:
+ useChildren(node);
+ break;
+ default:
+ break;
}
flushRegisters();
@@ -3431,8 +3738,8 @@ void SpeculativeJIT::compile(Node& node)
GPRResult result(this);
callOperation(
- operationNewArray, result.gpr(), globalObject->arrayStructure(),
- static_cast<void *>(buffer), node.numChildren());
+ operationNewArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()),
+ static_cast<void*>(buffer), node.numChildren());
if (scratchSize) {
GPRTemporary scratch(this);
@@ -3471,17 +3778,30 @@ void SpeculativeJIT::compile(Node& node)
emitAllocateBasicStorage(resultGPR, storageGPR));
m_jit.subPtr(scratchGPR, storageGPR);
emitAllocateBasicJSObject<JSArray, MarkedBlock::None>(
- TrustedImmPtr(globalObject->arrayStructure()), resultGPR, scratchGPR,
+ TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), resultGPR, scratchGPR,
storageGPR, sizeof(JSArray), slowCases);
m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
+ if (hasDouble(node.indexingType())) {
+ JSValue nan = JSValue(JSValue::EncodeAsDouble, QNaN);
+
+ m_jit.move(sizeGPR, scratchGPR);
+ MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratchGPR);
+ MacroAssembler::Label loop = m_jit.label();
+ m_jit.sub32(TrustedImm32(1), scratchGPR);
+ m_jit.store32(TrustedImm32(nan.u.asBits.tag), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)));
+ m_jit.store32(TrustedImm32(nan.u.asBits.payload), MacroAssembler::BaseIndex(storageGPR, scratchGPR, MacroAssembler::TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)));
+ m_jit.branchTest32(MacroAssembler::NonZero, scratchGPR).linkTo(loop, &m_jit);
+ done.link(&m_jit);
+ }
+
addSlowPathGenerator(adoptPtr(
new CallArrayAllocatorWithVariableSizeSlowPathGenerator(
slowCases, this, operationNewArrayWithSize, resultGPR,
- globalObject->arrayStructure(),
- globalObject->arrayStructureWithArrayStorage(),
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()),
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage),
sizeGPR)));
cellResult(resultGPR, m_compileIndex);
@@ -3492,15 +3812,24 @@ void SpeculativeJIT::compile(Node& node)
GPRReg sizeGPR = size.gpr();
flushRegisters();
GPRResult result(this);
+ GPRReg resultGPR = result.gpr();
+ GPRReg structureGPR = selectScratchGPR(sizeGPR);
+ MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX));
+ m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), structureGPR);
+ MacroAssembler::Jump done = m_jit.jump();
+ bigLength.link(&m_jit);
+ m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage)), structureGPR);
+ done.link(&m_jit);
callOperation(
- operationNewArrayWithSize, result.gpr(), globalObject->arrayStructure(), sizeGPR);
- cellResult(result.gpr(), m_compileIndex);
+ operationNewArrayWithSize, resultGPR, structureGPR, sizeGPR);
+ cellResult(resultGPR, m_compileIndex);
break;
}
case NewArrayBuffer: {
JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin);
- if (!globalObject->isHavingABadTime()) {
+ IndexingType indexingType = node.indexingType();
+ if (!globalObject->isHavingABadTime() && !hasArrayStorage(indexingType)) {
globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint());
unsigned numElements = node.numConstants();
@@ -3511,12 +3840,25 @@ void SpeculativeJIT::compile(Node& node)
GPRReg resultGPR = result.gpr();
GPRReg storageGPR = storage.gpr();
- emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements);
+ emitAllocateJSArray(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), resultGPR, storageGPR, numElements);
- int32_t* data = bitwise_cast<int32_t*>(m_jit.codeBlock()->constantBuffer(node.startConstant()));
- for (unsigned index = 0; index < node.numConstants() * 2; ++index) {
- m_jit.store32(
- Imm32(data[index]), MacroAssembler::Address(storageGPR, sizeof(int32_t) * index));
+ if (node.indexingType() == ArrayWithDouble) {
+ JSValue* data = m_jit.codeBlock()->constantBuffer(node.startConstant());
+ for (unsigned index = 0; index < node.numConstants(); ++index) {
+ union {
+ int32_t halves[2];
+ double value;
+ } u;
+ u.value = data[index].asNumber();
+ m_jit.store32(Imm32(u.halves[0]), MacroAssembler::Address(storageGPR, sizeof(double) * index));
+ m_jit.store32(Imm32(u.halves[1]), MacroAssembler::Address(storageGPR, sizeof(double) * index + sizeof(int32_t)));
+ }
+ } else {
+ int32_t* data = bitwise_cast<int32_t*>(m_jit.codeBlock()->constantBuffer(node.startConstant()));
+ for (unsigned index = 0; index < node.numConstants() * 2; ++index) {
+ m_jit.store32(
+ Imm32(data[index]), MacroAssembler::Address(storageGPR, sizeof(int32_t) * index));
+ }
}
cellResult(resultGPR, m_compileIndex);
@@ -3526,7 +3868,7 @@ void SpeculativeJIT::compile(Node& node)
flushRegisters();
GPRResult result(this);
- callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructure(), node.startConstant(), node.numConstants());
+ callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), node.startConstant(), node.numConstants());
cellResult(result.gpr(), m_compileIndex);
break;
@@ -3630,6 +3972,12 @@ void SpeculativeJIT::compile(Node& node)
break;
}
+ case InheritorIDWatchpoint: {
+ jsCast<JSFunction*>(node.function())->addInheritorIDWatchpoint(speculationWatchpoint());
+ noResult(m_compileIndex);
+ break;
+ }
+
case NewObject: {
GPRTemporary result(this);
GPRTemporary scratch(this);
@@ -3639,9 +3987,9 @@ void SpeculativeJIT::compile(Node& node)
MacroAssembler::JumpList slowPath;
- emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(m_jit.globalObjectFor(node.codeOrigin)->emptyObjectStructure()), resultGPR, scratchGPR, slowPath);
+ emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(node.structure()), resultGPR, scratchGPR, slowPath);
- addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR));
+ addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR, node.structure()));
cellResult(resultGPR, m_compileIndex);
break;
@@ -3810,7 +4158,7 @@ void SpeculativeJIT::compile(Node& node)
case CheckFunction: {
SpeculateCellOperand function(this, node.child1());
- speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function()));
+ speculationCheck(BadCache, JSValueSource::unboxedCell(function.gpr()), node.child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function()));
noResult(m_compileIndex);
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 6c066c388..da6583c70 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -29,8 +29,10 @@
#if ENABLE(DFG_JIT)
#include "Arguments.h"
+#include "ArrayPrototype.h"
#include "DFGCallArrayAllocatorSlowPathGenerator.h"
#include "DFGSlowPathGenerator.h"
+#include "ObjectPrototype.h"
namespace JSC { namespace DFG {
@@ -1056,14 +1058,14 @@ void SpeculativeJIT::emitCall(Node& node)
jsValueResult(resultGPR, m_compileIndex, DataFormatJS, UseChildrenCalledExplicitly);
- m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, at(m_compileIndex).codeOrigin);
+ m_jit.addJSCall(fastCall, slowCall, targetToCheck, callType, calleeGPR, at(m_compileIndex).codeOrigin);
}
template<bool strict>
GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat& returnFormat)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpecInt@%d ", nodeIndex);
+ dataLogF("SpecInt@%d ", nodeIndex);
#endif
SpeculatedType type = m_state.forNode(nodeIndex).m_type;
Node& node = at(nodeIndex);
@@ -1211,7 +1213,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntStrict(NodeIndex nodeIndex)
FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpecDouble@%d ", nodeIndex);
+ dataLogF("SpecDouble@%d ", nodeIndex);
#endif
SpeculatedType type = m_state.forNode(nodeIndex).m_type;
Node& node = at(nodeIndex);
@@ -1365,7 +1367,7 @@ FPRReg SpeculativeJIT::fillSpeculateDouble(NodeIndex nodeIndex)
GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpeculation)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpecCell@%d ", nodeIndex);
+ dataLogF("SpecCell@%d ", nodeIndex);
#endif
SpeculatedType type = m_state.forNode(nodeIndex).m_type;
Node& node = at(nodeIndex);
@@ -1441,7 +1443,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex, bool isForwardSpec
GPRReg SpeculativeJIT::fillSpeculateBoolean(NodeIndex nodeIndex)
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("SpecBool@%d ", nodeIndex);
+ dataLogF("SpecBool@%d ", nodeIndex);
#endif
SpeculatedType type = m_state.forNode(nodeIndex).m_type;
Node& node = at(nodeIndex);
@@ -2128,6 +2130,16 @@ void SpeculativeJIT::compile(Node& node)
m_jit.addWeakReference(node.weakConstant());
initConstantInfo(m_compileIndex);
break;
+
+ case Identity: {
+ // This could be done a lot better. We take the cheap way out because Identity
+ // is only going to stick around after CSE if we had prediction weirdness.
+ JSValueOperand operand(this, node.child1());
+ GPRTemporary result(this, operand);
+ m_jit.move(operand.gpr(), result.gpr());
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
case GetLocal: {
SpeculatedType prediction = node.variableAccessData()->prediction();
@@ -2403,7 +2415,8 @@ void SpeculativeJIT::compile(Node& node)
break;
case ArithDiv: {
- if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) {
+ if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2()))
+ && node.canSpeculateInteger()) {
compileIntegerArithDivForX86(node);
break;
}
@@ -2426,7 +2439,8 @@ void SpeculativeJIT::compile(Node& node)
}
case ArithAbs: {
- if (at(node.child1()).shouldSpeculateInteger() && node.canSpeculateInteger()) {
+ if (at(node.child1()).shouldSpeculateIntegerForArithmetic()
+ && node.canSpeculateInteger()) {
SpeculateIntegerOperand op1(this, node.child1());
GPRTemporary result(this);
GPRTemporary scratch(this);
@@ -2450,7 +2464,8 @@ void SpeculativeJIT::compile(Node& node)
case ArithMin:
case ArithMax: {
- if (Node::shouldSpeculateInteger(at(node.child1()), at(node.child2())) && node.canSpeculateInteger()) {
+ if (Node::shouldSpeculateIntegerForArithmetic(at(node.child1()), at(node.child2()))
+ && node.canSpeculateInteger()) {
SpeculateStrictInt32Operand op1(this, node.child1());
SpeculateStrictInt32Operand op2(this, node.child2());
GPRTemporary result(this, op1);
@@ -2598,6 +2613,7 @@ void SpeculativeJIT::compile(Node& node)
jsValueResult(result.gpr(), m_compileIndex);
break;
}
+ case Array::Int32:
case Array::Contiguous: {
if (node.arrayMode().isInBounds()) {
SpeculateStrictInt32Operand property(this, node.child2());
@@ -2614,7 +2630,7 @@ void SpeculativeJIT::compile(Node& node)
GPRTemporary result(this);
m_jit.load64(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.gpr());
speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchTest64(MacroAssembler::Zero, result.gpr()));
- jsValueResult(result.gpr(), m_compileIndex);
+ jsValueResult(result.gpr(), m_compileIndex, node.arrayMode().type() == Array::Int32 ? DataFormatJSInteger : DataFormatJS);
break;
}
@@ -2647,6 +2663,68 @@ void SpeculativeJIT::compile(Node& node)
jsValueResult(resultReg, m_compileIndex);
break;
}
+
+ case Array::Double: {
+ if (node.arrayMode().isInBounds()) {
+ if (node.arrayMode().isSaneChain()) {
+ JSGlobalObject* globalObject = m_jit.globalObjectFor(node.codeOrigin);
+ ASSERT(globalObject->arrayPrototypeChainIsSane());
+ globalObject->arrayPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint());
+ globalObject->objectPrototype()->structure()->addTransitionWatchpoint(speculationWatchpoint());
+ }
+
+ SpeculateStrictInt32Operand property(this, node.child2());
+ StorageOperand storage(this, node.child3());
+
+ GPRReg propertyReg = property.gpr();
+ GPRReg storageReg = storage.gpr();
+
+ if (!m_compileOkay)
+ return;
+
+ speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
+
+ FPRTemporary result(this);
+ m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), result.fpr());
+ if (!node.arrayMode().isSaneChain())
+ speculationCheck(OutOfBounds, JSValueRegs(), NoNode, m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, result.fpr(), result.fpr()));
+ doubleResult(result.fpr(), m_compileIndex);
+ break;
+ }
+
+ SpeculateCellOperand base(this, node.child1());
+ SpeculateStrictInt32Operand property(this, node.child2());
+ StorageOperand storage(this, node.child3());
+
+ GPRReg baseReg = base.gpr();
+ GPRReg propertyReg = property.gpr();
+ GPRReg storageReg = storage.gpr();
+
+ if (!m_compileOkay)
+ return;
+
+ GPRTemporary result(this);
+ FPRTemporary temp(this);
+ GPRReg resultReg = result.gpr();
+ FPRReg tempReg = temp.fpr();
+
+ MacroAssembler::JumpList slowCases;
+
+ slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(storageReg, Butterfly::offsetOfPublicLength())));
+
+ m_jit.loadDouble(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight), tempReg);
+ slowCases.append(m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempReg, tempReg));
+ boxDouble(tempReg, resultReg);
+
+ addSlowPathGenerator(
+ slowPathCall(
+ slowCases, this, operationGetByValArrayInt,
+ result.gpr(), baseReg, propertyReg));
+
+ jsValueResult(resultReg, m_compileIndex);
+ break;
+ }
+
case Array::ArrayStorage:
case Array::SlowPutArrayStorage: {
if (node.arrayMode().isInBounds()) {
@@ -2789,6 +2867,7 @@ void SpeculativeJIT::compile(Node& node)
GPRReg propertyReg = property.gpr();
switch (arrayMode.type()) {
+ case Array::Int32:
case Array::Contiguous: {
JSValueOperand value(this, child3);
@@ -2796,8 +2875,15 @@ void SpeculativeJIT::compile(Node& node)
if (!m_compileOkay)
return;
+
+ if (arrayMode.type() == Array::Int32
+ && !isInt32Speculation(m_state.forNode(child3).m_type)) {
+ speculationCheck(
+ BadType, JSValueRegs(valueReg), child3,
+ m_jit.branch64(MacroAssembler::Below, valueReg, GPRInfo::tagTypeNumberRegister));
+ }
- if (Heap::isWriteBarrierEnabled()) {
+ if (arrayMode.type() == Array::Contiguous && Heap::isWriteBarrierEnabled()) {
GPRTemporary scratch(this);
writeBarrier(baseReg, value.gpr(), child3, WriteBarrierForPropertyAccess, scratch.gpr());
}
@@ -2857,6 +2943,11 @@ void SpeculativeJIT::compile(Node& node)
break;
}
+ case Array::Double: {
+ compileDoublePutByVal(node, base, property);
+ break;
+ }
+
case Array::ArrayStorage:
case Array::SlowPutArrayStorage: {
JSValueOperand value(this, child3);
@@ -3081,23 +3172,31 @@ void SpeculativeJIT::compile(Node& node)
ASSERT(node.arrayMode().isJSArray());
SpeculateCellOperand base(this, node.child1());
- JSValueOperand value(this, node.child2());
GPRTemporary storageLength(this);
GPRReg baseGPR = base.gpr();
- GPRReg valueGPR = value.gpr();
GPRReg storageLengthGPR = storageLength.gpr();
- if (Heap::isWriteBarrierEnabled()) {
- GPRTemporary scratch(this);
- writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR);
- }
-
StorageOperand storage(this, node.child3());
GPRReg storageGPR = storage.gpr();
switch (node.arrayMode().type()) {
+ case Array::Int32:
case Array::Contiguous: {
+ JSValueOperand value(this, node.child2());
+ GPRReg valueGPR = value.gpr();
+
+ if (node.arrayMode().type() == Array::Int32 && !isInt32Speculation(m_state.forNode(node.child2()).m_type)) {
+ speculationCheck(
+ BadType, JSValueRegs(valueGPR), node.child2(),
+ m_jit.branch64(MacroAssembler::Below, valueGPR, GPRInfo::tagTypeNumberRegister));
+ }
+
+ if (node.arrayMode().type() != Array::Int32 && Heap::isWriteBarrierEnabled()) {
+ GPRTemporary scratch(this);
+ writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR);
+ }
+
m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR);
MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
m_jit.store64(valueGPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight));
@@ -3114,7 +3213,43 @@ void SpeculativeJIT::compile(Node& node)
break;
}
+ case Array::Double: {
+ SpeculateDoubleOperand value(this, node.child2());
+ FPRReg valueFPR = value.fpr();
+
+ if (!isRealNumberSpeculation(m_state.forNode(node.child2()).m_type)) {
+ // FIXME: We need a way of profiling these, and we need to hoist them into
+ // SpeculateDoubleOperand.
+ speculationCheck(
+ BadType, JSValueRegs(), NoNode,
+ m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, valueFPR, valueFPR));
+ }
+
+ m_jit.load32(MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR);
+ MacroAssembler::Jump slowPath = m_jit.branch32(MacroAssembler::AboveOrEqual, storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
+ m_jit.storeDouble(valueFPR, MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight));
+ m_jit.add32(TrustedImm32(1), storageLengthGPR);
+ m_jit.store32(storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
+ m_jit.or64(GPRInfo::tagTypeNumberRegister, storageLengthGPR);
+
+ addSlowPathGenerator(
+ slowPathCall(
+ slowPath, this, operationArrayPushDouble, NoResult, storageLengthGPR,
+ valueFPR, baseGPR));
+
+ jsValueResult(storageLengthGPR, m_compileIndex);
+ break;
+ }
+
case Array::ArrayStorage: {
+ JSValueOperand value(this, node.child2());
+ GPRReg valueGPR = value.gpr();
+
+ if (Heap::isWriteBarrierEnabled()) {
+ GPRTemporary scratch(this);
+ writeBarrier(baseGPR, valueGPR, node.child2(), WriteBarrierForPropertyAccess, scratch.gpr(), storageLengthGPR);
+ }
+
m_jit.load32(MacroAssembler::Address(storageGPR, ArrayStorage::lengthOffset()), storageLengthGPR);
// Refuse to handle bizarre lengths.
@@ -3152,13 +3287,17 @@ void SpeculativeJIT::compile(Node& node)
StorageOperand storage(this, node.child2());
GPRTemporary value(this);
GPRTemporary storageLength(this);
+ FPRTemporary temp(this); // This is kind of lame, since we don't always need it. I'm relying on the fact that we don't have FPR pressure, especially in code that uses pop().
GPRReg baseGPR = base.gpr();
GPRReg storageGPR = storage.gpr();
GPRReg valueGPR = value.gpr();
GPRReg storageLengthGPR = storageLength.gpr();
+ FPRReg tempFPR = temp.fpr();
switch (node.arrayMode().type()) {
+ case Array::Int32:
+ case Array::Double:
case Array::Contiguous: {
m_jit.load32(
MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()), storageLengthGPR);
@@ -3167,14 +3306,27 @@ void SpeculativeJIT::compile(Node& node)
m_jit.sub32(TrustedImm32(1), storageLengthGPR);
m_jit.store32(
storageLengthGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
- m_jit.load64(
- MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight),
- valueGPR);
- // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old
- // length and the new length.
- m_jit.store64(
+ MacroAssembler::Jump slowCase;
+ if (node.arrayMode().type() == Array::Double) {
+ m_jit.loadDouble(
+ MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight),
+ tempFPR);
+ // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old
+ // length and the new length.
+ m_jit.store64(
+ MacroAssembler::TrustedImm64((int64_t)0), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight));
+ slowCase = m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, tempFPR, tempFPR);
+ boxDouble(tempFPR, valueGPR);
+ } else {
+ m_jit.load64(
+ MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight),
+ valueGPR);
+ // FIXME: This would not have to be here if changing the publicLength also zeroed the values between the old
+ // length and the new length.
+ m_jit.store64(
MacroAssembler::TrustedImm64((int64_t)0), MacroAssembler::BaseIndex(storageGPR, storageLengthGPR, MacroAssembler::TimesEight));
- MacroAssembler::Jump slowCase = m_jit.branchTest64(MacroAssembler::Zero, valueGPR);
+ slowCase = m_jit.branchTest64(MacroAssembler::Zero, valueGPR);
+ }
addSlowPathGenerator(
slowPathMove(
@@ -3184,6 +3336,7 @@ void SpeculativeJIT::compile(Node& node)
slowPathCall(
slowCase, this, operationArrayPopAndRecoverLength, valueGPR, baseGPR));
+ // We can't know for sure that the result is an int because of the slow paths. :-/
jsValueResult(valueGPR, m_compileIndex);
break;
}
@@ -3338,10 +3491,16 @@ void SpeculativeJIT::compile(Node& node)
case NewArray: {
JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin);
- if (!globalObject->isHavingABadTime()) {
+ if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) {
globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint());
- ASSERT(hasContiguous(globalObject->arrayStructure()->indexingType()));
+ Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType());
+ ASSERT(structure->indexingType() == node.indexingType());
+ ASSERT(
+ hasUndecided(structure->indexingType())
+ || hasInt32(structure->indexingType())
+ || hasDouble(structure->indexingType())
+ || hasContiguous(structure->indexingType()));
unsigned numElements = node.numChildren();
@@ -3351,15 +3510,50 @@ void SpeculativeJIT::compile(Node& node)
GPRReg resultGPR = result.gpr();
GPRReg storageGPR = storage.gpr();
- emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements);
+ emitAllocateJSArray(structure, resultGPR, storageGPR, numElements);
// At this point, one way or another, resultGPR and storageGPR have pointers to
// the JSArray and the Butterfly, respectively.
+ ASSERT(!hasUndecided(structure->indexingType()) || !node.numChildren());
+
for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) {
- JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]);
- GPRReg opGPR = operand.gpr();
- m_jit.store64(opGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx));
+ Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx];
+ switch (node.indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ CRASH();
+ break;
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ SpeculateDoubleOperand operand(this, use);
+ FPRReg opFPR = operand.fpr();
+ if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) {
+ // FIXME: We need a way of profiling these, and we need to hoist them into
+ // SpeculateDoubleOperand.
+ speculationCheck(
+ BadType, JSValueRegs(), NoNode,
+ m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR));
+ }
+
+ m_jit.storeDouble(opFPR, MacroAssembler::Address(storageGPR, sizeof(double) * operandIdx));
+ break;
+ }
+ case ALL_INT32_INDEXING_TYPES:
+ case ALL_CONTIGUOUS_INDEXING_TYPES: {
+ JSValueOperand operand(this, use);
+ GPRReg opGPR = operand.gpr();
+ if (hasInt32(node.indexingType()) && !isInt32Speculation(m_state.forNode(use).m_type)) {
+ speculationCheck(
+ BadType, JSValueRegs(opGPR), use.index(),
+ m_jit.branch64(MacroAssembler::Below, opGPR, GPRInfo::tagTypeNumberRegister));
+ }
+ m_jit.store64(opGPR, MacroAssembler::Address(storageGPR, sizeof(JSValue) * operandIdx));
+ break;
+ }
+ default:
+ CRASH();
+ break;
+ }
}
// Yuck, we should *really* have a way of also returning the storageGPR. But
@@ -3376,7 +3570,7 @@ void SpeculativeJIT::compile(Node& node)
if (!node.numChildren()) {
flushRegisters();
GPRResult result(this);
- callOperation(operationNewEmptyArray, result.gpr(), globalObject->arrayStructure());
+ callOperation(operationNewEmptyArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()));
cellResult(result.gpr(), m_compileIndex);
break;
}
@@ -3386,11 +3580,65 @@ void SpeculativeJIT::compile(Node& node)
EncodedJSValue* buffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0;
for (unsigned operandIdx = 0; operandIdx < node.numChildren(); ++operandIdx) {
- JSValueOperand operand(this, m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx]);
- GPRReg opGPR = operand.gpr();
- operand.use();
-
- m_jit.store64(opGPR, buffer + operandIdx);
+ // Need to perform the speculations that this node promises to perform. If we're
+ // emitting code here and the indexing type is not array storage then there is
+ // probably something hilarious going on and we're already failing at all the
+ // things, but at least we're going to be sound.
+ Edge use = m_jit.graph().m_varArgChildren[node.firstChild() + operandIdx];
+ switch (node.indexingType()) {
+ case ALL_BLANK_INDEXING_TYPES:
+ case ALL_UNDECIDED_INDEXING_TYPES:
+ CRASH();
+ break;
+ case ALL_DOUBLE_INDEXING_TYPES: {
+ SpeculateDoubleOperand operand(this, use);
+ GPRTemporary scratch(this);
+ FPRReg opFPR = operand.fpr();
+ GPRReg scratchGPR = scratch.gpr();
+ if (!isRealNumberSpeculation(m_state.forNode(use).m_type)) {
+ // FIXME: We need a way of profiling these, and we need to hoist them into
+ // SpeculateDoubleOperand.
+ speculationCheck(
+ BadType, JSValueRegs(), NoNode,
+ m_jit.branchDouble(MacroAssembler::DoubleNotEqualOrUnordered, opFPR, opFPR));
+ }
+
+ m_jit.boxDouble(opFPR, scratchGPR);
+ m_jit.store64(scratchGPR, buffer + operandIdx);
+ break;
+ }
+ case ALL_INT32_INDEXING_TYPES: {
+ JSValueOperand operand(this, use);
+ GPRReg opGPR = operand.gpr();
+ if (hasInt32(node.indexingType()) && !isInt32Speculation(m_state.forNode(use).m_type)) {
+ speculationCheck(
+ BadType, JSValueRegs(opGPR), use.index(),
+ m_jit.branch64(MacroAssembler::Below, opGPR, GPRInfo::tagTypeNumberRegister));
+ }
+ m_jit.store64(opGPR, buffer + operandIdx);
+ break;
+ }
+ case ALL_CONTIGUOUS_INDEXING_TYPES:
+ case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
+ JSValueOperand operand(this, use);
+ GPRReg opGPR = operand.gpr();
+ m_jit.store64(opGPR, buffer + operandIdx);
+ operand.use();
+ break;
+ }
+ default:
+ CRASH();
+ break;
+ }
+ }
+
+ switch (node.indexingType()) {
+ case ALL_DOUBLE_INDEXING_TYPES:
+ case ALL_INT32_INDEXING_TYPES:
+ useChildren(node);
+ break;
+ default:
+ break;
}
flushRegisters();
@@ -3406,7 +3654,7 @@ void SpeculativeJIT::compile(Node& node)
GPRResult result(this);
callOperation(
- operationNewArray, result.gpr(), globalObject->arrayStructure(),
+ operationNewArray, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()),
static_cast<void*>(buffer), node.numChildren());
if (scratchSize) {
@@ -3422,18 +3670,26 @@ void SpeculativeJIT::compile(Node& node)
case NewArrayWithSize: {
JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin);
- if (!globalObject->isHavingABadTime()) {
+ if (!globalObject->isHavingABadTime() && !hasArrayStorage(node.indexingType())) {
globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint());
SpeculateStrictInt32Operand size(this, node.child1());
GPRTemporary result(this);
GPRTemporary storage(this);
GPRTemporary scratch(this);
+ GPRTemporary scratch2;
GPRReg sizeGPR = size.gpr();
GPRReg resultGPR = result.gpr();
GPRReg storageGPR = storage.gpr();
GPRReg scratchGPR = scratch.gpr();
+ GPRReg scratch2GPR = InvalidGPRReg;
+
+ if (hasDouble(node.indexingType())) {
+ GPRTemporary realScratch2(this, size);
+ scratch2.adopt(realScratch2);
+ scratch2GPR = scratch2.gpr();
+ }
MacroAssembler::JumpList slowCases;
slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX)));
@@ -3446,17 +3702,28 @@ void SpeculativeJIT::compile(Node& node)
emitAllocateBasicStorage(resultGPR, storageGPR));
m_jit.subPtr(scratchGPR, storageGPR);
emitAllocateBasicJSObject<JSArray, MarkedBlock::None>(
- TrustedImmPtr(globalObject->arrayStructure()), resultGPR, scratchGPR,
+ TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), resultGPR, scratchGPR,
storageGPR, sizeof(JSArray), slowCases);
m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfPublicLength()));
m_jit.store32(sizeGPR, MacroAssembler::Address(storageGPR, Butterfly::offsetOfVectorLength()));
+ if (hasDouble(node.indexingType())) {
+ m_jit.move(TrustedImm64(bitwise_cast<int64_t>(QNaN)), scratchGPR);
+ m_jit.move(sizeGPR, scratch2GPR);
+ MacroAssembler::Jump done = m_jit.branchTest32(MacroAssembler::Zero, scratch2GPR);
+ MacroAssembler::Label loop = m_jit.label();
+ m_jit.sub32(TrustedImm32(1), scratch2GPR);
+ m_jit.store64(scratchGPR, MacroAssembler::BaseIndex(storageGPR, scratch2GPR, MacroAssembler::TimesEight));
+ m_jit.branchTest32(MacroAssembler::NonZero, scratch2GPR).linkTo(loop, &m_jit);
+ done.link(&m_jit);
+ }
+
addSlowPathGenerator(adoptPtr(
new CallArrayAllocatorWithVariableSizeSlowPathGenerator(
slowCases, this, operationNewArrayWithSize, resultGPR,
- globalObject->arrayStructure(),
- globalObject->arrayStructureWithArrayStorage(),
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()),
+ globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage),
sizeGPR)));
cellResult(resultGPR, m_compileIndex);
@@ -3470,10 +3737,10 @@ void SpeculativeJIT::compile(Node& node)
GPRReg resultGPR = result.gpr();
GPRReg structureGPR = selectScratchGPR(sizeGPR);
MacroAssembler::Jump bigLength = m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_SPARSE_ARRAY_INDEX));
- m_jit.move(TrustedImmPtr(globalObject->arrayStructure()), structureGPR);
+ m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType())), structureGPR);
MacroAssembler::Jump done = m_jit.jump();
bigLength.link(&m_jit);
- m_jit.move(TrustedImmPtr(globalObject->arrayStructureWithArrayStorage()), structureGPR);
+ m_jit.move(TrustedImmPtr(globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithArrayStorage)), structureGPR);
done.link(&m_jit);
callOperation(operationNewArrayWithSize, resultGPR, structureGPR, sizeGPR);
cellResult(resultGPR, m_compileIndex);
@@ -3520,7 +3787,8 @@ void SpeculativeJIT::compile(Node& node)
case NewArrayBuffer: {
JSGlobalObject* globalObject = m_jit.graph().globalObjectFor(node.codeOrigin);
- if (!globalObject->isHavingABadTime()) {
+ IndexingType indexingType = node.indexingType();
+ if (!globalObject->isHavingABadTime() && !hasArrayStorage(indexingType)) {
globalObject->havingABadTimeWatchpoint()->add(speculationWatchpoint());
unsigned numElements = node.numConstants();
@@ -3531,13 +3799,23 @@ void SpeculativeJIT::compile(Node& node)
GPRReg resultGPR = result.gpr();
GPRReg storageGPR = storage.gpr();
- emitAllocateJSArray(globalObject->arrayStructure(), resultGPR, storageGPR, numElements);
+ emitAllocateJSArray(globalObject->arrayStructureForIndexingTypeDuringAllocation(indexingType), resultGPR, storageGPR, numElements);
+ ASSERT(indexingType & IsArray);
JSValue* data = m_jit.codeBlock()->constantBuffer(node.startConstant());
- for (unsigned index = 0; index < node.numConstants(); ++index) {
- m_jit.store64(
- Imm64(JSValue::encode(data[index])),
- MacroAssembler::Address(storageGPR, sizeof(JSValue) * index));
+ if (indexingType == ArrayWithDouble) {
+ for (unsigned index = 0; index < node.numConstants(); ++index) {
+ double value = data[index].asNumber();
+ m_jit.store64(
+ Imm64(bitwise_cast<int64_t>(value)),
+ MacroAssembler::Address(storageGPR, sizeof(double) * index));
+ }
+ } else {
+ for (unsigned index = 0; index < node.numConstants(); ++index) {
+ m_jit.store64(
+ Imm64(JSValue::encode(data[index])),
+ MacroAssembler::Address(storageGPR, sizeof(JSValue) * index));
+ }
}
cellResult(resultGPR, m_compileIndex);
@@ -3547,7 +3825,7 @@ void SpeculativeJIT::compile(Node& node)
flushRegisters();
GPRResult result(this);
- callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructure(), node.startConstant(), node.numConstants());
+ callOperation(operationNewArrayBuffer, result.gpr(), globalObject->arrayStructureForIndexingTypeDuringAllocation(node.indexingType()), node.startConstant(), node.numConstants());
cellResult(result.gpr(), m_compileIndex);
break;
@@ -3645,6 +3923,12 @@ void SpeculativeJIT::compile(Node& node)
cellResult(resultGPR, m_compileIndex);
break;
}
+
+ case InheritorIDWatchpoint: {
+ jsCast<JSFunction*>(node.function())->addInheritorIDWatchpoint(speculationWatchpoint());
+ noResult(m_compileIndex);
+ break;
+ }
case NewObject: {
GPRTemporary result(this);
@@ -3655,9 +3939,9 @@ void SpeculativeJIT::compile(Node& node)
MacroAssembler::JumpList slowPath;
- emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(m_jit.globalObjectFor(node.codeOrigin)->emptyObjectStructure()), resultGPR, scratchGPR, slowPath);
+ emitAllocateJSFinalObject(MacroAssembler::TrustedImmPtr(node.structure()), resultGPR, scratchGPR, slowPath);
- addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR));
+ addSlowPathGenerator(slowPathCall(slowPath, this, operationNewObject, resultGPR, node.structure()));
cellResult(resultGPR, m_compileIndex);
break;
@@ -3813,7 +4097,7 @@ void SpeculativeJIT::compile(Node& node)
case CheckFunction: {
SpeculateCellOperand function(this, node.child1());
- speculationCheck(BadCache, JSValueRegs(), NoNode, m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function()));
+ speculationCheck(BadCache, JSValueRegs(function.gpr()), node.child1(), m_jit.branchWeakPtr(JITCompiler::NotEqual, function.gpr(), node.function()));
noResult(m_compileIndex);
break;
}
diff --git a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
index 22b9395b5..9d6060d60 100644
--- a/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGStructureCheckHoistingPhase.cpp
@@ -167,7 +167,7 @@ public:
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",
+ dataLogF("Zeroing the structure to hoist for %s because the ratio is %lf.\n",
m_graph.nameOfVariableAccessData(variable), variable->voteRatio());
#endif
iter->value.m_structure = 0;
@@ -200,7 +200,7 @@ public:
JSValue value = m_graph.m_mustHandleValues[i];
if (!value || !value.isCell()) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Zeroing the structure to hoist for %s because the OSR entry value is not a cell: %s.\n",
+ dataLogF("Zeroing the structure to hoist for %s because the OSR entry value is not a cell: %s.\n",
m_graph.nameOfVariableAccessData(variable), value.description());
#endif
iter->value.m_structure = 0;
@@ -208,7 +208,7 @@ public:
}
if (value.asCell()->structure() != iter->value.m_structure) {
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog("Zeroing the structure to hoist for %s because the OSR entry value has structure %p and we wanted %p.\n",
+ dataLogF("Zeroing the structure to hoist for %s because the OSR entry value has structure %p and we wanted %p.\n",
m_graph.nameOfVariableAccessData(variable), value.asCell()->structure(), iter->value.m_structure);
#endif
iter->value.m_structure = 0;
@@ -223,10 +223,10 @@ public:
for (HashMap<VariableAccessData*, CheckData>::iterator it = m_map.begin();
it != m_map.end(); ++it) {
if (!it->value.m_structure) {
- dataLog("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->key));
+ dataLogF("Not hoisting checks for %s because of heuristics.\n", m_graph.nameOfVariableAccessData(it->key));
continue;
}
- dataLog("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->key));
+ dataLogF("Hoisting checks for %s\n", m_graph.nameOfVariableAccessData(it->key));
}
#endif // DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
diff --git a/Source/JavaScriptCore/dfg/DFGThunks.cpp b/Source/JavaScriptCore/dfg/DFGThunks.cpp
index 74d1967a8..ac0b45f60 100644
--- a/Source/JavaScriptCore/dfg/DFGThunks.cpp
+++ b/Source/JavaScriptCore/dfg/DFGThunks.cpp
@@ -213,6 +213,18 @@ MacroAssemblerCodeRef linkConstructThunkGenerator(JSGlobalData* globalData)
return linkForThunkGenerator(globalData, CodeForConstruct);
}
+// For closure optimizations, we only include calls, since if you're using closures for
+// object construction then you're going to lose big time anyway.
+MacroAssemblerCodeRef linkClosureCallThunkGenerator(JSGlobalData* globalData)
+{
+ CCallHelpers jit(globalData);
+
+ slowPathFor(jit, globalData, operationLinkClosureCall);
+
+ LinkBuffer patchBuffer(*globalData, &jit, GLOBAL_THUNK_ID);
+ return FINALIZE_CODE(patchBuffer, ("DFG link closure call slow path thunk"));
+}
+
static MacroAssemblerCodeRef virtualForThunkGenerator(
JSGlobalData* globalData, CodeSpecializationKind kind)
{
diff --git a/Source/JavaScriptCore/dfg/DFGThunks.h b/Source/JavaScriptCore/dfg/DFGThunks.h
index 11a06d107..c97e3bfb6 100644
--- a/Source/JavaScriptCore/dfg/DFGThunks.h
+++ b/Source/JavaScriptCore/dfg/DFGThunks.h
@@ -45,6 +45,8 @@ MacroAssemblerCodeRef throwExceptionFromCallSlowPathGenerator(JSGlobalData*);
MacroAssemblerCodeRef linkCallThunkGenerator(JSGlobalData*);
MacroAssemblerCodeRef linkConstructThunkGenerator(JSGlobalData*);
+MacroAssemblerCodeRef linkClosureCallThunkGenerator(JSGlobalData*);
+
MacroAssemblerCodeRef virtualCallThunkGenerator(JSGlobalData*);
MacroAssemblerCodeRef virtualConstructThunkGenerator(JSGlobalData*);
diff --git a/Source/JavaScriptCore/dfg/DFGValidate.cpp b/Source/JavaScriptCore/dfg/DFGValidate.cpp
index 2b26123d8..274b544b5 100644
--- a/Source/JavaScriptCore/dfg/DFGValidate.cpp
+++ b/Source/JavaScriptCore/dfg/DFGValidate.cpp
@@ -45,9 +45,9 @@ public:
#define VALIDATE(context, assertion) do { \
if (!(assertion)) { \
- dataLog("\n\n\nAt "); \
+ dataLogF("\n\n\nAt "); \
reportValidationContext context; \
- dataLog(": validation %s (%s:%d) failed.\n", #assertion, __FILE__, __LINE__); \
+ dataLogF(": validation %s (%s:%d) failed.\n", #assertion, __FILE__, __LINE__); \
dumpGraphIfAppropriate(); \
WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #assertion); \
CRASH(); \
@@ -56,13 +56,13 @@ public:
#define V_EQUAL(context, left, right) do { \
if (left != right) { \
- dataLog("\n\n\nAt "); \
+ dataLogF("\n\n\nAt "); \
reportValidationContext context; \
- dataLog(": validation (%s = ", #left); \
+ dataLogF(": validation (%s = ", #left); \
dumpData(left); \
- dataLog(") == (%s = ", #right); \
+ dataLogF(") == (%s = ", #right); \
dumpData(right); \
- dataLog(") (%s:%d) failed.\n", __FILE__, __LINE__); \
+ dataLogF(") (%s:%d) failed.\n", __FILE__, __LINE__); \
dumpGraphIfAppropriate(); \
WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, #left " == " #right); \
CRASH(); \
@@ -290,60 +290,60 @@ private:
void reportValidationContext(NodeIndex nodeIndex)
{
- dataLog("@%u", nodeIndex);
+ dataLogF("@%u", nodeIndex);
}
enum BlockTag { Block };
void reportValidationContext(BlockTag, BlockIndex blockIndex)
{
- dataLog("Block #%u", blockIndex);
+ dataLogF("Block #%u", blockIndex);
}
void reportValidationContext(NodeIndex nodeIndex, Edge edge)
{
- dataLog("@%u -> %s@%u", nodeIndex, useKindToString(edge.useKind()), edge.index());
+ dataLogF("@%u -> %s@%u", nodeIndex, useKindToString(edge.useKind()), edge.index());
}
void reportValidationContext(
VirtualRegister local, BlockIndex sourceBlockIndex, BlockTag, BlockIndex destinationBlockIndex)
{
- dataLog("r%d in Block #%u -> #%u", local, sourceBlockIndex, destinationBlockIndex);
+ dataLogF("r%d in Block #%u -> #%u", local, sourceBlockIndex, destinationBlockIndex);
}
void reportValidationContext(
VirtualRegister local, BlockIndex sourceBlockIndex, NodeIndex prevNodeIndex)
{
- dataLog("@%u for r%d in Block #%u", prevNodeIndex, local, sourceBlockIndex);
+ dataLogF("@%u for r%d in Block #%u", prevNodeIndex, local, sourceBlockIndex);
}
void reportValidationContext(
NodeIndex nodeIndex, BlockIndex blockIndex)
{
- dataLog("@%u in Block #%u", nodeIndex, blockIndex);
+ dataLogF("@%u in Block #%u", nodeIndex, blockIndex);
}
void reportValidationContext(
NodeIndex nodeIndex, NodeIndex nodeIndex2, BlockIndex blockIndex)
{
- dataLog("@%u and @%u in Block #%u", nodeIndex, nodeIndex2, blockIndex);
+ dataLogF("@%u and @%u in Block #%u", nodeIndex, nodeIndex2, blockIndex);
}
void reportValidationContext(
NodeIndex nodeIndex, BlockIndex blockIndex, NodeIndex expectedNodeIndex, Edge incomingEdge)
{
- dataLog("@%u in Block #%u, searching for @%u from @%u", nodeIndex, blockIndex, expectedNodeIndex, incomingEdge.index());
+ dataLogF("@%u in Block #%u, searching for @%u from @%u", nodeIndex, blockIndex, expectedNodeIndex, incomingEdge.index());
}
void dumpData(unsigned value)
{
- dataLog("%u", value);
+ dataLogF("%u", value);
}
void dumpGraphIfAppropriate()
{
if (m_graphDumpMode == DontDumpGraph)
return;
- dataLog("Graph at time of failure:\n");
+ dataLogF("Graph at time of failure:\n");
m_graph.dump();
}
};
diff --git a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
index fa36ccdb5..7fa109b62 100644
--- a/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
+++ b/Source/JavaScriptCore/dfg/DFGVariableEventStream.cpp
@@ -36,9 +36,9 @@ namespace JSC { namespace DFG {
void VariableEventStream::logEvent(const VariableEvent& event)
{
- dataLog("seq#%u:", static_cast<unsigned>(size()));
+ dataLogF("seq#%u:", static_cast<unsigned>(size()));
event.dump(WTF::dataFile());
- dataLog(" ");
+ dataLogF(" ");
}
struct MinifiedGenerationInfo {
@@ -103,7 +103,7 @@ void VariableEventStream::reconstruct(
startIndex--;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Computing OSR exit recoveries starting at seq#%u.\n", startIndex);
+ dataLogF("Computing OSR exit recoveries starting at seq#%u.\n", startIndex);
#endif
// Step 2: Create a mock-up of the DFG's state and execute the events.
diff --git a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp
index 86b33835d..eb3232e69 100644
--- a/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGVirtualRegisterAllocationPhase.cpp
@@ -43,9 +43,9 @@ public:
bool run()
{
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Preserved vars: ");
+ dataLogF("Preserved vars: ");
m_graph.m_preservedVars.dump(WTF::dataFile());
- dataLog("\n");
+ dataLogF("\n");
#endif
ScoreBoard scoreBoard(m_graph, m_graph.m_preservedVars);
scoreBoard.assertClear();
@@ -62,8 +62,8 @@ public:
NodeIndex nodeIndex = block->at(indexInBlock);
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
if (needsNewLine)
- dataLog("\n");
- dataLog(" @%u:", nodeIndex);
+ dataLogF("\n");
+ dataLogF(" @%u:", nodeIndex);
needsNewLine = true;
#endif
Node& node = m_graph[nodeIndex];
@@ -92,7 +92,7 @@ public:
VirtualRegister virtualRegister = scoreBoard.allocate();
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
- dataLog(" Assigning virtual register %u to node %u.",
+ dataLogF(" Assigning virtual register %u to node %u.",
virtualRegister, nodeIndex);
#endif
node.setVirtualRegister(virtualRegister);
@@ -105,7 +105,7 @@ public:
}
#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
if (needsNewLine)
- dataLog("\n");
+ dataLogF("\n");
#endif
// 'm_numCalleeRegisters' is the number of locals and temporaries allocated
@@ -123,7 +123,7 @@ public:
if ((unsigned)codeBlock()->m_numCalleeRegisters < calleeRegisters)
codeBlock()->m_numCalleeRegisters = calleeRegisters;
#if DFG_ENABLE(DEBUG_VERBOSE)
- dataLog("Num callee registers: %u\n", calleeRegisters);
+ dataLogF("Num callee registers: %u\n", calleeRegisters);
#endif
return true;