]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGJITCodeGenerator.h
JavaScriptCore-903.tar.gz
[apple/javascriptcore.git] / dfg / DFGJITCodeGenerator.h
diff --git a/dfg/DFGJITCodeGenerator.h b/dfg/DFGJITCodeGenerator.h
new file mode 100644 (file)
index 0000000..3c0998d
--- /dev/null
@@ -0,0 +1,957 @@
+/*
+ * 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. 
+ */
+
+#ifndef DFGJITCodeGenerator_h
+#define DFGJITCodeGenerator_h
+
+#if ENABLE(DFG_JIT)
+
+#include "CodeBlock.h"
+#include <dfg/DFGGenerationInfo.h>
+#include <dfg/DFGGraph.h>
+#include <dfg/DFGJITCompiler.h>
+#include <dfg/DFGOperations.h>
+#include <dfg/DFGRegisterBank.h>
+
+namespace JSC { namespace DFG {
+
+class SpeculateIntegerOperand;
+class SpeculateStrictInt32Operand;
+class SpeculateCellOperand;
+
+
+// === JITCodeGenerator ===
+//
+// This class provides common infrastructure used by the speculative &
+// non-speculative JITs. Provides common mechanisms for virtual and
+// physical register management, calls out from JIT code to helper
+// functions, etc.
+class JITCodeGenerator {
+protected:
+    typedef MacroAssembler::TrustedImm32 TrustedImm32;
+    typedef MacroAssembler::Imm32 Imm32;
+
+    // These constants are used to set priorities for spill order for
+    // the register allocator.
+    enum SpillOrder {
+        SpillOrderConstant = 1, // no spill, and cheap fill
+        SpillOrderSpilled = 2,  // no spill
+        SpillOrderJS = 4,       // needs spill
+        SpillOrderCell = 4,     // needs spill
+        SpillOrderInteger = 5,  // needs spill and box
+        SpillOrderDouble = 6,   // needs spill and convert
+    };
+
+
+public:
+    GPRReg fillInteger(NodeIndex, DataFormat& returnFormat);
+    FPRReg fillDouble(NodeIndex);
+    GPRReg fillJSValue(NodeIndex);
+
+    // lock and unlock GPR & FPR registers.
+    void lock(GPRReg reg)
+    {
+        m_gprs.lock(reg);
+    }
+    void lock(FPRReg reg)
+    {
+        m_fprs.lock(reg);
+    }
+    void unlock(GPRReg reg)
+    {
+        m_gprs.unlock(reg);
+    }
+    void unlock(FPRReg reg)
+    {
+        m_fprs.unlock(reg);
+    }
+
+    // Used to check whether a child node is on its last use,
+    // and its machine registers may be reused.
+    bool canReuse(NodeIndex nodeIndex)
+    {
+        VirtualRegister virtualRegister = m_jit.graph()[nodeIndex].virtualRegister();
+        GenerationInfo& info = m_generationInfo[virtualRegister];
+        return info.canReuse();
+    }
+    GPRReg reuse(GPRReg reg)
+    {
+        m_gprs.lock(reg);
+        return reg;
+    }
+    FPRReg reuse(FPRReg reg)
+    {
+        m_fprs.lock(reg);
+        return reg;
+    }
+
+    // Allocate a gpr/fpr.
+    GPRReg allocate()
+    {
+        VirtualRegister spillMe;
+        GPRReg gpr = m_gprs.allocate(spillMe);
+        if (spillMe != InvalidVirtualRegister)
+            spill(spillMe);
+        return gpr;
+    }
+    FPRReg fprAllocate()
+    {
+        VirtualRegister spillMe;
+        FPRReg fpr = m_fprs.allocate(spillMe);
+        if (spillMe != InvalidVirtualRegister)
+            spill(spillMe);
+        return fpr;
+    }
+
+    // Check whether a VirtualRegsiter is currently in a machine register.
+    // We use this when filling operands to fill those that are already in
+    // machine registers first (by locking VirtualRegsiters that are already
+    // in machine register before filling those that are not we attempt to
+    // avoid spilling values we will need immediately).
+    bool isFilled(NodeIndex nodeIndex)
+    {
+        VirtualRegister virtualRegister = m_jit.graph()[nodeIndex].virtualRegister();
+        GenerationInfo& info = m_generationInfo[virtualRegister];
+        return info.registerFormat() != DataFormatNone;
+    }
+    bool isFilledDouble(NodeIndex nodeIndex)
+    {
+        VirtualRegister virtualRegister = m_jit.graph()[nodeIndex].virtualRegister();
+        GenerationInfo& info = m_generationInfo[virtualRegister];
+        return info.registerFormat() == DataFormatDouble;
+    }
+
+protected:
+    JITCodeGenerator(JITCompiler& jit, bool isSpeculative)
+        : m_jit(jit)
+        , m_isSpeculative(isSpeculative)
+        , m_compileIndex(0)
+        , m_generationInfo(m_jit.codeBlock()->m_numCalleeRegisters)
+        , m_blockHeads(jit.graph().m_blocks.size())
+    {
+    }
+
+    // These methods convert between doubles, and doubles boxed and JSValues.
+    GPRReg boxDouble(FPRReg fpr, GPRReg gpr)
+    {
+        m_jit.moveDoubleToPtr(fpr, gpr);
+        m_jit.subPtr(GPRInfo::tagTypeNumberRegister, gpr);
+        return gpr;
+    }
+    FPRReg unboxDouble(GPRReg gpr, FPRReg fpr)
+    {
+        m_jit.addPtr(GPRInfo::tagTypeNumberRegister, gpr);
+        m_jit.movePtrToDouble(gpr, fpr);
+        return fpr;
+    }
+    GPRReg boxDouble(FPRReg fpr)
+    {
+        return boxDouble(fpr, allocate());
+    }
+    FPRReg unboxDouble(GPRReg gpr)
+    {
+        return unboxDouble(gpr, fprAllocate());
+    }
+
+    // Called on an operand once it has been consumed by a parent node.
+    void use(NodeIndex nodeIndex)
+    {
+        VirtualRegister virtualRegister = m_jit.graph()[nodeIndex].virtualRegister();
+        GenerationInfo& info = m_generationInfo[virtualRegister];
+
+        // use() returns true when the value becomes dead, and any
+        // associated resources may be freed.
+        if (!info.use())
+            return;
+
+        // Release the associated machine registers.
+        DataFormat registerFormat = info.registerFormat();
+        if (registerFormat == DataFormatDouble)
+            m_fprs.release(info.fpr());
+        else if (registerFormat != DataFormatNone)
+            m_gprs.release(info.gpr());
+    }
+
+    // Spill a VirtualRegister to the RegisterFile.
+    void spill(VirtualRegister spillMe)
+    {
+        GenerationInfo& info = m_generationInfo[spillMe];
+
+        // Check the GenerationInfo to see if this value need writing
+        // to the RegisterFile - if not, mark it as spilled & return.
+        if (!info.needsSpill()) {
+            info.setSpilled();
+            return;
+        }
+
+        DataFormat spillFormat = info.registerFormat();
+        if (spillFormat == DataFormatDouble) {
+            // All values are spilled as JSValues, so box the double via a temporary gpr.
+            GPRReg gpr = boxDouble(info.fpr());
+            m_jit.storePtr(gpr, JITCompiler::addressFor(spillMe));
+            unlock(gpr);
+            info.spill(DataFormatJSDouble);
+            return;
+        }
+
+        // The following code handles JSValues, int32s, and cells.
+        ASSERT(spillFormat == DataFormatInteger || spillFormat == DataFormatCell || spillFormat & DataFormatJS);
+
+        GPRReg reg = info.gpr();
+        // We need to box int32 and cell values ...
+        // but on JSVALUE64 boxing a cell is a no-op!
+        if (spillFormat == DataFormatInteger)
+            m_jit.orPtr(GPRInfo::tagTypeNumberRegister, reg);
+
+        // Spill the value, and record it as spilled in its boxed form.
+        m_jit.storePtr(reg, JITCompiler::addressFor(spillMe));
+        info.spill((DataFormat)(spillFormat | DataFormatJS));
+    }
+
+    // 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); }
+    int32_t valueOfInt32Constant(NodeIndex nodeIndex) { return m_jit.valueOfInt32Constant(nodeIndex); }
+    double valueOfDoubleConstant(NodeIndex nodeIndex) { return m_jit.valueOfDoubleConstant(nodeIndex); }
+    JSValue valueOfJSConstant(NodeIndex nodeIndex) { return m_jit.valueOfJSConstant(nodeIndex); }
+
+    Identifier* identifier(unsigned index)
+    {
+        return &m_jit.codeBlock()->identifier(index);
+    }
+
+    // Spill all VirtualRegisters back to the RegisterFile.
+    void flushRegisters()
+    {
+        for (gpr_iterator iter = m_gprs.begin(); iter != m_gprs.end(); ++iter) {
+            if (iter.name() != InvalidVirtualRegister) {
+                spill(iter.name());
+                iter.release();
+            }
+        }
+        for (fpr_iterator iter = m_fprs.begin(); iter != m_fprs.end(); ++iter) {
+            if (iter.name() != InvalidVirtualRegister) {
+                spill(iter.name());
+                iter.release();
+            }
+        }
+    }
+
+#ifndef NDEBUG
+    // Used to ASSERT flushRegisters() has been called prior to
+    // calling out from JIT code to a C helper function.
+    bool isFlushed()
+    {
+        for (gpr_iterator iter = m_gprs.begin(); iter != m_gprs.end(); ++iter) {
+            if (iter.name() != InvalidVirtualRegister)
+                return false;
+        }
+        for (fpr_iterator iter = m_fprs.begin(); iter != m_fprs.end(); ++iter) {
+            if (iter.name() != InvalidVirtualRegister)
+                return false;
+        }
+        return true;
+    }
+#endif
+
+    // Get the JSValue representation of a constant.
+    JSValue constantAsJSValue(NodeIndex nodeIndex)
+    {
+        Node& node = m_jit.graph()[nodeIndex];
+        if (isInt32Constant(nodeIndex))
+            return jsNumber(node.int32Constant());
+        if (isDoubleConstant(nodeIndex))
+            return JSValue(JSValue::EncodeAsDouble, node.numericConstant());
+        ASSERT(isJSConstant(nodeIndex));
+        return valueOfJSConstant(nodeIndex);
+    }
+    MacroAssembler::ImmPtr constantAsJSValueAsImmPtr(NodeIndex nodeIndex)
+    {
+        return MacroAssembler::ImmPtr(JSValue::encode(constantAsJSValue(nodeIndex)));
+    }
+
+    // Helper functions to enable code sharing in implementations of bit/shift ops.
+    void bitOp(NodeType op, int32_t imm, GPRReg op1, GPRReg result)
+    {
+        switch (op) {
+        case BitAnd:
+            m_jit.and32(Imm32(imm), op1, result);
+            break;
+        case BitOr:
+            m_jit.or32(Imm32(imm), op1, result);
+            break;
+        case BitXor:
+            m_jit.xor32(Imm32(imm), op1, result);
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+    }
+    void bitOp(NodeType op, GPRReg op1, GPRReg op2, GPRReg result)
+    {
+        switch (op) {
+        case BitAnd:
+            m_jit.and32(op1, op2, result);
+            break;
+        case BitOr:
+            m_jit.or32(op1, op2, result);
+            break;
+        case BitXor:
+            m_jit.xor32(op1, op2, result);
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+    }
+    void shiftOp(NodeType op, GPRReg op1, int32_t shiftAmount, GPRReg result)
+    {
+        switch (op) {
+        case BitRShift:
+            m_jit.rshift32(op1, Imm32(shiftAmount), result);
+            break;
+        case BitLShift:
+            m_jit.lshift32(op1, Imm32(shiftAmount), result);
+            break;
+        case BitURShift:
+            m_jit.urshift32(op1, Imm32(shiftAmount), result);
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+    }
+    void shiftOp(NodeType op, GPRReg op1, GPRReg shiftAmount, GPRReg result)
+    {
+        switch (op) {
+        case BitRShift:
+            m_jit.rshift32(op1, shiftAmount, result);
+            break;
+        case BitLShift:
+            m_jit.lshift32(op1, shiftAmount, result);
+            break;
+        case BitURShift:
+            m_jit.urshift32(op1, shiftAmount, result);
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+    }
+
+    // Called once a node has completed code generation but prior to setting
+    // its result, to free up its children. (This must happen prior to setting
+    // the nodes result, since the node may have the same VirtualRegister as
+    // a child, and as such will use the same GeneratioInfo).
+    void useChildren(Node&);
+
+    // These method called to initialize the the GenerationInfo
+    // to describe the result of an operation.
+    void integerResult(GPRReg reg, NodeIndex nodeIndex, DataFormat format = DataFormatInteger)
+    {
+        Node& node = m_jit.graph()[nodeIndex];
+        useChildren(node);
+
+        VirtualRegister virtualRegister = node.virtualRegister();
+        GenerationInfo& info = m_generationInfo[virtualRegister];
+
+        if (format == DataFormatInteger) {
+            m_jit.jitAssertIsInt32(reg);
+            m_gprs.retain(reg, virtualRegister, SpillOrderInteger);
+            info.initInteger(nodeIndex, node.refCount(), reg);
+        } else {
+            ASSERT(format == DataFormatJSInteger);
+            m_jit.jitAssertIsJSInt32(reg);
+            m_gprs.retain(reg, virtualRegister, SpillOrderJS);
+            info.initJSValue(nodeIndex, node.refCount(), reg, format);
+        }
+    }
+    void noResult(NodeIndex nodeIndex)
+    {
+        Node& node = m_jit.graph()[nodeIndex];
+        useChildren(node);
+    }
+    void cellResult(GPRReg reg, NodeIndex nodeIndex)
+    {
+        Node& node = m_jit.graph()[nodeIndex];
+        useChildren(node);
+
+        VirtualRegister virtualRegister = node.virtualRegister();
+        m_gprs.retain(reg, virtualRegister, SpillOrderCell);
+        GenerationInfo& info = m_generationInfo[virtualRegister];
+        info.initCell(nodeIndex, node.refCount(), reg);
+    }
+    void jsValueResult(GPRReg reg, NodeIndex nodeIndex, DataFormat format = DataFormatJS)
+    {
+        if (format == DataFormatJSInteger)
+            m_jit.jitAssertIsJSInt32(reg);
+        
+        Node& node = m_jit.graph()[nodeIndex];
+        useChildren(node);
+
+        VirtualRegister virtualRegister = node.virtualRegister();
+        m_gprs.retain(reg, virtualRegister, SpillOrderJS);
+        GenerationInfo& info = m_generationInfo[virtualRegister];
+        info.initJSValue(nodeIndex, node.refCount(), reg, format);
+    }
+    void doubleResult(FPRReg reg, NodeIndex nodeIndex)
+    {
+        Node& node = m_jit.graph()[nodeIndex];
+        useChildren(node);
+
+        VirtualRegister virtualRegister = node.virtualRegister();
+        m_fprs.retain(reg, virtualRegister, SpillOrderDouble);
+        GenerationInfo& info = m_generationInfo[virtualRegister];
+        info.initDouble(nodeIndex, node.refCount(), reg);
+    }
+    void initConstantInfo(NodeIndex nodeIndex)
+    {
+        ASSERT(isInt32Constant(nodeIndex) || isDoubleConstant(nodeIndex) || isJSConstant(nodeIndex));
+        Node& node = m_jit.graph()[nodeIndex];
+        m_generationInfo[node.virtualRegister()].initConstant(nodeIndex, node.refCount());
+    }
+
+    // These methods used to sort arguments into the correct registers.
+    template<GPRReg destA, GPRReg destB>
+    void setupTwoStubArgs(GPRReg srcA, GPRReg srcB)
+    {
+        // Assuming that srcA != srcB, there are 7 interesting states the registers may be in:
+        // (1) both are already in arg regs, the right way around.
+        // (2) both are already in arg regs, the wrong way around.
+        // (3) neither are currently in arg registers.
+        // (4) srcA in in its correct reg.
+        // (5) srcA in in the incorrect reg.
+        // (6) srcB in in its correct reg.
+        // (7) srcB in in the incorrect reg.
+        //
+        // The trivial approach is to simply emit two moves, to put srcA in place then srcB in
+        // place (the MacroAssembler will omit redundant moves). This apporach will be safe in
+        // cases 1, 3, 4, 5, 6, and in cases where srcA==srcB. The two problem cases are 2
+        // (requires a swap) and 7 (must move srcB first, to avoid trampling.)
+
+        if (srcB != destA) {
+            // Handle the easy cases - two simple moves.
+            m_jit.move(srcA, destA);
+            m_jit.move(srcB, destB);
+        } else if (srcA != destB) {
+            // Handle the non-swap case - just put srcB in place first.
+            m_jit.move(srcB, destB);
+            m_jit.move(srcA, destA);
+        } else
+            m_jit.swap(destB, destB);
+    }
+    template<FPRReg destA, FPRReg destB>
+    void setupTwoStubArgs(FPRReg srcA, FPRReg srcB)
+    {
+        // Assuming that srcA != srcB, there are 7 interesting states the registers may be in:
+        // (1) both are already in arg regs, the right way around.
+        // (2) both are already in arg regs, the wrong way around.
+        // (3) neither are currently in arg registers.
+        // (4) srcA in in its correct reg.
+        // (5) srcA in in the incorrect reg.
+        // (6) srcB in in its correct reg.
+        // (7) srcB in in the incorrect reg.
+        //
+        // The trivial approach is to simply emit two moves, to put srcA in place then srcB in
+        // place (the MacroAssembler will omit redundant moves). This apporach will be safe in
+        // cases 1, 3, 4, 5, 6, and in cases where srcA==srcB. The two problem cases are 2
+        // (requires a swap) and 7 (must move srcB first, to avoid trampling.)
+
+        if (srcB != destA) {
+            // Handle the easy cases - two simple moves.
+            m_jit.moveDouble(srcA, destA);
+            m_jit.moveDouble(srcB, destB);
+            return;
+        }
+        
+        if (srcA != destB) {
+            // Handle the non-swap case - just put srcB in place first.
+            m_jit.moveDouble(srcB, destB);
+            m_jit.moveDouble(srcA, destA);
+            return;
+        }
+
+        ASSERT(srcB == destA && srcA == destB);
+        // Need to swap; pick a temporary register.
+        FPRReg temp;
+        if (destA != FPRInfo::argumentFPR3 && destA != FPRInfo::argumentFPR3)
+            temp = FPRInfo::argumentFPR3;
+        else if (destA != FPRInfo::argumentFPR2 && destA != FPRInfo::argumentFPR2)
+            temp = FPRInfo::argumentFPR2;
+        else {
+            ASSERT(destA != FPRInfo::argumentFPR1 && destA != FPRInfo::argumentFPR1);
+            temp = FPRInfo::argumentFPR1;
+        }
+        m_jit.moveDouble(destA, temp);
+        m_jit.moveDouble(destB, destA);
+        m_jit.moveDouble(temp, destB);
+    }
+    void setupStubArguments(GPRReg arg1, GPRReg arg2)
+    {
+        setupTwoStubArgs<GPRInfo::argumentGPR1, GPRInfo::argumentGPR2>(arg1, arg2);
+    }
+    void setupStubArguments(GPRReg arg1, GPRReg arg2, GPRReg arg3)
+    {
+        // If neither of arg2/arg3 are in our way, then we can move arg1 into place.
+        // Then we can use setupTwoStubArgs to fix arg2/arg3.
+        if (arg2 != GPRInfo::argumentGPR1 && arg3 != GPRInfo::argumentGPR1) {
+            m_jit.move(arg1, GPRInfo::argumentGPR1);
+            setupTwoStubArgs<GPRInfo::argumentGPR2, GPRInfo::argumentGPR3>(arg2, arg3);
+            return;
+        }
+
+        // If neither of arg1/arg3 are in our way, then we can move arg2 into place.
+        // Then we can use setupTwoStubArgs to fix arg1/arg3.
+        if (arg1 != GPRInfo::argumentGPR2 && arg3 != GPRInfo::argumentGPR2) {
+            m_jit.move(arg2, GPRInfo::argumentGPR2);
+            setupTwoStubArgs<GPRInfo::argumentGPR1, GPRInfo::argumentGPR3>(arg1, arg3);
+            return;
+        }
+
+        // If neither of arg1/arg2 are in our way, then we can move arg3 into place.
+        // Then we can use setupTwoStubArgs to fix arg1/arg2.
+        if (arg1 != GPRInfo::argumentGPR3 && arg2 != GPRInfo::argumentGPR3) {
+            m_jit.move(arg3, GPRInfo::argumentGPR3);
+            setupTwoStubArgs<GPRInfo::argumentGPR1, GPRInfo::argumentGPR2>(arg1, arg2);
+            return;
+        }
+
+        // If we get here, we haven't been able to move any of arg1/arg2/arg3.
+        // Since all three are blocked, then all three must already be in the argument register.
+        // But are they in the right ones?
+
+        // First, ensure arg1 is in place.
+        if (arg1 != GPRInfo::argumentGPR1) {
+            m_jit.swap(arg1, GPRInfo::argumentGPR1);
+
+            // If arg1 wasn't in argumentGPR1, one of arg2/arg3 must be.
+            ASSERT(arg2 == GPRInfo::argumentGPR1 || arg3 == GPRInfo::argumentGPR1);
+            // If arg2 was in argumentGPR1 it no longer is (due to the swap).
+            // Otherwise arg3 must have been. Mark him as moved.
+            if (arg2 == GPRInfo::argumentGPR1)
+                arg2 = arg1;
+            else
+                arg3 = arg1;
+        }
+
+        // Either arg2 & arg3 need swapping, or we're all done.
+        ASSERT((arg2 == GPRInfo::argumentGPR2 || arg3 == GPRInfo::argumentGPR3)
+            || (arg2 == GPRInfo::argumentGPR3 || arg3 == GPRInfo::argumentGPR2));
+
+        if (arg2 != GPRInfo::argumentGPR2)
+            m_jit.swap(GPRInfo::argumentGPR2, GPRInfo::argumentGPR3);
+    }
+
+    // These methods add calls to C++ helper functions.
+    void callOperation(J_DFGOperation_EJP operation, GPRReg result, GPRReg arg1, void* pointer)
+    {
+        ASSERT(isFlushed());
+
+        m_jit.move(arg1, GPRInfo::argumentGPR1);
+        m_jit.move(JITCompiler::TrustedImmPtr(pointer), GPRInfo::argumentGPR2);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+
+        appendCallWithExceptionCheck(operation);
+        m_jit.move(GPRInfo::returnValueGPR, result);
+    }
+    void callOperation(J_DFGOperation_EJI operation, GPRReg result, GPRReg arg1, Identifier* identifier)
+    {
+        callOperation((J_DFGOperation_EJP)operation, result, arg1, identifier);
+    }
+    void callOperation(J_DFGOperation_EJ operation, GPRReg result, GPRReg arg1)
+    {
+        ASSERT(isFlushed());
+
+        m_jit.move(arg1, GPRInfo::argumentGPR1);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+
+        appendCallWithExceptionCheck(operation);
+        m_jit.move(GPRInfo::returnValueGPR, result);
+    }
+    void callOperation(Z_DFGOperation_EJ operation, GPRReg result, GPRReg arg1)
+    {
+        ASSERT(isFlushed());
+
+        m_jit.move(arg1, GPRInfo::argumentGPR1);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+
+        appendCallWithExceptionCheck(operation);
+        m_jit.move(GPRInfo::returnValueGPR, result);
+    }
+    void callOperation(Z_DFGOperation_EJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        ASSERT(isFlushed());
+
+        setupStubArguments(arg1, arg2);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+
+        appendCallWithExceptionCheck(operation);
+        m_jit.move(GPRInfo::returnValueGPR, result);
+    }
+    void callOperation(J_DFGOperation_EJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        ASSERT(isFlushed());
+
+        setupStubArguments(arg1, arg2);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+
+        appendCallWithExceptionCheck(operation);
+        m_jit.move(GPRInfo::returnValueGPR, result);
+    }
+    void callOperation(V_DFGOperation_EJJP operation, GPRReg arg1, GPRReg arg2, void* pointer)
+    {
+        ASSERT(isFlushed());
+
+        setupStubArguments(arg1, arg2);
+        m_jit.move(JITCompiler::TrustedImmPtr(pointer), GPRInfo::argumentGPR3);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+
+        appendCallWithExceptionCheck(operation);
+    }
+    void callOperation(V_DFGOperation_EJJI operation, GPRReg arg1, GPRReg arg2, Identifier* identifier)
+    {
+        callOperation((V_DFGOperation_EJJP)operation, arg1, arg2, identifier);
+    }
+    void callOperation(V_DFGOperation_EJJJ operation, GPRReg arg1, GPRReg arg2, GPRReg arg3)
+    {
+        ASSERT(isFlushed());
+
+        setupStubArguments(arg1, arg2, arg3);
+        m_jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0);
+
+        appendCallWithExceptionCheck(operation);
+    }
+    void callOperation(D_DFGOperation_DD operation, FPRReg result, FPRReg arg1, FPRReg arg2)
+    {
+        ASSERT(isFlushed());
+
+        setupTwoStubArgs<FPRInfo::argumentFPR0, FPRInfo::argumentFPR1>(arg1, arg2);
+
+        m_jit.appendCall(operation);
+        m_jit.moveDouble(FPRInfo::returnValueFPR, result);
+    }
+
+    void appendCallWithExceptionCheck(const FunctionPtr& function)
+    {
+        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
+
+#if DFG_CONSISTENCY_CHECK
+    void checkConsistency();
+#else
+    void checkConsistency() {}
+#endif
+
+    // The JIT, while also provides MacroAssembler functionality.
+    JITCompiler& m_jit;
+    // This flag is used to distinguish speculative and non-speculative
+    // code generation. This is significant when filling spilled values
+    // from the RegisterFile. When spilling we attempt to store information
+    // as to the type of boxed value being stored (int32, double, cell), and
+    // when filling on the speculative path we will retrieve this type info
+    // where available. On the non-speculative path, however, we cannot rely
+    // on the spill format info, since the a value being loaded might have
+    // been spilled by either the speculative or non-speculative paths (where
+    // we entered the non-speculative path on an intervening bail-out), and
+    // 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<GPRInfo> m_gprs;
+    RegisterBank<FPRInfo> 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 ===
+//
+// IntegerOperand, DoubleOperand and JSValueOperand.
+//
+// These classes are used to lock the operands to a node into machine
+// registers. These classes implement of pattern of locking a value
+// into register at the point of construction only if it is already in
+// registers, and otherwise loading it lazily at the point it is first
+// used. We do so in order to attempt to avoid spilling one operand
+// in order to make space available for another.
+
+class IntegerOperand {
+public:
+    explicit IntegerOperand(JITCodeGenerator* jit, NodeIndex index)
+        : m_jit(jit)
+        , m_index(index)
+        , m_gprOrInvalid(InvalidGPRReg)
+#ifndef NDEBUG
+        , m_format(DataFormatNone)
+#endif
+    {
+        ASSERT(m_jit);
+        if (jit->isFilled(index))
+            gpr();
+    }
+
+    ~IntegerOperand()
+    {
+        ASSERT(m_gprOrInvalid != InvalidGPRReg);
+        m_jit->unlock(m_gprOrInvalid);
+    }
+
+    NodeIndex index() const
+    {
+        return m_index;
+    }
+
+    DataFormat format()
+    {
+        gpr(); // m_format is set when m_gpr is locked.
+        ASSERT(m_format == DataFormatInteger || m_format == DataFormatJSInteger);
+        return m_format;
+    }
+
+    GPRReg gpr()
+    {
+        if (m_gprOrInvalid == InvalidGPRReg)
+            m_gprOrInvalid = m_jit->fillInteger(index(), m_format);
+        return m_gprOrInvalid;
+    }
+
+private:
+    JITCodeGenerator* m_jit;
+    NodeIndex m_index;
+    GPRReg m_gprOrInvalid;
+    DataFormat m_format;
+};
+
+class DoubleOperand {
+public:
+    explicit DoubleOperand(JITCodeGenerator* jit, NodeIndex index)
+        : m_jit(jit)
+        , m_index(index)
+        , m_fprOrInvalid(InvalidFPRReg)
+    {
+        ASSERT(m_jit);
+        if (jit->isFilledDouble(index))
+            fpr();
+    }
+
+    ~DoubleOperand()
+    {
+        ASSERT(m_fprOrInvalid != InvalidFPRReg);
+        m_jit->unlock(m_fprOrInvalid);
+    }
+
+    NodeIndex index() const
+    {
+        return m_index;
+    }
+
+    FPRReg fpr()
+    {
+        if (m_fprOrInvalid == InvalidFPRReg)
+            m_fprOrInvalid = m_jit->fillDouble(index());
+        return m_fprOrInvalid;
+    }
+
+private:
+    JITCodeGenerator* m_jit;
+    NodeIndex m_index;
+    FPRReg m_fprOrInvalid;
+};
+
+class JSValueOperand {
+public:
+    explicit JSValueOperand(JITCodeGenerator* jit, NodeIndex index)
+        : m_jit(jit)
+        , m_index(index)
+        , m_gprOrInvalid(InvalidGPRReg)
+    {
+        ASSERT(m_jit);
+        if (jit->isFilled(index))
+            gpr();
+    }
+
+    ~JSValueOperand()
+    {
+        ASSERT(m_gprOrInvalid != InvalidGPRReg);
+        m_jit->unlock(m_gprOrInvalid);
+    }
+
+    NodeIndex index() const
+    {
+        return m_index;
+    }
+
+    GPRReg gpr()
+    {
+        if (m_gprOrInvalid == InvalidGPRReg)
+            m_gprOrInvalid = m_jit->fillJSValue(index());
+        return m_gprOrInvalid;
+    }
+
+private:
+    JITCodeGenerator* m_jit;
+    NodeIndex m_index;
+    GPRReg m_gprOrInvalid;
+};
+
+
+// === Temporaries ===
+//
+// These classes are used to allocate temporary registers.
+// A mechanism is provided to attempt to reuse the registers
+// currently allocated to child nodes whose value is consumed
+// by, and not live after, this operation.
+
+class GPRTemporary {
+public:
+    GPRTemporary(JITCodeGenerator*);
+    GPRTemporary(JITCodeGenerator*, SpeculateIntegerOperand&);
+    GPRTemporary(JITCodeGenerator*, SpeculateIntegerOperand&, SpeculateIntegerOperand&);
+    GPRTemporary(JITCodeGenerator*, IntegerOperand&);
+    GPRTemporary(JITCodeGenerator*, IntegerOperand&, IntegerOperand&);
+    GPRTemporary(JITCodeGenerator*, SpeculateCellOperand&);
+    GPRTemporary(JITCodeGenerator*, JSValueOperand&);
+
+    ~GPRTemporary()
+    {
+        m_jit->unlock(gpr());
+    }
+
+    GPRReg gpr()
+    {
+        ASSERT(m_gpr != InvalidGPRReg);
+        return m_gpr;
+    }
+
+protected:
+    GPRTemporary(JITCodeGenerator* jit, GPRReg lockedGPR)
+        : m_jit(jit)
+        , m_gpr(lockedGPR)
+    {
+    }
+
+private:
+    JITCodeGenerator* m_jit;
+    GPRReg m_gpr;
+};
+
+class FPRTemporary {
+public:
+    FPRTemporary(JITCodeGenerator*);
+    FPRTemporary(JITCodeGenerator*, DoubleOperand&);
+    FPRTemporary(JITCodeGenerator*, DoubleOperand&, DoubleOperand&);
+
+    ~FPRTemporary()
+    {
+        m_jit->unlock(fpr());
+    }
+
+    FPRReg fpr() const
+    {
+        ASSERT(m_fpr != InvalidFPRReg);
+        return m_fpr;
+    }
+
+protected:
+    FPRTemporary(JITCodeGenerator* jit, FPRReg lockedFPR)
+        : m_jit(jit)
+        , m_fpr(lockedFPR)
+    {
+    }
+
+private:
+    JITCodeGenerator* m_jit;
+    FPRReg m_fpr;
+};
+
+
+// === Results ===
+//
+// These classes lock the result of a call to a C++ helper function.
+
+class GPRResult : public GPRTemporary {
+public:
+    GPRResult(JITCodeGenerator* jit)
+        : GPRTemporary(jit, lockedResult(jit))
+    {
+    }
+
+private:
+    static GPRReg lockedResult(JITCodeGenerator* jit)
+    {
+        jit->lock(GPRInfo::returnValueGPR);
+        return GPRInfo::returnValueGPR;
+    }
+};
+
+class FPRResult : public FPRTemporary {
+public:
+    FPRResult(JITCodeGenerator* jit)
+        : FPRTemporary(jit, lockedResult(jit))
+    {
+    }
+
+private:
+    static FPRReg lockedResult(JITCodeGenerator* jit)
+    {
+        jit->lock(FPRInfo::returnValueFPR);
+        return FPRInfo::returnValueFPR;
+    }
+};
+
+} } // namespace JSC::DFG
+
+#endif
+#endif
+