darling-JavaScriptCore/bytecode/ValueRecovery.h

445 lines
12 KiB
C++

/*
* Copyright (C) 2011-2019 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
#include "DFGMinifiedID.h"
#include "DataFormat.h"
#if ENABLE(JIT)
#include "GPRInfo.h"
#include "FPRInfo.h"
#include "Reg.h"
#endif
#include "JSCJSValue.h"
#include "MacroAssembler.h"
#include "VirtualRegister.h"
namespace JSC {
struct DumpContext;
struct InlineCallFrame;
// Describes how to recover a given bytecode virtual register at a given
// code point.
enum ValueRecoveryTechnique : uint8_t {
// It's in a register.
InGPR,
UnboxedInt32InGPR,
UnboxedInt52InGPR,
UnboxedStrictInt52InGPR,
UnboxedBooleanInGPR,
UnboxedCellInGPR,
#if USE(JSVALUE32_64)
InPair,
#endif
InFPR,
UnboxedDoubleInFPR,
// It's in the stack, but at a different location.
DisplacedInJSStack,
// It's in the stack, at a different location, and it's unboxed.
Int32DisplacedInJSStack,
Int52DisplacedInJSStack,
StrictInt52DisplacedInJSStack,
DoubleDisplacedInJSStack,
CellDisplacedInJSStack,
BooleanDisplacedInJSStack,
// It's an Arguments object. This arises because of the simplified arguments simplification done by the DFG.
DirectArgumentsThatWereNotCreated,
ClonedArgumentsThatWereNotCreated,
// It's a constant.
Constant,
// Don't know how to recover it.
DontKnow
};
class ValueRecovery {
public:
ValueRecovery()
: m_technique(DontKnow)
{
}
bool isSet() const { return m_technique != DontKnow; }
bool operator!() const { return !isSet(); }
#if ENABLE(JIT)
static ValueRecovery inRegister(Reg reg, DataFormat dataFormat)
{
if (reg.isGPR())
return inGPR(reg.gpr(), dataFormat);
ASSERT(reg.isFPR());
return inFPR(reg.fpr(), dataFormat);
}
#endif
explicit operator bool() const { return isSet(); }
static ValueRecovery inGPR(MacroAssembler::RegisterID gpr, DataFormat dataFormat)
{
ASSERT(dataFormat != DataFormatNone);
#if USE(JSVALUE32_64)
ASSERT(dataFormat == DataFormatInt32 || dataFormat == DataFormatCell || dataFormat == DataFormatBoolean);
#endif
ValueRecovery result;
if (dataFormat == DataFormatInt32)
result.m_technique = UnboxedInt32InGPR;
else if (dataFormat == DataFormatInt52)
result.m_technique = UnboxedInt52InGPR;
else if (dataFormat == DataFormatStrictInt52)
result.m_technique = UnboxedStrictInt52InGPR;
else if (dataFormat == DataFormatBoolean)
result.m_technique = UnboxedBooleanInGPR;
else if (dataFormat == DataFormatCell)
result.m_technique = UnboxedCellInGPR;
else
result.m_technique = InGPR;
UnionType u;
u.gpr = gpr;
result.m_source = WTFMove(u);
return result;
}
#if USE(JSVALUE32_64)
static ValueRecovery inPair(MacroAssembler::RegisterID tagGPR, MacroAssembler::RegisterID payloadGPR)
{
ValueRecovery result;
result.m_technique = InPair;
UnionType u;
u.pair.tagGPR = tagGPR;
u.pair.payloadGPR = payloadGPR;
result.m_source = WTFMove(u);
return result;
}
#endif
static ValueRecovery inFPR(MacroAssembler::FPRegisterID fpr, DataFormat dataFormat)
{
ASSERT(dataFormat == DataFormatDouble || dataFormat & DataFormatJS);
ValueRecovery result;
if (dataFormat == DataFormatDouble)
result.m_technique = UnboxedDoubleInFPR;
else
result.m_technique = InFPR;
UnionType u;
u.fpr = fpr;
result.m_source = WTFMove(u);
return result;
}
static ValueRecovery displacedInJSStack(VirtualRegister virtualReg, DataFormat dataFormat)
{
ValueRecovery result;
switch (dataFormat) {
case DataFormatInt32:
result.m_technique = Int32DisplacedInJSStack;
break;
case DataFormatInt52:
result.m_technique = Int52DisplacedInJSStack;
break;
case DataFormatStrictInt52:
result.m_technique = StrictInt52DisplacedInJSStack;
break;
case DataFormatDouble:
result.m_technique = DoubleDisplacedInJSStack;
break;
case DataFormatCell:
result.m_technique = CellDisplacedInJSStack;
break;
case DataFormatBoolean:
result.m_technique = BooleanDisplacedInJSStack;
break;
default:
ASSERT(dataFormat != DataFormatNone && dataFormat != DataFormatStorage);
result.m_technique = DisplacedInJSStack;
break;
}
UnionType u;
u.virtualReg = virtualReg.offset();
result.m_source = WTFMove(u);
return result;
}
static ValueRecovery constant(JSValue value)
{
ValueRecovery result;
result.m_technique = Constant;
UnionType u;
u.constant = JSValue::encode(value);
result.m_source = WTFMove(u);
return result;
}
static ValueRecovery directArgumentsThatWereNotCreated(DFG::MinifiedID id)
{
ValueRecovery result;
result.m_technique = DirectArgumentsThatWereNotCreated;
UnionType u;
u.nodeID = id.bits();
result.m_source = WTFMove(u);
return result;
}
static ValueRecovery clonedArgumentsThatWereNotCreated(DFG::MinifiedID id)
{
ValueRecovery result;
result.m_technique = ClonedArgumentsThatWereNotCreated;
UnionType u;
u.nodeID = id.bits();
result.m_source = WTFMove(u);
return result;
}
ValueRecoveryTechnique technique() const { return m_technique; }
bool isConstant() const { return m_technique == Constant; }
bool isInGPR() const
{
switch (m_technique) {
case InGPR:
case UnboxedInt32InGPR:
case UnboxedBooleanInGPR:
case UnboxedCellInGPR:
case UnboxedInt52InGPR:
case UnboxedStrictInt52InGPR:
return true;
default:
return false;
}
}
bool isInFPR() const
{
switch (m_technique) {
case InFPR:
case UnboxedDoubleInFPR:
return true;
default:
return false;
}
}
bool isInRegisters() const
{
return isInJSValueRegs() || isInGPR() || isInFPR();
}
bool isInJSStack() const
{
switch (m_technique) {
case DisplacedInJSStack:
case Int32DisplacedInJSStack:
case Int52DisplacedInJSStack:
case StrictInt52DisplacedInJSStack:
case DoubleDisplacedInJSStack:
case CellDisplacedInJSStack:
case BooleanDisplacedInJSStack:
return true;
default:
return false;
}
}
DataFormat dataFormat() const
{
switch (m_technique) {
case InGPR:
case InFPR:
case DisplacedInJSStack:
case Constant:
#if USE(JSVALUE32_64)
case InPair:
#endif
return DataFormatJS;
case UnboxedInt32InGPR:
case Int32DisplacedInJSStack:
return DataFormatInt32;
case UnboxedInt52InGPR:
case Int52DisplacedInJSStack:
return DataFormatInt52;
case UnboxedStrictInt52InGPR:
case StrictInt52DisplacedInJSStack:
return DataFormatStrictInt52;
case UnboxedBooleanInGPR:
case BooleanDisplacedInJSStack:
return DataFormatBoolean;
case UnboxedCellInGPR:
case CellDisplacedInJSStack:
return DataFormatCell;
case UnboxedDoubleInFPR:
case DoubleDisplacedInJSStack:
return DataFormatDouble;
default:
return DataFormatNone;
}
}
MacroAssembler::RegisterID gpr() const
{
ASSERT(isInGPR());
return m_source.get().gpr;
}
#if USE(JSVALUE32_64)
MacroAssembler::RegisterID tagGPR() const
{
ASSERT(m_technique == InPair);
return m_source.get().pair.tagGPR;
}
MacroAssembler::RegisterID payloadGPR() const
{
ASSERT(m_technique == InPair);
return m_source.get().pair.payloadGPR;
}
bool isInJSValueRegs() const
{
return m_technique == InPair;
}
#if ENABLE(JIT)
JSValueRegs jsValueRegs() const
{
ASSERT(isInJSValueRegs());
return JSValueRegs(tagGPR(), payloadGPR());
}
#endif // ENABLE(JIT)
#else
bool isInJSValueRegs() const
{
return isInGPR();
}
#endif // USE(JSVALUE32_64)
MacroAssembler::FPRegisterID fpr() const
{
ASSERT(isInFPR());
return m_source.get().fpr;
}
VirtualRegister virtualRegister() const
{
ASSERT(isInJSStack());
return VirtualRegister(m_source.get().virtualReg);
}
ValueRecovery withLocalsOffset(int offset) const
{
switch (m_technique) {
case DisplacedInJSStack:
case Int32DisplacedInJSStack:
case DoubleDisplacedInJSStack:
case CellDisplacedInJSStack:
case BooleanDisplacedInJSStack:
case Int52DisplacedInJSStack:
case StrictInt52DisplacedInJSStack: {
ValueRecovery result;
result.m_technique = m_technique;
UnionType u;
u.virtualReg = m_source.get().virtualReg + offset;
result.m_source = WTFMove(u);
return result;
}
default:
return *this;
}
}
JSValue constant() const
{
ASSERT(isConstant());
return JSValue::decode(m_source.get().constant);
}
DFG::MinifiedID nodeID() const
{
ASSERT(m_technique == DirectArgumentsThatWereNotCreated || m_technique == ClonedArgumentsThatWereNotCreated);
return DFG::MinifiedID::fromBits(m_source.get().nodeID);
}
JSValue recover(CallFrame*) const;
#if ENABLE(JIT)
template<typename Func>
void forEachReg(const Func& func)
{
switch (m_technique) {
case InGPR:
case UnboxedInt32InGPR:
case UnboxedBooleanInGPR:
case UnboxedCellInGPR:
case UnboxedInt52InGPR:
case UnboxedStrictInt52InGPR:
func(gpr());
return;
case InFPR:
case UnboxedDoubleInFPR:
func(fpr());
return;
#if USE(JSVALUE32_64)
case InPair:
func(jsValueRegs().payloadGPR());
func(jsValueRegs().tagGPR());
return;
#endif
default:
return;
}
}
void dumpInContext(PrintStream& out, DumpContext* context) const;
void dump(PrintStream& out) const;
#endif
private:
ValueRecoveryTechnique m_technique;
union UnionType {
MacroAssembler::RegisterID gpr;
MacroAssembler::FPRegisterID fpr;
#if USE(JSVALUE32_64)
struct {
MacroAssembler::RegisterID tagGPR;
MacroAssembler::RegisterID payloadGPR;
} pair;
#endif
int virtualReg;
EncodedJSValue constant;
unsigned nodeID;
};
Packed<UnionType> m_source;
};
static_assert(alignof(ValueRecovery) == 1);
} // namespace JSC