aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jit/qv4assembler.cpp
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@qt.io>2017-11-14 10:45:06 +0100
committerLars Knoll <lars.knoll@qt.io>2017-11-17 11:53:42 +0000
commit3658f534cb1947663d29b9db00dcced462674aed (patch)
treea0c46c3c70c228426b4b774fb742b25a4854be9a /src/qml/jit/qv4assembler.cpp
parent75584bde397b1a7e92281855ea04e743fae7c1c5 (diff)
V4: Add a baseline JIT
This patch add a JIT back in for all platforms that supported JITting before, with the exception of MIPS. Change-Id: I51bc5ce3a2ac40e0510bd72a563af897c5b60343 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/qml/jit/qv4assembler.cpp')
-rw-r--r--src/qml/jit/qv4assembler.cpp1914
1 files changed, 1914 insertions, 0 deletions
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
new file mode 100644
index 0000000000..d2ef98f833
--- /dev/null
+++ b/src/qml/jit/qv4assembler.cpp
@@ -0,0 +1,1914 @@
+/****************************************************************************
+**
+** 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 "qv4engine_p.h"
+#include "qv4assembler_p.h"
+#include <private/qv4function_p.h>
+#include <private/qv4runtime_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
+
+#ifdef Q_STATIC_ASSERT_FOR_SANE_COMPILERS
+# undef Q_STATIC_ASSERT_FOR_SANE_COMPILERS
+#endif
+#if defined(Q_CC_MSVC) && _MSC_VER < 1900
+# define Q_STATIC_ASSERT_FOR_SANE_COMPILERS(x) // insane
+#else
+# define Q_STATIC_ASSERT_FOR_SANE_COMPILERS(x) Q_STATIC_ASSERT(x)
+#endif
+
+#ifdef V4_ENABLE_JIT
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace JIT {
+
+#define callHelper(x) PlatformAssemblerCommon::callRuntime(#x, reinterpret_cast<void *>(&x))
+
+static ReturnedValue toNumberHelper(ReturnedValue v)
+{
+ return Encode(Value::fromReturnedValue(v).toNumber());
+}
+
+static ReturnedValue toInt32Helper(ReturnedValue v)
+{
+ return Encode(Value::fromReturnedValue(v).toInt32());
+}
+
+#if defined(Q_PROCESSOR_X86_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN)
+
+struct PlatformAssembler_X86_64_SysV : JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
+{
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = RegisterID::r10;
+ static const RegisterID JSStackFrameRegister = RegisterID::r12;
+ static const RegisterID CppStackFrameRegister = RegisterID::r13;
+ static const RegisterID EngineRegister = RegisterID::r14;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+
+ static const RegisterID Arg0Reg = RegisterID::edi;
+ static const RegisterID Arg1Reg = RegisterID::esi;
+ static const RegisterID Arg2Reg = RegisterID::edx;
+ static const RegisterID Arg3Reg = RegisterID::ecx;
+ static const RegisterID Arg4Reg = RegisterID::r8;
+ static const RegisterID Arg5Reg = RegisterID::r9;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 6;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ push(RegisterID::ebp);
+ move(RegisterID::esp, RegisterID::ebp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
+ }
+
+ void generatePlatformFunctionExit()
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_X86_64_SysV PlatformAssemblerBase;
+
+#endif
+#if defined(Q_OS_WIN)
+
+struct PlatformAssembler_Win64 : JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
+{
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = RegisterID::r10;
+ static const RegisterID JSStackFrameRegister = RegisterID::r12;
+ static const RegisterID CppStackFrameRegister = RegisterID::r13;
+ static const RegisterID EngineRegister = RegisterID::r14;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+
+ static const RegisterID Arg0Reg = RegisterID::ecx;
+ static const RegisterID Arg1Reg = RegisterID::edx;
+ static const RegisterID Arg2Reg = RegisterID::r8;
+ static const RegisterID Arg3Reg = RegisterID::r9;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 4;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ push(RegisterID::ebp);
+ move(RegisterID::esp, RegisterID::ebp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
+ }
+
+ void generatePlatformFunctionExit()
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ subPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
+ call(ScratchRegister);
+ addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_Win64 PlatformAssemblerBase;
+
+#endif
+#endif
+
+#if (defined(Q_PROCESSOR_X86) && !defined(Q_PROCESSOR_X86_64)) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+
+struct PlatformAssembler_X86_All : JSC::MacroAssembler<JSC::MacroAssemblerX86>
+{
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegisterValue = RegisterID::eax;
+ static const RegisterID ReturnValueRegisterTag = RegisterID::edx;
+ static const RegisterID ScratchRegister = RegisterID::ecx;
+ static const RegisterID AccumulatorRegisterValue = ReturnValueRegisterValue;
+ static const RegisterID AccumulatorRegisterTag = ReturnValueRegisterTag;
+ static const RegisterID JSStackFrameRegister = RegisterID::ebx;
+ static const RegisterID CppStackFrameRegister = RegisterID::esi;
+ static const RegisterID EngineRegister = RegisterID::edi;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+
+ static const RegisterID Arg0Reg = NoRegister;
+ static const RegisterID Arg1Reg = NoRegister;
+ static const RegisterID Arg2Reg = NoRegister;
+ static const RegisterID Arg3Reg = NoRegister;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 0;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ push(RegisterID::ebp);
+ move(RegisterID::esp, RegisterID::ebp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegisterValue); push(AccumulatorRegisterValue); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ loadPtr(Address(FramePointerRegister, 2 * PointerSize), CppStackFrameRegister);
+ loadPtr(Address(FramePointerRegister, 3 * PointerSize), EngineRegister);
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
+ }
+
+ void generatePlatformFunctionExit()
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_X86_All PlatformAssemblerBase;
+
+#endif
+
+#if defined(Q_PROCESSOR_ARM_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+
+struct PlatformAssembler_ARM64 : JSC::MacroAssembler<JSC::MacroAssemblerARM64>
+{
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0;
+ static const RegisterID AccumulatorRegister = JSC::ARM64Registers::x9;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = JSC::ARM64Registers::x10;
+ static const RegisterID JSStackFrameRegister = JSC::ARM64Registers::x19;
+ static const RegisterID CppStackFrameRegister = JSC::ARM64Registers::x20;
+ static const RegisterID EngineRegister = JSC::ARM64Registers::x21;
+ static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp;
+ static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp;
+ static const FPRegisterID FPScratchRegister = JSC::ARM64Registers::q1;
+
+ static const RegisterID Arg0Reg = JSC::ARM64Registers::x0;
+ static const RegisterID Arg1Reg = JSC::ARM64Registers::x1;
+ static const RegisterID Arg2Reg = JSC::ARM64Registers::x2;
+ static const RegisterID Arg3Reg = JSC::ARM64Registers::x3;
+ static const RegisterID Arg4Reg = JSC::ARM64Registers::x4;
+ static const RegisterID Arg5Reg = JSC::ARM64Registers::x5;
+ static const RegisterID Arg6Reg = JSC::ARM64Registers::x6;
+ static const RegisterID Arg7Reg = JSC::ARM64Registers::x7;
+ static const int ArgInRegCount = 8;
+
+ void push(RegisterID src)
+ {
+ pushToSave(src);
+ }
+
+ void pop(RegisterID dest)
+ {
+ popToRestore(dest);
+ }
+
+ void pop()
+ {
+ add64(TrustedImm32(16), stackPointerRegister);
+ }
+
+ void popValue()
+ {
+ pop();
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ pushPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
+ move(RegisterID::sp, RegisterID::fp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); // exceptionHandler
+ pushPair(JSStackFrameRegister, AccumulatorRegister);
+ pushPair(EngineRegister, CppStackFrameRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ }
+
+ void generatePlatformFunctionExit()
+ {
+ move(AccumulatorRegister, ReturnValueRegister);
+ popPair(EngineRegister, CppStackFrameRegister);
+ popPair(JSStackFrameRegister, AccumulatorRegister);
+ popPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ pushToSave(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ popToRestore(reg);
+ }
+};
+
+typedef PlatformAssembler_ARM64 PlatformAssemblerBase;
+
+#endif
+
+#if defined(Q_PROCESSOR_ARM_32) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+
+struct PlatformAssembler_ARM32 : JSC::MacroAssembler<JSC::MacroAssemblerARMv7>
+{
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegisterValue = JSC::ARMRegisters::r0;
+ static const RegisterID ReturnValueRegisterTag = JSC::ARMRegisters::r1;
+ static const RegisterID ScratchRegister = JSC::ARMRegisters::r2;
+ static const RegisterID AccumulatorRegisterValue = JSC::ARMRegisters::r4;
+ static const RegisterID AccumulatorRegisterTag = JSC::ARMRegisters::r5;
+ // r6 is used by MacroAssemblerARMv7
+ static const RegisterID JSStackFrameRegister = JSC::ARMRegisters::r8;
+ static const RegisterID CppStackFrameRegister = JSC::ARMRegisters::r10;
+#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
+ static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7;
+ static const RegisterID EngineRegister = JSC::ARMRegisters::r11;
+#else // Thumbs down
+ static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11;
+ static const RegisterID EngineRegister = JSC::ARMRegisters::r7;
+#endif
+ static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13;
+ static const FPRegisterID FPScratchRegister = JSC::ARMRegisters::d1;
+
+ static const RegisterID Arg0Reg = JSC::ARMRegisters::r0;
+ static const RegisterID Arg1Reg = JSC::ARMRegisters::r1;
+ static const RegisterID Arg2Reg = JSC::ARMRegisters::r2;
+ static const RegisterID Arg3Reg = JSC::ARMRegisters::r3;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 4;
+
+ void popValue()
+ {
+ addPtr(TrustedImm32(sizeof(ReturnedValue)), StackPointerRegister);
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ push(JSC::ARMRegisters::lr);
+ push(FramePointerRegister);
+ move(StackPointerRegister, FramePointerRegister);
+ push(TrustedImm32(0)); // exceptionHandler
+ push(AccumulatorRegisterValue);
+ push(AccumulatorRegisterTag);
+ push(addressTempRegister);
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ subPtr(TrustedImm32(4), StackPointerRegister); // stack alignment
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ }
+
+ void generatePlatformFunctionExit()
+ {
+ move(AccumulatorRegisterValue, ReturnValueRegisterValue);
+ move(AccumulatorRegisterTag, ReturnValueRegisterTag);
+ addPtr(TrustedImm32(4), StackPointerRegister); // stack alignment
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(addressTempRegister);
+ pop(AccumulatorRegisterTag);
+ pop(AccumulatorRegisterValue);
+ pop(); // exceptionHandler
+ pop(FramePointerRegister);
+ pop(JSC::ARMRegisters::lr);
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), dataTempRegister);
+ call(dataTempRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_ARM32 PlatformAssemblerBase;
+
+#endif
+
+struct PlatformAssemblerCommon : PlatformAssemblerBase
+{
+ const Value* constantTable;
+ struct JumpTarget { JSC::MacroAssemblerBase::Jump jump; int offset; };
+ std::vector<JumpTarget> patches;
+ struct ExceptionHanlderTarget { JSC::MacroAssemblerBase::DataLabelPtr label; int offset; };
+ std::vector<ExceptionHanlderTarget> ehTargets;
+ QHash<int, JSC::MacroAssemblerBase::Label> labelsByOffset;
+ QHash<const void *, const char *> functions;
+ std::vector<Jump> catchyJumps;
+ Label functionExit;
+
+ Address exceptionHandlerAddress() const
+ {
+ return Address(FramePointerRegister, -1 * PointerSize);
+ }
+
+ Address contextAddress() const
+ {
+ return Address(JSStackFrameRegister, offsetof(CallData, context));
+ }
+
+ RegisterID registerForArg(int arg) const
+ {
+ Q_ASSERT(arg >= 0);
+ Q_ASSERT(arg < ArgInRegCount);
+ switch (arg) {
+ case 0: return Arg0Reg;
+ case 1: return Arg1Reg;
+ case 2: return Arg2Reg;
+ case 3: return Arg3Reg;
+ case 4: return Arg4Reg;
+ case 5: return Arg5Reg;
+ case 6: return Arg6Reg;
+ case 7: return Arg7Reg;
+ default:
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+ }
+ }
+
+ void callRuntime(const char *functionName, const void *funcPtr)
+ {
+ functions.insert(funcPtr, functionName);
+ callAbsolute(funcPtr);
+ }
+
+ Address loadFunctionPtr(RegisterID target)
+ {
+ Address addr(CppStackFrameRegister, offsetof(CppStackFrame, v4Function));
+ loadPtr(addr, target);
+ return Address(target);
+ }
+
+ Address loadCompilationUnitPtr(RegisterID target)
+ {
+ Address addr = loadFunctionPtr(target);
+ addr.offset = offsetof(QV4::Function, compilationUnit);
+ loadPtr(addr, target);
+ return Address(target);
+ }
+
+ Address loadConstAddress(int constIndex, RegisterID baseReg = ScratchRegister)
+ {
+ Address addr = loadCompilationUnitPtr(baseReg);
+ addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, constants);
+ loadPtr(addr, baseReg);
+ addr.offset = constIndex * int(sizeof(QV4::Value));
+ return addr;
+ }
+
+ Address loadStringAddress(int stringId)
+ {
+ Address addr = loadCompilationUnitPtr(ScratchRegister);
+ addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, runtimeStrings);
+ loadPtr(addr, ScratchRegister);
+ return Address(ScratchRegister, stringId * PointerSize);
+ }
+
+ void passAsArg(RegisterID src, int arg)
+ {
+ move(src, registerForArg(arg));
+ }
+
+ void generateCatchTrampoline(std::function<void()> loadUndefined)
+ {
+ for (Jump j : catchyJumps)
+ j.link(this);
+
+ loadPtr(exceptionHandlerAddress(), ScratchRegister);
+ Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0));
+ jump(ScratchRegister);
+ exitFunction.link(this);
+ loadUndefined();
+
+ if (functionExit.isSet())
+ jump(functionExit);
+ else
+ generateFunctionExit();
+ }
+
+ void addCatchyJump(Jump j)
+ {
+ Q_ASSERT(j.isSet());
+ catchyJumps.push_back(j);
+ }
+
+ void generateFunctionEntry()
+ {
+ generatePlatformFunctionEntry();
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
+ }
+
+ void generateFunctionExit()
+ {
+ if (functionExit.isSet()) {
+ jump(functionExit);
+ return;
+ }
+
+ functionExit = label();
+ generatePlatformFunctionExit();
+ }
+};
+
+#if QT_POINTER_SIZE == 8 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+struct PlatformAssembler64 : PlatformAssemblerCommon
+{
+ void callRuntime(const char *functionName, const void *funcPtr,
+ Assembler::CallResultDestination dest)
+ {
+ PlatformAssemblerCommon::callRuntime(functionName, funcPtr);
+ if (dest == Assembler::ResultInAccumulator)
+ move(ReturnValueRegister, AccumulatorRegister);
+ }
+
+ void loadUndefined(RegisterID dest = AccumulatorRegister)
+ {
+ move(TrustedImm64(0), dest);
+ }
+
+ void copyConst(int constIndex, Address dest)
+ {
+ //###
+ if (constantTable[constIndex].isUndefined()) {
+ loadUndefined(ScratchRegister);
+ } else {
+ load64(loadConstAddress(constIndex, ScratchRegister), ScratchRegister);
+ }
+ store64(ScratchRegister, dest);
+ }
+
+ void copyReg(Address src, Address dst)
+ {
+ loadReg(src, ScratchRegister);
+ store64(ScratchRegister, dst);
+ }
+
+ void loadReg(Address addr, RegisterID dest = AccumulatorRegister)
+ {
+ load64(addr, dest);
+ }
+
+ void loadAccumulator(Address addr)
+ {
+ load64(addr, AccumulatorRegister);
+ }
+
+ void storeAccumulator(Address addr)
+ {
+ store64(AccumulatorRegister, addr);
+ }
+
+ void loadString(int stringId)
+ {
+ loadAccumulator(loadStringAddress(stringId));
+ }
+
+ void loadValue(ReturnedValue value)
+ {
+ move(TrustedImm64(value), AccumulatorRegister);
+ }
+
+ void generateCatchTrampoline()
+ {
+ PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
+ }
+
+ 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()
+ {
+ move(AccumulatorRegister, registerForArg(0));
+ callHelper(toNumberHelper);
+ move(ReturnValueRegister, AccumulatorRegister);
+ }
+
+ void toInt32()
+ {
+ move(AccumulatorRegister, registerForArg(0));
+ callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper),
+ Assembler::ResultInAccumulator);
+ }
+
+ void regToInt32(Address srcReg, RegisterID targetReg)
+ {
+ pushAligned(AccumulatorRegister);
+ load64(srcReg, registerForArg(0));
+ callHelper(toInt32Helper);
+ move(ReturnValueRegister, targetReg);
+ popAligned(AccumulatorRegister);
+ }
+
+ 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);
+ }
+
+ 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);
+ patches.push_back({ 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));
+ patches.push_back({ isUndef, offset });
+ Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
+ patches.push_back({ notEqual, offset });
+ }
+
+ void setAccumulatorTag(QV4::Value::ValueTypeInternal tag)
+ {
+ or64(TrustedImm64(int64_t(tag) << 32), AccumulatorRegister);
+ }
+
+ void encodeDoubleIntoAccumulator(FPRegisterID src)
+ {
+ moveDoubleTo64(src, AccumulatorRegister);
+ move(TrustedImm64(Value::NaNEncodeMask), ScratchRegister);
+ xor64(ScratchRegister, AccumulatorRegister);
+ }
+
+ void pushValue(ReturnedValue v)
+ {
+ loadValue(v);
+ push(AccumulatorRegister);
+ }
+
+ void pushValueAligned(ReturnedValue v)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ pushValue(v);
+ }
+
+ void popValueAligned()
+ {
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler64 PlatformAssembler;
+#endif
+
+#if QT_POINTER_SIZE == 4 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+struct PlatformAssembler32 : PlatformAssemblerCommon
+{
+ void callRuntime(const char *functionName, const void *funcPtr,
+ Assembler::CallResultDestination dest)
+ {
+ PlatformAssemblerCommon::callRuntime(functionName, funcPtr);
+ if (dest == Assembler::ResultInAccumulator) {
+ move(ReturnValueRegisterValue, AccumulatorRegisterValue);
+ move(ReturnValueRegisterTag, AccumulatorRegisterTag);
+ }
+ }
+
+ void loadUndefined()
+ {
+ move(TrustedImm32(0), AccumulatorRegisterValue);
+ move(TrustedImm32(0), AccumulatorRegisterTag);
+ }
+
+ void copyConst(int constIndex, Address destRegAddr)
+ {
+ //###
+ if (constantTable[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 loadReg(Address addr)
+ {
+ load32(addr, AccumulatorRegisterValue);
+ addr.offset += 4;
+ load32(addr, AccumulatorRegisterTag);
+ }
+
+ 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 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 generateCatchTrampoline()
+ {
+ PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
+ }
+
+ void toNumber()
+ {
+ if (ArgInRegCount < 2) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ } else {
+ move(AccumulatorRegisterValue, registerForArg(0));
+ move(AccumulatorRegisterTag, registerForArg(1));
+ }
+ callRuntime("toNumberHelper", reinterpret_cast<void *>(&toNumberHelper),
+ Assembler::ResultInAccumulator);
+ move(ReturnValueRegisterValue, AccumulatorRegisterValue);
+ move(ReturnValueRegisterTag, AccumulatorRegisterTag);
+ if (ArgInRegCount < 2)
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ }
+
+ void toInt32()
+ {
+ if (ArgInRegCount < 2) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ } else {
+ move(AccumulatorRegisterValue, registerForArg(0));
+ move(AccumulatorRegisterTag, registerForArg(1));
+ }
+ callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper),
+ Assembler::ResultInAccumulator);
+ if (ArgInRegCount < 2)
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ }
+
+ void regToInt32(Address srcReg, RegisterID targetReg)
+ {
+ bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue
+ || AccumulatorRegisterTag == ReturnValueRegisterTag;
+ if (accumulatorNeedsSaving) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ }
+ if (ArgInRegCount < 2) {
+ srcReg.offset += 4;
+ load32(srcReg, targetReg);
+ push(targetReg);
+ srcReg.offset -= 4;
+ load32(srcReg, targetReg);
+ push(targetReg);
+ } else {
+ load32(srcReg, registerForArg(0));
+ srcReg.offset += 4;
+ load32(srcReg, registerForArg(1));
+ }
+ callHelper(toInt32Helper);
+ move(ReturnValueRegisterValue, targetReg);
+ if (ArgInRegCount < 2)
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ if (accumulatorNeedsSaving) {
+ pop(AccumulatorRegisterValue);
+ pop(AccumulatorRegisterTag);
+ }
+ }
+
+ 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);
+ }
+
+ void pushValue(ReturnedValue v)
+ {
+ push(TrustedImm32(v >> 32));
+ push(TrustedImm32(v));
+ }
+
+ 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) {
+ push(AccumulatorRegisterTag);
+ push(AccumulatorRegisterValue);
+ } else {
+ move(AccumulatorRegisterValue, registerForArg(0));
+ move(AccumulatorRegisterTag, registerForArg(1));
+ }
+ callHelper(Value::toBooleanImpl);
+ if (ArgInRegCount < 2)
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+
+ and32(TrustedImm32(1), ReturnValueRegisterValue, ScratchRegister);
+ if (accumulatorNeedsSaving) {
+ pop(AccumulatorRegisterValue);
+ pop(AccumulatorRegisterTag);
+ }
+ 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));
+ patches.push_back({ notEqUndefVal, offset });
+ lhsAddr.offset += 4;
+ load32(lhsAddr, ScratchRegister);
+ Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
+ patches.push_back({ 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);
+ patches.push_back({ notEqual, offset });
+ Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister);
+ lhsAddr.offset += 4;
+ load32(lhsAddr, ScratchRegister);
+ Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister);
+ patches.push_back({ equalUndef, offset });
+ notUndefValue.link(this);
+ }
+
+ void setAccumulatorTag(QV4::Value::ValueTypeInternal tag)
+ {
+ 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();
+ }
+};
+
+typedef PlatformAssembler32 PlatformAssembler;
+#endif
+
+typedef PlatformAssembler::TrustedImmPtr TrustedImmPtr;
+typedef PlatformAssembler::TrustedImm32 TrustedImm32;
+typedef PlatformAssembler::TrustedImm64 TrustedImm64;
+typedef PlatformAssembler::Address Address;
+typedef PlatformAssembler::RegisterID RegisterID;
+typedef PlatformAssembler::FPRegisterID FPRegisterID;
+
+#define pasm() reinterpret_cast<PlatformAssembler *>(this->d)
+
+static Address regAddr(int reg)
+{
+ return Address(PlatformAssembler::JSStackFrameRegister, reg * int(sizeof(QV4::Value)));
+}
+
+Assembler::Assembler(const Value *constantTable)
+ : d(new PlatformAssembler)
+{
+ pasm()->constantTable = constantTable;
+}
+
+Assembler::~Assembler()
+{
+ delete pasm();
+}
+
+void Assembler::generatePrologue()
+{
+ pasm()->generateFunctionEntry();
+}
+
+void Assembler::generateEpilogue()
+{
+ pasm()->generateCatchTrampoline();
+}
+
+namespace {
+class QIODevicePrintStream: public FilePrintStream
+{
+ Q_DISABLE_COPY(QIODevicePrintStream)
+
+public:
+ explicit QIODevicePrintStream(QIODevice *dest)
+ : FilePrintStream(0)
+ , dest(dest)
+ , buf(4096, '0')
+ {
+ Q_ASSERT(dest);
+ }
+
+ ~QIODevicePrintStream()
+ {}
+
+ void vprintf(const char* format, va_list argList) WTF_ATTRIBUTE_PRINTF(2, 0)
+ {
+ const int written = qvsnprintf(buf.data(), buf.size(), format, argList);
+ if (written > 0)
+ dest->write(buf.constData(), written);
+ memset(buf.data(), 0, qMin(written, buf.size()));
+ }
+
+ void flush()
+ {}
+
+private:
+ QIODevice *dest;
+ QByteArray buf;
+};
+} // anonymous namespace
+
+static void printDisassembledOutputWithCalls(QByteArray processedOutput,
+ const QHash<const void*, const char*>& functions)
+{
+ for (QHash<const void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
+ it != end; ++it) {
+ const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16);
+ int idx = 0;
+ while (idx >= 0) {
+ idx = processedOutput.indexOf(ptrString, idx);
+ if (idx < 0)
+ break;
+ idx = processedOutput.indexOf('\n', idx);
+ if (idx < 0)
+ break;
+ processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; ") + it.value());
+ }
+ }
+
+ qDebug("%s", processedOutput.constData());
+}
+
+void Assembler::link(Function *function)
+{
+ for (const auto &jumpTarget : pasm()->patches)
+ jumpTarget.jump.linkTo(pasm()->labelsByOffset[jumpTarget.offset], pasm());
+
+ JSC::JSGlobalData dummy(function->internalClass->engine->executableAllocator);
+ JSC::LinkBuffer<PlatformAssembler::MacroAssembler> linkBuffer(dummy, pasm(), 0);
+
+ for (const auto &ehTarget : pasm()->ehTargets) {
+ auto targetLabel = pasm()->labelsByOffset.value(ehTarget.offset);
+ linkBuffer.patch(ehTarget.label, linkBuffer.locationOf(targetLabel));
+ }
+
+ JSC::MacroAssemblerCodeRef codeRef;
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
+ if (showCode) {
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ WTF::setDataFile(new QIODevicePrintStream(&buf));
+
+ QByteArray name = function->name()->toQString().toUtf8();
+ if (name.isEmpty()) {
+ name = QByteArray::number(quintptr(function), 16);
+ name.prepend("QV4::Function(0x");
+ name.append(')');
+ }
+ codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
+
+ WTF::setDataFile(stderr);
+ printDisassembledOutputWithCalls(buf.data(), pasm()->functions);
+ } else {
+ codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
+ }
+
+ function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef);
+ function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress());
+}
+
+void Assembler::addLabel(int offset)
+{
+ pasm()->labelsByOffset[offset] = pasm()->label();
+}
+
+void Assembler::loadConst(int constIndex)
+{
+ //###
+ if (pasm()->constantTable[constIndex].isUndefined()) {
+ pasm()->loadUndefined();
+ } else {
+ pasm()->loadAccumulator(pasm()->loadConstAddress(constIndex));
+ }
+}
+
+void Assembler::copyConst(int constIndex, int destReg)
+{
+ pasm()->copyConst(constIndex, regAddr(destReg));
+}
+
+void Assembler::loadReg(int reg)
+{
+ pasm()->loadReg(regAddr(reg));
+}
+
+void Assembler::storeReg(int reg)
+{
+ pasm()->storeAccumulator(regAddr(reg));
+}
+
+void Assembler::loadString(int stringId)
+{
+ pasm()->loadString(stringId);
+}
+
+void Assembler::loadValue(ReturnedValue value)
+{
+ pasm()->loadValue(value);
+}
+
+void Assembler::toNumber()
+{
+ pasm()->toNumber();
+}
+
+void Assembler::uminus()
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(1);
+ passAccumulatorAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, ResultInAccumulator);
+ checkException();
+}
+
+void Assembler::ucompl()
+{
+ pasm()->toInt32();
+ pasm()->xor32(TrustedImm32(-1), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+static ReturnedValue incHelper(const Value &v)
+{
+ return Encode(v.toNumber() + 1.);
+}
+
+void Assembler::inc()
+{
+// auto done = pasm()->incFastPath();
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(1);
+ passAccumulatorAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(incHelper, ResultInAccumulator);
+ checkException();
+
+ // done.
+// done.link(pasm());
+}
+
+static ReturnedValue decHelper(const Value &v)
+{
+ return Encode(v.toNumber() - 1.);
+}
+
+void Assembler::dec()
+{
+// auto done = pasm()->decFastPath();
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(1);
+ passAccumulatorAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(decHelper, ResultInAccumulator);
+ checkException();
+
+ // done.
+// done.link(pasm());
+}
+
+void Assembler::unot()
+{
+ pasm()->toBoolean([this](PlatformAssembler::RegisterID resultReg){
+ pasm()->compare32(PlatformAssembler::Equal, resultReg,
+ TrustedImm32(0), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+ });
+}
+
+void Assembler::add(int lhs)
+{
+// PlatformAssembler::Address lhsAddr(PlatformAssembler::JSStackFrameRegister,
+// lhs * int(sizeof(QV4::Value)));
+// auto done = pasm()->binopFastPath(lhsAddr, [this](){
+// auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
+// PlatformAssembler::AccumulatorRegister,
+// PlatformAssembler::ScratchRegister);
+// pasm()->or64(PlatformAssembler::TrustedImm64(int64_t(QV4::Value::ValueTypeInternal::Integer) << 32),
+// PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegister);
+// return overflowed;
+// });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(3);
+ passAccumulatorAsArg(2);
+ passRegAsArg(lhs, 1);
+ passEngineAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_add, ResultInAccumulator);
+ checkException();
+
+ // done.
+// done.link(pasm());
+}
+
+void Assembler::bitAnd(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->popAligned(PlatformAssembler::ScratchRegister);
+ pasm()->and32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::bitOr(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->popAligned(PlatformAssembler::ScratchRegister);
+ pasm()->or32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::bitXor(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->popAligned(PlatformAssembler::ScratchRegister);
+ pasm()->xor32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::ushr(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->urshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan,
+ PlatformAssembler::AccumulatorRegisterValue,
+ TrustedImm32(0));
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+ auto done = pasm()->jump();
+
+ doubleEncode.link(pasm());
+ pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::FPScratchRegister,
+ PlatformAssembler::ScratchRegister);
+ pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister);
+ done.link(pasm());
+}
+
+void Assembler::shr(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->rshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::shl(int lhs)
+{
+ PlatformAssembler::Address lhsAddr = regAddr(lhs);
+ pasm()->regToInt32(lhsAddr, PlatformAssembler::ScratchRegister);
+ pasm()->pushAligned(PlatformAssembler::ScratchRegister);
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(0x1f), PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->popAligned(PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->lshift32(PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::bitAndConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::bitOrConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->or32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::bitXorConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->xor32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::ushrConst(int rhs)
+{
+ rhs &= 0x1f;
+ pasm()->toInt32();
+ if (rhs) // shift with 0 can act weird
+ pasm()->urshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ auto doubleEncode = pasm()->branch32(PlatformAssembler::LessThan,
+ PlatformAssembler::AccumulatorRegisterValue,
+ TrustedImm32(0));
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+ auto done = pasm()->jump();
+
+ doubleEncode.link(pasm());
+ pasm()->convertUInt32ToDouble(PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::FPScratchRegister,
+ PlatformAssembler::ScratchRegister);
+ pasm()->encodeDoubleIntoAccumulator(PlatformAssembler::FPScratchRegister);
+ done.link(pasm());
+}
+
+void Assembler::shrConst(int rhs)
+{
+ rhs &= 0x1f;
+ pasm()->toInt32();
+ if (rhs) // shift with 0 can act weird
+ pasm()->rshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::shlConst(int rhs)
+{
+ rhs &= 0x1f;
+ pasm()->toInt32();
+ if (rhs) // shift with 0 can act weird
+ pasm()->lshift32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Integer);
+}
+
+void Assembler::mul(int lhs)
+{
+// PlatformAssembler::Address lhsAddr(PlatformAssembler::JSStackFrameRegister, lhs * int(sizeof(QV4::Value)));
+// auto done = pasm()->binopFastPath(lhsAddr, [this](){
+// auto overflowed = pasm()->branchMul32(PlatformAssembler::Overflow,
+// PlatformAssembler::AccumulatorRegister,
+// PlatformAssembler::ScratchRegister);
+// pasm()->or64(PlatformAssembler::TrustedImm64(int64_t(QV4::Value::ValueTypeInternal::Integer) << 32),
+// PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegister);
+// return overflowed;
+// });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_mul, ResultInAccumulator);
+ checkException();
+
+ // done.
+ // done.link(pasm());
+}
+
+void Assembler::div(int lhs)
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_div, ResultInAccumulator);
+ checkException();
+}
+
+void Assembler::mod(int lhs)
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_mod, ResultInAccumulator);
+ checkException();
+}
+
+void Assembler::sub(int lhs)
+{
+// PlatformAssembler::Address lhsAddr(PlatformAssembler::JSStackFrameRegister, lhs * int(sizeof(QV4::Value)));
+// auto done = pasm()->binopFastPath(lhsAddr, [this](){
+// auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
+// PlatformAssembler::AccumulatorRegister,
+// PlatformAssembler::ScratchRegister);
+// pasm()->or64(PlatformAssembler::TrustedImm64(int64_t(QV4::Value::ValueTypeInternal::Integer) << 32),
+// PlatformAssembler::ScratchRegister, PlatformAssembler::AccumulatorRegister);
+// return overflowed;
+// });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_sub, ResultInAccumulator);
+ checkException();
+
+ // done.
+// done.link(pasm());
+}
+
+void Assembler::cmpeqNull()
+{
+ pasm()->isNullOrUndefined();
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+}
+
+void Assembler::cmpneNull()
+{
+ pasm()->isNullOrUndefined();
+ pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+}
+
+void Assembler::cmpeqInt(int lhs)
+{
+ saveAccumulatorInFrame();
+ pasm()->pushValueAligned(Encode(lhs));
+ if (PlatformAssembler::ArgInRegCount < 2)
+ pasm()->push(PlatformAssembler::StackPointerRegister);
+ else
+ pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
+ passAccumulatorAsArg_internal(0, true);
+ pasm()->callRuntime("Runtime::method_equal", (void*)Runtime::method_equal, ResultInAccumulator);
+ if (PlatformAssembler::ArgInRegCount < 2)
+ pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
+ pasm()->popValueAligned();
+}
+
+void Assembler::cmpneInt(int lhs)
+{
+ saveAccumulatorInFrame();
+ pasm()->pushValueAligned(Encode(lhs));
+ if (PlatformAssembler::ArgInRegCount < 2)
+ pasm()->push(PlatformAssembler::StackPointerRegister);
+ else
+ pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
+ passAccumulatorAsArg_internal(0, true);
+ pasm()->callRuntime("Runtime::method_notEqual", (void*)Runtime::method_notEqual, ResultInAccumulator);
+ if (PlatformAssembler::ArgInRegCount < 2)
+ pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
+ pasm()->popValueAligned();
+}
+
+void Assembler::cmp(int /*cond*/, CmpFunc function, const char *functionName, int lhs)
+{
+// PlatformAssembler::Address lhsAddr(PlatformAssembler::JSStackFrameRegister, lhs * int(sizeof(QV4::Value)));
+// auto done = pasm()->cmpFastPath(static_cast<PlatformAssembler::RelationalCondition>(cond), lhsAddr);
+
+ // slow path:
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+
+ callRuntime(functionName, reinterpret_cast<void*>(function), ResultInAccumulator);
+ checkException();
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+
+ // done.
+// done.link(pasm());
+}
+
+void Assembler::cmpeq(int lhs)
+{
+ cmp(PlatformAssembler::Equal, &Runtime::method_compareEqual,
+ "Runtime::method_compareEqual", lhs);
+}
+
+void Assembler::cmpne(int lhs)
+{
+ cmp(PlatformAssembler::NotEqual, &Runtime::method_compareNotEqual,
+ "Runtime::method_compareNotEqual", lhs);
+}
+
+void Assembler::cmpgt(int lhs)
+{
+ cmp(PlatformAssembler::GreaterThan, &Runtime::method_compareGreaterThan,
+ "Runtime::method_compareGreaterThan", lhs);
+}
+
+void Assembler::cmpge(int lhs)
+{
+ cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::method_compareGreaterEqual,
+ "Runtime::method_compareGreaterEqual", lhs);
+}
+
+void Assembler::cmplt(int lhs)
+{
+ cmp(PlatformAssembler::LessThan, &Runtime::method_compareLessThan,
+ "Runtime::method_compareLessThan", lhs);
+}
+
+void Assembler::cmple(int lhs)
+{
+ cmp(PlatformAssembler::LessThanOrEqual, &Runtime::method_compareLessEqual,
+ "Runtime::method_compareLessEqual", lhs);
+}
+
+void Assembler::cmpStrictEqual(int lhs)
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::strictEqual, ResultInAccumulator);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+}
+
+void Assembler::cmpStrictNotEqual(int lhs)
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passAccumulatorAsArg(1);
+ passRegAsArg(lhs, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::strictEqual, ResultInAccumulator);
+ pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
+}
+
+void Assembler::jump(int offset)
+{
+ pasm()->patches.push_back({ pasm()->jump(), offset });
+}
+
+void Assembler::jumpTrue(int offset)
+{
+ pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
+ auto jump = pasm()->branch32(PlatformAssembler::NotEqual, TrustedImm32(0), resultReg);
+ pasm()->patches.push_back({ jump, offset });
+ });
+}
+
+void Assembler::jumpFalse(int offset)
+{
+ pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
+ auto jump = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg);
+ pasm()->patches.push_back({ jump, offset });
+ });
+}
+
+void Assembler::jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
+{
+ pasm()->jumpStrictEqualStackSlotInt(lhs, rhs, offset);
+}
+
+void Assembler::jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
+{
+ pasm()->jumpStrictNotEqualStackSlotInt(lhs, rhs, offset);
+}
+
+void Assembler::prepareCallWithArgCount(int argc)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(remainingArgcForCall == NoCall);
+ remainingArgcForCall = argc;
+#endif
+
+ if (argc > PlatformAssembler::ArgInRegCount) {
+ argcOnStackForCall = int(WTF::roundUpToMultipleOf(16, size_t(argc - PlatformAssembler::ArgInRegCount) * PlatformAssembler::PointerSize));
+ pasm()->subPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister);
+ }
+}
+
+void Assembler::storeInstructionPointer(int instructionOffset)
+{
+ PlatformAssembler::Address addr(PlatformAssembler::CppStackFrameRegister,
+ offsetof(QV4::CppStackFrame, instructionPointer));
+ pasm()->store32(TrustedImm32(instructionOffset), addr);
+}
+
+Address argStackAddress(int arg)
+{
+ int offset = arg - PlatformAssembler::ArgInRegCount;
+ Q_ASSERT(offset >= 0);
+ return Address(PlatformAssembler::StackPointerRegister, offset * PlatformAssembler::PointerSize);
+}
+
+void Assembler::passAccumulatorAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ passAccumulatorAsArg_internal(arg, false);
+}
+
+void Assembler::passAccumulatorAsArg_internal(int arg, bool push)
+{
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)),
+ PlatformAssembler::JSStackFrameRegister,
+ pasm()->registerForArg(arg));
+ } else {
+ pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)),
+ PlatformAssembler::JSStackFrameRegister,
+ PlatformAssembler::ScratchRegister);
+ if (push)
+ pasm()->push(PlatformAssembler::ScratchRegister);
+ else
+ pasm()->storePtr(PlatformAssembler::ScratchRegister,
+ argStackAddress(arg));
+ }
+}
+
+void Assembler::passFunctionAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->loadFunctionPtr(pasm()->registerForArg(arg));
+ } else {
+ pasm()->loadFunctionPtr(PlatformAssembler::ScratchRegister);
+ pasm()->storePtr(PlatformAssembler::ScratchRegister,
+ argStackAddress(arg));
+ }
+}
+
+void Assembler::passEngineAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->move(PlatformAssembler::EngineRegister, pasm()->registerForArg(arg));
+ } else {
+ pasm()->storePtr(PlatformAssembler::EngineRegister, argStackAddress(arg));
+ }
+}
+
+void Assembler::passRegAsArg(int reg, int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->addPtr(TrustedImm32(reg * int(sizeof(QV4::Value))),
+ PlatformAssembler::JSStackFrameRegister,
+ pasm()->registerForArg(arg));
+ } else {
+ pasm()->addPtr(TrustedImm32(reg * int(sizeof(QV4::Value))),
+ PlatformAssembler::JSStackFrameRegister,
+ PlatformAssembler::ScratchRegister);
+ pasm()->storePtr(PlatformAssembler::ScratchRegister,
+ argStackAddress(arg));
+ }
+}
+
+void JIT::Assembler::passCppFrameAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->move(PlatformAssembler::CppStackFrameRegister, pasm()->registerForArg(arg));
+ } else {
+ pasm()->store32(PlatformAssembler::CppStackFrameRegister, argStackAddress(arg));
+ }
+}
+
+void Assembler::passInt32AsArg(int value, int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->move(TrustedImm32(value), pasm()->registerForArg(arg));
+ } else {
+ pasm()->store32(TrustedImm32(value), argStackAddress(arg));
+ }
+}
+
+void Assembler::callRuntime(const char *functionName, const void *funcPtr,
+ Assembler::CallResultDestination dest)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(remainingArgcForCall == 0);
+ remainingArgcForCall = NoCall;
+#endif
+ pasm()->callRuntime(functionName, funcPtr, dest);
+ if (argcOnStackForCall > 0) {
+ pasm()->addPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister);
+ argcOnStackForCall = 0;
+ }
+}
+
+void Assembler::saveAccumulatorInFrame()
+{
+ pasm()->storeAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
+ offsetof(CallData, accumulator)));
+}
+
+void Assembler::checkException()
+{
+ pasm()->addCatchyJump(
+ pasm()->branch32(
+ PlatformAssembler::NotEqual,
+ PlatformAssembler::Address(PlatformAssembler::EngineRegister,
+ offsetof(EngineBase, hasException)),
+ TrustedImm32(0)));
+}
+
+void Assembler::gotoCatchException()
+{
+ pasm()->addCatchyJump(pasm()->jump());
+}
+
+void Assembler::getException()
+{
+ Q_STATIC_ASSERT_FOR_SANE_COMPILERS(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(Primitive::emptyValue().asReturnedValue());
+
+ done.link(pasm());
+}
+
+void Assembler::setException()
+{
+ 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_FOR_SANE_COMPILERS(sizeof(QV4::EngineBase::hasException) == 1);
+ pasm()->store8(TrustedImm32(1), addr);
+}
+
+void Assembler::setExceptionHandler(int offset)
+{
+ auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
+ pasm()->ehTargets.push_back({ l, offset });
+}
+
+
+void Assembler::clearExceptionHandler()
+{
+ pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
+}
+
+void Assembler::pushCatchContext(int name, int reg)
+{
+ pasm()->copyReg(pasm()->contextAddress(), regAddr(reg));
+ prepareCallWithArgCount(2);
+ passInt32AsArg(name, 1);
+ passRegAsArg(CallData::Context, 0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, ResultInAccumulator);
+ pasm()->storeAccumulator(pasm()->contextAddress());
+}
+
+void Assembler::popContext(int reg)
+{
+ pasm()->copyReg(regAddr(reg), pasm()->contextAddress());
+}
+
+void Assembler::ret()
+{
+ pasm()->generateFunctionExit();
+}
+
+} // JIT namespace
+} // QV4 namepsace
+
+QT_END_NAMESPACE
+
+#endif // V4_ENABLE_JIT