/* * 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 DFGSpeculativeJIT_h #define DFGSpeculativeJIT_h #if ENABLE(DFG_JIT) #include namespace JSC { namespace DFG { class SpeculativeJIT; // This enum describes the types of additional recovery that // may need be performed should a speculation check fail. enum SpeculationRecoveryType { SpeculativeAdd }; // === SpeculationRecovery === // // This class provides additional information that may be associated with a // speculation check - for example class SpeculationRecovery { public: SpeculationRecovery(SpeculationRecoveryType type, GPRReg dest, GPRReg src) : m_type(type) , m_dest(dest) , m_src(src) { } SpeculationRecoveryType type() { return m_type; } GPRReg dest() { return m_dest; } GPRReg src() { return m_src; } private: // Indicates the type of additional recovery to be performed. SpeculationRecoveryType m_type; // different recovery types may required different additional information here. GPRReg m_dest; GPRReg m_src; }; // === SpeculationCheck === // // This structure records a bail-out from the speculative path, // which will need to be linked in to the non-speculative one. struct SpeculationCheck { SpeculationCheck(MacroAssembler::Jump, SpeculativeJIT*, unsigned recoveryIndex = 0); // The location of the jump out from the speculative path, // and the node we were generating code for. MacroAssembler::Jump m_check; NodeIndex m_nodeIndex; // Used to record any additional recovery to be performed; this // value is an index into the SpeculativeJIT's m_speculationRecoveryList // array, offset by 1. (m_recoveryIndex == 0) means no recovery. unsigned m_recoveryIndex; struct RegisterInfo { NodeIndex nodeIndex; DataFormat format; }; RegisterInfo m_gprInfo[numberOfGPRs]; NodeIndex m_fprInfo[numberOfFPRs]; }; // === SpeculativeJIT === // // The SpeculativeJIT is used to generate a fast, but potentially // incomplete code path for the dataflow. When code generating // we may make assumptions about operand types, dynamically check, // and bail-out to an alternate code path if these checks fail. // Importantly, the speculative code path cannot be reentered once // a speculative check has failed. This allows the SpeculativeJIT // to propagate type information (including information that has // only speculatively been asserted) through the dataflow. class SpeculativeJIT : public JITCodeGenerator { friend struct SpeculationCheck; public: // The speculation typedef SegmentedVector SpeculationCheckVector; SpeculativeJIT(JITCompiler& jit) : JITCodeGenerator(jit, true) , m_didTerminate(false) { } bool compile(); // Retrieve the list of bail-outs from the speculative path, // and additional recovery information. SpeculationCheckVector& speculationChecks() { return m_speculationChecks; } SpeculationRecovery* speculationRecovery(size_t index) { // SpeculationCheck::m_recoveryIndex is offset by 1, // 0 means no recovery. return index ? &m_speculationRecoveryList[index - 1] : 0; } // Called by the speculative operand types, below, to fill operand to // machine registers, implicitly generating speculation checks as needed. GPRReg fillSpeculateInt(NodeIndex, DataFormat& returnFormat); GPRReg fillSpeculateIntStrict(NodeIndex); GPRReg fillSpeculateCell(NodeIndex); private: bool compile(Node&); // Add a speculation check without additional recovery. void speculationCheck(MacroAssembler::Jump jumpToFail) { m_speculationChecks.append(SpeculationCheck(jumpToFail, this)); } // Add a speculation check with additional recovery. void speculationCheck(MacroAssembler::Jump jumpToFail, const SpeculationRecovery& recovery) { m_speculationRecoveryList.append(recovery); m_speculationChecks.append(SpeculationCheck(jumpToFail, this, m_speculationRecoveryList.size())); } // Called when we statically determine that a speculation will fail. void terminateSpeculativeExecution() { // FIXME: in cases where we can statically determine we're going to bail out from the speculative // JIT we should probably rewind code generation and only produce the non-speculative path. m_didTerminate = true; speculationCheck(m_jit.jump()); } template GPRReg fillSpeculateIntInternal(NodeIndex, DataFormat& returnFormat); // It is possible, during speculative generation, to reach a situation in which we // can statically determine a speculation will fail (for example, when two nodes // will make conflicting speculations about the same operand). In such cases this // flag is set, indicating no further code generation should take place. bool m_didTerminate; // This vector tracks bail-outs from the speculative path to the non-speculative one. SpeculationCheckVector m_speculationChecks; // Some bail-outs need to record additional information recording specific recovery // to be performed (for example, on detected overflow from an add, we may need to // reverse the addition if an operand is being overwritten). Vector m_speculationRecoveryList; }; // === Speculative Operand types === // // SpeculateIntegerOperand, SpeculateStrictInt32Operand and SpeculateCellOperand. // // These are used to lock the operands to a node into machine registers within the // SpeculativeJIT. The classes operate like those provided by the JITCodeGenerator, // however these will perform a speculative check for a more restrictive type than // we can statically determine the operand to have. If the operand does not have // the requested type, a bail-out to the non-speculative path will be taken. class SpeculateIntegerOperand { public: explicit SpeculateIntegerOperand(SpeculativeJIT* 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(); } ~SpeculateIntegerOperand() { 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->fillSpeculateInt(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: SpeculativeJIT* m_jit; NodeIndex m_index; GPRReg m_gprOrInvalid; DataFormat m_format; }; class SpeculateStrictInt32Operand { public: explicit SpeculateStrictInt32Operand(SpeculativeJIT* jit, NodeIndex index) : m_jit(jit) , m_index(index) , m_gprOrInvalid(InvalidGPRReg) { ASSERT(m_jit); if (jit->isFilled(index)) gpr(); } ~SpeculateStrictInt32Operand() { 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->fillSpeculateIntStrict(index()); return m_gprOrInvalid; } MacroAssembler::RegisterID registerID() { return JITCompiler::gprToRegisterID(gpr()); } private: SpeculativeJIT* m_jit; NodeIndex m_index; GPRReg m_gprOrInvalid; }; class SpeculateCellOperand { public: explicit SpeculateCellOperand(SpeculativeJIT* jit, NodeIndex index) : m_jit(jit) , m_index(index) , m_gprOrInvalid(InvalidGPRReg) { ASSERT(m_jit); if (jit->isFilled(index)) gpr(); } ~SpeculateCellOperand() { 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->fillSpeculateCell(index()); return m_gprOrInvalid; } MacroAssembler::RegisterID registerID() { return JITCompiler::gprToRegisterID(gpr()); } private: SpeculativeJIT* m_jit; NodeIndex m_index; GPRReg m_gprOrInvalid; }; // === SpeculationCheckIndexIterator === // // This class is used by the non-speculative JIT to check which // nodes require entry points from the speculative path. class SpeculationCheckIndexIterator { public: SpeculationCheckIndexIterator(SpeculativeJIT& speculativeJIT) : m_speculationChecks(speculativeJIT.speculationChecks()) , m_iter(m_speculationChecks.begin()) , m_end(m_speculationChecks.end()) { } bool hasCheckAtIndex(NodeIndex nodeIndex) { while (m_iter != m_end) { NodeIndex current = m_iter->m_nodeIndex; if (current >= nodeIndex) return current == nodeIndex; ++m_iter; } return false; } private: SpeculativeJIT::SpeculationCheckVector& m_speculationChecks; SpeculativeJIT::SpeculationCheckVector::Iterator m_iter; SpeculativeJIT::SpeculationCheckVector::Iterator m_end; }; } } // namespace JSC::DFG #endif #endif