diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h | 955 |
1 files changed, 955 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h b/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h new file mode 100644 index 0000000..a84cdc6 --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGJITCodeGenerator.h @@ -0,0 +1,955 @@ +/* + * 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 { + SpillOrderNone, + SpillOrderConstant = 1, // no spill, and cheap fill + SpillOrderSpilled = 2, // no spill + SpillOrderArgument = 3, // no spill, but we may lose typeinfo + SpillOrderJS = 4, // needs spill + SpillOrderCell = 4, // needs spill + SpillOrderInteger = 5, // needs spill and box + SpillOrderDouble = 6, // needs spill and convert + SpillOrderMax + }; + + +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) + { + } + + // These methods convert between doubles, and doubles boxed and JSValues. + GPRReg boxDouble(FPRReg fpr, GPRReg gpr) + { + JITCompiler::FPRegisterID fpReg = JITCompiler::fprToRegisterID(fpr); + JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(gpr); + m_jit.moveDoubleToPtr(fpReg, reg); + m_jit.subPtr(JITCompiler::tagTypeNumberRegister, reg); + return gpr; + } + FPRReg unboxDouble(GPRReg gpr, FPRReg fpr) + { + JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(gpr); + JITCompiler::FPRegisterID fpReg = JITCompiler::fprToRegisterID(fpr); + m_jit.addPtr(JITCompiler::tagTypeNumberRegister, reg); + m_jit.movePtrToDouble(reg, fpReg); + 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(JITCompiler::gprToRegisterID(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); + + JITCompiler::RegisterID reg = JITCompiler::gprToRegisterID(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(JITCompiler::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 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 (GPRReg gpr = gpr0; gpr < numberOfGPRs; next(gpr)) { + VirtualRegister name = m_gprs.name(gpr); + if (name != InvalidVirtualRegister) { + spill(name); + m_gprs.release(gpr); + } + } + for (FPRReg fpr = fpr0; fpr < numberOfFPRs; next(fpr)) { + VirtualRegister name = m_fprs.name(fpr); + if (name != InvalidVirtualRegister) { + spill(name); + m_fprs.release(fpr); + } + } + } + +#ifndef NDEBUG + // Used to ASSERT flushRegisters() has been called prior to + // calling out from JIT code to a C helper function. + bool isFlushed() + { + for (GPRReg gpr = gpr0; gpr < numberOfGPRs; next(gpr)) { + VirtualRegister name = m_gprs.name(gpr); + if (name != InvalidVirtualRegister) + return false; + } + for (FPRReg fpr = fpr0; fpr < numberOfFPRs; next(fpr)) { + VirtualRegister name = m_fprs.name(fpr); + if (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, MacroAssembler::RegisterID op1, MacroAssembler::RegisterID 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, MacroAssembler::RegisterID op1, MacroAssembler::RegisterID op2, MacroAssembler::RegisterID 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, MacroAssembler::RegisterID op1, int32_t shiftAmount, MacroAssembler::RegisterID 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, MacroAssembler::RegisterID op1, MacroAssembler::RegisterID shiftAmount, MacroAssembler::RegisterID 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); + + VirtualRegister virtualRegister = node.virtualRegister; + GenerationInfo& info = m_generationInfo[virtualRegister]; + info.initNone(nodeIndex, node.refCount); + } + 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); + } + void initArgumentInfo(NodeIndex nodeIndex) + { + Node& node = m_jit.graph()[nodeIndex]; + m_generationInfo[node.virtualRegister].initArgument(nodeIndex, node.refCount); + } + + // These methods used to sort arguments into the correct registers. + template<GPRReg destA, GPRReg destB> + 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(JITCompiler::gprToRegisterID(srcA), JITCompiler::gprToRegisterID(destA)); + m_jit.move(JITCompiler::gprToRegisterID(srcB), JITCompiler::gprToRegisterID(destB)); + } else if (srcA != destB) { + // Handle the non-swap case - just put srcB in place first. + m_jit.move(JITCompiler::gprToRegisterID(srcB), JITCompiler::gprToRegisterID(destB)); + m_jit.move(JITCompiler::gprToRegisterID(srcA), JITCompiler::gprToRegisterID(destA)); + } else + m_jit.swap(JITCompiler::gprToRegisterID(destB), JITCompiler::gprToRegisterID(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(JITCompiler::fprToRegisterID(srcA), JITCompiler::fprToRegisterID(destA)); + m_jit.moveDouble(JITCompiler::fprToRegisterID(srcB), JITCompiler::fprToRegisterID(destB)); + return; + } + + if (srcA != destB) { + // Handle the non-swap case - just put srcB in place first. + m_jit.moveDouble(JITCompiler::fprToRegisterID(srcB), JITCompiler::fprToRegisterID(destB)); + m_jit.moveDouble(JITCompiler::fprToRegisterID(srcA), JITCompiler::fprToRegisterID(destA)); + return; + } + + ASSERT(srcB == destA && srcA == destB); + // Need to swap; pick a temporary register. + FPRReg temp; + if (destA != JITCompiler::argumentFPR3 && destA != JITCompiler::argumentFPR3) + temp = JITCompiler::argumentFPR3; + else if (destA != JITCompiler::argumentFPR2 && destA != JITCompiler::argumentFPR2) + temp = JITCompiler::argumentFPR2; + else { + ASSERT(destA != JITCompiler::argumentFPR1 && destA != JITCompiler::argumentFPR1); + temp = JITCompiler::argumentFPR1; + } + m_jit.moveDouble(JITCompiler::fprToRegisterID(destA), JITCompiler::fprToRegisterID(temp)); + m_jit.moveDouble(JITCompiler::fprToRegisterID(destB), JITCompiler::fprToRegisterID(destA)); + m_jit.moveDouble(JITCompiler::fprToRegisterID(temp), JITCompiler::fprToRegisterID(destB)); + } + void setupStubArguments(GPRReg arg1, GPRReg arg2) + { + setupTwoStubArgs<JITCompiler::argumentGPR1, JITCompiler::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 != JITCompiler::argumentGPR1 && arg3 != JITCompiler::argumentGPR1) { + m_jit.move(JITCompiler::gprToRegisterID(arg1), JITCompiler::argumentRegister1); + setupTwoStubArgs<JITCompiler::argumentGPR2, JITCompiler::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 != JITCompiler::argumentGPR2 && arg3 != JITCompiler::argumentGPR2) { + m_jit.move(JITCompiler::gprToRegisterID(arg2), JITCompiler::argumentRegister2); + setupTwoStubArgs<JITCompiler::argumentGPR1, JITCompiler::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 != JITCompiler::argumentGPR3 && arg2 != JITCompiler::argumentGPR3) { + m_jit.move(JITCompiler::gprToRegisterID(arg3), JITCompiler::argumentRegister3); + setupTwoStubArgs<JITCompiler::argumentGPR1, JITCompiler::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 != JITCompiler::argumentGPR1) { + m_jit.swap(JITCompiler::gprToRegisterID(arg1), JITCompiler::argumentRegister1); + + // If arg1 wasn't in argumentGPR1, one of arg2/arg3 must be. + ASSERT(arg2 == JITCompiler::argumentGPR1 || arg3 == JITCompiler::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 == JITCompiler::argumentGPR1) + arg2 = arg1; + else + arg3 = arg1; + } + + // Either arg2 & arg3 need swapping, or we're all done. + ASSERT((arg2 == JITCompiler::argumentGPR2 || arg3 == JITCompiler::argumentGPR3) + || (arg2 == JITCompiler::argumentGPR3 || arg3 == JITCompiler::argumentGPR2)); + + if (arg2 != JITCompiler::argumentGPR2) + m_jit.swap(JITCompiler::argumentRegister2, JITCompiler::argumentRegister3); + } + + // 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(JITCompiler::gprToRegisterID(arg1), JITCompiler::argumentRegister1); + m_jit.move(JITCompiler::TrustedImmPtr(pointer), JITCompiler::argumentRegister2); + m_jit.move(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0); + + appendCallWithExceptionCheck(operation); + m_jit.move(JITCompiler::returnValueRegister, JITCompiler::gprToRegisterID(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(JITCompiler::gprToRegisterID(arg1), JITCompiler::argumentRegister1); + m_jit.move(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0); + + appendCallWithExceptionCheck(operation); + m_jit.move(JITCompiler::returnValueRegister, JITCompiler::gprToRegisterID(result)); + } + void callOperation(J_DFGOperation_EJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2) + { + ASSERT(isFlushed()); + + setupStubArguments(arg1, arg2); + m_jit.move(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0); + + appendCallWithExceptionCheck(operation); + m_jit.move(JITCompiler::returnValueRegister, JITCompiler::gprToRegisterID(result)); + } + void callOperation(V_DFGOperation_EJJP operation, GPRReg arg1, GPRReg arg2, void* pointer) + { + ASSERT(isFlushed()); + + setupStubArguments(arg1, arg2); + m_jit.move(JITCompiler::TrustedImmPtr(pointer), JITCompiler::argumentRegister3); + m_jit.move(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0); + + 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(JITCompiler::callFrameRegister, JITCompiler::argumentRegister0); + + appendCallWithExceptionCheck(operation); + } + void callOperation(D_DFGOperation_DD operation, FPRReg result, FPRReg arg1, FPRReg arg2) + { + ASSERT(isFlushed()); + + setupTwoStubArgs<JITCompiler::argumentFPR0, JITCompiler::argumentFPR1>(arg1, arg2); + + m_jit.appendCall(operation); + m_jit.moveDouble(JITCompiler::fpReturnValueRegister, JITCompiler::fprToRegisterID(result)); + } + + void appendCallWithExceptionCheck(const FunctionPtr& function) + { + m_jit.appendCallWithExceptionCheck(function, m_jit.graph()[m_compileIndex].exceptionInfo); + } + +#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. + NodeIndex m_compileIndex; + // Virtual and physical register maps. + Vector<GenerationInfo, 32> m_generationInfo; + RegisterBank<GPRReg, numberOfGPRs, SpillOrder, SpillOrderNone, SpillOrderMax> m_gprs; + RegisterBank<FPRReg, numberOfFPRs, SpillOrder, SpillOrderNone, SpillOrderMax> m_fprs; +}; + +// === 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; + } + + GPRReg gpr() + { + if (m_gprOrInvalid == InvalidGPRReg) + m_gprOrInvalid = m_jit->fillInteger(index(), m_format); + return m_gprOrInvalid; + } + + DataFormat format() + { + gpr(); // m_format is set when m_gpr is locked. + ASSERT(m_format == DataFormatInteger || m_format == DataFormatJSInteger); + return m_format; + } + + MacroAssembler::RegisterID registerID() + { + return JITCompiler::gprToRegisterID(gpr()); + } + +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; + } + + MacroAssembler::FPRegisterID registerID() + { + return JITCompiler::fprToRegisterID(fpr()); + } + +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; + } + + MacroAssembler::RegisterID registerID() + { + return JITCompiler::gprToRegisterID(gpr()); + } + +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() const + { + ASSERT(m_gpr != InvalidGPRReg); + return m_gpr; + } + + MacroAssembler::RegisterID registerID() + { + ASSERT(m_gpr != InvalidGPRReg); + return JITCompiler::gprToRegisterID(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; + } + + MacroAssembler::FPRegisterID registerID() + { + ASSERT(m_fpr != InvalidFPRReg); + return JITCompiler::fprToRegisterID(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(JITCompiler::returnValueGPR); + return JITCompiler::returnValueGPR; + } +}; + +class FPRResult : public FPRTemporary { +public: + FPRResult(JITCodeGenerator* jit) + : FPRTemporary(jit, lockedResult(jit)) + { + } + +private: + static FPRReg lockedResult(JITCodeGenerator* jit) + { + jit->lock(JITCompiler::returnValueFPR); + return JITCompiler::returnValueFPR; + } +}; + +} } // namespace JSC::DFG + +#endif +#endif + |