blob: 965cdbede6600a80b16efd9300591a0abd683872 [file] [log] [blame]
/*
* 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