blob: 593ed8b62fc47c938f5754d98a59223d9f0e1def [file]
/*
* Copyright (C) 2013-2018 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.
*/
#include "config.h"
#include "FTLOSRExit.h"
#if ENABLE(FTL_JIT)
#include "B3StackmapGenerationParams.h"
#include "FTLJITCode.h"
#include "FTLSlowPathCall.h"
#include "FTLState.h"
#include "JSCJSValueInlines.h"
#include <wtf/LEBDecoder.h>
#include <wtf/LEBEncoder.h>
namespace JSC { namespace FTL {
using namespace B3;
using namespace DFG;
namespace {
constexpr uint8_t maxDeadRunLength = 0x40;
constexpr uint8_t inJSStackAtOwnRegisterTag = 0x40;
constexpr uint8_t inJSStackTag = 0x44;
constexpr uint8_t constantTag = 0x48;
constexpr uint8_t materializationTag = 0x49;
constexpr uint8_t argumentTag = 0x4A;
static_assert(ExitValueInJSStackAsInt32 == ExitValueInJSStack + 1);
static_assert(ExitValueInJSStackAsInt52 == ExitValueInJSStack + 2);
static_assert(ExitValueInJSStackAsDouble == ExitValueInJSStack + 3);
static_assert(inJSStackAtOwnRegisterTag + 4 == inJSStackTag);
static_assert(inJSStackTag + 4 == constantTag);
ExitValueKind inJSStackKind(uint8_t tag, uint8_t baseTag)
{
return static_cast<ExitValueKind>(ExitValueInJSStack + (tag - baseTag));
}
Vector<ExitTimeObjectMaterialization*, 4> materializationTable(const Bag<ExitTimeObjectMaterialization>& materializations)
{
Vector<ExitTimeObjectMaterialization*, 4> table;
for (ExitTimeObjectMaterialization* materialization : materializations)
table.append(materialization);
return table;
}
void encodeValueRep(Vector<uint8_t>& bytes, const B3::ValueRep& rep)
{
bytes.append(static_cast<uint8_t>(rep.kind()));
switch (rep.kind()) {
case B3::ValueRep::Register:
bytes.append(static_cast<uint8_t>(rep.reg().index()));
break;
case B3::ValueRep::Stack:
WTF::LEBEncoder::encodeInt32(bytes, rep.offsetFromFP());
break;
case B3::ValueRep::Constant:
WTF::LEBEncoder::encodeInt64(bytes, rep.value());
break;
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
B3::ValueRep decodeValueRep(std::span<const uint8_t> bytes, size_t& offset)
{
switch (static_cast<B3::ValueRep::Kind>(bytes[offset++])) {
case B3::ValueRep::Register:
return B3::ValueRep::reg(Reg::fromIndex(bytes[offset++]));
case B3::ValueRep::Stack:
return B3::ValueRep::stack(WTF::LEBDecoder::decodeInt32OrCrash(bytes, offset));
case B3::ValueRep::Constant:
return B3::ValueRep::constant(WTF::LEBDecoder::decodeInt64OrCrash(bytes, offset));
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
} // anonymous namespace
unsigned OSRExitValueReps::append(std::span<const B3::ValueRep> reps)
{
unsigned offset = m_bytes.size();
WTF::LEBEncoder::encodeUInt32(m_bytes, reps.size());
for (const B3::ValueRep& rep : reps)
encodeValueRep(m_bytes, rep);
return offset;
}
FixedVector<B3::ValueRep> OSRExitValueReps::decode(unsigned offset) const
{
std::span<const uint8_t> bytes = m_bytes.span();
size_t position = offset;
unsigned count = WTF::LEBDecoder::decodeUInt32OrCrash(bytes, position);
return FixedVector<B3::ValueRep>::createWithSizeFromGenerator(count, [&](size_t) {
return decodeValueRep(bytes, position);
});
}
void OSRExitValues::encode(const Operands<ExitValue>& values, const Bag<ExitTimeObjectMaterialization>& materializationBag, JITCode& jitCode)
{
m_numberOfArguments = values.numberOfArguments();
m_numberOfLocals = values.numberOfLocals();
m_numberOfTmps = values.numberOfTmps();
Vector<ExitTimeObjectMaterialization*, 4> materializations = materializationTable(materializationBag);
Vector<EncodedJSValue>& constants = jitCode.osrExitConstants;
Vector<uint8_t, 128> bytes;
for (unsigned index = 0; index < values.size(); ++index) {
const ExitValue& value = values[index];
switch (value.kind()) {
case ExitValueDead: {
unsigned runLength = 1;
while (runLength < maxDeadRunLength && index + runLength < values.size() && values[index + runLength].isDead())
++runLength;
bytes.append(static_cast<uint8_t>(runLength - 1));
index += runLength - 1;
break;
}
case ExitValueInJSStack:
case ExitValueInJSStackAsInt32:
case ExitValueInJSStackAsInt52:
case ExitValueInJSStackAsDouble: {
Operand operand = values.operandForIndex(index);
VirtualRegister reg = value.virtualRegister();
if (!operand.isTmp() && operand.virtualRegister() == reg) {
bytes.append(inJSStackAtOwnRegisterTag + (value.kind() - ExitValueInJSStack));
break;
}
bytes.append(inJSStackTag + (value.kind() - ExitValueInJSStack));
WTF::LEBEncoder::encodeInt32(bytes, reg.offset());
break;
}
case ExitValueConstant: {
bytes.append(constantTag);
EncodedJSValue constant = JSValue::encode(value.constant());
size_t constantIndex = constants.find(constant);
if (constantIndex == notFound) {
constantIndex = constants.size();
constants.append(constant);
}
WTF::LEBEncoder::encodeUInt32(bytes, constantIndex);
break;
}
case ExitValueMaterializeNewObject: {
bytes.append(materializationTag);
size_t materializationIndex = materializations.find(value.objectMaterialization());
ASSERT(materializationIndex != notFound);
WTF::LEBEncoder::encodeUInt32(bytes, materializationIndex);
break;
}
case ExitValueArgument:
bytes.append(argumentTag);
bytes.append(static_cast<uint8_t>(value.exitArgument().format()));
WTF::LEBEncoder::encodeUInt32(bytes, value.exitArgument().argument());
break;
case InvalidExitValue:
RELEASE_ASSERT_NOT_REACHED();
break;
}
}
m_bytes = FixedVector<uint8_t>(bytes);
}
FixedOperands<ExitValue> OSRExitValues::decode(const JITCode& jitCode, const Bag<ExitTimeObjectMaterialization>& materializationBag) const
{
FixedOperands<ExitValue> values(m_numberOfArguments, m_numberOfLocals, m_numberOfTmps, ExitValue::dead());
int localsOffset = jitCode.osrExitLocalsOffset();
Vector<ExitTimeObjectMaterialization*, 4> materializations = materializationTable(materializationBag);
std::span<const uint8_t> bytes = m_bytes.span();
size_t offset = 0;
for (unsigned index = 0; index < values.size(); ++index) {
uint8_t tag = bytes[offset++];
if (tag < maxDeadRunLength) {
index += tag;
continue;
}
if (tag < constantTag) {
bool atOwnRegister = tag < inJSStackTag;
VirtualRegister reg;
if (atOwnRegister)
reg = values.operandForIndex(index).virtualRegister();
else
reg = VirtualRegister(WTF::LEBDecoder::decodeInt32OrCrash(bytes, offset));
values[index] = ExitValue::inJSStack(inJSStackKind(tag, atOwnRegister ? inJSStackAtOwnRegisterTag : inJSStackTag), reg).withLocalsOffset(localsOffset);
continue;
}
switch (tag) {
case constantTag:
values[index] = ExitValue::constant(JSValue::decode(jitCode.osrExitConstants[WTF::LEBDecoder::decodeUInt32OrCrash(bytes, offset)]));
break;
case materializationTag:
values[index] = ExitValue::materializeNewObject(materializations[WTF::LEBDecoder::decodeUInt32OrCrash(bytes, offset)]);
break;
case argumentTag: {
DataFormat format = static_cast<DataFormat>(bytes[offset++]);
values[index] = ExitValue::exitArgument(ExitArgument(format, WTF::LEBDecoder::decodeUInt32OrCrash(bytes, offset)));
break;
}
default:
RELEASE_ASSERT_NOT_REACHED();
}
}
RELEASE_ASSERT(offset == bytes.size());
return values;
}
OSRExitDescriptor::OSRExitDescriptor(DataFormat profileDataFormat, MethodOfGettingAValueProfile valueProfile)
: m_profileDataFormat(profileDataFormat)
, m_valueProfile(valueProfile)
{
}
void OSRExitDescriptor::validateReferences(const TrackedReferences& trackedReferences)
{
for (ExitTimeObjectMaterialization* materialization : m_materializations)
materialization->validateReferences(trackedReferences);
}
Ref<OSRExitHandle> OSRExitDescriptor::emitOSRExit(
State& state, ExitKind exitKind, const NodeOrigin& nodeOrigin, CCallHelpers& jit,
const StackmapGenerationParams& params, uint32_t dfgNodeIndex, unsigned offset)
{
Ref<OSRExitHandle> handle =
prepareOSRExitHandle(state, exitKind, nodeOrigin, params, dfgNodeIndex, offset);
handle->emitExitThunk(state, jit);
return handle;
}
Ref<OSRExitHandle> OSRExitDescriptor::emitOSRExitLater(
State& state, ExitKind exitKind, const NodeOrigin& nodeOrigin,
const StackmapGenerationParams& params, uint32_t dfgNodeIndex, unsigned offset)
{
RefPtr<OSRExitHandle> handle =
prepareOSRExitHandle(state, exitKind, nodeOrigin, params, dfgNodeIndex, offset);
params.addLatePath(
[handle, &state] (CCallHelpers& jit) {
handle->emitExitThunk(state, jit);
});
return handle.releaseNonNull();
}
Ref<OSRExitHandle> OSRExitDescriptor::prepareOSRExitHandle(
State& state, ExitKind exitKind, const NodeOrigin& nodeOrigin,
const StackmapGenerationParams& params, uint32_t dfgNodeIndex, unsigned offset)
{
unsigned valueRepsOffset = state.jitCode->osrExitValueReps.append(params.reps().subspan(offset));
OSRExit exit(this, exitKind, nodeOrigin.forExit, nodeOrigin.semantic, nodeOrigin.wasHoisted, dfgNodeIndex, valueRepsOffset);
if (exitKind == WillThrowOutOfMemoryError)
exit.m_exitCallSiteIndex = callSiteIndexForCodeOrigin(state, nodeOrigin.semantic);
unsigned index = state.jitCode->m_osrExit.size();
state.jitCode->m_osrExit.append(WTF::move(exit));
return adoptRef(*new OSRExitHandle(index, state.jitCode.get()));
}
OSRExit::OSRExit(
OSRExitDescriptor* descriptor, ExitKind exitKind, CodeOrigin codeOrigin,
CodeOrigin codeOriginForExitProfile, bool wasHoisted, uint32_t dfgNodeIndex, unsigned valueRepsOffset)
: OSRExitBase(exitKind, codeOrigin, codeOriginForExitProfile, wasHoisted, dfgNodeIndex)
, m_valueRepsOffset(valueRepsOffset)
, m_descriptor(descriptor)
{
}
FixedVector<B3::ValueRep> OSRExit::valueReps(const JITCode& jitCode) const
{
return jitCode.osrExitValueReps.decode(m_valueRepsOffset);
}
CodeLocationJump<JSInternalPtrTag> OSRExit::codeLocationForRepatch(CodeBlock* ftlCodeBlock) const
{
UNUSED_PARAM(ftlCodeBlock);
return m_patchableJump;
}
} } // namespace JSC::FTL
#endif // ENABLE(FTL_JIT)