summaryrefslogtreecommitdiffstats
path: root/Source/JavaScriptCore/dfg
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/dfg')
-rw-r--r--Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp546
-rw-r--r--Source/JavaScriptCore/dfg/DFGGenerationInfo.h21
-rw-r--r--Source/JavaScriptCore/dfg/DFGGraph.cpp190
-rw-r--r--Source/JavaScriptCore/dfg/DFGGraph.h48
-rw-r--r--Source/JavaScriptCore/dfg/DFGJITCodeGenerator.cpp19
-rw-r--r--Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h55
-rw-r--r--Source/JavaScriptCore/dfg/DFGJITCompiler.cpp46
-rw-r--r--Source/JavaScriptCore/dfg/DFGJITCompiler.h4
-rw-r--r--Source/JavaScriptCore/dfg/DFGNode.h84
-rw-r--r--Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.cpp290
-rw-r--r--Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.h140
-rw-r--r--Source/JavaScriptCore/dfg/DFGOperations.cpp25
-rw-r--r--Source/JavaScriptCore/dfg/DFGOperations.h9
-rw-r--r--Source/JavaScriptCore/dfg/DFGScoreBoard.h20
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp291
-rw-r--r--Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h32
16 files changed, 1351 insertions, 469 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 03f5d4f..1d4c36a 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -34,6 +34,13 @@
namespace JSC { namespace DFG {
+#if ENABLE(DFG_JIT_RESTRICTIONS)
+// FIXME: Temporarily disable arithmetic, until we fix associated performance regressions.
+#define ARITHMETIC_OP() m_parseFailed = true
+#else
+#define ARITHMETIC_OP() ((void)0)
+#endif
+
// === ByteCodeParser ===
//
// This class is used to compile the dataflow graph from a CodeBlock.
@@ -44,93 +51,137 @@ public:
, m_codeBlock(codeBlock)
, m_graph(graph)
, m_currentIndex(0)
- , m_noArithmetic(true)
+ , m_parseFailed(false)
, m_constantUndefined(UINT_MAX)
+ , m_constantNull(UINT_MAX)
, m_constant1(UINT_MAX)
+ , m_constants(codeBlock->numberOfConstantRegisters())
+ , m_arguments(codeBlock->m_numParameters)
+ , m_variables(codeBlock->m_numVars)
+ , m_temporaries(codeBlock->m_numCalleeRegisters - codeBlock->m_numVars)
{
- unsigned numberOfConstants = codeBlock->numberOfConstantRegisters();
- m_constantRecords.grow(numberOfConstants);
-
- unsigned numberOfParameters = codeBlock->m_numParameters;
- m_arguments.grow(numberOfParameters);
- for (unsigned i = 0; i < numberOfParameters; ++i)
- m_arguments[i] = NoNode;
-
- unsigned numberOfRegisters = codeBlock->m_numCalleeRegisters;
- m_calleeRegisters.grow(numberOfRegisters);
- for (unsigned i = 0; i < numberOfRegisters; ++i)
- m_calleeRegisters[i] = NoNode;
+ for (unsigned i = 0; i < m_temporaries.size(); ++i)
+ m_temporaries[i] = NoNode;
}
+ // Parse a full CodeBlock of bytecode.
bool parse();
private:
+ // Parse a single basic block of bytecode instructions.
+ bool parseBlock(unsigned limit);
+
// Get/Set the operands/result of a bytecode instruction.
NodeIndex get(int operand)
{
// Is this a constant?
if (operand >= FirstConstantRegisterIndex) {
unsigned constant = operand - FirstConstantRegisterIndex;
- ASSERT(constant < m_constantRecords.size());
+ ASSERT(constant < m_constants.size());
return getJSConstant(constant);
}
// Is this an argument?
- if (operand < 0) {
- unsigned argument = operand + m_codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize;
- ASSERT(argument < m_arguments.size());
- return getArgument(argument);
- }
-
- // Must be a local or temporary.
- ASSERT((unsigned)operand < m_calleeRegisters.size());
- return getRegister((unsigned)operand);
+ if (operand < 0)
+ return getArgument(operand);
+
+ // Is this a variable?
+ unsigned numVariables = m_variables.size();
+ if ((unsigned)operand < numVariables)
+ return getVariable((unsigned)operand);
+
+ // Must be a temporary.
+ unsigned temporary = (unsigned)operand - numVariables;
+ ASSERT(temporary < m_temporaries.size());
+ return getTemporary(temporary);
}
void set(int operand, NodeIndex value)
{
// Is this an argument?
if (operand < 0) {
- unsigned argument = operand + m_codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize;
- ASSERT(argument < m_arguments.size());
- return setArgument(argument, value);
+ setArgument(operand, value);
+ return;
+ }
+
+ // Is this a variable?
+ unsigned numVariables = m_variables.size();
+ if ((unsigned)operand < numVariables) {
+ setVariable((unsigned)operand, value);
+ return;
}
+
+ // Must be a temporary.
+ unsigned temporary = (unsigned)operand - numVariables;
+ ASSERT(temporary < m_temporaries.size());
+ setTemporary(temporary, value);
+ }
- // Must be a local or temporary.
- ASSERT((unsigned)operand < m_calleeRegisters.size());
- return setRegister((unsigned)operand, value);
+ // Used in implementing get/set, above, where the operand is a local variable.
+ NodeIndex getVariable(unsigned operand)
+ {
+ NodeIndex setNode = m_variables[operand].set;
+ if (setNode != NoNode)
+ return m_graph[setNode].child1;
+
+ NodeIndex getNode = m_variables[operand].get;
+ if (getNode != NoNode)
+ return getNode;
+
+ getNode = addToGraph(GetLocal, OpInfo(operand));
+ m_variables[operand].get = getNode;
+ return getNode;
+ }
+ void setVariable(unsigned operand, NodeIndex value)
+ {
+ NodeIndex priorSet = m_variables[operand].set;
+ m_variables[operand].set = addToGraph(SetLocal, OpInfo(operand), value);
+ if (priorSet != NoNode)
+ m_graph.deref(priorSet);
}
- // Used in implementing get/set, above, where the operand is a local or temporary.
- NodeIndex getRegister(unsigned operand)
+ // Used in implementing get/set, above, where the operand is a temporary.
+ NodeIndex getTemporary(unsigned operand)
{
- NodeIndex index = m_calleeRegisters[operand];
+ NodeIndex index = m_temporaries[operand];
if (index != NoNode)
return index;
- // We have not yet seen a definition for this value in this block.
- // For now, since we are only generating single block functions,
- // this value must be undefined.
- // For example:
- // function f() { var x; return x; }
+
+ // Detect a read of an temporary that is not a yet defined within this block (e.g. use of ?:).
+ m_parseFailed = true;
return constantUndefined();
}
- void setRegister(int operand, NodeIndex value)
+ void setTemporary(unsigned operand, NodeIndex value)
{
- m_calleeRegisters[operand] = value;
+ m_temporaries[operand] = value;
}
// Used in implementing get/set, above, where the operand is an argument.
- NodeIndex getArgument(unsigned argument)
+ NodeIndex getArgument(unsigned operand)
{
- NodeIndex index = m_arguments[argument];
- if (index != NoNode)
- return index;
- NodeIndex resultIndex = (NodeIndex)m_graph.size();
- m_graph.append(Node(Argument, m_currentIndex, OpInfo(argument)));
- return m_arguments[argument] = resultIndex;
+ unsigned argument = operand + m_codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize;
+ ASSERT(argument < m_arguments.size());
+
+ NodeIndex setNode = m_arguments[argument].set;
+ if (setNode != NoNode)
+ return m_graph[setNode].child1;
+
+ NodeIndex getNode = m_arguments[argument].get;
+ if (getNode != NoNode)
+ return getNode;
+
+ getNode = addToGraph(GetLocal, OpInfo(operand));
+ m_arguments[argument].get = getNode;
+ return getNode;
}
void setArgument(int operand, NodeIndex value)
{
- m_arguments[operand] = value;
+ unsigned argument = operand + m_codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize;
+ ASSERT(argument < m_arguments.size());
+
+ NodeIndex priorSet = m_arguments[argument].set;
+ m_arguments[argument].set = addToGraph(SetLocal, OpInfo(operand), value);
+ if (priorSet != NoNode)
+ m_graph.deref(priorSet);
}
// Get an operand, and perform a ToInt32/ToNumber conversion on it.
@@ -229,46 +280,43 @@ private:
// Used in implementing get, above, where the operand is a constant.
NodeIndex getInt32Constant(int32_t value, unsigned constant)
{
- NodeIndex index = m_constantRecords[constant].asInt32;
+ NodeIndex index = m_constants[constant].asInt32;
if (index != NoNode)
return index;
- NodeIndex resultIndex = (NodeIndex)m_graph.size();
- m_graph.append(Node(Int32Constant, m_currentIndex, OpInfo(constant)));
+ NodeIndex resultIndex = addToGraph(Int32Constant, OpInfo(constant));
m_graph[resultIndex].setInt32Constant(value);
- m_constantRecords[constant].asInt32 = resultIndex;
+ m_constants[constant].asInt32 = resultIndex;
return resultIndex;
}
NodeIndex getDoubleConstant(double value, unsigned constant)
{
- NodeIndex index = m_constantRecords[constant].asNumeric;
+ NodeIndex index = m_constants[constant].asNumeric;
if (index != NoNode)
return index;
- NodeIndex resultIndex = (NodeIndex)m_graph.size();
- m_graph.append(Node(DoubleConstant, m_currentIndex, OpInfo(constant)));
+ NodeIndex resultIndex = addToGraph(DoubleConstant, OpInfo(constant));
m_graph[resultIndex].setDoubleConstant(value);
- m_constantRecords[constant].asNumeric = resultIndex;
+ m_constants[constant].asNumeric = resultIndex;
return resultIndex;
}
NodeIndex getJSConstant(unsigned constant)
{
- NodeIndex index = m_constantRecords[constant].asJSValue;
+ NodeIndex index = m_constants[constant].asJSValue;
if (index != NoNode)
return index;
- NodeIndex resultIndex = (NodeIndex)m_graph.size();
- m_graph.append(Node(JSConstant, m_currentIndex, OpInfo(constant)));
- m_constantRecords[constant].asJSValue = resultIndex;
+ NodeIndex resultIndex = addToGraph(JSConstant, OpInfo(constant));
+ m_constants[constant].asJSValue = resultIndex;
return resultIndex;
}
// Helper functions to get/set the this value.
NodeIndex getThis()
{
- return getArgument(0);
+ return getArgument(m_codeBlock->thisRegister());
}
void setThis(NodeIndex value)
{
- setArgument(0, value);
+ setArgument(m_codeBlock->thisRegister(), value);
}
// Convenience methods for checking nodes for constants.
@@ -315,11 +363,11 @@ private:
return getJSConstant(m_constantUndefined);
}
- // Add undefined to the CodeBlock's constants, and add a corresponding slot in m_constantRecords.
- ASSERT(m_constantRecords.size() == numberOfConstants);
+ // Add undefined to the CodeBlock's constants, and add a corresponding slot in m_constants.
+ ASSERT(m_constants.size() == numberOfConstants);
m_codeBlock->addConstant(jsUndefined());
- m_constantRecords.append(ConstantRecord());
- ASSERT(m_constantRecords.size() == m_codeBlock->numberOfConstantRegisters());
+ m_constants.append(ConstantRecord());
+ ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters());
}
// m_constantUndefined must refer to an entry in the CodeBlock's constant pool that has the value 'undefined'.
@@ -327,6 +375,31 @@ private:
return getJSConstant(m_constantUndefined);
}
+ // This method returns a JSConstant with the value 'null'.
+ NodeIndex constantNull()
+ {
+ // Has m_constantNull been set up yet?
+ if (m_constantNull == UINT_MAX) {
+ // Search the constant pool for null, if we find it, we can just reuse this!
+ unsigned numberOfConstants = m_codeBlock->numberOfConstantRegisters();
+ for (m_constantNull = 0; m_constantNull < numberOfConstants; ++m_constantNull) {
+ JSValue testMe = m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constantNull);
+ if (testMe.isNull())
+ return getJSConstant(m_constantNull);
+ }
+
+ // Add null to the CodeBlock's constants, and add a corresponding slot in m_constants.
+ ASSERT(m_constants.size() == numberOfConstants);
+ m_codeBlock->addConstant(jsNull());
+ m_constants.append(ConstantRecord());
+ ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters());
+ }
+
+ // m_constantNull must refer to an entry in the CodeBlock's constant pool that has the value 'null'.
+ ASSERT(m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constantNull).isNull());
+ return getJSConstant(m_constantNull);
+ }
+
// This method returns a DoubleConstant with the value 1.
NodeIndex one()
{
@@ -340,11 +413,11 @@ private:
return getDoubleConstant(1, m_constant1);
}
- // Add the value 1 to the CodeBlock's constants, and add a corresponding slot in m_constantRecords.
- ASSERT(m_constantRecords.size() == numberOfConstants);
+ // Add the value 1 to the CodeBlock's constants, and add a corresponding slot in m_constants.
+ ASSERT(m_constants.size() == numberOfConstants);
m_codeBlock->addConstant(jsNumber(1));
- m_constantRecords.append(ConstantRecord());
- ASSERT(m_constantRecords.size() == m_codeBlock->numberOfConstantRegisters());
+ m_constants.append(ConstantRecord());
+ ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters());
}
// m_constant1 must refer to an entry in the CodeBlock's constant pool that has the integer value 1.
@@ -374,6 +447,15 @@ private:
m_graph.ref(resultIndex);
return resultIndex;
}
+ NodeIndex addToGraph(NodeType op, OpInfo info1, OpInfo info2, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
+ {
+ NodeIndex resultIndex = (NodeIndex)m_graph.size();
+ m_graph.append(Node(op, m_currentIndex, info1, info2, child1, child2, child3));
+
+ if (op & NodeMustGenerate)
+ m_graph.ref(resultIndex);
+ return resultIndex;
+ }
JSGlobalData* m_globalData;
CodeBlock* m_codeBlock;
@@ -382,8 +464,8 @@ private:
// The bytecode index of the current instruction being generated.
unsigned m_currentIndex;
- // FIXME: used to temporarily disable arithmetic, until we fix associated performance regressions.
- bool m_noArithmetic;
+ // Record failures due to unimplemented functionality or regressions.
+ bool m_parseFailed;
// We use these values during code generation, and to avoid the need for
// special handling we make sure they are available as constants in the
@@ -391,6 +473,7 @@ private:
// UINT_MAX, and lazily updated to hold an index into the CodeBlock's
// constant pool, as necessary.
unsigned m_constantUndefined;
+ unsigned m_constantNull;
unsigned m_constant1;
// A constant in the constant pool may be represented by more than one
@@ -407,12 +490,27 @@ private:
NodeIndex asNumeric;
NodeIndex asJSValue;
};
- Vector <ConstantRecord, 32> m_constantRecords;
+
+ // For every local variable we track any existing get or set of the value.
+ // We track the get so that these may be shared, and we track the set to
+ // retrieve the current value, and to reference the final definition.
+ struct VariableRecord {
+ VariableRecord()
+ : get(NoNode)
+ , set(NoNode)
+ {
+ }
+
+ NodeIndex get;
+ NodeIndex set;
+ };
// Track the index of the node whose result is the current value for every
// register value in the bytecode - argument, local, and temporary.
- Vector <NodeIndex, 32> m_arguments;
- Vector <NodeIndex, 32> m_calleeRegisters;
+ Vector <ConstantRecord, 32> m_constants;
+ Vector <VariableRecord, 32> m_arguments;
+ Vector <VariableRecord, 32> m_variables;
+ Vector <NodeIndex, 32> m_temporaries;
// These maps are used to unique ToNumber and ToInt32 operations.
typedef HashMap<NodeIndex, NodeIndex> UnaryOpMap;
@@ -422,15 +520,37 @@ private:
#define NEXT_OPCODE(name) \
m_currentIndex += OPCODE_LENGTH(name); \
- continue;
+ continue
-bool ByteCodeParser::parse()
+#define LAST_OPCODE(name) \
+ m_currentIndex += OPCODE_LENGTH(name); \
+ return !m_parseFailed
+
+bool ByteCodeParser::parseBlock(unsigned limit)
{
+ // No need to reset state initially, since it has been set by the constructor.
+ if (m_currentIndex) {
+ for (unsigned i = 0; i < m_constants.size(); ++i)
+ m_constants[i] = ConstantRecord();
+ for (unsigned i = 0; i < m_variables.size(); ++i)
+ m_variables[i] = VariableRecord();
+ for (unsigned i = 0; i < m_arguments.size(); ++i)
+ m_arguments[i] = VariableRecord();
+ for (unsigned i = 0; i < m_temporaries.size(); ++i)
+ m_temporaries[i] = NoNode;
+ }
+
AliasTracker aliases(m_graph);
Interpreter* interpreter = m_globalData->interpreter;
Instruction* instructionsBegin = m_codeBlock->instructions().begin();
while (true) {
+ // Don't extend over jump destinations.
+ if (m_currentIndex == limit) {
+ addToGraph(Jump, OpInfo(m_currentIndex));
+ return !m_parseFailed;
+ }
+
// Switch on the current bytecode opcode.
Instruction* currentInstruction = instructionsBegin + m_currentIndex;
switch (interpreter->getOpcodeID(currentInstruction->u.opcode)) {
@@ -438,8 +558,9 @@ bool ByteCodeParser::parse()
// === Function entry opcodes ===
case op_enter:
- // This is a no-op for now - may need to initialize locals, if
- // DCE analysis cannot determine that the values are never read.
+ // Initialize all locals to undefined.
+ for (int i = 0; i < m_codeBlock->m_numVars; ++i)
+ set(i, constantUndefined());
NEXT_OPCODE(op_enter);
case op_convert_this: {
@@ -561,7 +682,7 @@ bool ByteCodeParser::parse()
// === Arithmetic operations ===
case op_add: {
- m_noArithmetic = false;
+ ARITHMETIC_OP();
NodeIndex op1 = get(currentInstruction[2].u.operand);
NodeIndex op2 = get(currentInstruction[3].u.operand);
// If both operands can statically be determined to the numbers, then this is an arithmetic add.
@@ -574,7 +695,7 @@ bool ByteCodeParser::parse()
}
case op_sub: {
- m_noArithmetic = false;
+ ARITHMETIC_OP();
NodeIndex op1 = getToNumber(currentInstruction[2].u.operand);
NodeIndex op2 = getToNumber(currentInstruction[3].u.operand);
set(currentInstruction[1].u.operand, addToGraph(ArithSub, op1, op2));
@@ -582,7 +703,7 @@ bool ByteCodeParser::parse()
}
case op_mul: {
- m_noArithmetic = false;
+ ARITHMETIC_OP();
NodeIndex op1 = getToNumber(currentInstruction[2].u.operand);
NodeIndex op2 = getToNumber(currentInstruction[3].u.operand);
set(currentInstruction[1].u.operand, addToGraph(ArithMul, op1, op2));
@@ -590,7 +711,7 @@ bool ByteCodeParser::parse()
}
case op_mod: {
- m_noArithmetic = false;
+ ARITHMETIC_OP();
NodeIndex op1 = getToNumber(currentInstruction[2].u.operand);
NodeIndex op2 = getToNumber(currentInstruction[3].u.operand);
set(currentInstruction[1].u.operand, addToGraph(ArithMod, op1, op2));
@@ -598,7 +719,7 @@ bool ByteCodeParser::parse()
}
case op_div: {
- m_noArithmetic = false;
+ ARITHMETIC_OP();
NodeIndex op1 = getToNumber(currentInstruction[2].u.operand);
NodeIndex op2 = getToNumber(currentInstruction[3].u.operand);
set(currentInstruction[1].u.operand, addToGraph(ArithDiv, op1, op2));
@@ -613,6 +734,75 @@ bool ByteCodeParser::parse()
NEXT_OPCODE(op_mov);
}
+ case op_not: {
+ ARITHMETIC_OP();
+ NodeIndex value = get(currentInstruction[2].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(LogicalNot, value));
+ NEXT_OPCODE(op_not);
+ }
+
+ case op_less: {
+ ARITHMETIC_OP();
+ NodeIndex op1 = get(currentInstruction[2].u.operand);
+ NodeIndex op2 = get(currentInstruction[3].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(CompareLess, op1, op2));
+ NEXT_OPCODE(op_less);
+ }
+
+ case op_lesseq: {
+ ARITHMETIC_OP();
+ NodeIndex op1 = get(currentInstruction[2].u.operand);
+ NodeIndex op2 = get(currentInstruction[3].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(CompareLessEq, op1, op2));
+ NEXT_OPCODE(op_lesseq);
+ }
+
+ case op_eq: {
+ ARITHMETIC_OP();
+ NodeIndex op1 = get(currentInstruction[2].u.operand);
+ NodeIndex op2 = get(currentInstruction[3].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(CompareEq, op1, op2));
+ NEXT_OPCODE(op_eq);
+ }
+
+ case op_eq_null: {
+ ARITHMETIC_OP();
+ NodeIndex value = get(currentInstruction[2].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(CompareEq, value, constantNull()));
+ NEXT_OPCODE(op_eq_null);
+ }
+
+ case op_stricteq: {
+ ARITHMETIC_OP();
+ NodeIndex op1 = get(currentInstruction[2].u.operand);
+ NodeIndex op2 = get(currentInstruction[3].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(CompareStrictEq, op1, op2));
+ NEXT_OPCODE(op_stricteq);
+ }
+
+ case op_neq: {
+ ARITHMETIC_OP();
+ NodeIndex op1 = get(currentInstruction[2].u.operand);
+ NodeIndex op2 = get(currentInstruction[3].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(LogicalNot, addToGraph(CompareEq, op1, op2)));
+ NEXT_OPCODE(op_neq);
+ }
+
+ case op_neq_null: {
+ ARITHMETIC_OP();
+ NodeIndex value = get(currentInstruction[2].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(LogicalNot, addToGraph(CompareEq, value, constantNull())));
+ NEXT_OPCODE(op_neq_null);
+ }
+
+ case op_nstricteq: {
+ ARITHMETIC_OP();
+ NodeIndex op1 = get(currentInstruction[2].u.operand);
+ NodeIndex op2 = get(currentInstruction[3].u.operand);
+ set(currentInstruction[1].u.operand, addToGraph(LogicalNot, addToGraph(CompareStrictEq, op1, op2)));
+ NEXT_OPCODE(op_nstricteq);
+ }
+
// === Property access operations ===
case op_get_by_val: {
@@ -624,7 +814,7 @@ bool ByteCodeParser::parse()
aliases.recordGetByVal(getByVal);
NEXT_OPCODE(op_get_by_val);
- };
+ }
case op_put_by_val: {
NodeIndex base = get(currentInstruction[1].u.operand);
@@ -636,7 +826,7 @@ bool ByteCodeParser::parse()
aliases.recordPutByVal(putByVal);
NEXT_OPCODE(op_put_by_val);
- };
+ }
case op_get_by_id: {
NodeIndex base = get(currentInstruction[2].u.operand);
@@ -680,35 +870,169 @@ bool ByteCodeParser::parse()
// === Block terminators. ===
+ case op_jmp: {
+ unsigned relativeOffset = currentInstruction[1].u.operand;
+ addToGraph(Jump, OpInfo(m_currentIndex + relativeOffset));
+ LAST_OPCODE(op_jmp);
+ }
+
+ case op_loop: {
+ unsigned relativeOffset = currentInstruction[1].u.operand;
+ addToGraph(Jump, OpInfo(m_currentIndex + relativeOffset));
+ LAST_OPCODE(op_loop);
+ }
+
+ case op_jtrue: {
+ unsigned relativeOffset = currentInstruction[2].u.operand;
+ NodeIndex condition = get(currentInstruction[1].u.operand);
+ addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_jtrue)), condition);
+ LAST_OPCODE(op_jtrue);
+ }
+
+ case op_jfalse: {
+ unsigned relativeOffset = currentInstruction[2].u.operand;
+ NodeIndex condition = get(currentInstruction[1].u.operand);
+ addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jfalse)), OpInfo(m_currentIndex + relativeOffset), condition);
+ LAST_OPCODE(op_jfalse);
+ }
+
+ case op_loop_if_true: {
+ unsigned relativeOffset = currentInstruction[2].u.operand;
+ NodeIndex condition = get(currentInstruction[1].u.operand);
+ addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_loop_if_true)), condition);
+ LAST_OPCODE(op_loop_if_true);
+ }
+
+ case op_loop_if_false: {
+ unsigned relativeOffset = currentInstruction[2].u.operand;
+ NodeIndex condition = get(currentInstruction[1].u.operand);
+ addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_loop_if_false)), OpInfo(m_currentIndex + relativeOffset), condition);
+ LAST_OPCODE(op_loop_if_false);
+ }
+
+ case op_jeq_null: {
+ unsigned relativeOffset = currentInstruction[2].u.operand;
+ NodeIndex value = get(currentInstruction[1].u.operand);
+ NodeIndex condition = addToGraph(CompareEq, value, constantNull());
+ addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_jeq_null)), condition);
+ LAST_OPCODE(op_jeq_null);
+ }
+
+ case op_jneq_null: {
+ unsigned relativeOffset = currentInstruction[2].u.operand;
+ NodeIndex value = get(currentInstruction[1].u.operand);
+ NodeIndex condition = addToGraph(CompareEq, value, constantNull());
+ addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jneq_null)), OpInfo(m_currentIndex + relativeOffset), condition);
+ LAST_OPCODE(op_jneq_null);
+ }
+
+ case op_jnless: {
+ unsigned relativeOffset = currentInstruction[3].u.operand;
+ NodeIndex op1 = get(currentInstruction[1].u.operand);
+ NodeIndex op2 = get(currentInstruction[2].u.operand);
+ NodeIndex condition = addToGraph(CompareLess, op1, op2);
+ addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jnless)), OpInfo(m_currentIndex + relativeOffset), condition);
+ LAST_OPCODE(op_jnless);
+ }
+
+ case op_jnlesseq: {
+ unsigned relativeOffset = currentInstruction[3].u.operand;
+ NodeIndex op1 = get(currentInstruction[1].u.operand);
+ NodeIndex op2 = get(currentInstruction[2].u.operand);
+ NodeIndex condition = addToGraph(CompareLessEq, op1, op2);
+ addToGraph(Branch, OpInfo(m_currentIndex + OPCODE_LENGTH(op_jnlesseq)), OpInfo(m_currentIndex + relativeOffset), condition);
+ LAST_OPCODE(op_jnlesseq);
+ }
+
+ case op_jless: {
+ unsigned relativeOffset = currentInstruction[3].u.operand;
+ NodeIndex op1 = get(currentInstruction[1].u.operand);
+ NodeIndex op2 = get(currentInstruction[2].u.operand);
+ NodeIndex condition = addToGraph(CompareLess, op1, op2);
+ addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_jless)), condition);
+ LAST_OPCODE(op_jless);
+ }
+
+ case op_jlesseq: {
+ unsigned relativeOffset = currentInstruction[3].u.operand;
+ NodeIndex op1 = get(currentInstruction[1].u.operand);
+ NodeIndex op2 = get(currentInstruction[2].u.operand);
+ NodeIndex condition = addToGraph(CompareLessEq, op1, op2);
+ addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_jlesseq)), condition);
+ LAST_OPCODE(op_jlesseq);
+ }
+
+ case op_loop_if_less: {
+ unsigned relativeOffset = currentInstruction[3].u.operand;
+ NodeIndex op1 = get(currentInstruction[1].u.operand);
+ NodeIndex op2 = get(currentInstruction[2].u.operand);
+ NodeIndex condition = addToGraph(CompareLess, op1, op2);
+ addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_loop_if_less)), condition);
+ LAST_OPCODE(op_loop_if_less);
+ }
+
+ case op_loop_if_lesseq: {
+ unsigned relativeOffset = currentInstruction[3].u.operand;
+ NodeIndex op1 = get(currentInstruction[1].u.operand);
+ NodeIndex op2 = get(currentInstruction[2].u.operand);
+ NodeIndex condition = addToGraph(CompareLessEq, op1, op2);
+ addToGraph(Branch, OpInfo(m_currentIndex + relativeOffset), OpInfo(m_currentIndex + OPCODE_LENGTH(op_loop_if_lesseq)), condition);
+ LAST_OPCODE(op_loop_if_lesseq);
+ }
+
case op_ret: {
addToGraph(Return, get(currentInstruction[1].u.operand));
- m_currentIndex += OPCODE_LENGTH(op_ret);
-#if ENABLE(DFG_JIT_RESTRICTIONS)
- // FIXME: temporarily disabling the DFG JIT for functions containing arithmetic.
- return m_noArithmetic;
-#else
- return true;
-#endif
+
+ // FIXME: throw away terminal definitions of variables;
+ // should not be necessary once we have proper DCE!
+ for (unsigned i = 0; i < m_variables.size(); ++i) {
+ NodeIndex priorSet = m_variables[i].set;
+ if (priorSet != NoNode)
+ m_graph.deref(priorSet);
+ }
+
+ LAST_OPCODE(op_ret);
}
default:
- // parse failed!
+ // Parse failed!
return false;
}
}
}
-bool parse(Graph& graph, JSGlobalData* globalData, CodeBlock* codeBlock)
+bool ByteCodeParser::parse()
{
- // Call ByteCodeParser::parse to build the dataflow for the basic block at 'startIndex'.
- ByteCodeParser state(globalData, codeBlock, graph);
- if (!state.parse())
- return false;
+ // Set during construction.
+ ASSERT(!m_currentIndex);
+
+ for (unsigned jumpTargetIndex = 0; jumpTargetIndex <= m_codeBlock->numberOfJumpTargets(); ++jumpTargetIndex) {
+ // 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 < m_codeBlock->numberOfJumpTargets() ? m_codeBlock->jumpTarget(jumpTargetIndex) : m_codeBlock->instructions().size();
+ ASSERT(m_currentIndex < limit);
+
+ // Loop until we reach the current limit (i.e. next jump target).
+ do {
+ unsigned bytecodeBegin = m_currentIndex;
+ NodeIndex begin = m_graph.size();
+
+ if (!parseBlock(limit))
+ return false;
+ // We should not have gone beyond the limit.
+ ASSERT(m_currentIndex <= limit);
+
+ NodeIndex end = m_graph.size();
+ m_graph.m_blocks.append(BasicBlock(bytecodeBegin, begin, end));
+ } while (m_currentIndex < limit);
+ }
+
+ // Should have reached the end of the instructions.
+ ASSERT(m_currentIndex == m_codeBlock->instructions().size());
// Assign VirtualRegisters.
- ScoreBoard scoreBoard(graph);
- Node* nodes = graph.begin();
- size_t size = graph.size();
+ ScoreBoard scoreBoard(m_graph, m_variables.size());
+ Node* nodes = m_graph.begin();
+ size_t size = m_graph.size();
for (size_t i = 0; i < size; ++i) {
Node& node = nodes[i];
if (node.refCount) {
@@ -730,15 +1054,29 @@ bool parse(Graph& graph, JSGlobalData* globalData, CodeBlock* codeBlock)
// 'm_numCalleeRegisters' is the number of locals and temporaries allocated
// for the function (and checked for on entry). Since we perform a new and
// different allocation of temporaries, more registers may now be required.
- if ((unsigned)codeBlock->m_numCalleeRegisters < scoreBoard.allocatedCount())
- codeBlock->m_numCalleeRegisters = scoreBoard.allocatedCount();
+ unsigned calleeRegisters = scoreBoard.allocatedCount() + m_variables.size();
+ if ((unsigned)m_codeBlock->m_numCalleeRegisters < calleeRegisters)
+ m_codeBlock->m_numCalleeRegisters = calleeRegisters;
#if DFG_DEBUG_VERBOSE
- graph.dump(codeBlock);
+ m_graph.dump(m_codeBlock);
#endif
+
return true;
}
+bool parse(Graph& graph, JSGlobalData* globalData, CodeBlock* codeBlock)
+{
+#if DFG_DEBUG_LOCAL_DISBALE
+ UNUSED_PARAM(graph);
+ UNUSED_PARAM(globalData);
+ UNUSED_PARAM(codeBlock);
+ return false;
+#else
+ return ByteCodeParser(globalData, codeBlock, graph).parse();
+#endif
+}
+
} } // namespace JSC::DFG
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGGenerationInfo.h b/Source/JavaScriptCore/dfg/DFGGenerationInfo.h
index b3aa0cd..1c72e09 100644
--- a/Source/JavaScriptCore/dfg/DFGGenerationInfo.h
+++ b/Source/JavaScriptCore/dfg/DFGGenerationInfo.h
@@ -69,16 +69,6 @@ public:
{
}
- // Used to set the generation info according the the result
- // of various operations.
- void initArgument(NodeIndex nodeIndex, uint32_t useCount)
- {
- m_nodeIndex = nodeIndex;
- m_useCount = useCount;
- m_registerFormat = DataFormatNone;
- m_spillFormat = DataFormatNone;
- m_canFill = true;
- }
void initConstant(NodeIndex nodeIndex, uint32_t useCount)
{
m_nodeIndex = nodeIndex;
@@ -168,12 +158,12 @@ public:
{
// This should only be called on values that are currently in a register.
ASSERT(m_registerFormat != DataFormatNone);
- // Constants and arguments do not need spilling, nor do values
- // that have already been spilled to the RegisterFile.
+ // Constants do not need spilling, nor do values that have already been
+ // spilled to the RegisterFile.
return !m_canFill;
}
- // Called when a VirtualRegister is being spilled †o the RegisterFile for the first time.
+ // Called when a VirtualRegister is being spilled to the RegisterFile for the first time.
void spill(DataFormat spillFormat)
{
// We shouldn't be spill values that don't need spilling.
@@ -190,9 +180,8 @@ public:
m_canFill = true;
}
- // Called on values that don't need spilling (constants, arguments,
- // values that have already been spilled), to mark them as no longer
- // being in machine registers.
+ // Called on values that don't need spilling (constants and values that have
+ // already been spilled), to mark them as no longer being in machine registers.
void setSpilled()
{
// Should only be called on values that don't need spilling, and are currently in registers.
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.cpp b/Source/JavaScriptCore/dfg/DFGGraph.cpp
index be3beec..84e2d4d 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.cpp
+++ b/Source/JavaScriptCore/dfg/DFGGraph.cpp
@@ -33,88 +33,108 @@
namespace JSC { namespace DFG {
#ifndef NDEBUG
-void Graph::dump(CodeBlock* codeBlock)
-{
- // Creates an array of stringized names.
+
+// Creates an array of stringized names.
+static const char* dfgOpNames[] = {
#define STRINGIZE_DFG_OP_ENUM(opcode, flags) #opcode ,
- const char* dfgOpNames[] = {
- FOR_EACH_DFG_OP(STRINGIZE_DFG_OP_ENUM)
- };
+ FOR_EACH_DFG_OP(STRINGIZE_DFG_OP_ENUM)
#undef STRINGIZE_DFG_OP_ENUM
+};
+
+void Graph::dump(NodeIndex nodeIndex, CodeBlock* codeBlock)
+{
+ Node& node = at(nodeIndex);
+ NodeType op = node.op;
+
+ unsigned refCount = node.refCount;
+ if (!refCount)
+ return;
+ bool mustGenerate = node.mustGenerate();
+ if (mustGenerate)
+ --refCount;
+
+ // Example/explanation of dataflow dump output
+ //
+ // 14: <!2:7> GetByVal(@3, @13)
+ // ^1 ^2 ^3 ^4 ^5
+ //
+ // (1) The nodeIndex of this operation.
+ // (2) The reference count. The number printed is the 'real' count,
+ // not including the 'mustGenerate' ref. If the node is
+ // 'mustGenerate' then the count it prefixed with '!'.
+ // (3) The virtual register slot assigned to this node.
+ // (4) The name of the operation.
+ // (5) The arguments to the operation. The may be of the form:
+ // @# - a NodeIndex referencing a prior node in the graph.
+ // arg# - an argument number.
+ // $# - 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.
+ printf("% 4d:\t<%c%u:%u>\t%s(", (int)nodeIndex, mustGenerate ? '!' : ' ', refCount, node.virtualRegister, dfgOpNames[op & NodeIdMask]);
+ if (node.child1 != NoNode)
+ printf("@%u", node.child1);
+ if (node.child2 != NoNode)
+ printf(", @%u", node.child2);
+ if (node.child3 != NoNode)
+ printf(", @%u", node.child3);
+ bool hasPrinted = node.child1 != NoNode;
+
+ if (node.hasVarNumber()) {
+ printf("%svar%u", hasPrinted ? ", " : "", node.varNumber());
+ hasPrinted = true;
+ }
+ if (node.hasIdentifier()) {
+ if (codeBlock)
+ printf("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), codeBlock->identifier(node.identifierNumber()).ustring().utf8().data());
+ else
+ printf("%sid%u", hasPrinted ? ", " : "", node.identifierNumber());
+ hasPrinted = true;
+ }
+ if (node.hasLocal()) {
+ int local = node.local();
+ if (local < 0)
+ printf("%sarg%u", hasPrinted ? ", " : "", local - codeBlock->thisRegister());
+ else
+ printf("%sr%u", hasPrinted ? ", " : "", local);
+ hasPrinted = true;
+ }
+ if (op == Int32Constant) {
+ printf("%s$%u{%d|0x%08x}", hasPrinted ? ", " : "", node.constantNumber(), node.int32Constant(), node.int32Constant());
+ hasPrinted = true;
+ }
+ if (op == DoubleConstant) {
+ printf("%s$%u{%f})", hasPrinted ? ", " : "", node.constantNumber(), node.numericConstant());
+ hasPrinted = true;
+ }
+ if (op == JSConstant) {
+ printf("%s$%u", hasPrinted ? ", " : "", node.constantNumber());
+ hasPrinted = true;
+ }
+ if (node.isBranch() || node.isJump()) {
+ printf("%sT:#%u", hasPrinted ? ", " : "", blockIndexForBytecodeOffset(node.takenBytecodeOffset()));
+ hasPrinted = true;
+ }
+ if (node.isBranch()) {
+ printf("%sF:#%u", hasPrinted ? ", " : "", blockIndexForBytecodeOffset(node.notTakenBytecodeOffset()));
+ hasPrinted = true;
+ }
+
+ printf(")\n");
+}
- Node* nodes = this->begin();
-
- for (size_t i = 0; i < size(); ++i) {
- Node& node = nodes[i];
- NodeType op = node.op;
-
- unsigned refCount = node.refCount;
- if (!refCount)
- continue;
- bool mustGenerate = node.mustGenerate();
- if (mustGenerate)
- --refCount;
-
- // Example/explanation of dataflow dump output
- //
- // 14: <!2:7> GetByVal(@3, @13)
- // ^1 ^2 ^3 ^4 ^5
- //
- // (1) The nodeIndex of this operation.
- // (2) The reference count. The number printed is the 'real' count,
- // not including the 'mustGenerate' ref. If the node is
- // 'mustGenerate' then the count it prefixed with '!'.
- // (3) The virtual register slot assigned to this node.
- // (4) The name of the operation.
- // (5) The arguments to the operation. The may be of the form:
- // @# - a NodeIndex referencing a prior node in the graph.
- // arg# - an argument number.
- // $# - 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.
- printf("% 4d:\t<%c%u:%u>\t%s(", (int)i, mustGenerate ? '!' : ' ', refCount, node.virtualRegister, dfgOpNames[op & NodeIdMask]);
- if (node.child1 != NoNode)
- printf("@%u", node.child1);
- if (node.child2 != NoNode)
- printf(", @%u", node.child2);
- if (node.child3 != NoNode)
- printf(", @%u", node.child3);
- bool hasPrinted = node.child1 != NoNode;
-
- if (node.hasVarNumber()) {
- printf("%svar%u", hasPrinted ? ", " : "", node.varNumber());
- hasPrinted = true;
- }
- if (node.hasIdentifier()) {
- if (codeBlock)
- printf("%sid%u{%s}", hasPrinted ? ", " : "", node.identifierNumber(), codeBlock->identifier(node.identifierNumber()).ustring().utf8().data());
- else
- printf("%sid%u", hasPrinted ? ", " : "", node.identifierNumber());
- hasPrinted = true;
- }
- if (node.isArgument()) {
- printf("%sarg%u", hasPrinted ? ", " : "", node.argumentNumber());
- hasPrinted = true;
- }
- if (op == Int32Constant) {
- printf("%s$%u{%d|0x%08x}", hasPrinted ? ", " : "", node.constantNumber(), node.int32Constant(), node.int32Constant());
- hasPrinted = true;
- }
- if (op == DoubleConstant) {
- printf("%s$%u{%f})", hasPrinted ? ", " : "", node.constantNumber(), node.numericConstant());
- hasPrinted = true;
- }
- if (op == JSConstant) {
- printf("%s$%u", hasPrinted ? ", " : "", node.constantNumber());
- hasPrinted = true;
- }
-
- printf(")\n");
+void Graph::dump(CodeBlock* codeBlock)
+{
+ for (size_t b = 0; b < m_blocks.size(); ++b) {
+ printf("Block #%u:\n", (int)b);
+ BasicBlock& block = m_blocks[b];
+ for (size_t i = block.begin; i < block.end; ++i)
+ dump(i, codeBlock);
}
}
+
#endif
-// FIXME: Convert this method to be iterative, not recursive.
+// FIXME: Convert these methods to be iterative, not recursive.
void Graph::refChildren(NodeIndex op)
{
Node& node = at(op);
@@ -135,6 +155,26 @@ void Graph::refChildren(NodeIndex op)
return;
ref(node.child3);
}
+void Graph::derefChildren(NodeIndex op)
+{
+ Node& node = at(op);
+
+ if (node.child1 == NoNode) {
+ ASSERT(node.child2 == NoNode && node.child3 == NoNode);
+ return;
+ }
+ deref(node.child1);
+
+ if (node.child2 == NoNode) {
+ ASSERT(node.child3 == NoNode);
+ return;
+ }
+ deref(node.child2);
+
+ if (node.child3 == NoNode)
+ return;
+ deref(node.child3);
+}
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGGraph.h b/Source/JavaScriptCore/dfg/DFGGraph.h
index c02110e..c6bc7df 100644
--- a/Source/JavaScriptCore/dfg/DFGGraph.h
+++ b/Source/JavaScriptCore/dfg/DFGGraph.h
@@ -30,6 +30,7 @@
#include <dfg/DFGNode.h>
#include <wtf/Vector.h>
+#include <wtf/StdLibExtras.h>
namespace JSC {
@@ -37,6 +38,26 @@ class CodeBlock;
namespace DFG {
+typedef uint32_t BlockIndex;
+
+struct BasicBlock {
+ BasicBlock(unsigned bytecodeBegin, NodeIndex begin, NodeIndex end)
+ : bytecodeBegin(bytecodeBegin)
+ , begin(begin)
+ , end(end)
+ {
+ }
+
+ static inline BlockIndex getBytecodeBegin(BasicBlock* block)
+ {
+ return block->bytecodeBegin;
+ }
+
+ unsigned bytecodeBegin;
+ NodeIndex begin;
+ NodeIndex end;
+};
+
//
// === Graph ===
//
@@ -48,19 +69,40 @@ public:
// Mark a node as being referenced.
void ref(NodeIndex nodeIndex)
{
- // If the value (before incrementing) was at reCount zero then we need to ref its children.
- if (!at(nodeIndex).refCount++)
+ Node& node = at(nodeIndex);
+ // If the value (before incrementing) was at refCount zero then we need to ref its children.
+ if (!node.refCount++)
refChildren(nodeIndex);
}
+ void deref(NodeIndex nodeIndex)
+ {
+ Node& node = at(nodeIndex);
+ ASSERT(node.refCount);
+ // If the value (after decrementing) becomes refCount zero then we need to deref its children.
+ if (!--node.refCount)
+ derefChildren(nodeIndex);
+ }
#ifndef NDEBUG
// CodeBlock is optional, but may allow additional information to be dumped (e.g. Identifier names).
void dump(CodeBlock* = 0);
+ void dump(NodeIndex, CodeBlock* = 0);
#endif
+ Vector<BasicBlock> m_blocks;
+
+ BlockIndex blockIndexForBytecodeOffset(unsigned bytecodeBegin)
+ {
+ BasicBlock* begin = m_blocks.begin();
+ BasicBlock* block = binarySearch<BasicBlock, unsigned, BasicBlock::getBytecodeBegin>(begin, m_blocks.size(), bytecodeBegin);
+ ASSERT(block >= m_blocks.begin() && block < m_blocks.end());
+ return static_cast<BlockIndex>(block - begin);
+ }
+
private:
- // When a node's refCount goes from 0 to 1, it must (logically) recursively ref all of its children.
+ // When a node's refCount goes from 0 to 1, it must (logically) recursively ref all of its children, and vice versa.
void refChildren(NodeIndex);
+ void derefChildren(NodeIndex);
};
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.cpp b/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.cpp
index b945b5a..52e0abe 100644
--- a/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.cpp
+++ b/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.cpp
@@ -60,9 +60,6 @@ GPRReg JITCodeGenerator::fillInteger(NodeIndex nodeIndex, DataFormat& returnForm
JSValue jsValue = valueOfJSConstant(nodeIndex);
m_jit.move(MacroAssembler::ImmPtr(JSValue::encode(jsValue)), reg);
}
- } else if (node.isArgument()) {
- m_gprs.retain(gpr, virtualRegister, SpillOrderArgument);
- m_jit.loadPtr(m_jit.addressForArgument(m_jit.graph()[nodeIndex].argumentNumber()), reg);
} else {
ASSERT(info.spillFormat() == DataFormatJS || info.spillFormat() == DataFormatJSInteger);
m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled);
@@ -143,11 +140,6 @@ FPRReg JITCodeGenerator::fillDouble(NodeIndex nodeIndex)
info.fillJSValue(gpr, DataFormatJS);
unlock(gpr);
}
- } else if (node.isArgument()) {
- m_gprs.retain(gpr, virtualRegister, SpillOrderArgument);
- m_jit.loadPtr(m_jit.addressForArgument(m_jit.graph()[nodeIndex].argumentNumber()), reg);
- info.fillJSValue(gpr, DataFormatJS);
- unlock(gpr);
} else {
DataFormat spillFormat = info.spillFormat();
ASSERT(spillFormat & DataFormatJS);
@@ -267,10 +259,6 @@ GPRReg JITCodeGenerator::fillJSValue(NodeIndex nodeIndex)
}
m_gprs.retain(gpr, virtualRegister, SpillOrderConstant);
- } else if (node.isArgument()) {
- m_gprs.retain(gpr, virtualRegister, SpillOrderArgument);
- m_jit.loadPtr(m_jit.addressForArgument(m_jit.graph()[nodeIndex].argumentNumber()), reg);
- info.fillJSValue(gpr, DataFormatJS);
} else {
DataFormat spillFormat = info.spillFormat();
ASSERT(spillFormat & DataFormatJS);
@@ -283,6 +271,13 @@ GPRReg JITCodeGenerator::fillJSValue(NodeIndex nodeIndex)
case DataFormatInteger: {
GPRReg gpr = info.gpr();
+ // If the register has already been locked we need to take a copy.
+ // If not, we'll zero extend in place, so mark on the info that this is now type DataFormatInteger, not DataFormatJSInteger.
+ if (m_gprs.isLocked(gpr)) {
+ GPRReg result = allocate();
+ m_jit.orPtr(JITCompiler::tagTypeNumberRegister, JITCompiler::gprToRegisterID(gpr), JITCompiler::gprToRegisterID(result));
+ return result;
+ }
m_gprs.lock(gpr);
m_jit.orPtr(JITCompiler::tagTypeNumberRegister, JITCompiler::gprToRegisterID(gpr));
info.fillJSValue(gpr, DataFormatJSInteger);
diff --git a/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h b/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h
index a84cdc6..0abd3c7 100644
--- a/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h
+++ b/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h
@@ -59,7 +59,6 @@ protected:
SpillOrderNone,
SpillOrderConstant = 1, // no spill, and cheap fill
SpillOrderSpilled = 2, // no spill
- SpillOrderArgument = 3, // no spill, but we may lose typeinfo
SpillOrderJS = 4, // needs spill
SpillOrderCell = 4, // needs spill
SpillOrderInteger = 5, // needs spill and box
@@ -152,6 +151,7 @@ protected:
, m_isSpeculative(isSpeculative)
, m_compileIndex(0)
, m_generationInfo(m_jit.codeBlock()->m_numCalleeRegisters)
+ , m_blockHeads(jit.graph().m_blocks.size())
{
}
@@ -237,6 +237,7 @@ protected:
}
// Checks/accessors for constant values.
+ bool isConstant(NodeIndex nodeIndex) { return m_jit.isConstant(nodeIndex); }
bool isInt32Constant(NodeIndex nodeIndex) { return m_jit.isInt32Constant(nodeIndex); }
bool isDoubleConstant(NodeIndex nodeIndex) { return m_jit.isDoubleConstant(nodeIndex); }
bool isJSConstant(NodeIndex nodeIndex) { return m_jit.isJSConstant(nodeIndex); }
@@ -444,11 +445,6 @@ protected:
Node& node = m_jit.graph()[nodeIndex];
m_generationInfo[node.virtualRegister].initConstant(nodeIndex, node.refCount);
}
- void initArgumentInfo(NodeIndex nodeIndex)
- {
- Node& node = m_jit.graph()[nodeIndex];
- m_generationInfo[node.virtualRegister].initArgument(nodeIndex, node.refCount);
- }
// These methods used to sort arguments into the correct registers.
template<GPRReg destA, GPRReg destB>
@@ -607,6 +603,26 @@ protected:
appendCallWithExceptionCheck(operation);
m_jit.move(JITCompiler::returnValueRegister, JITCompiler::gprToRegisterID(result));
}
+ void callOperation(Z_DFGOperation_EJ operation, GPRReg result, GPRReg arg1)
+ {
+ ASSERT(isFlushed());
+
+ m_jit.move(JITCompiler::gprToRegisterID(arg1), JITCompiler::argumentRegister1);
+ m_jit.move(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0);
+
+ appendCallWithExceptionCheck(operation);
+ m_jit.move(JITCompiler::returnValueRegister, JITCompiler::gprToRegisterID(result));
+ }
+ void callOperation(Z_DFGOperation_EJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+ {
+ ASSERT(isFlushed());
+
+ setupStubArguments(arg1, arg2);
+ m_jit.move(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0);
+
+ appendCallWithExceptionCheck(operation);
+ m_jit.move(JITCompiler::returnValueRegister, JITCompiler::gprToRegisterID(result));
+ }
void callOperation(J_DFGOperation_EJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
{
ASSERT(isFlushed());
@@ -655,6 +671,19 @@ protected:
m_jit.appendCallWithExceptionCheck(function, m_jit.graph()[m_compileIndex].exceptionInfo);
}
+ void addBranch(const MacroAssembler::Jump& jump, BlockIndex destination)
+ {
+ m_branches.append(BranchRecord(jump, destination));
+ }
+
+ void linkBranches()
+ {
+ for (size_t i = 0; i < m_branches.size(); ++i) {
+ BranchRecord& branch = m_branches[i];
+ branch.jump.linkTo(m_blockHeads[branch.destination], &m_jit);
+ }
+ }
+
#ifndef NDEBUG
void dump(const char* label = 0);
#endif
@@ -679,11 +708,25 @@ protected:
// the value may have been boxed differently on the two paths.
bool m_isSpeculative;
// The current node being generated.
+ BlockIndex m_block;
NodeIndex m_compileIndex;
// Virtual and physical register maps.
Vector<GenerationInfo, 32> m_generationInfo;
RegisterBank<GPRReg, numberOfGPRs, SpillOrder, SpillOrderNone, SpillOrderMax> m_gprs;
RegisterBank<FPRReg, numberOfFPRs, SpillOrder, SpillOrderNone, SpillOrderMax> m_fprs;
+
+ Vector<MacroAssembler::Label> m_blockHeads;
+ struct BranchRecord {
+ BranchRecord(MacroAssembler::Jump jump, BlockIndex destination)
+ : jump(jump)
+ , destination(destination)
+ {
+ }
+
+ MacroAssembler::Jump jump;
+ BlockIndex destination;
+ };
+ Vector<BranchRecord, 8> m_branches;
};
// === Operand types ===
diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
index 5c5d5fe..5cd044a 100644
--- a/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
+++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp
@@ -45,9 +45,6 @@ void JITCompiler::fillNumericToDouble(NodeIndex nodeIndex, FPRReg fpr, GPRReg te
Node& node = graph()[nodeIndex];
MacroAssembler::RegisterID tempReg = gprToRegisterID(temporary);
- // Arguments can't be know to be double, would need to have been a ValueToNumber node in the way!
- ASSERT(!node.isArgument());
-
if (node.isConstant()) {
ASSERT(node.op == DoubleConstant);
move(MacroAssembler::ImmPtr(reinterpret_cast<void*>(reinterpretDoubleToIntptr(valueOfDoubleConstant(nodeIndex)))), tempReg);
@@ -70,9 +67,6 @@ void JITCompiler::fillInt32ToInteger(NodeIndex nodeIndex, GPRReg gpr)
{
Node& node = graph()[nodeIndex];
- // Arguments can't be know to be int32, would need to have been a ValueToInt32 node in the way!
- ASSERT(!node.isArgument());
-
if (node.isConstant()) {
ASSERT(node.op == Int32Constant);
move(MacroAssembler::Imm32(valueOfInt32Constant(nodeIndex)), gprToRegisterID(gpr));
@@ -91,11 +85,6 @@ void JITCompiler::fillToJS(NodeIndex nodeIndex, GPRReg gpr)
{
Node& node = graph()[nodeIndex];
- if (node.isArgument()) {
- loadPtr(addressForArgument(node.argumentNumber()), gprToRegisterID(gpr));
- return;
- }
-
if (node.isConstant()) {
if (isInt32Constant(nodeIndex)) {
JSValue jsValue = jsNumber(valueOfInt32Constant(nodeIndex));
@@ -198,8 +187,8 @@ void JITCompiler::jumpFromSpeculativeToNonSpeculative(const SpeculationCheck& ch
void JITCompiler::linkSpeculationChecks(SpeculativeJIT& speculative, NonSpeculativeJIT& nonSpeculative)
{
// Iterators to walk over the set of bail outs & corresponding entry points.
- SpeculativeJIT::SpeculationCheckVector::Iterator checksIter = speculative.speculationChecks().begin();
- SpeculativeJIT::SpeculationCheckVector::Iterator checksEnd = speculative.speculationChecks().end();
+ SpeculationCheckVector::Iterator checksIter = speculative.speculationChecks().begin();
+ SpeculationCheckVector::Iterator checksEnd = speculative.speculationChecks().end();
NonSpeculativeJIT::EntryLocationVector::Iterator entriesIter = nonSpeculative.entryLocations().begin();
NonSpeculativeJIT::EntryLocationVector::Iterator entriesEnd = nonSpeculative.entryLocations().end();
@@ -267,25 +256,36 @@ void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWi
// register values around, rebox values, and ensure spilled, to match the
// non-speculative path's requirements).
-#if DFG_JIT_BREAK_ON_ENTRY
+#if DFG_JIT_BREAK_ON_EVERY_FUNCTION
// Handy debug tool!
breakpoint();
#endif
// First generate the speculative path.
+ Label speculativePathBegin = label();
SpeculativeJIT speculative(*this);
- speculative.compile();
+ bool compiledSpeculative = speculative.compile();
// Next, generate the non-speculative path. We pass this a SpeculationCheckIndexIterator
// to allow it to check which nodes in the graph may bail out, and may need to reenter the
// non-speculative path.
- SpeculationCheckIndexIterator checkIterator(speculative);
- NonSpeculativeJIT nonSpeculative(*this);
- nonSpeculative.compile(checkIterator);
-
- // Link the bail-outs from the speculative path to the corresponding entry points into the non-speculative one.
- linkSpeculationChecks(speculative, nonSpeculative);
+ if (compiledSpeculative) {
+ SpeculationCheckIndexIterator checkIterator(speculative.speculationChecks());
+ NonSpeculativeJIT nonSpeculative(*this);
+ nonSpeculative.compile(checkIterator);
+ // Link the bail-outs from the speculative path to the corresponding entry points into the non-speculative one.
+ linkSpeculationChecks(speculative, nonSpeculative);
+ } else {
+ // If compilation through the SpeculativeJIT failed, throw away the code we generated.
+ m_calls.clear();
+ rewindToLabel(speculativePathBegin);
+
+ SpeculationCheckVector noChecks;
+ SpeculationCheckIndexIterator checkIterator(noChecks);
+ NonSpeculativeJIT nonSpeculative(*this);
+ nonSpeculative.compile(checkIterator);
+ }
// === Stage 3 - Function footer code generation ===
//
@@ -349,6 +349,10 @@ void JITCompiler::compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWi
LinkBuffer linkBuffer(this, m_globalData->executableAllocator.poolForSize(m_assembler.size()), 0);
+#if DFG_DEBUG_VERBOSE
+ fprintf(stderr, "JIT code start at %p\n", linkBuffer.debugAddress());
+#endif
+
// Link all calls out from the JIT code to their respective functions.
for (unsigned i = 0; i < m_calls.size(); ++i)
linkBuffer.link(m_calls[i].m_call, m_calls[i].m_function);
diff --git a/Source/JavaScriptCore/dfg/DFGJITCompiler.h b/Source/JavaScriptCore/dfg/DFGJITCompiler.h
index 8b68434..03ae2b8 100644
--- a/Source/JavaScriptCore/dfg/DFGJITCompiler.h
+++ b/Source/JavaScriptCore/dfg/DFGJITCompiler.h
@@ -248,6 +248,10 @@ public:
}
// Helper methods to check nodes for constants.
+ bool isConstant(NodeIndex nodeIndex)
+ {
+ return graph()[nodeIndex].isConstant();
+ }
bool isInt32Constant(NodeIndex nodeIndex)
{
return graph()[nodeIndex].op == Int32Constant;
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index 11dbf0d..2a5b6dd 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -26,10 +26,6 @@
#ifndef DFGNode_h
#define DFGNode_h
-#if ENABLE(DFG_JIT)
-
-#include <wtf/Vector.h>
-
// Emit various logging information for debugging, including dumping the dataflow graphs.
#define DFG_DEBUG_VERBOSE 0
// Enable generation of dynamic checks into the instruction stream.
@@ -37,9 +33,21 @@
// Consistency check contents compiler data structures.
#define DFG_CONSISTENCY_CHECK 0
// Emit a breakpoint into the head of every generated function, to aid debugging in GDB.
-#define DFG_JIT_BREAK_ON_ENTRY 0
+#define DFG_JIT_BREAK_ON_EVERY_FUNCTION 0
+// Emit a breakpoint into the head of every generated node, to aid debugging in GDB.
+#define DFG_JIT_BREAK_ON_EVERY_BLOCK 0
+// Emit a breakpoint into the head of every generated node, to aid debugging in GDB.
+#define DFG_JIT_BREAK_ON_EVERY_NODE 0
+// Disable the DFG JIT without having to touch Platform.h!
+#define DFG_DEBUG_LOCAL_DISBALE 0
+// Generate stats on how successful we were in making use of the DFG jit, and remaining on the hot path.
+#define DFG_SUCCESS_STATS 0
+#if ENABLE(DFG_JIT)
+
+#include <wtf/Vector.h>
+
namespace JSC { namespace DFG {
// Type for a virtual register number (spill location).
@@ -61,6 +69,8 @@ typedef uint32_t ExceptionInfo;
#define NodeResultMask 0xF000
#define NodeMustGenerate 0x10000 // set on nodes that have side effects, and may not trivially be removed by DCE.
#define NodeIsConstant 0x20000
+#define NodeIsJump 0x40000
+#define NodeIsBranch 0x80000
// These values record the result type of the node (as checked by NodeResultMask, above), 0 for no result.
#define NodeResultJS 0x1000
@@ -73,9 +83,12 @@ typedef uint32_t ExceptionInfo;
macro(JSConstant, NodeResultJS | NodeIsConstant) \
macro(Int32Constant, NodeResultJS | NodeIsConstant) \
macro(DoubleConstant, NodeResultJS | NodeIsConstant) \
- macro(Argument, NodeResultJS) \
macro(ConvertThis, NodeResultJS) \
\
+ /* Nodes for local variable access. */\
+ macro(GetLocal, NodeResultJS) \
+ macro(SetLocal, NodeMustGenerate) \
+ \
/* Nodes for bitwise operations. */\
macro(BitAnd, NodeResultInt32) \
macro(BitOr, NodeResultInt32) \
@@ -115,6 +128,18 @@ typedef uint32_t ExceptionInfo;
macro(GetGlobalVar, NodeResultJS | NodeMustGenerate) \
macro(PutGlobalVar, NodeMustGenerate) \
\
+ /* Nodes for comparison operations. */\
+ macro(CompareLess, NodeResultJS | NodeMustGenerate) \
+ macro(CompareLessEq, NodeResultJS | NodeMustGenerate) \
+ macro(CompareEq, NodeResultJS | NodeMustGenerate) \
+ macro(CompareStrictEq, NodeResultJS) \
+ \
+ /* Nodes for misc operations. */\
+ macro(LogicalNot, NodeResultJS) \
+ \
+ /* Block terminals. */\
+ macro(Jump, NodeMustGenerate | NodeIsJump) \
+ macro(Branch, NodeMustGenerate | NodeIsBranch) \
macro(Return, NodeMustGenerate)
// This enum generates a monotonically increasing id for all Node types,
@@ -170,6 +195,20 @@ struct Node {
{
}
+ // Construct a node with up to 3 children and two immediate values.
+ Node(NodeType op, ExceptionInfo exceptionInfo, OpInfo imm1, OpInfo imm2, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
+ : op(op)
+ , exceptionInfo(exceptionInfo)
+ , child1(child1)
+ , child2(child2)
+ , child3(child3)
+ , virtualRegister(InvalidVirtualRegister)
+ , refCount(0)
+ , m_opInfo(imm1.m_value)
+ {
+ m_constantValue.opInfo2 = imm2.m_value;
+ }
+
bool mustGenerate()
{
return op & NodeMustGenerate;
@@ -186,15 +225,15 @@ struct Node {
return m_opInfo;
}
- bool isArgument()
+ bool hasLocal()
{
- return op == Argument;
+ return op == GetLocal || op == SetLocal;
}
- unsigned argumentNumber()
+ VirtualRegister local()
{
- ASSERT(isArgument());
- return m_opInfo;
+ ASSERT(hasLocal());
+ return (VirtualRegister)m_opInfo;
}
bool hasIdentifier()
@@ -266,6 +305,28 @@ struct Node {
m_constantValue.asDouble = value;
}
+ bool isJump()
+ {
+ return op & NodeIsJump;
+ }
+
+ bool isBranch()
+ {
+ return op & NodeIsBranch;
+ }
+
+ unsigned takenBytecodeOffset()
+ {
+ ASSERT(isBranch() || isJump());
+ return m_opInfo;
+ }
+
+ unsigned notTakenBytecodeOffset()
+ {
+ ASSERT(isBranch());
+ return m_constantValue.opInfo2;
+ }
+
// This enum value describes the type of the node.
NodeType op;
// Used to look up exception handling information (currently implemented as a bytecode index).
@@ -284,6 +345,7 @@ private:
union {
int32_t asInt32;
double asDouble;
+ unsigned opInfo2;
} m_constantValue;
};
diff --git a/Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.cpp
index 945c98a..87c4234 100644
--- a/Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.cpp
@@ -78,7 +78,7 @@ void NonSpeculativeJIT::valueToNumber(JSValueOperand& operand, FPRReg fpr)
// Next handle cells (& other JS immediates)
nonNumeric.link(&m_jit);
- silentSpillAllRegisters(jsValueGpr);
+ silentSpillAllRegisters(fpr, jsValueGpr);
m_jit.move(jsValueReg, JITCompiler::argumentRegister1);
m_jit.move(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0);
appendCallWithExceptionCheck(dfgConvertJSValueToNumber);
@@ -105,7 +105,7 @@ void NonSpeculativeJIT::valueToInt32(JSValueOperand& operand, GPRReg result)
JITCompiler::Jump isInteger = m_jit.branchPtr(MacroAssembler::AboveOrEqual, jsValueReg, JITCompiler::tagTypeNumberRegister);
// First handle non-integers
- silentSpillAllRegisters(jsValueGpr);
+ silentSpillAllRegisters(result, jsValueGpr);
m_jit.move(jsValueReg, JITCompiler::argumentRegister1);
m_jit.move(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0);
appendCallWithExceptionCheck(dfgConvertJSValueToInt32);
@@ -126,7 +126,7 @@ void NonSpeculativeJIT::numberToInt32(FPRReg fpr, GPRReg gpr)
JITCompiler::Jump truncatedToInteger = m_jit.branchTruncateDoubleToInt32(fpReg, reg, JITCompiler::BranchIfTruncateSuccessful);
- silentSpillAllRegisters(gpr); // don't really care!
+ silentSpillAllRegisters(gpr);
m_jit.moveDouble(fpReg, JITCompiler::fpArgumentRegister0);
appendCallWithExceptionCheck(toInt32);
@@ -137,6 +137,40 @@ void NonSpeculativeJIT::numberToInt32(FPRReg fpr, GPRReg gpr)
truncatedToInteger.link(&m_jit);
}
+bool NonSpeculativeJIT::isKnownInteger(NodeIndex nodeIndex)
+{
+ GenerationInfo& info = m_generationInfo[m_jit.graph()[nodeIndex].virtualRegister];
+
+ DataFormat registerFormat = info.registerFormat();
+ if (registerFormat != DataFormatNone)
+ return (registerFormat | DataFormatJS) == DataFormatJSInteger;
+
+ DataFormat spillFormat = info.spillFormat();
+ if (spillFormat != DataFormatNone)
+ return (spillFormat | DataFormatJS) == DataFormatJSInteger;
+
+ ASSERT(isConstant(nodeIndex));
+ return isInt32Constant(nodeIndex);
+}
+
+bool NonSpeculativeJIT::isKnownNumeric(NodeIndex nodeIndex)
+{
+ GenerationInfo& info = m_generationInfo[m_jit.graph()[nodeIndex].virtualRegister];
+
+ DataFormat registerFormat = info.registerFormat();
+ if (registerFormat != DataFormatNone)
+ return (registerFormat | DataFormatJS) == DataFormatJSInteger
+ || (registerFormat | DataFormatJS) == DataFormatJSDouble;
+
+ DataFormat spillFormat = info.spillFormat();
+ if (spillFormat != DataFormatNone)
+ return (spillFormat | DataFormatJS) == DataFormatJSInteger
+ || (spillFormat | DataFormatJS) == DataFormatJSDouble;
+
+ ASSERT(isConstant(nodeIndex));
+ return isInt32Constant(nodeIndex) || isDoubleConstant(nodeIndex);
+}
+
void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, Node& node)
{
// ...
@@ -144,7 +178,6 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
trackEntry(m_jit.label());
checkConsistency();
-
NodeType op = node.op;
switch (op) {
@@ -164,10 +197,20 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
case JSConstant:
initConstantInfo(m_compileIndex);
break;
-
- case Argument:
- initArgumentInfo(m_compileIndex);
+
+ case GetLocal: {
+ GPRTemporary result(this);
+ m_jit.loadPtr(JITCompiler::addressFor(node.local()), result.registerID());
+ jsValueResult(result.gpr(), m_compileIndex);
break;
+ }
+
+ case SetLocal: {
+ JSValueOperand value(this, node.child1);
+ m_jit.storePtr(value.registerID(), JITCompiler::addressFor(node.local()));
+ noResult(m_compileIndex);
+ break;
+ }
case BitAnd:
case BitOr:
@@ -250,10 +293,8 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
case NumberToInt32:
case ValueToInt32: {
ASSERT(!isInt32Constant(node.child1));
- GenerationInfo& operandInfo = m_generationInfo[m_jit.graph()[node.child1].virtualRegister];
- switch (operandInfo.registerFormat()) {
- case DataFormatInteger: {
+ if (isKnownInteger(node.child1)) {
IntegerOperand op1(this, node.child1);
GPRTemporary result(this, op1);
m_jit.move(op1.registerID(), result.registerID());
@@ -261,7 +302,7 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
break;
}
- case DataFormatDouble: {
+ if (isKnownNumeric(node.child1)) {
DoubleOperand op1(this, node.child1);
GPRTemporary result(this);
numberToInt32(op1.fpr(), result.gpr());
@@ -269,84 +310,29 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
break;
}
- default: {
- JSValueOperand op1(this, node.child1);
- GPRTemporary result(this, op1);
- op1.gpr(); // force op1 to be filled!
- result.gpr(); // force result to be allocated!
-
- switch (operandInfo.registerFormat()) {
- case DataFormatNone:
- case DataFormatInteger:
- case DataFormatDouble:
- // The operand has been filled as a JSValue; it cannot be in a !DataFormatJS state.
- CRASH();
-
- case DataFormatCell:
- case DataFormatJS:
- case DataFormatJSCell: {
- if (op == NumberToInt32) {
- FPRTemporary fpTemp(this);
- FPRReg fpr = fpTemp.fpr();
-
- JITCompiler::Jump isInteger = m_jit.branchPtr(MacroAssembler::AboveOrEqual, op1.registerID(), JITCompiler::tagTypeNumberRegister);
-
- m_jit.move(op1.registerID(), result.registerID());
- m_jit.addPtr(JITCompiler::tagTypeNumberRegister, result.registerID());
- m_jit.movePtrToDouble(result.registerID(), fpTemp.registerID());
- numberToInt32(fpr, result.gpr());
- JITCompiler::Jump wasDouble = m_jit.jump();
-
- isInteger.link(&m_jit);
- m_jit.zeroExtend32ToPtr(op1.registerID(), result.registerID());
-
- wasDouble.link(&m_jit);
- } else
- valueToInt32(op1, result.gpr());
- integerResult(result.gpr(), m_compileIndex);
- break;
- }
-
- case DataFormatJSDouble: {
- FPRTemporary fpTemp(this);
- m_jit.move(op1.registerID(), result.registerID());
- m_jit.addPtr(JITCompiler::tagTypeNumberRegister, result.registerID());
- m_jit.movePtrToDouble(result.registerID(), fpTemp.registerID());
- numberToInt32(fpTemp.fpr(), result.gpr());
- integerResult(result.gpr(), m_compileIndex);
- break;
- }
-
- case DataFormatJSInteger: {
- m_jit.move(op1.registerID(), result.registerID());
- jsValueResult(result.gpr(), m_compileIndex, DataFormatJSInteger);
- break;
- }
- }
- }
+ // We should have handled this via isKnownInteger, or isKnownNumeric!
+ ASSERT(op != NumberToInt32);
- }
+ JSValueOperand op1(this, node.child1);
+ GPRTemporary result(this, op1);
+ valueToInt32(op1, result.gpr());
+ integerResult(result.gpr(), m_compileIndex);
break;
}
case ValueToNumber: {
ASSERT(!isInt32Constant(node.child1));
ASSERT(!isDoubleConstant(node.child1));
- GenerationInfo& operandInfo = m_generationInfo[m_jit.graph()[node.child1].virtualRegister];
- switch (operandInfo.registerFormat()) {
- case DataFormatNone:
- case DataFormatCell:
- case DataFormatJS:
- case DataFormatJSCell: {
- JSValueOperand op1(this, node.child1);
+
+ if (isKnownInteger(node.child1)) {
+ IntegerOperand op1(this, node.child1);
FPRTemporary result(this);
- valueToNumber(op1, result.fpr());
+ m_jit.convertInt32ToDouble(op1.registerID(), result.registerID());
doubleResult(result.fpr(), m_compileIndex);
break;
}
- case DataFormatJSDouble:
- case DataFormatDouble: {
+ if (isKnownNumeric(node.child1)) {
DoubleOperand op1(this, node.child1);
FPRTemporary result(this, op1);
m_jit.moveDouble(op1.registerID(), result.registerID());
@@ -354,15 +340,10 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
break;
}
- case DataFormatJSInteger:
- case DataFormatInteger: {
- IntegerOperand op1(this, node.child1);
- FPRTemporary result(this);
- m_jit.convertInt32ToDouble(op1.registerID(), result.registerID());
- doubleResult(result.fpr(), m_compileIndex);
- break;
- }
- }
+ JSValueOperand op1(this, node.child1);
+ FPRTemporary result(this);
+ valueToNumber(op1, result.fpr());
+ doubleResult(result.fpr(), m_compileIndex);
break;
}
@@ -446,6 +427,80 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
break;
}
+ case LogicalNot: {
+ JSValueOperand arg1(this, node.child1);
+ GPRReg arg1GPR = arg1.gpr();
+ flushRegisters();
+
+ GPRResult result(this);
+ callOperation(dfgConvertJSValueToBoolean, result.gpr(), arg1GPR);
+
+ // If we add a DataFormatBool, we should use it here.
+ m_jit.xor32(TrustedImm32(ValueTrue), result.registerID());
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case CompareLess: {
+ JSValueOperand arg1(this, node.child1);
+ JSValueOperand arg2(this, node.child2);
+ GPRReg arg1GPR = arg1.gpr();
+ GPRReg arg2GPR = arg2.gpr();
+ flushRegisters();
+
+ GPRResult result(this);
+ callOperation(operationCompareLess, result.gpr(), arg1GPR, arg2GPR);
+ m_jit.or32(TrustedImm32(ValueFalse), result.registerID());
+
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case CompareLessEq: {
+ JSValueOperand arg1(this, node.child1);
+ JSValueOperand arg2(this, node.child2);
+ GPRReg arg1GPR = arg1.gpr();
+ GPRReg arg2GPR = arg2.gpr();
+ flushRegisters();
+
+ GPRResult result(this);
+ callOperation(operationCompareLessEq, result.gpr(), arg1GPR, arg2GPR);
+ m_jit.or32(TrustedImm32(ValueFalse), result.registerID());
+
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case CompareEq: {
+ JSValueOperand arg1(this, node.child1);
+ JSValueOperand arg2(this, node.child2);
+ GPRReg arg1GPR = arg1.gpr();
+ GPRReg arg2GPR = arg2.gpr();
+ flushRegisters();
+
+ GPRResult result(this);
+ callOperation(operationCompareEq, result.gpr(), arg1GPR, arg2GPR);
+ m_jit.or32(TrustedImm32(ValueFalse), result.registerID());
+
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case CompareStrictEq: {
+ JSValueOperand arg1(this, node.child1);
+ JSValueOperand arg2(this, node.child2);
+ GPRReg arg1GPR = arg1.gpr();
+ GPRReg arg2GPR = arg2.gpr();
+ flushRegisters();
+
+ GPRResult result(this);
+ callOperation(operationCompareStrictEq, result.gpr(), arg1GPR, arg2GPR);
+ m_jit.or32(TrustedImm32(ValueFalse), result.registerID());
+
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
case GetByVal: {
JSValueOperand arg1(this, node.child1);
JSValueOperand arg2(this, node.child2);
@@ -535,11 +590,43 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
break;
}
+ case DFG::Jump: {
+ BlockIndex taken = m_jit.graph().blockIndexForBytecodeOffset(node.takenBytecodeOffset());
+ if (taken != (m_block + 1))
+ addBranch(m_jit.jump(), taken);
+ noResult(m_compileIndex);
+ break;
+ }
+
+ case Branch: {
+ JSValueOperand value(this, node.child1);
+ GPRReg valueGPR = value.gpr();
+ flushRegisters();
+
+ GPRResult result(this);
+ callOperation(dfgConvertJSValueToBoolean, result.gpr(), valueGPR);
+
+ BlockIndex taken = m_jit.graph().blockIndexForBytecodeOffset(node.takenBytecodeOffset());
+ BlockIndex notTaken = m_jit.graph().blockIndexForBytecodeOffset(node.notTakenBytecodeOffset());
+
+ addBranch(m_jit.branchTest8(MacroAssembler::NonZero, result.registerID()), taken);
+ if (notTaken != (m_block + 1))
+ addBranch(m_jit.jump(), notTaken);
+
+ noResult(m_compileIndex);
+ break;
+ }
+
case Return: {
ASSERT(JITCompiler::callFrameRegister != JITCompiler::regT1);
ASSERT(JITCompiler::regT1 != JITCompiler::returnValueRegister);
ASSERT(JITCompiler::returnValueRegister != JITCompiler::callFrameRegister);
+#if DFG_SUCCESS_STATS
+ static SamplingCounter counter("NonSpeculativeJIT");
+ m_jit.emitCount(counter);
+#endif
+
// Return the result in returnValueRegister.
JSValueOperand op1(this, node.child1);
m_jit.move(op1.registerID(), JITCompiler::returnValueRegister);
@@ -563,23 +650,40 @@ void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, No
checkConsistency();
}
-void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator)
+void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator, BasicBlock& block)
{
- ASSERT(!m_compileIndex);
- Node* nodes = m_jit.graph().begin();
+ ASSERT(m_compileIndex == block.begin);
+ m_blockHeads[m_block] = m_jit.label();
- for (; m_compileIndex < m_jit.graph().size(); ++m_compileIndex) {
-#if DFG_DEBUG_VERBOSE
- fprintf(stderr, "index(%d)\n", (int)m_compileIndex);
+#if DFG_JIT_BREAK_ON_EVERY_BLOCK
+ m_jit.breakpoint();
#endif
- Node& node = nodes[m_compileIndex];
+ for (; m_compileIndex < block.end; ++m_compileIndex) {
+ Node& node = m_jit.graph()[m_compileIndex];
if (!node.refCount)
continue;
+
+#if DFG_DEBUG_VERBOSE
+ fprintf(stderr, "NonSpeculativeJIT generating Node @%d at code offset 0x%x\n", (int)m_compileIndex, m_jit.debugOffset());
+#endif
+#if DFG_JIT_BREAK_ON_EVERY_NODE
+ m_jit.breakpoint();
+#endif
+
compile(checkIterator, node);
}
}
+void NonSpeculativeJIT::compile(SpeculationCheckIndexIterator& checkIterator)
+{
+ ASSERT(!m_compileIndex);
+ Vector<BasicBlock> blocks = m_jit.graph().m_blocks;
+ for (m_block = 0; m_block < blocks.size(); ++m_block)
+ compile(checkIterator, blocks[m_block]);
+ linkBranches();
+}
+
} } // namespace JSC::DFG
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.h
index e140e45..de4c04b 100644
--- a/Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.h
@@ -80,92 +80,140 @@ public:
private:
void compile(SpeculationCheckIndexIterator&, Node&);
+ void compile(SpeculationCheckIndexIterator&, BasicBlock&);
+
+ bool isKnownInteger(NodeIndex);
+ bool isKnownNumeric(NodeIndex);
// These methods are used when generating 'unexpected'
// calls out from JIT code to C++ helper routines -
// they spill all live values to the appropriate
// slots in the RegisterFile without changing any state
// in the GenerationInfo.
- void silentSpill(VirtualRegister spillMe, GPRReg canTrample)
+ void silentSpillGPR(VirtualRegister spillMe, GPRReg exclude = InvalidGPRReg)
{
GenerationInfo& info = m_generationInfo[spillMe];
- ASSERT(info.registerFormat() != DataFormatNone);
- if (info.needsSpill()) {
- DataFormat spillFormat = info.registerFormat();
-
- if (spillFormat == DataFormatDouble) {
- boxDouble(info.fpr(), canTrample);
- m_jit.storePtr(JITCompiler::gprToRegisterID(canTrample), JITCompiler::addressFor(spillMe));
- } else {
- JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(info.gpr());
-
- if (spillFormat == DataFormatInteger) {
- m_jit.orPtr(JITCompiler::tagTypeNumberRegister, reg);
- m_jit.storePtr(reg, JITCompiler::addressFor(spillMe));
- } else {
- ASSERT(spillFormat & DataFormatJS || spillFormat == DataFormatCell);
- m_jit.storePtr(reg, JITCompiler::addressFor(spillMe));
- }
- }
+ ASSERT(info.registerFormat() != DataFormatNone && info.registerFormat() != DataFormatDouble);
+
+ if (!info.needsSpill() || (info.gpr() == exclude))
+ return;
+
+ DataFormat registerFormat = info.registerFormat();
+ JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(info.gpr());
+
+ if (registerFormat == DataFormatInteger) {
+ m_jit.orPtr(JITCompiler::tagTypeNumberRegister, reg);
+ m_jit.storePtr(reg, JITCompiler::addressFor(spillMe));
+ } else {
+ ASSERT(registerFormat & DataFormatJS || registerFormat == DataFormatCell);
+ m_jit.storePtr(reg, JITCompiler::addressFor(spillMe));
}
}
- void silentFill(VirtualRegister spillMe, GPRReg canTrample)
+ void silentSpillFPR(VirtualRegister spillMe, GPRReg canTrample, FPRReg exclude = InvalidFPRReg)
+ {
+ GenerationInfo& info = m_generationInfo[spillMe];
+ ASSERT(info.registerFormat() == DataFormatDouble);
+
+ if (!info.needsSpill() || (info.fpr() == exclude))
+ return;
+
+ boxDouble(info.fpr(), canTrample);
+ m_jit.storePtr(JITCompiler::gprToRegisterID(canTrample), JITCompiler::addressFor(spillMe));
+ }
+
+ void silentFillGPR(VirtualRegister spillMe, GPRReg exclude = InvalidGPRReg)
{
GenerationInfo& info = m_generationInfo[spillMe];
+ if (info.gpr() == exclude)
+ return;
+
NodeIndex nodeIndex = info.nodeIndex();
Node& node = m_jit.graph()[nodeIndex];
- ASSERT(info.registerFormat() != DataFormatNone);
- DataFormat spillFormat = info.registerFormat();
+ ASSERT(info.registerFormat() != DataFormatNone && info.registerFormat() != DataFormatDouble);
+ DataFormat registerFormat = info.registerFormat();
+ JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(info.gpr());
+
+ if (registerFormat == DataFormatInteger) {
+ if (node.isConstant()) {
+ ASSERT(isInt32Constant(nodeIndex));
+ m_jit.move(Imm32(valueOfInt32Constant(nodeIndex)), reg);
+ } else
+ m_jit.load32(JITCompiler::addressFor(spillMe), reg);
+ return;
+ }
+
+ if (node.isConstant())
+ m_jit.move(constantAsJSValueAsImmPtr(nodeIndex), reg);
+ else {
+ ASSERT(registerFormat & DataFormatJS || registerFormat == DataFormatCell);
+ m_jit.loadPtr(JITCompiler::addressFor(spillMe), reg);
+ }
+ }
+ void silentFillFPR(VirtualRegister spillMe, GPRReg canTrample, FPRReg exclude = InvalidFPRReg)
+ {
+ GenerationInfo& info = m_generationInfo[spillMe];
+ if (info.fpr() == exclude)
+ return;
+
+ NodeIndex nodeIndex = info.nodeIndex();
+ Node& node = m_jit.graph()[nodeIndex];
+ ASSERT(info.registerFormat() == DataFormatDouble);
if (node.isConstant()) {
JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(info.gpr());
m_jit.move(constantAsJSValueAsImmPtr(nodeIndex), reg);
- } else if (node.isArgument()) {
- JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(info.gpr());
- m_jit.loadPtr(m_jit.addressForArgument(m_jit.graph()[nodeIndex].argumentNumber()), reg);
- } else if (spillFormat == DataFormatDouble) {
+ } else {
m_jit.loadPtr(JITCompiler::addressFor(spillMe), JITCompiler::gprToRegisterID(canTrample));
unboxDouble(canTrample, info.fpr());
- } else if (spillFormat == DataFormatInteger) {
- JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(info.gpr());
- m_jit.load32(JITCompiler::addressFor(spillMe), reg);
- } else {
- ASSERT(spillFormat & DataFormatJS || spillFormat == DataFormatCell);
- JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(info.gpr());
- m_jit.loadPtr(JITCompiler::addressFor(spillMe), reg);
}
}
- void silentSpillAllRegisters(GPRReg dontTrample)
+
+ void silentSpillAllRegisters(GPRReg exclude, GPRReg preserve = InvalidGPRReg)
+ {
+ GPRReg canTrample = (preserve == gpr0) ? gpr1 : gpr0;
+
+ for (GPRReg gpr = gpr0; gpr < numberOfGPRs; next(gpr)) {
+ VirtualRegister name = m_gprs.name(gpr);
+ if (name != InvalidVirtualRegister)
+ silentSpillGPR(name, exclude);
+ }
+ for (FPRReg fpr = fpr0; fpr < numberOfFPRs; next(fpr)) {
+ VirtualRegister name = m_fprs.name(fpr);
+ if (name != InvalidVirtualRegister)
+ silentSpillFPR(name, canTrample);
+ }
+ }
+ void silentSpillAllRegisters(FPRReg exclude, GPRReg preserve = InvalidGPRReg)
{
- GPRReg canTrample = (dontTrample == gpr0) ? gpr1 : gpr0;
+ GPRReg canTrample = (preserve == gpr0) ? gpr1 : gpr0;
for (GPRReg gpr = gpr0; gpr < numberOfGPRs; next(gpr)) {
VirtualRegister name = m_gprs.name(gpr);
if (name != InvalidVirtualRegister)
- silentSpill(name, canTrample);
+ silentSpillGPR(name);
}
for (FPRReg fpr = fpr0; fpr < numberOfFPRs; next(fpr)) {
VirtualRegister name = m_fprs.name(fpr);
if (name != InvalidVirtualRegister)
- silentSpill(name, canTrample);
+ silentSpillFPR(name, canTrample, exclude);
}
}
- void silentFillAllRegisters(GPRReg dontTrample)
+ void silentFillAllRegisters(GPRReg exclude)
{
- GPRReg canTrample = (dontTrample == gpr0) ? gpr1 : gpr0;
+ GPRReg canTrample = (exclude == gpr0) ? gpr1 : gpr0;
for (FPRReg fpr = fpr0; fpr < numberOfFPRs; next(fpr)) {
VirtualRegister name = m_fprs.name(fpr);
if (name != InvalidVirtualRegister)
- silentFill(name, canTrample);
+ silentFillFPR(name, canTrample);
}
for (GPRReg gpr = gpr0; gpr < numberOfGPRs; next(gpr)) {
VirtualRegister name = m_gprs.name(gpr);
if (name != InvalidVirtualRegister)
- silentFill(name, canTrample);
+ silentFillGPR(name, exclude);
}
}
- void silentFillAllRegisters(FPRReg dontTrample)
+ void silentFillAllRegisters(FPRReg exclude)
{
GPRReg canTrample = gpr0;
@@ -173,17 +221,17 @@ private:
VirtualRegister name = m_fprs.name(fpr);
if (name != InvalidVirtualRegister) {
#ifndef NDEBUG
- ASSERT(fpr != dontTrample);
+ ASSERT(fpr != exclude);
#else
- UNUSED_PARAM(dontTrample);
+ UNUSED_PARAM(exclude);
#endif
- silentFill(name, canTrample);
+ silentFillFPR(name, canTrample, exclude);
}
}
for (GPRReg gpr = gpr0; gpr < numberOfGPRs; next(gpr)) {
VirtualRegister name = m_gprs.name(gpr);
if (name != InvalidVirtualRegister)
- silentFill(name, canTrample);
+ silentFillGPR(name);
}
}
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.cpp b/Source/JavaScriptCore/dfg/DFGOperations.cpp
index de14415..a310d22 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.cpp
+++ b/Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -192,6 +192,26 @@ void operationPutByIdDirectNonStrict(ExecState* exec, EncodedJSValue encodedValu
JSValue::decode(encodedBase).putDirect(exec, *identifier, JSValue::decode(encodedValue), slot);
}
+bool operationCompareLess(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ return jsLess(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
+}
+
+bool operationCompareLessEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ return jsLessEq(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
+}
+
+bool operationCompareEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ return JSValue::equal(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
+}
+
+bool operationCompareStrictEq(ExecState* exec, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)
+{
+ return JSValue::strictEqual(exec, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2));
+}
+
DFGHandler lookupExceptionHandler(ExecState* exec, ReturnAddressPtr faultLocation)
{
JSValue exceptionValue = exec->exception();
@@ -215,6 +235,11 @@ int32_t dfgConvertJSValueToInt32(ExecState* exec, EncodedJSValue value)
return JSValue::decode(value).toInt32(exec);
}
+bool dfgConvertJSValueToBoolean(ExecState* exec, EncodedJSValue encodedOp)
+{
+ return JSValue::decode(encodedOp).toBoolean(exec);
+}
+
} } // namespace JSC::DFG
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGOperations.h b/Source/JavaScriptCore/dfg/DFGOperations.h
index 18570e2..d4c7c0f 100644
--- a/Source/JavaScriptCore/dfg/DFGOperations.h
+++ b/Source/JavaScriptCore/dfg/DFGOperations.h
@@ -42,6 +42,8 @@ typedef EncodedJSValue (*J_DFGOperation_EJJ)(ExecState*, EncodedJSValue, Encoded
typedef EncodedJSValue (*J_DFGOperation_EJ)(ExecState*, EncodedJSValue);
typedef EncodedJSValue (*J_DFGOperation_EJP)(ExecState*, EncodedJSValue, void*);
typedef EncodedJSValue (*J_DFGOperation_EJI)(ExecState*, EncodedJSValue, Identifier*);
+typedef bool (*Z_DFGOperation_EJ)(ExecState*, EncodedJSValue);
+typedef bool (*Z_DFGOperation_EJJ)(ExecState*, EncodedJSValue, EncodedJSValue);
typedef void (*V_DFGOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
typedef void (*V_DFGOperation_EJJP)(ExecState*, EncodedJSValue, EncodedJSValue, void*);
typedef void (*V_DFGOperation_EJJI)(ExecState*, EncodedJSValue, EncodedJSValue, Identifier*);
@@ -58,6 +60,10 @@ void operationPutByIdStrict(ExecState*, EncodedJSValue encodedValue, EncodedJSVa
void operationPutByIdNonStrict(ExecState*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, Identifier*);
void operationPutByIdDirectStrict(ExecState*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, Identifier*);
void operationPutByIdDirectNonStrict(ExecState*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, Identifier*);
+bool operationCompareLess(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
+bool operationCompareLessEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
+bool operationCompareEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
+bool operationCompareStrictEq(ExecState*, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2);
// 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.
@@ -73,9 +79,10 @@ struct DFGHandler {
};
DFGHandler lookupExceptionHandler(ExecState*, ReturnAddressPtr faultLocation);
-// These operations implement the implicitly called ToInt32 and ToNumber conversions from ES5.
+// These operations implement the implicitly called ToInt32, ToNumber, and ToBoolean conversions from ES5.
double dfgConvertJSValueToNumber(ExecState*, EncodedJSValue);
int32_t dfgConvertJSValueToInt32(ExecState*, EncodedJSValue);
+bool dfgConvertJSValueToBoolean(ExecState*, EncodedJSValue);
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGScoreBoard.h b/Source/JavaScriptCore/dfg/DFGScoreBoard.h
index eefed9e..b9bf1fd 100644
--- a/Source/JavaScriptCore/dfg/DFGScoreBoard.h
+++ b/Source/JavaScriptCore/dfg/DFGScoreBoard.h
@@ -42,8 +42,9 @@ namespace JSC { namespace DFG {
// another node.
class ScoreBoard {
public:
- ScoreBoard(Graph& graph)
+ ScoreBoard(Graph& graph, uint32_t firstTemporary)
: m_graph(graph)
+ , m_firstTemporary(firstTemporary)
{
}
@@ -58,7 +59,7 @@ public:
// * By setting m_used to a non-zero value after checking it, we are checking that all
// entries in m_free are unique (otherwise the second test of m_used will fail).
for (size_t i = 0; i < m_free.size(); ++i) {
- VirtualRegister virtualRegister = m_free[i];
+ uint32_t virtualRegister = m_free[i];
ASSERT(!m_used[virtualRegister]);
m_used[virtualRegister] = 1;
}
@@ -70,17 +71,17 @@ public:
// Do we have any VirtualRegsiters in the free list, that were used by
// prior nodes, but are now available?
if (!m_free.isEmpty()) {
- VirtualRegister result = m_free.last();
+ uint32_t index = m_free.last();
m_free.removeLast();
// Use count must have hit zero for it to have been added to the free list!
- ASSERT(!m_used[result]);
- return result;
+ ASSERT(!m_used[index]);
+ return (VirtualRegister)(m_firstTemporary + index);
}
// Allocate a new VirtualRegister, and add a corresponding entry to m_used.
size_t next = allocatedCount();
m_used.append(0);
- return (VirtualRegister)next;
+ return (VirtualRegister)(m_firstTemporary + next);
}
// Increment the usecount for the VirtualRegsiter associated with 'child',
@@ -92,7 +93,7 @@ public:
// Find the virtual register number for this child, increment its use count.
Node& node = m_graph[child];
- VirtualRegister index = node.virtualRegister;
+ uint32_t index = node.virtualRegister - m_firstTemporary;
if (node.refCount == ++m_used[index]) {
// 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.
@@ -111,6 +112,9 @@ public:
private:
// The graph, so we can get refCounts for nodes, to determine when values are dead.
Graph& m_graph;
+ // The first VirtualRegsiter to be used as a temporary.
+ uint32_t m_firstTemporary;
+
// For every virtual register that has been allocated (either currently alive, or in
// the free list), we keep a count of the number of remaining uses until it is dead
// (0, in the case of entries in the free list). Since there is an entry for every
@@ -118,7 +122,7 @@ private:
// next available VirtualRegister number.
Vector<uint32_t, 64> m_used;
// A free list of VirtualRegsiters no longer alive.
- Vector<VirtualRegister, 64> m_free;
+ Vector<uint32_t, 64> m_free;
};
} } // namespace JSC::DFG
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 95472e1..7963184 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -51,9 +51,6 @@ GPRReg SpeculativeJIT::fillSpeculateIntInternal(NodeIndex nodeIndex, DataFormat&
return gpr;
}
m_jit.move(constantAsJSValueAsImmPtr(nodeIndex), reg);
- } else if (node.isArgument()) {
- m_gprs.retain(gpr, virtualRegister, SpillOrderArgument);
- m_jit.loadPtr(m_jit.addressForArgument(m_jit.graph()[nodeIndex].argumentNumber()), reg);
} else {
DataFormat spillFormat = info.spillFormat();
ASSERT(spillFormat & DataFormatJS);
@@ -203,13 +200,6 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
terminateSpeculativeExecution();
return gpr;
}
- if (node.isArgument()) {
- m_gprs.retain(gpr, virtualRegister, SpillOrderArgument);
- m_jit.loadPtr(m_jit.addressForArgument(m_jit.graph()[nodeIndex].argumentNumber()), reg);
- speculationCheck(m_jit.branchTestPtr(MacroAssembler::NonZero, reg, JITCompiler::tagMaskRegister));
- info.fillJSValue(gpr, DataFormatJSCell);
- return gpr;
- }
ASSERT(info.spillFormat() & DataFormatJS);
m_gprs.retain(gpr, virtualRegister, SpillOrderSpilled);
m_jit.loadPtr(JITCompiler::addressFor(virtualRegister), reg);
@@ -252,7 +242,6 @@ GPRReg SpeculativeJIT::fillSpeculateCell(NodeIndex nodeIndex)
bool SpeculativeJIT::compile(Node& node)
{
checkConsistency();
-
NodeType op = node.op;
switch (op) {
@@ -261,10 +250,20 @@ bool SpeculativeJIT::compile(Node& node)
case JSConstant:
initConstantInfo(m_compileIndex);
break;
-
- case Argument:
- initArgumentInfo(m_compileIndex);
+
+ case GetLocal: {
+ GPRTemporary result(this);
+ m_jit.loadPtr(JITCompiler::addressFor(node.local()), result.registerID());
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case SetLocal: {
+ JSValueOperand value(this, node.child1);
+ m_jit.storePtr(value.registerID(), JITCompiler::addressFor(node.local()));
+ noResult(m_compileIndex);
break;
+ }
case BitAnd:
case BitOr:
@@ -347,6 +346,7 @@ bool SpeculativeJIT::compile(Node& node)
integerResult(result.gpr(), m_compileIndex, op1.format());
break;
}
+
case ValueToInt32: {
SpeculateIntegerOperand op1(this, node.child1);
GPRTemporary result(this, op1);
@@ -365,6 +365,30 @@ bool SpeculativeJIT::compile(Node& node)
case ValueAdd:
case ArithAdd: {
+ int32_t imm1;
+ if (isDoubleConstantWithInt32Value(node.child1, imm1)) {
+ SpeculateIntegerOperand op2(this, node.child2);
+ GPRTemporary result(this);
+
+ MacroAssembler::RegisterID reg = op2.registerID();
+ speculationCheck(m_jit.branchAdd32(MacroAssembler::Overflow, reg, Imm32(imm1), result.registerID()));
+
+ integerResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ int32_t imm2;
+ if (isDoubleConstantWithInt32Value(node.child2, imm2)) {
+ SpeculateIntegerOperand op1(this, node.child1);
+ GPRTemporary result(this);
+
+ MacroAssembler::RegisterID reg = op1.registerID();
+ speculationCheck(m_jit.branchAdd32(MacroAssembler::Overflow, reg, Imm32(imm2), result.registerID()));
+
+ integerResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
SpeculateIntegerOperand op1(this, node.child1);
SpeculateIntegerOperand op2(this, node.child2);
GPRTemporary result(this, op1, op2);
@@ -386,6 +410,18 @@ bool SpeculativeJIT::compile(Node& node)
}
case ArithSub: {
+ int32_t imm2;
+ if (isDoubleConstantWithInt32Value(node.child2, imm2)) {
+ SpeculateIntegerOperand op1(this, node.child1);
+ GPRTemporary result(this);
+
+ MacroAssembler::RegisterID reg = op1.registerID();
+ speculationCheck(m_jit.branchSub32(MacroAssembler::Overflow, reg, Imm32(imm2), result.registerID()));
+
+ integerResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
SpeculateIntegerOperand op1(this, node.child1);
SpeculateIntegerOperand op2(this, node.child2);
GPRTemporary result(this);
@@ -406,7 +442,11 @@ bool SpeculativeJIT::compile(Node& node)
MacroAssembler::RegisterID reg1 = op1.registerID();
MacroAssembler::RegisterID reg2 = op2.registerID();
speculationCheck(m_jit.branchMul32(MacroAssembler::Overflow, reg1, reg2, result.registerID()));
- speculationCheck(m_jit.branchTest32(MacroAssembler::Zero, result.registerID()));
+
+ MacroAssembler::Jump resultNonZero = m_jit.branchTest32(MacroAssembler::NonZero, result.registerID());
+ speculationCheck(m_jit.branch32(MacroAssembler::LessThan, reg1, TrustedImm32(0)));
+ speculationCheck(m_jit.branch32(MacroAssembler::LessThan, reg2, TrustedImm32(0)));
+ resultNonZero.link(&m_jit);
integerResult(result.gpr(), m_compileIndex);
break;
@@ -434,6 +474,72 @@ bool SpeculativeJIT::compile(Node& node)
break;
}
+ case LogicalNot: {
+ JSValueOperand value(this, node.child1);
+ GPRTemporary result(this); // FIXME: We could reuse, but on speculation fail would need recovery to restore tag (akin to add).
+
+ m_jit.move(value.registerID(), result.registerID());
+ m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueFalse)), result.registerID());
+ speculationCheck(m_jit.branchTestPtr(JITCompiler::NonZero, result.registerID(), TrustedImm32(static_cast<int32_t>(~1))));
+ m_jit.xorPtr(TrustedImm32(static_cast<int32_t>(ValueTrue)), result.registerID());
+
+ // If we add a DataFormatBool, we should use it here.
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case CompareLess: {
+ SpeculateIntegerOperand op1(this, node.child1);
+ SpeculateIntegerOperand op2(this, node.child2);
+ GPRTemporary result(this, op1, op2);
+
+ m_jit.set32Compare32(JITCompiler::LessThan, op1.registerID(), op2.registerID(), result.registerID());
+
+ // If we add a DataFormatBool, we should use it here.
+ m_jit.or32(TrustedImm32(ValueFalse), result.registerID());
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case CompareLessEq: {
+ SpeculateIntegerOperand op1(this, node.child1);
+ SpeculateIntegerOperand op2(this, node.child2);
+ GPRTemporary result(this, op1, op2);
+
+ m_jit.set32Compare32(JITCompiler::LessThanOrEqual, op1.registerID(), op2.registerID(), result.registerID());
+
+ // If we add a DataFormatBool, we should use it here.
+ m_jit.or32(TrustedImm32(ValueFalse), result.registerID());
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case CompareEq: {
+ SpeculateIntegerOperand op1(this, node.child1);
+ SpeculateIntegerOperand op2(this, node.child2);
+ GPRTemporary result(this, op1, op2);
+
+ m_jit.set32Compare32(JITCompiler::Equal, op1.registerID(), op2.registerID(), result.registerID());
+
+ // If we add a DataFormatBool, we should use it here.
+ m_jit.or32(TrustedImm32(ValueFalse), result.registerID());
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
+ case CompareStrictEq: {
+ SpeculateIntegerOperand op1(this, node.child1);
+ SpeculateIntegerOperand op2(this, node.child2);
+ GPRTemporary result(this, op1, op2);
+
+ m_jit.set32Compare32(JITCompiler::Equal, op1.registerID(), op2.registerID(), result.registerID());
+
+ // If we add a DataFormatBool, we should use it here.
+ m_jit.or32(TrustedImm32(ValueFalse), result.registerID());
+ jsValueResult(result.gpr(), m_compileIndex);
+ break;
+ }
+
case GetByVal: {
NodeIndex alias = node.child3;
if (alias != NoNode) {
@@ -472,73 +578,110 @@ bool SpeculativeJIT::compile(Node& node)
break;
}
- case PutByVal:
- case PutByValAlias: {
+ case PutByVal: {
+ SpeculateCellOperand base(this, node.child1);
SpeculateStrictInt32Operand property(this, node.child2);
+ JSValueOperand value(this, node.child3);
GPRTemporary storage(this);
- MacroAssembler::RegisterID propertyReg;
- MacroAssembler::RegisterID storageReg;
+ // Map base, property & value into registers, allocate a register for storage.
+ MacroAssembler::RegisterID baseReg = base.registerID();
+ MacroAssembler::RegisterID propertyReg = property.registerID();
+ MacroAssembler::RegisterID valueReg = value.registerID();
+ MacroAssembler::RegisterID storageReg = storage.registerID();
- // This block also defines the scope for base, and all bails to the non-speculative path.
- // At the end of this scope base will be release, and as such may be reused by for 'value'.
- //
- // If we've already read from this location on the speculative pass, then it cannot be beyond array bounds, or a hole.
- if (op == PutByValAlias) {
- SpeculateCellOperand base(this, node.child1);
+ // Check that base is an array, and that property is contained within m_vector (< m_vectorLength).
+ speculationCheck(m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
+ speculationCheck(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
- // Map base & property into registers, allocate a register for storage.
- propertyReg = property.registerID();
- storageReg = storage.registerID();
- MacroAssembler::RegisterID baseReg = base.registerID();
+ // Get the array storage.
+ m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
- // Get the array storage.
- m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
- } else {
- SpeculateCellOperand base(this, node.child1);
+ // Check if we're writing to a hole; if so increment m_numValuesInVector.
+ MacroAssembler::Jump notHoleValue = m_jit.branchTestPtr(MacroAssembler::NonZero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
+ m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)));
- // Map base & property into registers, allocate a register for storage.
- propertyReg = property.registerID();
- storageReg = storage.registerID();
- MacroAssembler::RegisterID baseReg = base.registerID();
+ // If we're writing to a hole we might be growing the array;
+ MacroAssembler::Jump lengthDoesNotNeedUpdate = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_length)));
+ m_jit.add32(TrustedImm32(1), propertyReg);
+ m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_length)));
+ m_jit.sub32(TrustedImm32(1), propertyReg);
- // Check that base is an array, and that property is contained within m_vector (< m_vectorLength).
- speculationCheck(m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
- speculationCheck(m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
+ lengthDoesNotNeedUpdate.link(&m_jit);
+ notHoleValue.link(&m_jit);
- // Get the array storage.
- m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
+ // Store the value to the array.
+ m_jit.storePtr(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
- // Check if we're writing to a hole; if so increment m_numValuesInVector.
- MacroAssembler::Jump notHoleValue = m_jit.branchTestPtr(MacroAssembler::NonZero, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
- m_jit.add32(TrustedImm32(1), MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_numValuesInVector)));
+ noResult(m_compileIndex);
+ break;
+ }
- // If we're writing to a hole we might be growing the array;
- MacroAssembler::Jump lengthDoesNotNeedUpdate = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_length)));
- m_jit.add32(TrustedImm32(1), propertyReg);
- m_jit.store32(propertyReg, MacroAssembler::Address(storageReg, OBJECT_OFFSETOF(ArrayStorage, m_length)));
- m_jit.sub32(TrustedImm32(1), propertyReg);
+ case PutByValAlias: {
+ SpeculateCellOperand base(this, node.child1);
+ SpeculateStrictInt32Operand property(this, node.child2);
+ JSValueOperand value(this, node.child3);
+ GPRTemporary storage(this, base); // storage may overwrite base.
- lengthDoesNotNeedUpdate.link(&m_jit);
- notHoleValue.link(&m_jit);
- }
- // After this point base goes out of scope. This may free the register.
- // As such, after this point we'd better not have any bails out to the non-speculative path!
+ // Get the array storage.
+ MacroAssembler::RegisterID storageReg = storage.registerID();
+ m_jit.loadPtr(MacroAssembler::Address(base.registerID(), JSArray::storageOffset()), storageReg);
- // Store the value to the array.
- JSValueOperand value(this, node.child3);
+ // Map property & value into registers.
+ MacroAssembler::RegisterID propertyReg = property.registerID();
MacroAssembler::RegisterID valueReg = value.registerID();
+
+ // Store the value to the array.
m_jit.storePtr(valueReg, MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])));
noResult(m_compileIndex);
break;
}
+ case DFG::Jump: {
+ BlockIndex taken = m_jit.graph().blockIndexForBytecodeOffset(node.takenBytecodeOffset());
+ if (taken != (m_block + 1))
+ addBranch(m_jit.jump(), taken);
+ noResult(m_compileIndex);
+ break;
+ }
+
+ case Branch: {
+ JSValueOperand value(this, node.child1);
+ MacroAssembler::RegisterID valueReg = value.registerID();
+
+ BlockIndex taken = m_jit.graph().blockIndexForBytecodeOffset(node.takenBytecodeOffset());
+ BlockIndex notTaken = m_jit.graph().blockIndexForBytecodeOffset(node.notTakenBytecodeOffset());
+
+ // Integers
+ addBranch(m_jit.branchPtr(MacroAssembler::Equal, valueReg, MacroAssembler::ImmPtr(JSValue::encode(jsNumber(0)))), notTaken);
+ MacroAssembler::Jump isNonZeroInteger = m_jit.branchPtr(MacroAssembler::AboveOrEqual, valueReg, JITCompiler::tagTypeNumberRegister);
+
+ // Booleans
+ addBranch(m_jit.branchPtr(MacroAssembler::Equal, valueReg, MacroAssembler::ImmPtr(JSValue::encode(jsBoolean(false)))), notTaken);
+ speculationCheck(m_jit.branchPtr(MacroAssembler::NotEqual, valueReg, MacroAssembler::ImmPtr(JSValue::encode(jsBoolean(true)))));
+
+ if (taken == (m_block + 1))
+ isNonZeroInteger.link(&m_jit);
+ else {
+ addBranch(isNonZeroInteger, taken);
+ addBranch(m_jit.jump(), taken);
+ }
+
+ noResult(m_compileIndex);
+ break;
+ }
+
case Return: {
ASSERT(JITCompiler::callFrameRegister != JITCompiler::regT1);
ASSERT(JITCompiler::regT1 != JITCompiler::returnValueRegister);
ASSERT(JITCompiler::returnValueRegister != JITCompiler::callFrameRegister);
+#if DFG_SUCCESS_STATS
+ static SamplingCounter counter("SpeculativeJIT");
+ m_jit.emitCount(counter);
+#endif
+
// Return the result in returnValueRegister.
JSValueOperand op1(this, node.child1);
m_jit.move(op1.registerID(), JITCompiler::returnValueRegister);
@@ -639,25 +782,43 @@ bool SpeculativeJIT::compile(Node& node)
return true;
}
-bool SpeculativeJIT::compile()
+bool SpeculativeJIT::compile(BasicBlock& block)
{
- ASSERT(!m_compileIndex);
- Node* nodes = m_jit.graph().begin();
-
- for (; m_compileIndex < m_jit.graph().size(); ++m_compileIndex) {
-#if DFG_DEBUG_VERBOSE
- fprintf(stderr, "index(%d)\n", (int)m_compileIndex);
+ ASSERT(m_compileIndex == block.begin);
+ m_blockHeads[m_block] = m_jit.label();
+#if DFG_JIT_BREAK_ON_EVERY_BLOCK
+ m_jit.breakpoint();
#endif
- Node& node = nodes[m_compileIndex];
+ for (; m_compileIndex < block.end; ++m_compileIndex) {
+ Node& node = m_jit.graph()[m_compileIndex];
if (!node.refCount)
continue;
+
+#if DFG_DEBUG_VERBOSE
+ fprintf(stderr, "SpeculativeJIT generating Node @%d at JIT offset 0x%x\n", (int)m_compileIndex, m_jit.debugOffset());
+#endif
+#if DFG_JIT_BREAK_ON_EVERY_NODE
+ m_jit.breakpoint();
+#endif
if (!compile(node))
return false;
}
return true;
}
+bool SpeculativeJIT::compile()
+{
+ ASSERT(!m_compileIndex);
+ Vector<BasicBlock> blocks = m_jit.graph().m_blocks;
+ for (m_block = 0; m_block < blocks.size(); ++m_block) {
+ if (!compile(blocks[m_block]))
+ return false;
+ }
+ linkBranches();
+ return true;
+}
+
} } // namespace JSC::DFG
#endif
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 93983c6..965cdbe 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -88,6 +88,7 @@ struct SpeculationCheck {
RegisterInfo m_gprInfo[numberOfGPRs];
NodeIndex m_fprInfo[numberOfFPRs];
};
+typedef SegmentedVector<SpeculationCheck, 16> SpeculationCheckVector;
// === SpeculativeJIT ===
@@ -103,9 +104,6 @@ struct SpeculationCheck {
class SpeculativeJIT : public JITCodeGenerator {
friend struct SpeculationCheck;
public:
- // The speculation
- typedef SegmentedVector<SpeculationCheck, 16> SpeculationCheckVector;
-
SpeculativeJIT(JITCompiler& jit)
: JITCodeGenerator(jit, true)
, m_didTerminate(false)
@@ -135,6 +133,23 @@ public:
private:
bool compile(Node&);
+ bool compile(BasicBlock&);
+
+ bool isDoubleConstantWithInt32Value(NodeIndex nodeIndex, int32_t& out)
+ {
+ if (!m_jit.isDoubleConstant(nodeIndex))
+ return false;
+ double value = m_jit.valueOfDoubleConstant(nodeIndex);
+
+ int32_t asInt32 = static_cast<int32_t>(value);
+ if (value != asInt32)
+ return false;
+ if (!asInt32 && signbit(value))
+ return false;
+
+ out = asInt32;
+ return true;
+ }
// Add a speculation check without additional recovery.
void speculationCheck(MacroAssembler::Jump jumpToFail)
@@ -325,8 +340,8 @@ private:
// nodes require entry points from the speculative path.
class SpeculationCheckIndexIterator {
public:
- SpeculationCheckIndexIterator(SpeculativeJIT& speculativeJIT)
- : m_speculationChecks(speculativeJIT.speculationChecks())
+ SpeculationCheckIndexIterator(SpeculationCheckVector& speculationChecks)
+ : m_speculationChecks(speculationChecks)
, m_iter(m_speculationChecks.begin())
, m_end(m_speculationChecks.end())
{
@@ -344,11 +359,12 @@ public:
}
private:
- SpeculativeJIT::SpeculationCheckVector& m_speculationChecks;
- SpeculativeJIT::SpeculationCheckVector::Iterator m_iter;
- SpeculativeJIT::SpeculationCheckVector::Iterator m_end;
+ SpeculationCheckVector& m_speculationChecks;
+ SpeculationCheckVector::Iterator m_iter;
+ SpeculationCheckVector::Iterator m_end;
};
+
} } // namespace JSC::DFG
#endif