blob: dd6a40afe0dea8145491a3a498ea06a91cee8423 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QBuffer>
#include <QFile>
#include "qv4engine_p.h"
#include "qv4baselineassembler_p.h"
#include "qv4assemblercommon_p.h"
#include <private/qv4function_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4stackframe_p.h>
#include <wtf/Vector.h>
#include <assembler/MacroAssembler.h>
#include <assembler/MacroAssemblerCodeRef.h>
#include <assembler/LinkBuffer.h>
#include <WTFStubs.h>
#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
#define ASM_GENERATE_RUNTIME_CALL(function, destination) \
pasm()->GENERATE_RUNTIME_CALL(function, destination)
#define callHelper(x) \
PlatformAssemblerCommon::callRuntimeUnchecked(reinterpret_cast<void *>(&x), #x)
const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer;
static ReturnedValue toNumberHelper(ReturnedValue v)
{
return Encode(Value::fromReturnedValue(v).toNumber());
}
static ReturnedValue toInt32Helper(ReturnedValue v)
{
return Encode(Value::fromReturnedValue(v).toInt32());
}
#if QT_POINTER_SIZE == 8 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
class PlatformAssembler64 : public PlatformAssemblerCommon
{
public:
PlatformAssembler64(const Value *constantTable)
: PlatformAssemblerCommon(constantTable)
{}
void callRuntime(const void *funcPtr, CallResultDestination dest)
{
PlatformAssemblerCommon::callRuntime(funcPtr);
if (dest == CallResultDestination::InAccumulator)
saveReturnValueInAccumulator();
else if (AccumulatorRegister == ReturnValueRegister)
loadUndefined();
}
void saveReturnValueInAccumulator()
{
move(ReturnValueRegister, AccumulatorRegister);
}
void loadUndefined(RegisterID dest = AccumulatorRegister)
{
move(TrustedImm64(0), dest);
}
void copyConst(int constIndex, Address dest)
{
//###
if (constant(constIndex).isUndefined()) {
loadUndefined(ScratchRegister);
} else {
load64(loadConstAddress(constIndex, ScratchRegister), ScratchRegister);
}
store64(ScratchRegister, dest);
}
void copyReg(Address src, Address dst)
{
load64(src, ScratchRegister);
store64(ScratchRegister, dst);
}
void loadPointerFromValue(Address addr, RegisterID dest = AccumulatorRegister)
{
load64(addr, dest);
}
void loadAccumulator(Address addr)
{
load64(addr, AccumulatorRegister);
}
void storeAccumulator(Address addr)
{
store64(AccumulatorRegister, addr);
}
void moveReg(Address sourceRegAddress, Address destRegAddress)
{
load64(sourceRegAddress, ScratchRegister);
store64(ScratchRegister, destRegAddress);
}
void loadString(int stringId)
{
loadAccumulator(loadStringAddress(stringId));
}
void loadValue(ReturnedValue value)
{
move(TrustedImm64(value), AccumulatorRegister);
}
void storeHeapObject(RegisterID source, Address addr)
{
store64(source, addr);
}
void generateCatchTrampoline()
{
PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
}
void jumpNotUndefined(int offset)
{
auto jump = branch64(NotEqual, AccumulatorRegister, TrustedImm64(0));
addJumpToOffset(jump, offset);
}
Jump jumpEmpty()
{
return branch64(Equal, AccumulatorRegister, TrustedImm64(Value::emptyValue().asReturnedValue()));
}
Jump jumpNotEmpty()
{
return branch64(NotEqual, AccumulatorRegister, TrustedImm64(Value::emptyValue().asReturnedValue()));
}
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
auto needsConversion = branch32(NotEqual, TrustedImm32(1), ScratchRegister);
continuation(AccumulatorRegister);
Jump done = jump();
// slow path:
needsConversion.link(this);
push(AccumulatorRegister);
move(AccumulatorRegister, registerForArg(0));
callHelper(Value::toBooleanImpl);
and32(TrustedImm32(1), ReturnValueRegister, ScratchRegister);
pop(AccumulatorRegister);
continuation(ScratchRegister);
done.link(this);
}
void toNumber()
{
urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister);
auto isNumber = branch32(GreaterThanOrEqual, ScratchRegister, TrustedImm32(Value::QT_Int));
move(AccumulatorRegister, registerForArg(0));
callHelper(toNumberHelper);
saveReturnValueInAccumulator();
isNumber.link(this);
}
// this converts both the lhs and the accumulator to int32
void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
{
load64(lhs, lhsTarget);
urshift64(lhsTarget, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
auto lhsIsInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
const Address accumulatorStackAddress(JSStackFrameRegister,
offsetof(CallData, accumulator));
storeAccumulator(accumulatorStackAddress);
move(lhsTarget, registerForArg(0));
callHelper(toInt32Helper);
move(ReturnValueRegister, lhsTarget);
loadAccumulator(accumulatorStackAddress);
lhsIsInt.link(this);
urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
pushAligned(lhsTarget);
move(AccumulatorRegister, registerForArg(0));
callHelper(toInt32Helper);
saveReturnValueInAccumulator();
popAligned(lhsTarget);
isInt.link(this);
}
void toInt32()
{
urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
move(AccumulatorRegister, registerForArg(0));
callHelper(toInt32Helper);
saveReturnValueInAccumulator();
isInt.link(this);
}
void regToInt32(Address srcReg, RegisterID targetReg)
{
load64(srcReg, targetReg);
urshift64(targetReg, TrustedImm32(Value::QuickType_Shift), ScratchRegister2);
auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
pushAligned(AccumulatorRegister);
move(targetReg, registerForArg(0));
callHelper(toInt32Helper);
move(ReturnValueRegister, targetReg);
popAligned(AccumulatorRegister);
isInt.link(this);
}
void isNullOrUndefined()
{
move(AccumulatorRegister, ScratchRegister);
compare64(Equal, ScratchRegister, TrustedImm32(0), AccumulatorRegister);
Jump isUndef = branch32(NotEqual, TrustedImm32(0), AccumulatorRegister);
// not undefined
rshift64(TrustedImm32(32), ScratchRegister);
compare32(Equal, ScratchRegister, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)),
AccumulatorRegister);
isUndef.link(this);
}
Jump isIntOrBool()
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerOrBool_Shift), ScratchRegister);
return branch32(Equal, TrustedImm32(3), ScratchRegister);
}
void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
{
Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
load64(lhsAddr, ScratchRegister);
Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister);
addJumpToOffset(equal, offset);
isUndef.link(this);
}
void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
{
Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
load64(lhsAddr, ScratchRegister);
Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
addJumpToOffset(isUndef, offset);
Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
addJumpToOffset(notEqual, offset);
}
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
{
if (sourceReg == NoRegister)
or64(TrustedImm64(int64_t(tag) << 32), AccumulatorRegister);
else
or64(TrustedImm64(int64_t(tag) << 32), sourceReg, AccumulatorRegister);
}
void encodeDoubleIntoAccumulator(FPRegisterID src)
{
moveDoubleTo64(src, AccumulatorRegister);
move(TrustedImm64(Value::NaNEncodeMask), ScratchRegister);
xor64(ScratchRegister, AccumulatorRegister);
}
void pushValueAligned(ReturnedValue v)
{
loadValue(v);
pushAligned(AccumulatorRegister);
}
void popValueAligned()
{
addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
}
Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath)
{
urshift64(AccumulatorRegister, TrustedImm32(32), ScratchRegister);
Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister);
load64(lhsAddr, ScratchRegister);
urshift64(ScratchRegister, TrustedImm32(32), ScratchRegister2);
Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister2);
// both integer
Jump failure = fastPath();
Jump done = jump();
// all other cases
if (failure.isSet())
failure.link(this);
accNotInt.link(this);
lhsNotInt.link(this);
return done;
}
Jump unopIntPath(std::function<Jump(void)> fastPath)
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
Jump accNotIntConvertible = branch32(NotEqual, TrustedImm32(1), ScratchRegister);
// both integer
Jump failure = fastPath();
Jump done = jump();
// all other cases
if (failure.isSet())
failure.link(this);
accNotIntConvertible.link(this);
return done;
}
void callWithAccumulatorByValueAsFirstArgument(std::function<void()> doCall)
{
passAsArg(AccumulatorRegister, 0);
doCall();
}
};
typedef PlatformAssembler64 PlatformAssembler;
#endif
#if QT_POINTER_SIZE == 4 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
class PlatformAssembler32 : public PlatformAssemblerCommon
{
public:
PlatformAssembler32(const Value *constantTable)
: PlatformAssemblerCommon(constantTable)
{}
void callRuntime(const void *funcPtr, CallResultDestination dest)
{
PlatformAssemblerCommon::callRuntime(funcPtr);
if (dest == CallResultDestination::InAccumulator)
saveReturnValueInAccumulator();
else if (AccumulatorRegisterValue == ReturnValueRegisterValue)
loadUndefined();
}
void saveReturnValueInAccumulator()
{
move(ReturnValueRegisterValue, AccumulatorRegisterValue);
move(ReturnValueRegisterTag, AccumulatorRegisterTag);
}
void loadUndefined()
{
move(TrustedImm32(0), AccumulatorRegisterValue);
move(TrustedImm32(0), AccumulatorRegisterTag);
}
void copyConst(int constIndex, Address destRegAddr)
{
//###
if (constant(constIndex).isUndefined()) {
move(TrustedImm32(0), ScratchRegister);
store32(ScratchRegister, destRegAddr);
destRegAddr.offset += 4;
store32(ScratchRegister, destRegAddr);
} else {
Address src = loadConstAddress(constIndex);
loadDouble(src, FPScratchRegister);
storeDouble(FPScratchRegister, destRegAddr);
}
}
void copyReg(Address src, Address dest)
{
loadDouble(src, FPScratchRegister);
storeDouble(FPScratchRegister, dest);
}
void loadPointerFromValue(Address addr, RegisterID dest = AccumulatorRegisterValue)
{
load32(addr, dest);
}
void loadAccumulator(Address src)
{
load32(src, AccumulatorRegisterValue);
src.offset += 4;
load32(src, AccumulatorRegisterTag);
}
void storeAccumulator(Address addr)
{
store32(AccumulatorRegisterValue, addr);
addr.offset += 4;
store32(AccumulatorRegisterTag, addr);
}
void moveReg(Address sourceRegAddress, Address destRegAddress)
{
load32(sourceRegAddress, ScratchRegister);
store32(ScratchRegister, destRegAddress);
sourceRegAddress.offset += 4;
destRegAddress.offset += 4;
load32(sourceRegAddress, ScratchRegister);
store32(ScratchRegister, destRegAddress);
}
void loadString(int stringId)
{
load32(loadStringAddress(stringId), AccumulatorRegisterValue);
move(TrustedImm32(0), AccumulatorRegisterTag);
}
void loadValue(ReturnedValue value)
{
move(TrustedImm32(Value::fromReturnedValue(value).value()), AccumulatorRegisterValue);
move(TrustedImm32(Value::fromReturnedValue(value).tag()), AccumulatorRegisterTag);
}
void storeHeapObject(RegisterID source, Address addr)
{
store32(source, addr);
addr.offset += 4;
store32(TrustedImm32(0), addr);
}
void generateCatchTrampoline()
{
PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
}
void toNumber()
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::QuickType_Shift - 32), ScratchRegister);
auto isNumber = branch32(GreaterThanOrEqual, ScratchRegister, TrustedImm32(Value::QT_Int));
if (ArgInRegCount < 2) {
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); // stack alignment
push(AccumulatorRegisterTag);
push(AccumulatorRegisterValue);
} else {
move(AccumulatorRegisterValue, registerForArg(0));
move(AccumulatorRegisterTag, registerForArg(1));
}
callHelper(toNumberHelper);
saveReturnValueInAccumulator();
if (ArgInRegCount < 2)
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
isNumber.link(this);
}
// this converts both the lhs and the accumulator to int32
void toInt32LhsAcc(Address lhs, RegisterID lhsTarget)
{
bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
|| AccumulatorRegisterTag == ReturnValueRegisterTag;
lhs.offset += 4;
load32(lhs, lhsTarget);
lhs.offset -= 4;
auto lhsIsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), lhsTarget);
load32(lhs, lhsTarget);
auto lhsIsInt = jump();
lhsIsNotInt.link(this);
// Save accumulator from being garbage collected, no matter if we will reuse the register.
const Address accumulatorStackAddress(JSStackFrameRegister,
offsetof(CallData, accumulator));
storeAccumulator(accumulatorStackAddress);
if (ArgInRegCount < 2) {
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
push(lhsTarget);
load32(lhs, lhsTarget);
push(lhsTarget);
} else {
move(lhsTarget, registerForArg(1));
load32(lhs, registerForArg(0));
}
callHelper(toInt32Helper);
move(ReturnValueRegisterValue, lhsTarget);
if (ArgInRegCount < 2)
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
if (accumulatorNeedsSaving) // otherwise it's still the same
loadAccumulator(accumulatorStackAddress);
lhsIsInt.link(this);
auto rhsIsInt = branch32(Equal, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag);
pushAligned(lhsTarget);
if (ArgInRegCount < 2) {
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
push(AccumulatorRegisterTag);
push(AccumulatorRegisterValue);
} else {
move(AccumulatorRegisterValue, registerForArg(0));
move(AccumulatorRegisterTag, registerForArg(1));
}
callHelper(toInt32Helper);
saveReturnValueInAccumulator();
if (ArgInRegCount < 2)
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
popAligned(lhsTarget);
rhsIsInt.link(this);
}
void toInt32()
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::QuickType_Shift - 32), ScratchRegister);
auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister);
if (ArgInRegCount < 2) {
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); // align the stack on a 16-byte boundary
push(AccumulatorRegisterTag);
push(AccumulatorRegisterValue);
} else {
move(AccumulatorRegisterValue, registerForArg(0));
move(AccumulatorRegisterTag, registerForArg(1));
}
callHelper(toInt32Helper);
saveReturnValueInAccumulator();
if (ArgInRegCount < 2)
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
isInt.link(this);
}
void regToInt32(Address srcReg, RegisterID targetReg)
{
bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
|| AccumulatorRegisterTag == ReturnValueRegisterTag;
if (accumulatorNeedsSaving) {
push(AccumulatorRegisterTag);
push(AccumulatorRegisterValue);
}
if (ArgInRegCount < 2) {
if (!accumulatorNeedsSaving)
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
srcReg.offset += 4;
load32(srcReg, targetReg);
push(targetReg);
srcReg.offset -= 4;
load32(srcReg, targetReg);
push(targetReg);
} else {
if (accumulatorNeedsSaving)
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
load32(srcReg, registerForArg(0));
srcReg.offset += 4;
load32(srcReg, registerForArg(1));
}
callHelper(toInt32Helper);
move(ReturnValueRegisterValue, targetReg);
if (accumulatorNeedsSaving) {
addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
pop(AccumulatorRegisterValue);
pop(AccumulatorRegisterTag);
} else if (ArgInRegCount < 2) {
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
}
}
void isNullOrUndefined()
{
Jump notUndefOrPtr = branch32(NotEqual, TrustedImm32(0), AccumulatorRegisterTag);
compare32(Equal, AccumulatorRegisterValue, TrustedImm32(0), AccumulatorRegisterValue);
auto done = jump();
// not undefined or managed
notUndefOrPtr.link(this);
compare32(Equal, AccumulatorRegisterTag, TrustedImm32(int(QV4::Value::ValueTypeInternal::Null)),
AccumulatorRegisterValue);
done.link(this);
}
Jump isIntOrBool()
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerOrBool_Shift - 32), ScratchRegister);
return branch32(Equal, TrustedImm32(3), ScratchRegister);
}
void pushValue(ReturnedValue v)
{
push(TrustedImm32(v >> 32));
push(TrustedImm32(v));
}
void jumpNotUndefined(int offset)
{
move(AccumulatorRegisterTag, ScratchRegister);
or32(AccumulatorRegisterValue, ScratchRegister);
auto jump = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
addJumpToOffset(jump, offset);
}
Jump jumpEmpty()
{
return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Value::emptyValue().asReturnedValue() >> 32));
}
Jump jumpNotEmpty()
{
return branch32(NotEqual, AccumulatorRegisterTag, TrustedImm32(Value::emptyValue().asReturnedValue() >> 32));
}
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
ScratchRegister);
auto needsConversion = branch32(NotEqual, TrustedImm32(1), ScratchRegister);
continuation(AccumulatorRegisterValue);
Jump done = jump();
// slow path:
needsConversion.link(this);
bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
|| AccumulatorRegisterTag == ReturnValueRegisterTag;
if (accumulatorNeedsSaving) {
push(AccumulatorRegisterTag);
push(AccumulatorRegisterValue);
}
if (ArgInRegCount < 2) {
if (!accumulatorNeedsSaving)
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
push(AccumulatorRegisterTag);
push(AccumulatorRegisterValue);
} else {
if (accumulatorNeedsSaving)
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
move(AccumulatorRegisterValue, registerForArg(0));
move(AccumulatorRegisterTag, registerForArg(1));
}
callHelper(Value::toBooleanImpl);
and32(TrustedImm32(1), ReturnValueRegisterValue, ScratchRegister);
if (accumulatorNeedsSaving) {
addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
pop(AccumulatorRegisterValue);
pop(AccumulatorRegisterTag);
} else if (ArgInRegCount < 2) {
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
}
continuation(ScratchRegister);
done.link(this);
}
void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
{
Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
load32(lhsAddr, ScratchRegister);
Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs));
Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
addJumpToOffset(notEqUndefVal, offset);
lhsAddr.offset += 4;
load32(lhsAddr, ScratchRegister);
Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
addJumpToOffset(notEqUndefTag, offset);
notEqInt.link(this);
}
void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
{
Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
load32(lhsAddr, ScratchRegister);
Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
addJumpToOffset(notEqual, offset);
Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister);
lhsAddr.offset += 4;
load32(lhsAddr, ScratchRegister);
Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister);
addJumpToOffset(equalUndef, offset);
notUndefValue.link(this);
}
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
{
if (sourceReg != NoRegister)
move(sourceReg, AccumulatorRegisterValue);
move(TrustedImm32(int(tag)), AccumulatorRegisterTag);
}
void encodeDoubleIntoAccumulator(FPRegisterID src)
{
moveDoubleToInts(src, AccumulatorRegisterValue, AccumulatorRegisterTag);
xor32(TrustedImm32(Value::NaNEncodeMask >> 32), AccumulatorRegisterTag);
}
void pushValueAligned(ReturnedValue v)
{
pushValue(v);
}
void popValueAligned()
{
popValue();
}
Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath)
{
Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag);
Address lhsAddrTag = lhsAddr; lhsAddrTag.offset += Value::tagOffset();
load32(lhsAddrTag, ScratchRegister);
Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister);
// both integer
Address lhsAddrValue = lhsAddr; lhsAddrValue.offset += Value::valueOffset();
load32(lhsAddrValue, ScratchRegister);
Jump failure = fastPath();
Jump done = jump();
// all other cases
if (failure.isSet())
failure.link(this);
accNotInt.link(this);
lhsNotInt.link(this);
return done;
}
Jump unopIntPath(std::function<Jump(void)> fastPath)
{
Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag);
// both integer
Jump failure = fastPath();
Jump done = jump();
// all other cases
if (failure.isSet())
failure.link(this);
accNotInt.link(this);
return done;
}
void callWithAccumulatorByValueAsFirstArgument(std::function<void()> doCall)
{
if (ArgInRegCount < 2) {
subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
push(AccumulatorRegisterTag);
push(AccumulatorRegisterValue);
} else {
move(AccumulatorRegisterValue, registerForArg(0));
move(AccumulatorRegisterTag, registerForArg(1));
}
doCall();
if (ArgInRegCount < 2)
addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
}
};
typedef PlatformAssembler32 PlatformAssembler;
#endif
#define pasm() reinterpret_cast<PlatformAssembler *>(this->d)
typedef PlatformAssembler::TrustedImmPtr TrustedImmPtr;
typedef PlatformAssembler::TrustedImm32 TrustedImm32;
typedef PlatformAssembler::TrustedImm64 TrustedImm64;
typedef PlatformAssembler::Address Address;
typedef PlatformAssembler::RegisterID RegisterID;
typedef PlatformAssembler::FPRegisterID FPRegisterID;
static Address regAddr(int reg)
{
return Address(PlatformAssembler::JSStackFrameRegister, reg * int(sizeof(QV4::Value)));
}
BaselineAssembler::BaselineAssembler(const Value *constantTable)
: d(new PlatformAssembler(constantTable))
{
}
BaselineAssembler::~BaselineAssembler()
{
delete pasm();
}
void BaselineAssembler::generatePrologue()
{
pasm()->generateFunctionEntry();
}
void BaselineAssembler::generateEpilogue()
{
pasm()->generateCatchTrampoline();
}
void BaselineAssembler::link(Function *function)
{
pasm()->link(function, "BaselineJIT");
}
void BaselineAssembler::addLabel(int offset)
{
pasm()->addLabelForOffset(offset);
}
void BaselineAssembler::loadConst(int constIndex)
{
//###
if (pasm()->constant(constIndex).isUndefined()) {
pasm()->loadUndefined();
} else {
pasm()->loadAccumulator(pasm()->loadConstAddress(constIndex));
}
}
void BaselineAssembler::copyConst(int constIndex, int destReg)
{
pasm()->copyConst(constIndex, regAddr(destReg));
}
void BaselineAssembler::loadReg(int reg)
{
pasm()->loadAccumulator(regAddr(reg));
}
void JIT::BaselineAssembler::moveReg(int sourceReg, int destReg)
{
pasm()->moveReg(regAddr(sourceReg), regAddr(destReg));
}
void BaselineAssembler::storeReg(int reg)
{
pasm()->storeAccumulator(regAddr(reg));
}
void BaselineAssembler::loadLocal(int index, int level)
{
Heap::CallContext ctx;
Q_UNUSED(ctx)
pasm()->loadPointerFromValue(regAddr(CallData::Context), PlatformAssembler::ScratchRegister);
while (level) {
pasm()->loadPtr(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), PlatformAssembler::ScratchRegister);
--level;
}
pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister, ctx.locals.offset + offsetof(ValueArray<0>, values) + sizeof(Value)*index));
}
void BaselineAssembler::storeLocal(int index, int level)
{
Heap::CallContext ctx;
Q_UNUSED(ctx)
pasm()->loadPtr(regAddr(CallData::Context), PlatformAssembler::ScratchRegister);
while (level) {
pasm()->loadPtr(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), PlatformAssembler::ScratchRegister);
--level;
}
pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister, ctx.locals.offset + offsetof(ValueArray<0>, values) + sizeof(Value)*index));
}
void BaselineAssembler::loadString(int stringId)
{
pasm()->loadString(stringId);
}
void BaselineAssembler::loadValue(ReturnedValue value)
{
pasm()->loadValue(value);
}
void BaselineAssembler::storeHeapObject(int reg)
{
pasm()->storeHeapObject(PlatformAssembler::ReturnValueRegisterValue, regAddr(reg));
}
void BaselineAssembler::loadImport(int index)
{
Address addr = pasm()->loadCompilationUnitPtr(PlatformAssembler::ScratchRegister);
addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, imports);
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
addr.offset = index * int(sizeof(QV4::Value*));
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister));
}
void BaselineAssembler::toNumber()
{
pasm()->toNumber();
}
void BaselineAssembler::uminus()
{
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(1);
pasm()->passAccumulatorAsArg(0);
ASM_GENERATE_RUNTIME_CALL(UMinus, CallResultDestination::InAccumulator);
checkException();
}
void BaselineAssembler::ucompl()
{
pasm()->toInt32();
pasm()->xor32(TrustedImm32(-1), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
static ReturnedValue incHelper(const Value v)
{
double d;
if (Q_LIKELY(v.isDouble()))
d = v.doubleValue();
else
d = v.toNumberImpl();
return Encode(d + 1.);
}
void BaselineAssembler::inc()
{
auto done = pasm()->unopIntPath([this](){
auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
PlatformAssembler::AccumulatorRegisterValue,
TrustedImm32(1),
PlatformAssembler::ScratchRegister);
pasm()->setAccumulatorTag(IntegerTag, PlatformAssembler::ScratchRegister);
return overflowed;
});
// slow path:
pasm()->callWithAccumulatorByValueAsFirstArgument([this]() {
pasm()->callHelper(incHelper);
pasm()->saveReturnValueInAccumulator();
});
checkException();
// done.
done.link(pasm());
}
static ReturnedValue decHelper(const Value v)
{
double d;
if (Q_LIKELY(v.isDouble()))
d = v.doubleValue();
else
d = v.toNumberImpl();
return Encode(d - 1.);
}
void BaselineAssembler::dec()
{
auto done = pasm()->unopIntPath([this](){
auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
PlatformAssembler::AccumulatorRegisterValue,
TrustedImm32(1),
PlatformAssembler::ScratchRegister);
pasm()->setAccumulatorTag(IntegerTag, PlatformAssembler::ScratchRegister);
return overflowed;
});
// slow path:
pasm()->callWithAccumulatorByValueAsFirstArgument([this]() {
pasm()->callHelper(decHelper);
pasm()->saveReturnValueInAccumulator();
});
checkException();
// done.
done.link(pasm());
}
void BaselineAssembler::unot()
{
pasm()->toBoolean([this](PlatformAssembler::RegisterID resultReg){
pasm()->compare32(PlatformAssembler::Equal, resultReg,
TrustedImm32(0), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
});
}
void BaselineAssembler::add(int lhs)
{
auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
PlatformAssembler::AccumulatorRegisterValue,
PlatformAssembler::ScratchRegister);
pasm()->setAccumulatorTag(IntegerTag,
PlatformAssembler::ScratchRegister);
return overflowed;
});
// slow path:
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(3);
pasm()->passAccumulatorAsArg(2);
pasm()->passJSSlotAsArg(lhs, 1);
pasm()->passEngineAsArg(0);
ASM_GENERATE_RUNTIME_CALL(Add, CallResultDestination::InAccumulator);
checkException();
// done.
done.link(pasm());
}
void BaselineAssembler::bitAnd(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
pasm()->and32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::bitOr(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
pasm()->or32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::bitXor(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
pasm()->xor32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::ushr(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue);
pasm()->urshift32(PlatformAssembler::AccumulatorRegisterValue, PlatformAssembler::ScratchRegister);
pasm()->move(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan,
PlatformAssembler::AccumulatorRegisterValue,
TrustedImm32(0));
pasm()->setAccumulatorTag(IntegerTag);
auto done = pasm()->jump();
doubleEncode.link(pasm());
pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue,
PlatformAssembler::FPScratchRegister,
PlatformAssembler::ScratchRegister);
pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister);
done.link(pasm());
}
void BaselineAssembler::shr(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue);
pasm()->rshift32(PlatformAssembler::AccumulatorRegisterValue, PlatformAssembler::ScratchRegister);
pasm()->move(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::shl(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue);
pasm()->lshift32(PlatformAssembler::AccumulatorRegisterValue, PlatformAssembler::ScratchRegister);
pasm()->move(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::bitAndConst(int rhs)
{
pasm()->toInt32();
pasm()->and32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::bitOrConst(int rhs)
{
pasm()->toInt32();
pasm()->or32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::bitXorConst(int rhs)
{
pasm()->toInt32();
pasm()->xor32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::ushrConst(int rhs)
{
rhs &= 0x1f;
pasm()->toInt32();
if (rhs) {
// a non zero shift will always give a number encodable as an int
pasm()->urshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
} else {
// shift with 0 can lead to a negative result
auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan,
PlatformAssembler::AccumulatorRegisterValue,
TrustedImm32(0));
pasm()->setAccumulatorTag(IntegerTag);
auto done = pasm()->jump();
doubleEncode.link(pasm());
pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue,
PlatformAssembler::FPScratchRegister,
PlatformAssembler::ScratchRegister);
pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister);
done.link(pasm());
}
}
void BaselineAssembler::shrConst(int rhs)
{
rhs &= 0x1f;
pasm()->toInt32();
if (rhs)
pasm()->rshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::shlConst(int rhs)
{
rhs &= 0x1f;
pasm()->toInt32();
if (rhs)
pasm()->lshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
void BaselineAssembler::mul(int lhs)
{
auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
auto overflowed = pasm()->branchMul32(PlatformAssembler::Overflow,
PlatformAssembler::AccumulatorRegisterValue,
PlatformAssembler::ScratchRegister);
pasm()->setAccumulatorTag(IntegerTag,
PlatformAssembler::ScratchRegister);
return overflowed;
});
// slow path:
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
ASM_GENERATE_RUNTIME_CALL(Mul, CallResultDestination::InAccumulator);
checkException();
// done.
done.link(pasm());
}
void BaselineAssembler::div(int lhs)
{
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
ASM_GENERATE_RUNTIME_CALL(Div, CallResultDestination::InAccumulator);
checkException();
}
void BaselineAssembler::mod(int lhs)
{
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
ASM_GENERATE_RUNTIME_CALL(Mod, CallResultDestination::InAccumulator);
checkException();
}
void BaselineAssembler::sub(int lhs)
{
auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
PlatformAssembler::AccumulatorRegisterValue,
PlatformAssembler::ScratchRegister);
pasm()->setAccumulatorTag(IntegerTag,
PlatformAssembler::ScratchRegister);
return overflowed;
});
// slow path:
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
ASM_GENERATE_RUNTIME_CALL(Sub, CallResultDestination::InAccumulator);
checkException();
// done.
done.link(pasm());
}
void BaselineAssembler::cmpeqNull()
{
pasm()->isNullOrUndefined();
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
}
void BaselineAssembler::cmpneNull()
{
pasm()->isNullOrUndefined();
pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
}
void BaselineAssembler::cmpeqInt(int lhs)
{
auto isIntOrBool = pasm()->isIntOrBool();
saveAccumulatorInFrame();
pasm()->pushValueAligned(Encode(lhs));
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->push(PlatformAssembler::StackPointerRegister);
else
pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
pasm()->pushAccumulatorAsArg(0);
pasm()->callRuntimeUnchecked((void*)Runtime::Equal::call);
pasm()->saveReturnValueInAccumulator();
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
pasm()->popValueAligned();
auto done = pasm()->jump();
isIntOrBool.link(pasm());
pasm()->compare32(PlatformAssembler::Equal, PlatformAssembler::AccumulatorRegisterValue,
TrustedImm32(lhs),
PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
done.link(pasm());
}
void BaselineAssembler::cmpneInt(int lhs)
{
auto isIntOrBool = pasm()->isIntOrBool();
saveAccumulatorInFrame();
pasm()->pushValueAligned(Encode(lhs));
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->push(PlatformAssembler::StackPointerRegister);
else
pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
pasm()->pushAccumulatorAsArg(0);
pasm()->callRuntimeUnchecked((void*)Runtime::NotEqual::call);
pasm()->saveReturnValueInAccumulator();
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
pasm()->popValueAligned();
auto done = pasm()->jump();
isIntOrBool.link(pasm());
pasm()->compare32(PlatformAssembler::NotEqual, PlatformAssembler::AccumulatorRegisterValue,
TrustedImm32(lhs),
PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
done.link(pasm());
}
void BaselineAssembler::cmp(int cond, CmpFunc function, int lhs)
{
auto c = static_cast<PlatformAssembler::RelationalCondition>(cond);
auto done = pasm()->binopBothIntPath(regAddr(lhs), [this, c](){
pasm()->compare32(c, PlatformAssembler::ScratchRegister,
PlatformAssembler::AccumulatorRegisterValue,
PlatformAssembler::AccumulatorRegisterValue);
return PlatformAssembler::Jump();
});
// slow path:
saveAccumulatorInFrame();
pasm()->prepareCallWithArgCount(2);
pasm()->passAccumulatorAsArg(1);
pasm()->passJSSlotAsArg(lhs, 0);
callRuntime(reinterpret_cast<void*>(function), CallResultDestination::InAccumulator);
checkException();
// done.
done.link(pasm());
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
}
void BaselineAssembler::cmpeq(int lhs)
{
cmp(PlatformAssembler::Equal, &Runtime::CompareEqual::call, lhs);
}
void BaselineAssembler::cmpne(int lhs)
{
cmp(PlatformAssembler::NotEqual, &Runtime::CompareNotEqual::call, lhs);
}
void BaselineAssembler::cmpgt(int lhs)
{
cmp(PlatformAssembler::GreaterThan, &Runtime::CompareGreaterThan::call, lhs);
}
void BaselineAssembler::cmpge(int lhs)
{
cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::CompareGreaterEqual::call, lhs);
}
void BaselineAssembler::cmplt(int lhs)
{
cmp(PlatformAssembler::LessThan, &Runtime::CompareLessThan::call, lhs);
}
void BaselineAssembler::cmple(int lhs)
{
cmp(PlatformAssembler::LessThanOrEqual, &Runtime::CompareLessEqual::call, lhs);
}
void BaselineAssembler::cmpStrictEqual(int lhs)
{
cmp(PlatformAssembler::Equal, &Runtime::CompareStrictEqual::call, lhs);
}
void BaselineAssembler::cmpStrictNotEqual(int lhs)
{
cmp(PlatformAssembler::NotEqual, &Runtime::CompareStrictNotEqual::call, lhs);
}
int BaselineAssembler::jump(int offset)
{
pasm()->addJumpToOffset(pasm()->jump(), offset);
return offset;
}
int BaselineAssembler::jumpTrue(int offset)
{
pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
auto jump = pasm()->branch32(PlatformAssembler::NotEqual, TrustedImm32(0), resultReg);
pasm()->addJumpToOffset(jump, offset);
});
return offset;
}
int BaselineAssembler::jumpFalse(int offset)
{
pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
auto jump = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg);
pasm()->addJumpToOffset(jump, offset);
});
return offset;
}
int BaselineAssembler::jumpNoException(int offset)
{
auto jump = pasm()->branch32(
PlatformAssembler::Equal,
PlatformAssembler::Address(PlatformAssembler::EngineRegister,
offsetof(EngineBase, hasException)),
TrustedImm32(0));
pasm()->addJumpToOffset(jump, offset);
return offset;
}
int BaselineAssembler::jumpNotUndefined(int offset)
{
pasm()->jumpNotUndefined(offset);
return offset;
}
void BaselineAssembler::prepareCallWithArgCount(int argc)
{
pasm()->prepareCallWithArgCount(argc);
}
void BaselineAssembler::storeInstructionPointer(int instructionOffset)
{
pasm()->storeInstructionPointer(instructionOffset);
}
void BaselineAssembler::passAccumulatorAsArg(int arg)
{
pasm()->passAccumulatorAsArg(arg);
}
void BaselineAssembler::passFunctionAsArg(int arg)
{
pasm()->passFunctionAsArg(arg);
}
void BaselineAssembler::passEngineAsArg(int arg)
{
pasm()->passEngineAsArg(arg);
}
void BaselineAssembler::passJSSlotAsArg(int reg, int arg)
{
pasm()->passJSSlotAsArg(reg, arg);
}
void BaselineAssembler::passCppFrameAsArg(int arg)
{
pasm()->passCppFrameAsArg(arg);
}
void BaselineAssembler::passInt32AsArg(int value, int arg)
{
pasm()->passInt32AsArg(value, arg);
}
void BaselineAssembler::passPointerAsArg(void *ptr, int arg)
{
pasm()->passPointerAsArg(ptr, arg);
}
void BaselineAssembler::callRuntime(const void *funcPtr, CallResultDestination dest)
{
pasm()->callRuntime(funcPtr, dest);
}
void BaselineAssembler::saveAccumulatorInFrame()
{
pasm()->storeAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
offsetof(CallData, accumulator)));
}
void BaselineAssembler::loadAccumulatorFromFrame()
{
pasm()->loadAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
offsetof(CallData, accumulator)));
}
static ReturnedValue TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing(CppStackFrame *frame, ExecutionEngine *engine)
{
return Runtime::TailCall::call(frame, engine);
}
void BaselineAssembler::jsTailCall(int func, int thisObject, int argc, int argv)
{
Address tos = pasm()->jsAlloca(4);
int32_t argcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argc;
int32_t argvOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argv;
int32_t thisOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_thisObject;
int32_t funcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_function;
pasm()->storeInt32AsValue(argc, Address(tos.base, argcOffset));
pasm()->storeInt32AsValue(argv, Address(tos.base, argvOffset));
pasm()->moveReg(regAddr(thisObject), Address(tos.base, thisOffset));
pasm()->moveReg(regAddr(func), Address(tos.base, funcOffset));
pasm()->tailCallRuntime(
reinterpret_cast<void *>(TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing),
"TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing");
}
void BaselineAssembler::checkException()
{
pasm()->checkException();
}
void BaselineAssembler::gotoCatchException()
{
pasm()->addCatchyJump(pasm()->jump());
}
void BaselineAssembler::getException()
{
Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
Address hasExceptionAddr(PlatformAssembler::EngineRegister,
offsetof(EngineBase, hasException));
PlatformAssembler::Jump nope = pasm()->branch8(PlatformAssembler::Equal,
hasExceptionAddr,
TrustedImm32(0));
pasm()->loadPtr(Address(PlatformAssembler::EngineRegister,
offsetof(EngineBase, exceptionValue)),
PlatformAssembler::ScratchRegister);
pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister));
pasm()->store8(TrustedImm32(0), hasExceptionAddr);
auto done = pasm()->jump();
nope.link(pasm());
pasm()->loadValue(Value::emptyValue().asReturnedValue());
done.link(pasm());
}
void BaselineAssembler::setException()
{
auto noException = pasm()->jumpEmpty();
Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue));
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister));
addr.offset = offsetof(EngineBase, hasException);
Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
pasm()->store8(TrustedImm32(1), addr);
noException.link(pasm());
}
int BaselineAssembler::setUnwindHandler(int offset)
{
auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
pasm()->addEHTarget(l, offset);
return offset;
}
void BaselineAssembler::clearUnwindHandler()
{
pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
}
void JIT::BaselineAssembler::unwindDispatch()
{
checkException();
pasm()->load32(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)), PlatformAssembler::ScratchRegister);
auto noUnwind = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
pasm()->sub32(TrustedImm32(1), PlatformAssembler::ScratchRegister);
pasm()->store32(PlatformAssembler::ScratchRegister, Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
auto jump = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
gotoCatchException();
jump.link(pasm());
pasm()->loadPtr(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)), PlatformAssembler::ScratchRegister);
pasm()->jump(PlatformAssembler::ScratchRegister);
noUnwind.link(pasm());
}
int JIT::BaselineAssembler::unwindToLabel(int level, int offset)
{
auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)));
pasm()->addEHTarget(l, offset);
pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
gotoCatchException();
return offset;
}
void BaselineAssembler::pushCatchContext(int index, int name)
{
pasm()->prepareCallWithArgCount(3);
pasm()->passInt32AsArg(name, 2);
pasm()->passInt32AsArg(index, 1);
pasm()->passEngineAsArg(0);
ASM_GENERATE_RUNTIME_CALL(PushCatchContext, CallResultDestination::Ignore);
}
void BaselineAssembler::popContext()
{
Heap::CallContext ctx;
Q_UNUSED(ctx)
pasm()->loadPointerFromValue(regAddr(CallData::Context), PlatformAssembler::ScratchRegister);
pasm()->loadPtr(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), PlatformAssembler::ScratchRegister);
pasm()->storeHeapObject(PlatformAssembler::ScratchRegister, regAddr(CallData::Context));
}
void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variableName)
{
auto valueIsAliveJump = pasm()->jumpNotEmpty();
storeInstructionPointer(offsetForSavedIP);
prepareCallWithArgCount(2);
passInt32AsArg(variableName, 1);
passEngineAsArg(0);
ASM_GENERATE_RUNTIME_CALL(ThrowReferenceError, CallResultDestination::Ignore);
gotoCatchException();
valueIsAliveJump.link(pasm());
}
void BaselineAssembler::ret()
{
pasm()->generateFunctionExit();
}
} // JIT namespace
} // QV4 namepsace
QT_END_NAMESPACE