X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/1df5f87f1309a8daa30dabdee855f48ae40d14ab..6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174:/dfg/DFGJITCodeGenerator.h diff --git a/dfg/DFGJITCodeGenerator.h b/dfg/DFGJITCodeGenerator.h deleted file mode 100644 index 3c0998d..0000000 --- a/dfg/DFGJITCodeGenerator.h +++ /dev/null @@ -1,957 +0,0 @@ -/* - * 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 -#include -#include -#include -#include - -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 - 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 - 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(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(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(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(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(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 m_generationInfo; - RegisterBank m_gprs; - RegisterBank m_fprs; - - Vector m_blockHeads; - struct BranchRecord { - BranchRecord(MacroAssembler::Jump jump, BlockIndex destination) - : jump(jump) - , destination(destination) - { - } - - MacroAssembler::Jump jump; - BlockIndex destination; - }; - Vector 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 -