]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGByteCodeParser.cpp
JavaScriptCore-903.tar.gz
[apple/javascriptcore.git] / dfg / DFGByteCodeParser.cpp
diff --git a/dfg/DFGByteCodeParser.cpp b/dfg/DFGByteCodeParser.cpp
new file mode 100644 (file)
index 0000000..7c333de
--- /dev/null
@@ -0,0 +1,1198 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGByteCodeParser.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGAliasTracker.h"
+#include "DFGScoreBoard.h"
+#include "CodeBlock.h"
+
+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.
+class ByteCodeParser {
+public:
+    ByteCodeParser(JSGlobalData* globalData, CodeBlock* codeBlock, Graph& graph)
+        : m_globalData(globalData)
+        , m_codeBlock(codeBlock)
+        , m_graph(graph)
+        , m_currentIndex(0)
+        , m_parseFailed(false)
+        , m_constantUndefined(UINT_MAX)
+        , m_constantNull(UINT_MAX)
+        , m_constant1(UINT_MAX)
+        , m_constants(codeBlock->numberOfConstantRegisters())
+        , m_numArguments(codeBlock->m_numParameters)
+        , m_numLocals(codeBlock->m_numCalleeRegisters)
+        , m_preservedVars(codeBlock->m_numVars)
+    {
+    }
+
+    // Parse a full CodeBlock of bytecode.
+    bool parse();
+
+private:
+    // Parse a single basic block of bytecode instructions.
+    bool parseBlock(unsigned limit);
+    // Setup predecessor links in the graph's BasicBlocks.
+    void setupPredecessors();
+    // Link GetLocal & SetLocal nodes, to ensure live values are generated.
+    enum PhiStackType {
+        LocalPhiStack,
+        ArgumentPhiStack
+    };
+    template<PhiStackType stackType>
+    void processPhiStack();
+    // Add spill locations to nodes.
+    void allocateVirtualRegisters();
+
+    // 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_constants.size());
+            return getJSConstant(constant);
+        }
+
+        // Is this an argument?
+        if (operandIsArgument(operand))
+            return getArgument(operand);
+
+        // Must be a local.
+        return getLocal((unsigned)operand);
+    }
+    void set(int operand, NodeIndex value, PredictedType prediction = PredictNone)
+    {
+        m_graph.predict(operand, prediction);
+
+        // Is this an argument?
+        if (operandIsArgument(operand)) {
+            setArgument(operand, value);
+            return;
+        }
+
+        // Must be a local.
+        setLocal((unsigned)operand, value);
+    }
+
+    // Used in implementing get/set, above, where the operand is a local variable.
+    NodeIndex getLocal(unsigned operand)
+    {
+        NodeIndex nodeIndex = m_currentBlock->m_locals[operand].value;
+
+        if (nodeIndex != NoNode) {
+            Node& node = m_graph[nodeIndex];
+            if (node.op == GetLocal)
+                return nodeIndex;
+            ASSERT(node.op == SetLocal);
+            return node.child1;
+        }
+
+        // Check for reads of temporaries from prior blocks,
+        // expand m_preservedVars to cover these.
+        m_preservedVars = std::max(m_preservedVars, operand + 1);
+
+        NodeIndex phi = addToGraph(Phi);
+        m_localPhiStack.append(PhiStackEntry(m_currentBlock, phi, operand));
+        nodeIndex = addToGraph(GetLocal, OpInfo(operand), phi);
+        m_currentBlock->m_locals[operand].value = nodeIndex;
+        return nodeIndex;
+    }
+    void setLocal(unsigned operand, NodeIndex value)
+    {
+        m_currentBlock->m_locals[operand].value = addToGraph(SetLocal, OpInfo(operand), value);
+    }
+
+    // Used in implementing get/set, above, where the operand is an argument.
+    NodeIndex getArgument(unsigned operand)
+    {
+        unsigned argument = operand + m_codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize;
+        ASSERT(argument < m_numArguments);
+
+        NodeIndex nodeIndex = m_currentBlock->m_arguments[argument].value;
+
+        if (nodeIndex != NoNode) {
+            Node& node = m_graph[nodeIndex];
+            if (node.op == GetLocal)
+                return nodeIndex;
+            ASSERT(node.op == SetLocal);
+            return node.child1;
+        }
+
+        NodeIndex phi = addToGraph(Phi);
+        m_argumentPhiStack.append(PhiStackEntry(m_currentBlock, phi, argument));
+        nodeIndex = addToGraph(GetLocal, OpInfo(operand), phi);
+        m_currentBlock->m_arguments[argument].value = nodeIndex;
+        return nodeIndex;
+    }
+    void setArgument(int operand, NodeIndex value)
+    {
+        unsigned argument = operand + m_codeBlock->m_numParameters + RegisterFile::CallFrameHeaderSize;
+        ASSERT(argument < m_numArguments);
+
+        m_currentBlock->m_arguments[argument].value = addToGraph(SetLocal, OpInfo(operand), value);
+    }
+
+    // Get an operand, and perform a ToInt32/ToNumber conversion on it.
+    NodeIndex getToInt32(int operand)
+    {
+        // Avoid wastefully adding a JSConstant node to the graph, only to
+        // replace it with a Int32Constant (which is what would happen if
+        // we called 'toInt32(get(operand))' in this case).
+        if (operand >= FirstConstantRegisterIndex) {
+            JSValue v = m_codeBlock->getConstant(operand);
+            if (v.isInt32())
+                return getInt32Constant(v.asInt32(), operand - FirstConstantRegisterIndex);
+        }
+        return toInt32(get(operand));
+    }
+    NodeIndex getToNumber(int operand)
+    {
+        // Avoid wastefully adding a JSConstant node to the graph, only to
+        // replace it with a DoubleConstant (which is what would happen if
+        // we called 'toNumber(get(operand))' in this case).
+        if (operand >= FirstConstantRegisterIndex) {
+            JSValue v = m_codeBlock->getConstant(operand);
+            if (v.isNumber())
+                return getDoubleConstant(v.uncheckedGetNumber(), operand - FirstConstantRegisterIndex);
+        }
+        return toNumber(get(operand));
+    }
+
+    // Perform an ES5 ToInt32 operation - returns a node of type NodeResultInt32.
+    NodeIndex toInt32(NodeIndex index)
+    {
+        Node& node = m_graph[index];
+
+        if (node.hasInt32Result())
+            return index;
+
+        if (node.hasDoubleResult()) {
+            if (node.op == DoubleConstant)
+                return getInt32Constant(JSC::toInt32(valueOfDoubleConstant(index)), node.constantNumber());
+            // 'NumberToInt32(Int32ToNumber(X))' == X, and 'NumberToInt32(UInt32ToNumber(X)) == X'
+            if (node.op == Int32ToNumber || node.op == UInt32ToNumber)
+                return node.child1;
+
+            // We unique NumberToInt32 nodes in a map to prevent duplicate conversions.
+            pair<UnaryOpMap::iterator, bool> result = m_numberToInt32Nodes.add(index, NoNode);
+            // Either we added a new value, or the existing value in the map is non-zero.
+            ASSERT(result.second == (result.first->second == NoNode));
+            if (result.second)
+                result.first->second = addToGraph(NumberToInt32, index);
+            return result.first->second;
+        }
+
+        // Check for numeric constants boxed as JSValues.
+        if (node.op == JSConstant) {
+            JSValue v = valueOfJSConstant(index);
+            if (v.isInt32())
+                return getInt32Constant(v.asInt32(), node.constantNumber());
+            if (v.isNumber())
+                return getInt32Constant(JSC::toInt32(v.uncheckedGetNumber()), node.constantNumber());
+        }
+
+        return addToGraph(ValueToInt32, index);
+    }
+
+    // Perform an ES5 ToNumber operation - returns a node of type NodeResultDouble.
+    NodeIndex toNumber(NodeIndex index)
+    {
+        Node& node = m_graph[index];
+
+        if (node.hasDoubleResult())
+            return index;
+
+        if (node.hasInt32Result()) {
+            if (node.op == Int32Constant)
+                return getDoubleConstant(valueOfInt32Constant(index), node.constantNumber());
+
+            // We unique Int32ToNumber nodes in a map to prevent duplicate conversions.
+            pair<UnaryOpMap::iterator, bool> result = m_int32ToNumberNodes.add(index, NoNode);
+            // Either we added a new value, or the existing value in the map is non-zero.
+            ASSERT(result.second == (result.first->second == NoNode));
+            if (result.second)
+                result.first->second = addToGraph(Int32ToNumber, index);
+            return result.first->second;
+        }
+
+        if (node.op == JSConstant) {
+            JSValue v = valueOfJSConstant(index);
+            if (v.isNumber())
+                return getDoubleConstant(v.uncheckedGetNumber(), node.constantNumber());
+        }
+
+        return addToGraph(ValueToNumber, index);
+    }
+
+
+    // Used in implementing get, above, where the operand is a constant.
+    NodeIndex getInt32Constant(int32_t value, unsigned constant)
+    {
+        NodeIndex index = m_constants[constant].asInt32;
+        if (index != NoNode)
+            return index;
+        NodeIndex resultIndex = addToGraph(Int32Constant, OpInfo(constant));
+        m_graph[resultIndex].setInt32Constant(value);
+        m_constants[constant].asInt32 = resultIndex;
+        return resultIndex;
+    }
+    NodeIndex getDoubleConstant(double value, unsigned constant)
+    {
+        NodeIndex index = m_constants[constant].asNumeric;
+        if (index != NoNode)
+            return index;
+        NodeIndex resultIndex = addToGraph(DoubleConstant, OpInfo(constant));
+        m_graph[resultIndex].setDoubleConstant(value);
+        m_constants[constant].asNumeric = resultIndex;
+        return resultIndex;
+    }
+    NodeIndex getJSConstant(unsigned constant)
+    {
+        NodeIndex index = m_constants[constant].asJSValue;
+        if (index != NoNode)
+            return index;
+
+        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(m_codeBlock->thisRegister());
+    }
+    void setThis(NodeIndex value)
+    {
+        setArgument(m_codeBlock->thisRegister(), value);
+    }
+
+    // Convenience methods for checking nodes for constants.
+    bool isInt32Constant(NodeIndex index)
+    {
+        return m_graph[index].op == Int32Constant;
+    }
+    bool isDoubleConstant(NodeIndex index)
+    {
+        return m_graph[index].op == DoubleConstant;
+    }
+    bool isJSConstant(NodeIndex index)
+    {
+        return m_graph[index].op == JSConstant;
+    }
+
+    // Convenience methods for getting constant values.
+    int32_t valueOfInt32Constant(NodeIndex index)
+    {
+        ASSERT(isInt32Constant(index));
+        return m_graph[index].int32Constant();
+    }
+    double valueOfDoubleConstant(NodeIndex index)
+    {
+        ASSERT(isDoubleConstant(index));
+        return m_graph[index].numericConstant();
+    }
+    JSValue valueOfJSConstant(NodeIndex index)
+    {
+        ASSERT(isJSConstant(index));
+        return m_codeBlock->getConstant(FirstConstantRegisterIndex + m_graph[index].constantNumber());
+    }
+
+    // This method returns a JSConstant with the value 'undefined'.
+    NodeIndex constantUndefined()
+    {
+        // Has m_constantUndefined been set up yet?
+        if (m_constantUndefined == UINT_MAX) {
+            // Search the constant pool for undefined, if we find it, we can just reuse this!
+            unsigned numberOfConstants = m_codeBlock->numberOfConstantRegisters();
+            for (m_constantUndefined = 0; m_constantUndefined < numberOfConstants; ++m_constantUndefined) {
+                JSValue testMe = m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constantUndefined);
+                if (testMe.isUndefined())
+                    return getJSConstant(m_constantUndefined);
+            }
+
+            // 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_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'.
+        ASSERT(m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constantUndefined).isUndefined());
+        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()
+    {
+        // Has m_constant1 been set up yet?
+        if (m_constant1 == UINT_MAX) {
+            // Search the constant pool for the value 1, if we find it, we can just reuse this!
+            unsigned numberOfConstants = m_codeBlock->numberOfConstantRegisters();
+            for (m_constant1 = 0; m_constant1 < numberOfConstants; ++m_constant1) {
+                JSValue testMe = m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constant1);
+                if (testMe.isInt32() && testMe.asInt32() == 1)
+                    return getDoubleConstant(1, m_constant1);
+            }
+
+            // 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_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.
+        ASSERT(m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constant1).isInt32());
+        ASSERT(m_codeBlock->getConstant(FirstConstantRegisterIndex + m_constant1).asInt32() == 1);
+        return getDoubleConstant(1, m_constant1);
+    }
+
+
+    // These methods create a node and add it to the graph. If nodes of this type are
+    // 'mustGenerate' then the node  will implicitly be ref'ed to ensure generation.
+    NodeIndex addToGraph(NodeType op, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
+    {
+        NodeIndex resultIndex = (NodeIndex)m_graph.size();
+        m_graph.append(Node(op, m_currentIndex, child1, child2, child3));
+
+        if (op & NodeMustGenerate)
+            m_graph.ref(resultIndex);
+        return resultIndex;
+    }
+    NodeIndex addToGraph(NodeType op, OpInfo info, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
+    {
+        NodeIndex resultIndex = (NodeIndex)m_graph.size();
+        m_graph.append(Node(op, m_currentIndex, info, child1, child2, child3));
+
+        if (op & NodeMustGenerate)
+            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;
+    }
+
+    void predictArray(NodeIndex nodeIndex)
+    {
+        Node* nodePtr = &m_graph[nodeIndex];
+
+        if (nodePtr->op == GetLocal)
+            m_graph.predict(nodePtr->local(), PredictArray);
+    }
+
+    void predictInt32(NodeIndex nodeIndex)
+    {
+        Node* nodePtr = &m_graph[nodeIndex];
+
+        if (nodePtr->op == ValueToNumber)
+            nodePtr = &m_graph[nodePtr->child1];
+
+        if (nodePtr->op == ValueToInt32)
+            nodePtr = &m_graph[nodePtr->child1];
+
+        if (nodePtr->op == NumberToInt32)
+            nodePtr = &m_graph[nodePtr->child1];
+
+        if (nodePtr->op == GetLocal)
+            m_graph.predict(nodePtr->local(), PredictInt32);
+    }
+
+    JSGlobalData* m_globalData;
+    CodeBlock* m_codeBlock;
+    Graph& m_graph;
+
+    // The current block being generated.
+    BasicBlock* m_currentBlock;
+    // The bytecode index of the current instruction being generated.
+    unsigned m_currentIndex;
+
+    // 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
+    // CodeBlock's constant pool. These variables are initialized to
+    // 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
+    // node in the graph, depending on the context in which it is being used.
+    struct ConstantRecord {
+        ConstantRecord()
+            : asInt32(NoNode)
+            , asNumeric(NoNode)
+            , asJSValue(NoNode)
+        {
+        }
+
+        NodeIndex asInt32;
+        NodeIndex asNumeric;
+        NodeIndex asJSValue;
+    };
+
+    // Track the index of the node whose result is the current value for every
+    // register value in the bytecode - argument, local, and temporary.
+    Vector<ConstantRecord, 16> m_constants;
+
+    // The number of arguments passed to the function.
+    unsigned m_numArguments;
+    // The number of locals (vars + temporaries) used in the function.
+    unsigned m_numLocals;
+    // The number of registers we need to preserve across BasicBlock boundaries;
+    // typically equal to the number vars, but we expand this to cover all
+    // temporaries that persist across blocks (dues to ?:, &&, ||, etc).
+    unsigned m_preservedVars;
+
+    struct PhiStackEntry {
+        PhiStackEntry(BasicBlock* block, NodeIndex phi, unsigned varNo)
+            : m_block(block)
+            , m_phi(phi)
+            , m_varNo(varNo)
+        {
+        }
+
+        BasicBlock* m_block;
+        NodeIndex m_phi;
+        unsigned m_varNo;
+    };
+    Vector<PhiStackEntry, 16> m_argumentPhiStack;
+    Vector<PhiStackEntry, 16> m_localPhiStack;
+
+    // These maps are used to unique ToNumber and ToInt32 operations.
+    typedef HashMap<NodeIndex, NodeIndex> UnaryOpMap;
+    UnaryOpMap m_int32ToNumberNodes;
+    UnaryOpMap m_numberToInt32Nodes;
+};
+
+#define NEXT_OPCODE(name) \
+    m_currentIndex += OPCODE_LENGTH(name); \
+    continue
+
+#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();
+    }
+
+    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)) {
+
+        // === Function entry opcodes ===
+
+        case op_enter:
+            // 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: {
+            NodeIndex op1 = getThis();
+            setThis(addToGraph(ConvertThis, op1));
+            NEXT_OPCODE(op_convert_this);
+        }
+
+        // === Bitwise operations ===
+
+        case op_bitand: {
+            NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
+            NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
+            predictInt32(op1);
+            predictInt32(op2);
+            set(currentInstruction[1].u.operand, addToGraph(BitAnd, op1, op2), PredictInt32);
+            NEXT_OPCODE(op_bitand);
+        }
+
+        case op_bitor: {
+            NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
+            NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
+            predictInt32(op1);
+            predictInt32(op2);
+            set(currentInstruction[1].u.operand, addToGraph(BitOr, op1, op2), PredictInt32);
+            NEXT_OPCODE(op_bitor);
+        }
+
+        case op_bitxor: {
+            NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
+            NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
+            predictInt32(op1);
+            predictInt32(op2);
+            set(currentInstruction[1].u.operand, addToGraph(BitXor, op1, op2), PredictInt32);
+            NEXT_OPCODE(op_bitxor);
+        }
+
+        case op_rshift: {
+            NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
+            NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
+            predictInt32(op1);
+            predictInt32(op2);
+            NodeIndex result;
+            // Optimize out shifts by zero.
+            if (isInt32Constant(op2) && !(valueOfInt32Constant(op2) & 0x1f))
+                result = op1;
+            else
+                result = addToGraph(BitRShift, op1, op2);
+            set(currentInstruction[1].u.operand, result, PredictInt32);
+            NEXT_OPCODE(op_rshift);
+        }
+
+        case op_lshift: {
+            NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
+            NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
+            predictInt32(op1);
+            predictInt32(op2);
+            NodeIndex result;
+            // Optimize out shifts by zero.
+            if (isInt32Constant(op2) && !(valueOfInt32Constant(op2) & 0x1f))
+                result = op1;
+            else
+                result = addToGraph(BitLShift, op1, op2);
+            set(currentInstruction[1].u.operand, result, PredictInt32);
+            NEXT_OPCODE(op_lshift);
+        }
+
+        case op_urshift: {
+            NodeIndex op1 = getToInt32(currentInstruction[2].u.operand);
+            NodeIndex op2 = getToInt32(currentInstruction[3].u.operand);
+            predictInt32(op1);
+            predictInt32(op2);
+            NodeIndex result;
+            // The result of a zero-extending right shift is treated as an unsigned value.
+            // This means that if the top bit is set, the result is not in the int32 range,
+            // and as such must be stored as a double. If the shift amount is a constant,
+            // we may be able to optimize.
+            if (isInt32Constant(op2)) {
+                // If we know we are shifting by a non-zero amount, then since the operation
+                // zero fills we know the top bit of the result must be zero, and as such the
+                // result must be within the int32 range. Conversely, if this is a shift by
+                // zero, then the result may be changed by the conversion to unsigned, but it
+                // is not necessary to perform the shift!
+                if (valueOfInt32Constant(op2) & 0x1f)
+                    result = addToGraph(BitURShift, op1, op2);
+                else
+                    result = addToGraph(UInt32ToNumber, op1);
+            }  else {
+                // Cannot optimize at this stage; shift & potentially rebox as a double.
+                result = addToGraph(BitURShift, op1, op2);
+                result = addToGraph(UInt32ToNumber, result);
+            }
+            set(currentInstruction[1].u.operand, result, PredictInt32);
+            NEXT_OPCODE(op_urshift);
+        }
+
+        // === Increment/Decrement opcodes ===
+
+        case op_pre_inc: {
+            unsigned srcDst = currentInstruction[1].u.operand;
+            NodeIndex op = getToNumber(srcDst);
+            predictInt32(op);
+            set(srcDst, addToGraph(ArithAdd, op, one()));
+            NEXT_OPCODE(op_pre_inc);
+        }
+
+        case op_post_inc: {
+            unsigned result = currentInstruction[1].u.operand;
+            unsigned srcDst = currentInstruction[2].u.operand;
+            NodeIndex op = getToNumber(srcDst);
+            predictInt32(op);
+            set(result, op);
+            set(srcDst, addToGraph(ArithAdd, op, one()));
+            NEXT_OPCODE(op_post_inc);
+        }
+
+        case op_pre_dec: {
+            unsigned srcDst = currentInstruction[1].u.operand;
+            NodeIndex op = getToNumber(srcDst);
+            predictInt32(op);
+            set(srcDst, addToGraph(ArithSub, op, one()));
+            NEXT_OPCODE(op_pre_dec);
+        }
+
+        case op_post_dec: {
+            unsigned result = currentInstruction[1].u.operand;
+            unsigned srcDst = currentInstruction[2].u.operand;
+            NodeIndex op = getToNumber(srcDst);
+            predictInt32(op);
+            set(result, op);
+            set(srcDst, addToGraph(ArithSub, op, one()));
+            NEXT_OPCODE(op_post_dec);
+        }
+
+        // === Arithmetic operations ===
+
+        case op_add: {
+            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.
+            // Otherwise, we must assume this may be performing a concatenation to a string.
+            if (m_graph[op1].hasNumericResult() && m_graph[op2].hasNumericResult())
+                set(currentInstruction[1].u.operand, addToGraph(ArithAdd, toNumber(op1), toNumber(op2)));
+            else
+                set(currentInstruction[1].u.operand, addToGraph(ValueAdd, op1, op2));
+            NEXT_OPCODE(op_add);
+        }
+
+        case op_sub: {
+            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));
+            NEXT_OPCODE(op_sub);
+        }
+
+        case op_mul: {
+            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));
+            NEXT_OPCODE(op_mul);
+        }
+
+        case op_mod: {
+            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));
+            NEXT_OPCODE(op_mod);
+        }
+
+        case op_div: {
+            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));
+            NEXT_OPCODE(op_div);
+        }
+
+        // === Misc operations ===
+
+        case op_mov: {
+            NodeIndex op = get(currentInstruction[2].u.operand);
+            set(currentInstruction[1].u.operand, op);
+            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: {
+            NodeIndex base = get(currentInstruction[2].u.operand);
+            NodeIndex property = get(currentInstruction[3].u.operand);
+            predictArray(base);
+            predictInt32(property);
+
+            NodeIndex getByVal = addToGraph(GetByVal, base, property, aliases.lookupGetByVal(base, property));
+            set(currentInstruction[1].u.operand, getByVal);
+            aliases.recordGetByVal(getByVal);
+
+            NEXT_OPCODE(op_get_by_val);
+        }
+
+        case op_put_by_val: {
+            NodeIndex base = get(currentInstruction[1].u.operand);
+            NodeIndex property = get(currentInstruction[2].u.operand);
+            NodeIndex value = get(currentInstruction[3].u.operand);
+            predictArray(base);
+            predictInt32(property);
+
+            NodeIndex aliasedGet = aliases.lookupGetByVal(base, property);
+            NodeIndex putByVal = addToGraph(aliasedGet != NoNode ? PutByValAlias : PutByVal, base, property, value);
+            aliases.recordPutByVal(putByVal);
+
+            NEXT_OPCODE(op_put_by_val);
+        }
+
+        case op_get_by_id: {
+            NodeIndex base = get(currentInstruction[2].u.operand);
+            unsigned identifier = currentInstruction[3].u.operand;
+
+            NodeIndex getById = addToGraph(GetById, OpInfo(identifier), base);
+            set(currentInstruction[1].u.operand, getById);
+            aliases.recordGetById(getById);
+
+            NEXT_OPCODE(op_get_by_id);
+        }
+
+        case op_put_by_id: {
+            NodeIndex value = get(currentInstruction[3].u.operand);
+            NodeIndex base = get(currentInstruction[1].u.operand);
+            unsigned identifier = currentInstruction[2].u.operand;
+            bool direct = currentInstruction[8].u.operand;
+
+            if (direct) {
+                NodeIndex putByIdDirect = addToGraph(PutByIdDirect, OpInfo(identifier), base, value);
+                aliases.recordPutByIdDirect(putByIdDirect);
+            } else {
+                NodeIndex putById = addToGraph(PutById, OpInfo(identifier), base, value);
+                aliases.recordPutById(putById);
+            }
+
+            NEXT_OPCODE(op_put_by_id);
+        }
+
+        case op_get_global_var: {
+            NodeIndex getGlobalVar = addToGraph(GetGlobalVar, OpInfo(currentInstruction[2].u.operand));
+            set(currentInstruction[1].u.operand, getGlobalVar);
+            NEXT_OPCODE(op_get_global_var);
+        }
+
+        case op_put_global_var: {
+            NodeIndex value = get(currentInstruction[2].u.operand);
+            addToGraph(PutGlobalVar, OpInfo(currentInstruction[1].u.operand), value);
+            NEXT_OPCODE(op_put_global_var);
+        }
+
+        // === 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));
+            LAST_OPCODE(op_ret);
+        }
+
+        default:
+            // Parse failed!
+            return false;
+        }
+    }
+}
+
+template<ByteCodeParser::PhiStackType stackType>
+void ByteCodeParser::processPhiStack()
+{
+    Vector<PhiStackEntry, 16>& phiStack = (stackType == ArgumentPhiStack) ? m_argumentPhiStack : m_localPhiStack;
+
+    while (!phiStack.isEmpty()) {
+        PhiStackEntry entry = phiStack.last();
+        phiStack.removeLast();
+        
+        Node& phiNode = m_graph[entry.m_phi];
+        PredecessorList& predecessors = entry.m_block->m_predecessors;
+        unsigned varNo = entry.m_varNo;
+
+        for (size_t i = 0; i < predecessors.size(); ++i) {
+            BasicBlock* predecessorBlock = m_graph.m_blocks[predecessors[i]].get();
+
+            VariableRecord& var = (stackType == ArgumentPhiStack) ? predecessorBlock->m_arguments[varNo] : predecessorBlock->m_locals[varNo];
+
+            NodeIndex valueInPredecessor = var.value;
+            if (valueInPredecessor == NoNode) {
+                valueInPredecessor = addToGraph(Phi);
+                var.value = valueInPredecessor;
+                phiStack.append(PhiStackEntry(predecessorBlock, valueInPredecessor, varNo));
+            } else if (m_graph[valueInPredecessor].op == GetLocal)
+                valueInPredecessor = m_graph[valueInPredecessor].child1;
+            ASSERT(m_graph[valueInPredecessor].op == SetLocal || m_graph[valueInPredecessor].op == Phi);
+
+            if (phiNode.refCount())
+                m_graph.ref(valueInPredecessor);
+
+            if (phiNode.child1 == NoNode) {
+                phiNode.child1 = valueInPredecessor;
+                continue;
+            }
+            if (phiNode.child2 == NoNode) {
+                phiNode.child2 = valueInPredecessor;
+                continue;
+            }
+            if (phiNode.child3 == NoNode) {
+                phiNode.child3 = valueInPredecessor;
+                continue;
+            }
+
+            NodeIndex newPhi = addToGraph(Phi);
+            Node& newPhiNode = m_graph[newPhi];
+            if (phiNode.refCount())
+                m_graph.ref(newPhi);
+
+            newPhiNode.child1 = phiNode.child1;
+            newPhiNode.child2 = phiNode.child2;
+            newPhiNode.child3 = phiNode.child3;
+
+            phiNode.child1 = newPhi;
+            phiNode.child1 = valueInPredecessor;
+            phiNode.child3 = NoNode;
+        }
+    }
+}
+
+void ByteCodeParser::setupPredecessors()
+{
+    for (BlockIndex index = 0; index < m_graph.m_blocks.size(); ++index) {
+        BasicBlock* block = m_graph.m_blocks[index].get();
+        ASSERT(block->end != NoNode);
+        Node& node = m_graph[block->end - 1];
+        ASSERT(node.isTerminal());
+
+        if (node.isJump())
+            m_graph.blockForBytecodeOffset(node.takenBytecodeOffset()).m_predecessors.append(index);
+        else if (node.isBranch()) {
+            m_graph.blockForBytecodeOffset(node.takenBytecodeOffset()).m_predecessors.append(index);
+            m_graph.blockForBytecodeOffset(node.notTakenBytecodeOffset()).m_predecessors.append(index);
+        }
+    }
+}
+
+void ByteCodeParser::allocateVirtualRegisters()
+{
+    ScoreBoard scoreBoard(m_graph, m_preservedVars);
+    unsigned sizeExcludingPhiNodes = m_graph.m_blocks.last()->end;
+    for (size_t i = 0; i < sizeExcludingPhiNodes; ++i) {
+        Node& node = m_graph[i];
+        if (!node.shouldGenerate())
+            continue;
+
+        // GetLocal nodes are effectively phi nodes in the graph, referencing
+        // results from prior blocks.
+        if (node.op != GetLocal) {
+            // First, call use on all of the current node's children, then
+            // allocate a VirtualRegister for this node. We do so in this
+            // order so that if a child is on its last use, and a
+            // VirtualRegister is freed, then it may be reused for node.
+            scoreBoard.use(node.child1);
+            scoreBoard.use(node.child2);
+            scoreBoard.use(node.child3);
+        }
+
+        if (!node.hasResult())
+            continue;
+
+        node.setVirtualRegister(scoreBoard.allocate());
+        // 'mustGenerate' nodes have their useCount artificially elevated,
+        // call use now to account for this.
+        if (node.mustGenerate())
+            scoreBoard.use(i);
+    }
+
+    // '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.
+    unsigned calleeRegisters = scoreBoard.allocatedCount() + m_preservedVars;
+    if ((unsigned)m_codeBlock->m_numCalleeRegisters < calleeRegisters)
+        m_codeBlock->m_numCalleeRegisters = calleeRegisters;
+}
+
+bool ByteCodeParser::parse()
+{
+    // 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 {
+            OwnPtr<BasicBlock> block = adoptPtr(new BasicBlock(m_currentIndex, m_graph.size(), m_numArguments, m_numLocals));
+            m_currentBlock = block.get();
+            m_graph.m_blocks.append(block.release());
+
+            if (!parseBlock(limit))
+                return false;
+            // We should not have gone beyond the limit.
+            ASSERT(m_currentIndex <= limit);
+
+            m_currentBlock->end = m_graph.size();
+        } while (m_currentIndex < limit);
+    }
+
+    // Should have reached the end of the instructions.
+    ASSERT(m_currentIndex == m_codeBlock->instructions().size());
+
+    setupPredecessors();
+    processPhiStack<LocalPhiStack>();
+    processPhiStack<ArgumentPhiStack>();
+
+    allocateVirtualRegisters();
+
+#if DFG_DEBUG_VERBOSE
+    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