diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp | 546 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGGenerationInfo.h | 21 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGGraph.cpp | 190 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGGraph.h | 48 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGJITCodeGenerator.cpp | 19 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h | 55 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGJITCompiler.cpp | 46 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGJITCompiler.h | 4 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGNode.h | 84 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.cpp | 290 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGNonSpeculativeJIT.h | 140 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGOperations.cpp | 25 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGOperations.h | 9 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGScoreBoard.h | 20 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp | 291 | ||||
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h | 32 |
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 |