mirror of
https://github.com/darlinghq/darling-JavaScriptCore.git
synced 2025-04-10 10:50:45 +00:00
453 lines
15 KiB
C++
453 lines
15 KiB
C++
/*
|
|
* Copyright (C) 2011, 2013 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#if ENABLE(DFG_JIT)
|
|
|
|
#include "DFGMinifiedID.h"
|
|
#include "DFGVariableEvent.h"
|
|
#include "DFGVariableEventStream.h"
|
|
#include "DataFormat.h"
|
|
|
|
namespace JSC { namespace DFG {
|
|
|
|
// === GenerationInfo ===
|
|
//
|
|
// This class is used to track the current status of live values during code generation.
|
|
// Can provide information as to whether a value is in machine registers, and if so which,
|
|
// whether a value has been spilled to the RegisterFile, and if so may be able to provide
|
|
// details of the format in memory (all values are spilled in a boxed form, but we may be
|
|
// able to track the type of box), and tracks how many outstanding uses of a value remain,
|
|
// so that we know when the value is dead and the machine registers associated with it
|
|
// may be released.
|
|
class GenerationInfo {
|
|
public:
|
|
GenerationInfo()
|
|
: m_node(0)
|
|
, m_useCount(0)
|
|
, m_registerFormat(DataFormatNone)
|
|
, m_spillFormat(DataFormatNone)
|
|
, m_canFill(false)
|
|
, m_bornForOSR(false)
|
|
, m_isConstant(false)
|
|
{
|
|
}
|
|
|
|
void initConstant(Node* node, uint32_t useCount)
|
|
{
|
|
m_node = node;
|
|
m_useCount = useCount;
|
|
m_registerFormat = DataFormatNone;
|
|
m_spillFormat = DataFormatNone;
|
|
m_canFill = true;
|
|
m_bornForOSR = false;
|
|
m_isConstant = true;
|
|
ASSERT(m_useCount);
|
|
}
|
|
void initGPR(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format)
|
|
{
|
|
ASSERT(gpr != InvalidGPRReg);
|
|
m_node = node;
|
|
m_useCount = useCount;
|
|
m_registerFormat = format;
|
|
m_spillFormat = DataFormatNone;
|
|
m_canFill = false;
|
|
u.gpr = gpr;
|
|
m_bornForOSR = false;
|
|
m_isConstant = false;
|
|
ASSERT(m_useCount);
|
|
}
|
|
void initInt32(Node* node, uint32_t useCount, GPRReg gpr)
|
|
{
|
|
initGPR(node, useCount, gpr, DataFormatInt32);
|
|
}
|
|
void initInt52(Node* node, uint32_t useCount, GPRReg reg, DataFormat format)
|
|
{
|
|
ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52);
|
|
initGPR(node, useCount, reg, format);
|
|
}
|
|
void initInt52(Node* node, uint32_t useCount, GPRReg reg)
|
|
{
|
|
initGPR(node, useCount, reg, DataFormatInt52);
|
|
}
|
|
void initStrictInt52(Node* node, uint32_t useCount, GPRReg reg)
|
|
{
|
|
initGPR(node, useCount, reg, DataFormatStrictInt52);
|
|
}
|
|
#if USE(JSVALUE64)
|
|
void initJSValue(Node* node, uint32_t useCount, GPRReg gpr, DataFormat format = DataFormatJS)
|
|
{
|
|
ASSERT(format & DataFormatJS);
|
|
initGPR(node, useCount, gpr, format);
|
|
}
|
|
#elif USE(JSVALUE32_64)
|
|
void initJSValue(Node* node, uint32_t useCount, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS)
|
|
{
|
|
ASSERT(format & DataFormatJS);
|
|
|
|
m_node = node;
|
|
m_useCount = useCount;
|
|
m_registerFormat = format;
|
|
m_spillFormat = DataFormatNone;
|
|
m_canFill = false;
|
|
u.v.tagGPR = tagGPR;
|
|
u.v.payloadGPR = payloadGPR;
|
|
m_bornForOSR = false;
|
|
m_isConstant = false;
|
|
ASSERT(m_useCount);
|
|
}
|
|
#endif
|
|
void initCell(Node* node, uint32_t useCount, GPRReg gpr)
|
|
{
|
|
initGPR(node, useCount, gpr, DataFormatCell);
|
|
}
|
|
void initBoolean(Node* node, uint32_t useCount, GPRReg gpr)
|
|
{
|
|
initGPR(node, useCount, gpr, DataFormatBoolean);
|
|
}
|
|
void initDouble(Node* node, uint32_t useCount, FPRReg fpr)
|
|
{
|
|
ASSERT(fpr != InvalidFPRReg);
|
|
m_node = node;
|
|
m_useCount = useCount;
|
|
m_registerFormat = DataFormatDouble;
|
|
m_spillFormat = DataFormatNone;
|
|
m_canFill = false;
|
|
u.fpr = fpr;
|
|
m_bornForOSR = false;
|
|
m_isConstant = false;
|
|
ASSERT(m_useCount);
|
|
}
|
|
void initStorage(Node* node, uint32_t useCount, GPRReg gpr)
|
|
{
|
|
initGPR(node, useCount, gpr, DataFormatStorage);
|
|
}
|
|
|
|
// Get the node that produced this value.
|
|
Node* node() { return m_node; }
|
|
|
|
void noticeOSRBirth(VariableEventStream& stream, Node* node, VirtualRegister virtualRegister)
|
|
{
|
|
if (m_node != node)
|
|
return;
|
|
if (!alive())
|
|
return;
|
|
if (m_bornForOSR)
|
|
return;
|
|
|
|
m_bornForOSR = true;
|
|
|
|
if (m_isConstant)
|
|
appendBirth(stream);
|
|
else if (m_registerFormat != DataFormatNone)
|
|
appendFill(BirthToFill, stream);
|
|
else if (m_spillFormat != DataFormatNone)
|
|
appendSpill(BirthToSpill, stream, virtualRegister);
|
|
}
|
|
|
|
// Mark the value as having been used (decrement the useCount).
|
|
// Returns true if this was the last use of the value, and any
|
|
// associated machine registers may be freed.
|
|
bool use(VariableEventStream& stream)
|
|
{
|
|
ASSERT(m_useCount);
|
|
bool result = !--m_useCount;
|
|
|
|
if (result && m_bornForOSR) {
|
|
ASSERT(m_node);
|
|
stream.appendAndLog(VariableEvent::death(MinifiedID(m_node)));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Used to check the operands of operations to see if they are on
|
|
// their last use; in some cases it may be safe to reuse the same
|
|
// machine register for the result of the operation.
|
|
uint32_t useCount()
|
|
{
|
|
ASSERT(m_useCount);
|
|
return m_useCount;
|
|
}
|
|
|
|
// Get the format of the value in machine registers (or 'none').
|
|
DataFormat registerFormat() { return m_registerFormat; }
|
|
// Get the format of the value as it is spilled in the JSStack (or 'none').
|
|
DataFormat spillFormat() { return m_spillFormat; }
|
|
|
|
bool isFormat(DataFormat expectedFormat)
|
|
{
|
|
return registerFormat() == expectedFormat || spillFormat() == expectedFormat;
|
|
}
|
|
|
|
bool isJSFormat(DataFormat expectedFormat)
|
|
{
|
|
return JSC::isJSFormat(registerFormat(), expectedFormat) || JSC::isJSFormat(spillFormat(), expectedFormat);
|
|
}
|
|
|
|
bool isJSInt32()
|
|
{
|
|
return isJSFormat(DataFormatJSInt32);
|
|
}
|
|
|
|
bool isInt52()
|
|
{
|
|
return isFormat(DataFormatInt52);
|
|
}
|
|
|
|
bool isStrictInt52()
|
|
{
|
|
return isFormat(DataFormatStrictInt52);
|
|
}
|
|
|
|
bool isJSDouble()
|
|
{
|
|
return isJSFormat(DataFormatJSDouble);
|
|
}
|
|
|
|
bool isJSCell()
|
|
{
|
|
return isJSFormat(DataFormatJSCell);
|
|
}
|
|
|
|
bool isJSBoolean()
|
|
{
|
|
return isJSFormat(DataFormatJSBoolean);
|
|
}
|
|
|
|
bool isUnknownJS()
|
|
{
|
|
return spillFormat() == DataFormatNone
|
|
? registerFormat() == DataFormatJS || registerFormat() == DataFormatNone
|
|
: spillFormat() == DataFormatJS;
|
|
}
|
|
|
|
// Get the machine resister currently holding the value.
|
|
#if USE(JSVALUE64)
|
|
GPRReg gpr() { ASSERT(m_registerFormat && m_registerFormat != DataFormatDouble); return u.gpr; }
|
|
FPRReg fpr() { ASSERT(m_registerFormat == DataFormatDouble); return u.fpr; }
|
|
JSValueRegs jsValueRegs() { ASSERT(m_registerFormat & DataFormatJS); return JSValueRegs(u.gpr); }
|
|
#elif USE(JSVALUE32_64)
|
|
GPRReg gpr() { ASSERT(!(m_registerFormat & DataFormatJS) && m_registerFormat != DataFormatDouble); return u.gpr; }
|
|
GPRReg tagGPR() { ASSERT(m_registerFormat & DataFormatJS); return u.v.tagGPR; }
|
|
GPRReg payloadGPR() { ASSERT(m_registerFormat & DataFormatJS); return u.v.payloadGPR; }
|
|
FPRReg fpr() { ASSERT(m_registerFormat == DataFormatDouble || m_registerFormat == DataFormatJSDouble); return u.fpr; }
|
|
JSValueRegs jsValueRegs() { ASSERT(m_registerFormat & DataFormatJS); return JSValueRegs(u.v.tagGPR, u.v.payloadGPR); }
|
|
#endif
|
|
|
|
// Check whether a value needs spilling in order to free up any associated machine registers.
|
|
bool needsSpill()
|
|
{
|
|
// This should only be called on values that are currently in a register.
|
|
ASSERT(m_registerFormat != DataFormatNone);
|
|
// Constants do not need spilling, nor do values that have already been
|
|
// spilled to the JSStack.
|
|
return !m_canFill;
|
|
}
|
|
|
|
// Called when a VirtualRegister is being spilled to the JSStack for the first time.
|
|
void spill(VariableEventStream& stream, VirtualRegister virtualRegister, DataFormat spillFormat)
|
|
{
|
|
// We shouldn't be spill values that don't need spilling.
|
|
ASSERT(!m_canFill);
|
|
ASSERT(m_spillFormat == DataFormatNone);
|
|
// We should only be spilling values that are currently in machine registers.
|
|
ASSERT(m_registerFormat != DataFormatNone);
|
|
|
|
m_registerFormat = DataFormatNone;
|
|
m_spillFormat = spillFormat;
|
|
m_canFill = true;
|
|
|
|
if (m_bornForOSR)
|
|
appendSpill(Spill, stream, virtualRegister);
|
|
}
|
|
|
|
// Called on values that don't need spilling (constants and values that have
|
|
// already been spilled), to mark them as no longer being in machine registers.
|
|
void setSpilled(VariableEventStream& stream, VirtualRegister virtualRegister)
|
|
{
|
|
// Should only be called on values that don't need spilling, and are currently in registers.
|
|
ASSERT(m_canFill && m_registerFormat != DataFormatNone);
|
|
m_registerFormat = DataFormatNone;
|
|
|
|
if (m_bornForOSR)
|
|
appendSpill(Spill, stream, virtualRegister);
|
|
}
|
|
|
|
void killSpilled()
|
|
{
|
|
m_spillFormat = DataFormatNone;
|
|
m_canFill = false;
|
|
}
|
|
|
|
void fillGPR(VariableEventStream& stream, GPRReg gpr, DataFormat format)
|
|
{
|
|
ASSERT(gpr != InvalidGPRReg);
|
|
m_registerFormat = format;
|
|
u.gpr = gpr;
|
|
if (m_bornForOSR)
|
|
appendFill(Fill, stream);
|
|
}
|
|
|
|
// Record that this value is filled into machine registers,
|
|
// tracking which registers, and what format the value has.
|
|
#if USE(JSVALUE64)
|
|
void fillJSValue(VariableEventStream& stream, GPRReg gpr, DataFormat format = DataFormatJS)
|
|
{
|
|
ASSERT(format & DataFormatJS);
|
|
fillGPR(stream, gpr, format);
|
|
}
|
|
#elif USE(JSVALUE32_64)
|
|
void fillJSValue(VariableEventStream& stream, GPRReg tagGPR, GPRReg payloadGPR, DataFormat format = DataFormatJS)
|
|
{
|
|
ASSERT(format & DataFormatJS);
|
|
m_registerFormat = format;
|
|
u.v.tagGPR = tagGPR; // FIXME: for JSValues with known type (boolean, integer, cell etc.) no tagGPR is needed?
|
|
u.v.payloadGPR = payloadGPR;
|
|
|
|
if (m_bornForOSR)
|
|
appendFill(Fill, stream);
|
|
}
|
|
void fillCell(VariableEventStream& stream, GPRReg gpr)
|
|
{
|
|
fillGPR(stream, gpr, DataFormatCell);
|
|
}
|
|
#endif
|
|
void fillInt32(VariableEventStream& stream, GPRReg gpr)
|
|
{
|
|
fillGPR(stream, gpr, DataFormatInt32);
|
|
}
|
|
void fillInt52(VariableEventStream& stream, GPRReg gpr, DataFormat format)
|
|
{
|
|
ASSERT(format == DataFormatInt52 || format == DataFormatStrictInt52);
|
|
fillGPR(stream, gpr, format);
|
|
}
|
|
void fillInt52(VariableEventStream& stream, GPRReg gpr)
|
|
{
|
|
fillGPR(stream, gpr, DataFormatInt52);
|
|
}
|
|
void fillStrictInt52(VariableEventStream& stream, GPRReg gpr)
|
|
{
|
|
fillGPR(stream, gpr, DataFormatStrictInt52);
|
|
}
|
|
void fillBoolean(VariableEventStream& stream, GPRReg gpr)
|
|
{
|
|
fillGPR(stream, gpr, DataFormatBoolean);
|
|
}
|
|
void fillDouble(VariableEventStream& stream, FPRReg fpr)
|
|
{
|
|
ASSERT(fpr != InvalidFPRReg);
|
|
m_registerFormat = DataFormatDouble;
|
|
u.fpr = fpr;
|
|
|
|
if (m_bornForOSR)
|
|
appendFill(Fill, stream);
|
|
}
|
|
void fillStorage(VariableEventStream& stream, GPRReg gpr)
|
|
{
|
|
fillGPR(stream, gpr, DataFormatStorage);
|
|
}
|
|
|
|
bool alive()
|
|
{
|
|
return m_useCount;
|
|
}
|
|
|
|
ValueRecovery recovery(VirtualRegister spillSlot) const
|
|
{
|
|
if (m_isConstant)
|
|
return ValueRecovery::constant(m_node->constant()->value());
|
|
|
|
if (m_registerFormat == DataFormatDouble)
|
|
return ValueRecovery::inFPR(u.fpr, DataFormatDouble);
|
|
|
|
#if USE(JSVALUE32_64)
|
|
if (m_registerFormat & DataFormatJS) {
|
|
if (m_registerFormat == DataFormatJS)
|
|
return ValueRecovery::inPair(u.v.tagGPR, u.v.payloadGPR);
|
|
return ValueRecovery::inGPR(u.v.payloadGPR, static_cast<DataFormat>(m_registerFormat & ~DataFormatJS));
|
|
}
|
|
#endif
|
|
if (m_registerFormat)
|
|
return ValueRecovery::inGPR(u.gpr, m_registerFormat);
|
|
|
|
ASSERT(m_spillFormat);
|
|
|
|
return ValueRecovery::displacedInJSStack(spillSlot, m_spillFormat);
|
|
}
|
|
|
|
private:
|
|
void appendBirth(VariableEventStream& stream)
|
|
{
|
|
stream.appendAndLog(VariableEvent::birth(MinifiedID(m_node)));
|
|
}
|
|
|
|
void appendFill(VariableEventKind kind, VariableEventStream& stream)
|
|
{
|
|
ASSERT(m_bornForOSR);
|
|
|
|
if (m_registerFormat == DataFormatDouble) {
|
|
stream.appendAndLog(VariableEvent::fillFPR(kind, MinifiedID(m_node), u.fpr));
|
|
return;
|
|
}
|
|
#if USE(JSVALUE32_64)
|
|
if (m_registerFormat & DataFormatJS) {
|
|
stream.appendAndLog(VariableEvent::fillPair(kind, MinifiedID(m_node), u.v.tagGPR, u.v.payloadGPR));
|
|
return;
|
|
}
|
|
#endif
|
|
stream.appendAndLog(VariableEvent::fillGPR(kind, MinifiedID(m_node), u.gpr, m_registerFormat));
|
|
}
|
|
|
|
void appendSpill(VariableEventKind kind, VariableEventStream& stream, VirtualRegister virtualRegister)
|
|
{
|
|
stream.appendAndLog(VariableEvent::spill(kind, MinifiedID(m_node), virtualRegister, m_spillFormat));
|
|
}
|
|
|
|
// The node whose result is stored in this virtual register.
|
|
Node* m_node;
|
|
uint32_t m_useCount;
|
|
DataFormat m_registerFormat;
|
|
DataFormat m_spillFormat;
|
|
bool m_canFill;
|
|
bool m_bornForOSR;
|
|
bool m_isConstant;
|
|
union {
|
|
GPRReg gpr;
|
|
FPRReg fpr;
|
|
#if USE(JSVALUE32_64)
|
|
struct {
|
|
GPRReg tagGPR;
|
|
GPRReg payloadGPR;
|
|
} v;
|
|
#endif
|
|
} u;
|
|
};
|
|
|
|
} } // namespace JSC::DFG
|
|
|
|
#endif
|