| /* |
| * 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 <dfg/DFGJITCodeGenerator.h> |
| |
| 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]; |
| }; |
| typedef SegmentedVector<SpeculationCheck, 16> SpeculationCheckVector; |
| |
| |
| // === 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: |
| 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&); |
| bool compile(BasicBlock&); |
| |
| bool isDoubleConstantWithInt32Value(NodeIndex nodeIndex, int32_t& out) |
| { |
| if (!m_jit.isDoubleConstant(nodeIndex)) |
| return false; |
| double value = m_jit.valueOfDoubleConstant(nodeIndex); |
| |
| int32_t asInt32 = static_cast<int32_t>(value); |
| if (value != asInt32) |
| return false; |
| if (!asInt32 && signbit(value)) |
| return false; |
| |
| out = asInt32; |
| return true; |
| } |
| |
| // 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<bool strict> |
| 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<SpeculationRecovery, 16> 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(SpeculationCheckVector& speculationChecks) |
| : m_speculationChecks(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: |
| SpeculationCheckVector& m_speculationChecks; |
| SpeculationCheckVector::Iterator m_iter; |
| SpeculationCheckVector::Iterator m_end; |
| }; |
| |
| |
| } } // namespace JSC::DFG |
| |
| #endif |
| #endif |
| |