diff options
author | Erik Verbruggen <erik.verbruggen@qt.io> | 2017-11-14 10:45:06 +0100 |
---|---|---|
committer | Lars Knoll <lars.knoll@qt.io> | 2017-11-17 11:53:42 +0000 |
commit | 3658f534cb1947663d29b9db00dcced462674aed (patch) | |
tree | a0c46c3c70c228426b4b774fb742b25a4854be9a /src/qml | |
parent | 75584bde397b1a7e92281855ea04e743fae7c1c5 (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')
-rw-r--r-- | src/qml/jit/jit.pri | 10 | ||||
-rw-r--r-- | src/qml/jit/qv4assembler.cpp | 1914 | ||||
-rw-r--r-- | src/qml/jit/qv4assembler_p.h | 188 | ||||
-rw-r--r-- | src/qml/jit/qv4jit.cpp | 1365 | ||||
-rw-r--r-- | src/qml/jit/qv4jit_p.h | 265 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 11 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4engine_p.h | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function_p.h | 8 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4global_p.h | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4vme_moth.cpp | 33 | ||||
-rw-r--r-- | src/qml/qml.pro | 1 |
12 files changed, 3796 insertions, 11 deletions
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri new file mode 100644 index 0000000000..2080844d93 --- /dev/null +++ b/src/qml/jit/jit.pri @@ -0,0 +1,10 @@ +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +SOURCES += \ + $$PWD/qv4jit.cpp \ + $$PWD/qv4assembler.cpp + +HEADERS += \ + $$PWD/qv4jit_p.h \ + $$PWD/qv4assembler_p.h 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 diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h new file mode 100644 index 0000000000..eafe27c8bb --- /dev/null +++ b/src/qml/jit/qv4assembler_p.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QV4ASSEMBLER_P_H +#define QV4ASSEMBLER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> +#include <private/qv4function_p.h> +#include <QHash> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace JIT { + +#define JIT_STRINGIFYx(s) #s +#define JIT_STRINGIFY(s) JIT_STRINGIFYx(s) + +#define IN_JIT_GENERATE_RUNTIME_CALL(function, destination) \ + callRuntime(JIT_STRINGIFY(function), \ + reinterpret_cast<void *>(&function), \ + destination) +#define JIT_GENERATE_RUNTIME_CALL(function, destination) \ + as->IN_JIT_GENERATE_RUNTIME_CALL(function, destination) + +class Assembler { +public: + enum CallResultDestination { + IgnoreResult, + ResultInAccumulator, + }; + + Assembler(const Value* constantTable); + ~Assembler(); + + // codegen infrastructure + void generatePrologue(); + void generateEpilogue(); + void link(Function *function); + void addLabel(int offset); + + // loads/stores/moves + void loadConst(int constIndex); + void copyConst(int constIndex, int destReg); + void loadReg(int reg); + void storeReg(int reg); + void loadString(int stringId); + void loadValue(ReturnedValue value); + + // numeric ops + void unot(); + void toNumber(); + void uminus(); + void ucompl(); + void inc(); + void dec(); + void add(int lhs); + void bitAnd(int lhs); + void bitOr(int lhs); + void bitXor(int lhs); + void ushr(int lhs); + void shr(int lhs); + void shl(int lhs); + void bitAndConst(int rhs); + void bitOrConst(int rhs); + void bitXorConst(int rhs); + void ushrConst(int rhs); + void shrConst(int rhs); + void shlConst(int rhs); + void mul(int lhs); + void div(int lhs); + void mod(int lhs); + void sub(int lhs); + + // comparissons + void cmpeqNull(); + void cmpneNull(); + void cmpeqInt(int lhs); + void cmpneInt(int lhs); + void cmpeq(int lhs); + void cmpne(int lhs); + void cmpgt(int lhs); + void cmpge(int lhs); + void cmplt(int lhs); + void cmple(int lhs); + void cmpStrictEqual(int lhs); + void cmpStrictNotEqual(int lhs); + + // jumps + void jump(int offset); + void jumpTrue(int offset); + void jumpFalse(int offset); + void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset); + void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset); + + // stuff for runtime calls + void prepareCallWithArgCount(int argc); + void storeInstructionPointer(int instructionOffset); + void passAccumulatorAsArg(int arg); + void passFunctionAsArg(int arg); + void passEngineAsArg(int arg); + void passRegAsArg(int reg, int arg); + void passCppFrameAsArg(int arg); + void passInt32AsArg(int value, int arg); + void callRuntime(const char *functionName, const void *funcPtr, Assembler::CallResultDestination dest); + void saveAccumulatorInFrame(); + + // exception/context stuff + void checkException(); + void gotoCatchException(); + void getException(); + void setException(); + void setExceptionHandler(int offset); + void clearExceptionHandler(); + void pushCatchContext(int name, int reg); + void popContext(int reg); + + // other stuff + void ret(); + +protected: + void *d; + +#ifndef QT_NO_DEBUG + enum { NoCall = -1 }; + int remainingArgcForCall = NoCall; +#endif + int argcOnStackForCall = 0; + +private: + typedef unsigned(*CmpFunc)(const Value&,const Value&); + void cmp(int cond, CmpFunc function, const char *functionName, int lhs); + void passAccumulatorAsArg_internal(int arg, bool push); +}; + +} // namespace JIT +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4ASSEMBLER_P_H diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp new file mode 100644 index 0000000000..a8bdd20fbd --- /dev/null +++ b/src/qml/jit/qv4jit.cpp @@ -0,0 +1,1365 @@ +/**************************************************************************** +** +** 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 "qv4jit_p.h" +#include "qv4assembler_p.h" +#include <private/qv4lookup_p.h> + +#ifdef V4_ENABLE_JIT + +QT_USE_NAMESPACE +using namespace QV4; +using namespace QV4::JIT; +using namespace QV4::Moth; + +ByteCodeHandler::~ByteCodeHandler() +{ +} + +#define DISPATCH_INSTRUCTION(name, nargs, ...) \ + generate_##name( \ + __VA_ARGS__ \ + ); + +#define DECODE_AND_DISPATCH(instr) \ + { \ + INSTR_##instr(MOTH_DECODE_WITH_BASE) \ + Q_UNUSED(base_ptr); \ + startInstruction(Instr::Type::instr); \ + _offset = code - start; \ + INSTR_##instr(DISPATCH) \ + endInstruction(Instr::Type::instr); \ + continue; \ + } + +void ByteCodeHandler::decode(const char *code, uint len) +{ + MOTH_JUMP_TABLE; + + const char *start = code; + const char *end = code + len; + while (code < end) { + MOTH_DISPATCH() + + FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH) + } +} + +#undef DECODE_AND_DISPATCH +#undef DISPATCH_INSTRUCTION + +BaselineJIT::BaselineJIT(Function *function) + : function(function) + , as(new Assembler(function->compilationUnit->constants)) +{} + +BaselineJIT::~BaselineJIT() +{} + +void BaselineJIT::generate() +{ +// qDebug()<<"jitting" << function->name()->toQString(); + collectLabelsInBytecode(); + + as->generatePrologue(); + decode(reinterpret_cast<const char *>(function->codeData), function->compiledFunction->codeSize); + as->generateEpilogue(); + + as->link(function); +// qDebug()<<"done"; +} + +#define STORE_IP() as->storeInstructionPointer(instructionOffset()) +#define STORE_ACC() as->saveAccumulatorInFrame() + +void BaselineJIT::generate_Ret() +{ + as->ret(); +} + +void BaselineJIT::generate_Debug() { Q_UNREACHABLE(); } + +void BaselineJIT::generate_LoadConst(int index) +{ + as->loadConst(index); +} + +void BaselineJIT::generate_LoadZero() +{ + as->loadValue(Encode(int(0))); +} + +void BaselineJIT::generate_LoadTrue() +{ + as->loadValue(Encode(true)); +} + +void BaselineJIT::generate_LoadFalse() +{ + as->loadValue(Encode(false)); +} + +void BaselineJIT::generate_LoadNull() +{ + as->loadValue(Encode::null()); +} + +void BaselineJIT::generate_LoadUndefined() +{ + as->loadValue(Encode::undefined()); +} + +void BaselineJIT::generate_LoadInt(int value) +{ + //### + as->loadValue(Encode(value)); +} + +void BaselineJIT::generate_MoveConst(int constIndex, int destTemp) +{ + as->copyConst(constIndex, destTemp); +} + +void BaselineJIT::generate_LoadReg(int reg) +{ + as->loadReg(reg); +} + +void BaselineJIT::generate_StoreReg(int reg) +{ + as->storeReg(reg); +} + +void BaselineJIT::generate_MoveReg(int srcReg, int destReg) +{ + as->loadReg(srcReg); + as->storeReg(destReg); +} + +static ReturnedValue loadLocalHelper(const Value &context, int index) +{ + auto cc = static_cast<Heap::CallContext *>(context.m()); + return cc->locals[uint(index)].asReturnedValue(); +} + +void BaselineJIT::generate_LoadLocal(int index) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(index, 1); + as->passRegAsArg(CallData::Context, 0); + JIT_GENERATE_RUNTIME_CALL(loadLocalHelper, Assembler::ResultInAccumulator); +} + +static void storeLocalHelper(ExecutionEngine *engine, const Value &context, int index, const Value &acc) +{ + auto cc = static_cast<Heap::CallContext *>(context.m()); + QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, acc); +} + +void BaselineJIT::generate_StoreLocal(int index) +{ + as->checkException(); + as->prepareCallWithArgCount(4); + STORE_ACC(); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(index, 2); + as->passRegAsArg(CallData::Context, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(storeLocalHelper, Assembler::IgnoreResult); +} + +static inline Heap::CallContext *getScope(Value *stack, int level) +{ + Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); + while (level > 0) { + --level; + scope = scope->outer; + } + Q_ASSERT(scope); + return static_cast<Heap::CallContext *>(scope); +} + +static ReturnedValue loadScopedLocalHelper(Value *stack, int scope, int index) +{ + auto cc = getScope(stack, scope); + return cc->locals[uint(index)].asReturnedValue(); +} + +void BaselineJIT::generate_LoadScopedLocal(int scope, int index) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(index, 2); + as->passInt32AsArg(scope, 1); + as->passRegAsArg(0, 0); + JIT_GENERATE_RUNTIME_CALL(loadScopedLocalHelper, Assembler::ResultInAccumulator); +} + +static void storeScopedLocalHelper(ExecutionEngine *engine, Value *stack, int scope, int index, + const Value &acc) +{ + auto cc = getScope(stack, scope); + QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, acc); +} + +void BaselineJIT::generate_StoreScopedLocal(int scope, int index) +{ + as->checkException(); + as->prepareCallWithArgCount(5); + STORE_ACC(); + as->passAccumulatorAsArg(4); + as->passInt32AsArg(index, 3); + as->passInt32AsArg(scope, 2); + as->passRegAsArg(0, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(storeScopedLocalHelper, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_LoadRuntimeString(int stringId) +{ + as->loadString(stringId); +} + +void BaselineJIT::generate_LoadRegExp(int regExpId) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(regExpId, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_regexpLiteral, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_LoadClosure(int value) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(value, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_LoadName(int name) +{ + STORE_IP(); + as->prepareCallWithArgCount(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadName, Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue loadGlobalLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index) +{ + QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; + return l->globalGetter(l, engine); +} + +void BaselineJIT::generate_LoadGlobalLookup(int index) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(index, 2); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(loadGlobalLookupHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_StoreNameSloppy(int name) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameSloppy, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_StoreNameStrict(int name) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameStrict, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_LoadElement(int base, int index) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passRegAsArg(index, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_LoadElementA(int base) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator); + as->checkException(); +} + +static void storeElementHelper(QV4::Function *f, const Value &base, const Value &index, const Value &value) +{ + auto engine = f->internalClass->engine; + if (!Runtime::method_storeElement(engine, base, index, value) && f->isStrict()) + engine->throwTypeError(); +} + +void BaselineJIT::generate_StoreElement(int base, int index) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passRegAsArg(index, 2); + as->passRegAsArg(base, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(storeElementHelper, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_LoadProperty(int name, int base) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(name, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator); + as->checkException(); +} +void BaselineJIT::generate_LoadPropertyA(int name) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(name, 2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue getLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index, const QV4::Value &base) +{ + QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; + return l->getter(l, engine, base); +} + +void BaselineJIT::generate_GetLookup(int index, int base) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passRegAsArg(base, 3); + as->passInt32AsArg(index, 2); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_GetLookupA(int index) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(index, 2); + as->passFunctionAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +static void storePropertyHelper(QV4::Function *f, const Value &base, int name, const Value &value) +{ + auto engine = f->internalClass->engine; + if (!Runtime::method_storeProperty(engine, base, name, value) && f->isStrict()) + engine->throwTypeError(); +} + +void BaselineJIT::generate_StoreProperty(int name, int base) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(name, 2); + as->passRegAsArg(base, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(storePropertyHelper, Assembler::IgnoreResult); + as->checkException(); +} + +static void setLookupHelper(QV4::Function *f, int index, QV4::Value &base, const QV4::Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = f->compilationUnit->runtimeLookups + index; + if (!l->setter(l, engine, base, value) && f->isStrict()) + engine->throwTypeError(); +} + +void BaselineJIT::generate_SetLookup(int index, int base) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passRegAsArg(base, 2); + as->passInt32AsArg(index, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(setLookupHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex) +{ + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(propertyIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlScopeObjectProperty, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex) +{ + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passAccumulatorAsArg(3); + as->passInt32AsArg(propertyIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlContextObjectProperty, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(captureRequired, 3); + as->passInt32AsArg(propertyIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlScopeObjectProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(captureRequired, 3); + as->passInt32AsArg(propertyIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContextObjectProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_LoadIdObject(int index, int base) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(index, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlIdObject, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallValue(int argc, int argv) +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passRegAsArg(argv, 2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callValue, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passInt32AsArg(name, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callProperty, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passInt32AsArg(lookupIndex, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callPropertyLookup, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(5); + as->passInt32AsArg(argc, 4); + as->passRegAsArg(argv, 3); + as->passRegAsArg(index, 2); + as->passRegAsArg(base, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callElement, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallName(int name, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passRegAsArg(argv, 2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callName, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(argc, 2); + as->passRegAsArg(argv, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callPossiblyDirectEval, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passRegAsArg(argv, 2); + as->passInt32AsArg(index, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_callGlobalLookup, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_SetExceptionHandler(int offset) +{ + if (offset) + as->setExceptionHandler(instructionOffset() + offset); + else + as->clearExceptionHandler(); +} + +void BaselineJIT::generate_ThrowException() +{ + STORE_IP(); + STORE_ACC(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_throwException, Assembler::IgnoreResult); + as->gotoCatchException(); +} + +void BaselineJIT::generate_GetException() { as->getException(); } +void BaselineJIT::generate_SetException() { as->setException(); } + +static void createCallContextHelper(Value *stack, CppStackFrame *frame) +{ + stack[CallData::Context] = ExecutionContext::newCallContext(frame); +} + +void BaselineJIT::generate_CreateCallContext() +{ + as->prepareCallWithArgCount(2); + as->passCppFrameAsArg(1); + as->passRegAsArg(0, 0); + JIT_GENERATE_RUNTIME_CALL(createCallContextHelper, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_PushCatchContext(int name, int reg) { as->pushCatchContext(name, reg); } + +static void pushWithContextHelper(ExecutionEngine *engine, QV4::Value *stack, int reg) +{ + QV4::Value &accumulator = stack[CallData::Accumulator]; + accumulator = accumulator.toObject(engine); + if (engine->hasException) + return; + stack[reg] = stack[CallData::Context]; + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + stack[CallData::Context] = Runtime::method_createWithContext(c, accumulator); +} + +void BaselineJIT::generate_PushWithContext(int reg) +{ + STORE_IP(); + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(reg, 2); + as->passRegAsArg(0, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(pushWithContextHelper, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); } + +void BaselineJIT::generate_ForeachIteratorObject() +{ + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachIterator, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_ForeachNextPropertyName() +{ + as->saveAccumulatorInFrame(); + as->prepareCallWithArgCount(1); + as->passAccumulatorAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachNextPropertyName, + Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue deleteMemberHelper(QV4::Function *function, const QV4::Value &base, int member) +{ + auto engine = function->internalClass->engine; + if (!Runtime::method_deleteMember(engine, base, member)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +void BaselineJIT::generate_DeleteMember(int member, int base) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passInt32AsArg(member, 2); + as->passRegAsArg(base, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(deleteMemberHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue deleteSubscriptHelper(QV4::Function *function, const QV4::Value &base, const QV4::Value &index) +{ + auto engine = function->internalClass->engine; + if (!Runtime::method_deleteElement(engine, base, index)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +void BaselineJIT::generate_DeleteSubscript(int base, int index) +{ + STORE_IP(); + as->prepareCallWithArgCount(3); + as->passRegAsArg(index, 2); + as->passRegAsArg(base, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(deleteSubscriptHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +static ReturnedValue deleteNameHelper(QV4::Function *function, int name) +{ + auto engine = function->internalClass->engine; + if (!Runtime::method_deleteName(engine, name)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +void BaselineJIT::generate_DeleteName(int name) +{ + STORE_IP(); + as->prepareCallWithArgCount(2); + as->passInt32AsArg(name, 1); + as->passFunctionAsArg(0); + JIT_GENERATE_RUNTIME_CALL(deleteNameHelper, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_TypeofName(int name) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofName, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_TypeofValue() +{ + STORE_ACC(); + as->prepareCallWithArgCount(2); + as->passAccumulatorAsArg(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofValue, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_DeclareVar(int varName, int isDeletable) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(varName, 2); + as->passInt32AsArg(isDeletable, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_declareVar, Assembler::IgnoreResult); +} + +void BaselineJIT::generate_DefineArray(int argc, int args) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(argc, 2); + as->passRegAsArg(args, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int arrayValueCount, + int arrayGetterSetterCountAndFlags, int args) +{ + as->prepareCallWithArgCount(5); + as->passInt32AsArg(arrayGetterSetterCountAndFlags, 4); + as->passInt32AsArg(arrayValueCount, 3); + as->passInt32AsArg(internalClassId, 2); + as->passRegAsArg(args, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, Assembler::ResultInAccumulator); +} +void BaselineJIT::generate_CreateMappedArgumentsObject() +{ + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_createMappedArgumentsObject, + Assembler::ResultInAccumulator); +} + +void BaselineJIT::generate_CreateUnmappedArgumentsObject() +{ + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_createUnmappedArgumentsObject, + Assembler::ResultInAccumulator); +} + +static void convertThisToObjectHelper(ExecutionEngine *engine, Value *t) +{ + if (!t->isObject()) { + if (t->isNullOrUndefined()) { + *t = engine->globalObject->asReturnedValue(); + } else { + *t = t->toObject(engine)->asReturnedValue(); + } + } +} + +void BaselineJIT::generate_ConvertThisToObject() +{ + as->prepareCallWithArgCount(2); + as->passRegAsArg(CallData::This, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(convertThisToObjectHelper, Assembler::IgnoreResult); + as->checkException(); +} + +void BaselineJIT::generate_Construct(int func, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passRegAsArg(argv, 2); + as->passRegAsArg(func, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); } +void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); } +void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); } + +void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); } +void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); } +void BaselineJIT::generate_CmpEqInt(int lhs) { as->cmpeqInt(lhs); } +void BaselineJIT::generate_CmpNeInt(int lhs) { as->cmpneInt(lhs); } +void BaselineJIT::generate_CmpEq(int lhs) { as->cmpeq(lhs); } +void BaselineJIT::generate_CmpNe(int lhs) { as->cmpne(lhs); } +void BaselineJIT::generate_CmpGt(int lhs) { as->cmpgt(lhs); } +void BaselineJIT::generate_CmpGe(int lhs) { as->cmpge(lhs); } +void BaselineJIT::generate_CmpLt(int lhs) { as->cmplt(lhs); } +void BaselineJIT::generate_CmpLe(int lhs) { as->cmple(lhs); } +void BaselineJIT::generate_CmpStrictEqual(int lhs) { as->cmpStrictEqual(lhs); } +void BaselineJIT::generate_CmpStrictNotEqual(int lhs) { as->cmpStrictNotEqual(lhs); } + +void BaselineJIT::generate_CmpIn(int lhs) +{ + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(lhs, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_in, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_CmpInstanceOf(int lhs) +{ + STORE_ACC(); + as->prepareCallWithArgCount(3); + as->passAccumulatorAsArg(2); + as->passRegAsArg(lhs, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_instanceof, Assembler::ResultInAccumulator); + as->checkException(); +} + +void BaselineJIT::generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, int offset) +{ + as->jumpStrictEqualStackSlotInt(lhs, rhs, instructionOffset() + offset); +} + +void BaselineJIT::generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset) +{ + as->jumpStrictNotEqualStackSlotInt(lhs, rhs, instructionOffset() + offset); +} + +void BaselineJIT::generate_UNot() { as->unot(); } +void BaselineJIT::generate_UPlus() { as->toNumber(); } +void BaselineJIT::generate_UMinus() { as->uminus(); } +void BaselineJIT::generate_UCompl() { as->ucompl(); } +void BaselineJIT::generate_Increment() { as->inc(); } +void BaselineJIT::generate_Decrement() { as->dec(); } +void BaselineJIT::generate_Add(int lhs) { as->add(lhs); } + +void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); } +void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); } +void BaselineJIT::generate_BitXor(int lhs) { as->bitXor(lhs); } +void BaselineJIT::generate_UShr(int lhs) { as->ushr(lhs); } +void BaselineJIT::generate_Shr(int lhs) { as->shr(lhs); } +void BaselineJIT::generate_Shl(int lhs) { as->shl(lhs); } + +void BaselineJIT::generate_BitAndConst(int rhs) { as->bitAndConst(rhs); } +void BaselineJIT::generate_BitOrConst(int rhs) { as->bitOrConst(rhs); } +void BaselineJIT::generate_BitXorConst(int rhs) { as->bitXorConst(rhs); } +void BaselineJIT::generate_UShrConst(int rhs) { as->ushrConst(rhs); } +void BaselineJIT::generate_ShrConst(int rhs) { as->shrConst(rhs); } +void BaselineJIT::generate_ShlConst(int rhs) { as->shlConst(rhs); } + +void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); } +void BaselineJIT::generate_Div(int lhs) { as->div(lhs); } +void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); } +void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); } + +//void BaselineJIT::generate_BinopContext(int alu, int lhs) +//{ +// auto engine = function->internalClass->engine; +// void *op = engine->runtime.runtimeMethods[alu]; +// STORE_ACC(); +// as->passAccumulatorAsArg(2); +// as->passRegAsArg(lhs, 1); +// as->passEngineAsArg(0); +// as->callRuntime("binopContext", op, Assembler::ResultInAccumulator); +// as->checkException(); +//} + +void BaselineJIT::generate_LoadQmlContext(int result) +{ + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContext, Assembler::ResultInAccumulator); + as->storeReg(result); +} + +void BaselineJIT::generate_LoadQmlImportedScripts(int result) +{ + as->prepareCallWithArgCount(1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlImportedScripts, Assembler::ResultInAccumulator); + as->storeReg(result); +} + +void BaselineJIT::generate_LoadQmlSingleton(int name) +{ + as->prepareCallWithArgCount(2); + as->passInt32AsArg(name, 1); + as->passEngineAsArg(0); + JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlSingleton, Assembler::ResultInAccumulator); +} + +void BaselineJIT::startInstruction(Instr::Type /*instr*/) +{ + if (hasLabel()) + as->addLabel(instructionOffset()); +} + +void BaselineJIT::endInstruction(Instr::Type instr) +{ + Q_UNUSED(instr); +} + +#define MOTH_UNUSED_ARGS0() +#define MOTH_UNUSED_ARGS1(arg) \ + Q_UNUSED(arg); +#define MOTH_UNUSED_ARGS2(arg1, arg2) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); +#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); \ + Q_UNUSED(arg3); +#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \ + Q_UNUSED(arg1); \ + Q_UNUSED(arg2); \ + Q_UNUSED(arg3); \ + Q_UNUSED(arg4); + +#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__)) + +#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \ + MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__) + +#define MOTH_BEGIN_INSTR(instr) \ + { \ + INSTR_##instr(MOTH_DECODE_WITH_BASE) \ + INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \ + Q_UNUSED(base_ptr); + +#define MOTH_END_INSTR(instr) \ + continue; \ + } + +void BaselineJIT::collectLabelsInBytecode() +{ + MOTH_JUMP_TABLE; + + const char *code = reinterpret_cast<const char *>(function->codeData); + const char *start = code; + const char *end = code + function->compiledFunction->codeSize; + while (code < end) { + MOTH_DISPATCH() + Q_UNREACHABLE(); + + MOTH_BEGIN_INSTR(LoadReg) + MOTH_END_INSTR(LoadReg) + + MOTH_BEGIN_INSTR(StoreReg) + MOTH_END_INSTR(StoreReg) + + MOTH_BEGIN_INSTR(MoveReg) + MOTH_END_INSTR(MoveReg) + + MOTH_BEGIN_INSTR(LoadConst) + MOTH_END_INSTR(LoadConst) + + MOTH_BEGIN_INSTR(LoadNull) + MOTH_END_INSTR(LoadNull) + + MOTH_BEGIN_INSTR(LoadZero) + MOTH_END_INSTR(LoadZero) + + MOTH_BEGIN_INSTR(LoadTrue) + MOTH_END_INSTR(LoadTrue) + + MOTH_BEGIN_INSTR(LoadFalse) + MOTH_END_INSTR(LoadFalse) + + MOTH_BEGIN_INSTR(LoadUndefined) + MOTH_END_INSTR(LoadUndefined) + + MOTH_BEGIN_INSTR(LoadInt) + MOTH_END_INSTR(LoadInt) + + MOTH_BEGIN_INSTR(MoveConst) + MOTH_END_INSTR(MoveConst) + + MOTH_BEGIN_INSTR(LoadLocal) + MOTH_END_INSTR(LoadLocal) + + MOTH_BEGIN_INSTR(StoreLocal) + MOTH_END_INSTR(StoreLocal) + + MOTH_BEGIN_INSTR(LoadScopedLocal) + MOTH_END_INSTR(LoadScopedLocal) + + MOTH_BEGIN_INSTR(StoreScopedLocal) + MOTH_END_INSTR(StoreScopedLocal) + + MOTH_BEGIN_INSTR(LoadRuntimeString) + MOTH_END_INSTR(LoadRuntimeString) + + MOTH_BEGIN_INSTR(LoadRegExp) + MOTH_END_INSTR(LoadRegExp) + + MOTH_BEGIN_INSTR(LoadClosure) + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(LoadGlobalLookup) + MOTH_END_INSTR(LoadGlobalLookup) + + MOTH_BEGIN_INSTR(StoreNameSloppy) + MOTH_END_INSTR(StoreNameSloppy) + + MOTH_BEGIN_INSTR(StoreNameStrict) + MOTH_END_INSTR(StoreNameStrict) + + MOTH_BEGIN_INSTR(LoadElement) + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(LoadElementA) + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(LoadPropertyA) + MOTH_END_INSTR(LoadElementA) + + MOTH_BEGIN_INSTR(GetLookup) + MOTH_END_INSTR(GetLookup) + + MOTH_BEGIN_INSTR(GetLookupA) + MOTH_END_INSTR(GetLookupA) + + MOTH_BEGIN_INSTR(StoreProperty) + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(SetLookup) + MOTH_END_INSTR(SetLookup) + + MOTH_BEGIN_INSTR(StoreScopeObjectProperty) + MOTH_END_INSTR(StoreScopeObjectProperty) + + MOTH_BEGIN_INSTR(LoadScopeObjectProperty) + MOTH_END_INSTR(LoadScopeObjectProperty) + + MOTH_BEGIN_INSTR(StoreContextObjectProperty) + MOTH_END_INSTR(StoreContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadContextObjectProperty) + MOTH_END_INSTR(LoadContextObjectProperty) + + MOTH_BEGIN_INSTR(LoadIdObject) + MOTH_END_INSTR(LoadIdObject) + + MOTH_BEGIN_INSTR(CallValue) + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallPropertyLookup) + MOTH_END_INSTR(CallPropertyLookup) + + MOTH_BEGIN_INSTR(CallElement) + MOTH_END_INSTR(CallElement) + + MOTH_BEGIN_INSTR(CallName) + MOTH_END_INSTR(CallName) + + MOTH_BEGIN_INSTR(CallPossiblyDirectEval) + MOTH_END_INSTR(CallPossiblyDirectEval) + + MOTH_BEGIN_INSTR(CallGlobalLookup) + MOTH_END_INSTR(CallGlobalLookup) + + MOTH_BEGIN_INSTR(SetExceptionHandler) + labels.push_back(code - start + offset); + MOTH_END_INSTR(SetExceptionHandler) + + MOTH_BEGIN_INSTR(ThrowException) + MOTH_END_INSTR(ThrowException) + + MOTH_BEGIN_INSTR(GetException) + MOTH_END_INSTR(HasException) + + MOTH_BEGIN_INSTR(SetException) + MOTH_END_INSTR(SetExceptionFlag) + + MOTH_BEGIN_INSTR(CreateCallContext) + MOTH_END_INSTR(CreateCallContext) + + MOTH_BEGIN_INSTR(PushCatchContext) + MOTH_END_INSTR(PushCatchContext) + + MOTH_BEGIN_INSTR(PushWithContext) + MOTH_END_INSTR(PushWithContext) + + MOTH_BEGIN_INSTR(PopContext) + MOTH_END_INSTR(PopContext) + + MOTH_BEGIN_INSTR(ForeachIteratorObject) + MOTH_END_INSTR(ForeachIteratorObject) + + MOTH_BEGIN_INSTR(ForeachNextPropertyName) + MOTH_END_INSTR(ForeachNextPropertyName) + + MOTH_BEGIN_INSTR(DeleteMember) + MOTH_END_INSTR(DeleteMember) + + MOTH_BEGIN_INSTR(DeleteSubscript) + MOTH_END_INSTR(DeleteSubscript) + + MOTH_BEGIN_INSTR(DeleteName) + MOTH_END_INSTR(DeleteName) + + MOTH_BEGIN_INSTR(TypeofName) + MOTH_END_INSTR(TypeofName) + + MOTH_BEGIN_INSTR(TypeofValue) + MOTH_END_INSTR(TypeofValue) + + MOTH_BEGIN_INSTR(DeclareVar) + MOTH_END_INSTR(DeclareVar) + + MOTH_BEGIN_INSTR(DefineArray) + MOTH_END_INSTR(DefineArray) + + MOTH_BEGIN_INSTR(DefineObjectLiteral) + MOTH_END_INSTR(DefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) + MOTH_END_INSTR(CreateMappedArgumentsObject) + + MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) + MOTH_END_INSTR(CreateUnmappedArgumentsObject) + + MOTH_BEGIN_INSTR(ConvertThisToObject) + MOTH_END_INSTR(ConvertThisToObject) + + MOTH_BEGIN_INSTR(Construct) + MOTH_END_INSTR(Construct) + + MOTH_BEGIN_INSTR(Jump) + labels.push_back(code - start + offset); + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(JumpTrue) + labels.push_back(code - start + offset); + MOTH_END_INSTR(JumpTrue) + + MOTH_BEGIN_INSTR(JumpFalse) + labels.push_back(code - start + offset); + MOTH_END_INSTR(JumpFalse) + + MOTH_BEGIN_INSTR(CmpEqNull) + MOTH_END_INSTR(CmpEqNull) + + MOTH_BEGIN_INSTR(CmpNeNull) + MOTH_END_INSTR(CmpNeNull) + + MOTH_BEGIN_INSTR(CmpEqInt) + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNeInt) + MOTH_END_INSTR(CmpNeInt) + + MOTH_BEGIN_INSTR(CmpEq) + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNe) + MOTH_END_INSTR(CmpNe) + + MOTH_BEGIN_INSTR(CmpGt) + MOTH_END_INSTR(CmpGt) + + MOTH_BEGIN_INSTR(CmpGe) + MOTH_END_INSTR(CmpGe) + + MOTH_BEGIN_INSTR(CmpLt) + MOTH_END_INSTR(CmpLt) + + MOTH_BEGIN_INSTR(CmpLe) + MOTH_END_INSTR(CmpLe) + + MOTH_BEGIN_INSTR(CmpStrictEqual) + MOTH_END_INSTR(CmpStrictEqual) + + MOTH_BEGIN_INSTR(CmpStrictNotEqual) + MOTH_END_INSTR(CmpStrictNotEqual) + + MOTH_BEGIN_INSTR(CmpIn) + MOTH_END_INSTR(CmpIn) + + MOTH_BEGIN_INSTR(CmpInstanceOf) + MOTH_END_INSTR(CmpInstanceOf) + + MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt) + labels.push_back(code - start + offset); + MOTH_END_INSTR(JumpStrictEqualStackSlotInt) + + MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt) + labels.push_back(code - start + offset); + MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt) + + MOTH_BEGIN_INSTR(UNot) + MOTH_END_INSTR(UNot) + + MOTH_BEGIN_INSTR(UPlus) + MOTH_END_INSTR(UPlus) + + MOTH_BEGIN_INSTR(UMinus) + MOTH_END_INSTR(UMinus) + + MOTH_BEGIN_INSTR(UCompl) + MOTH_END_INSTR(UCompl) + + MOTH_BEGIN_INSTR(Increment) + MOTH_END_INSTR(PreIncrement) + + MOTH_BEGIN_INSTR(Decrement) + MOTH_END_INSTR(PreDecrement) + + MOTH_BEGIN_INSTR(Add) + MOTH_END_INSTR(Add) + + MOTH_BEGIN_INSTR(BitAnd) + MOTH_END_INSTR(BitAnd) + + MOTH_BEGIN_INSTR(BitOr) + MOTH_END_INSTR(BitOr) + + MOTH_BEGIN_INSTR(BitXor) + MOTH_END_INSTR(BitXor) + + MOTH_BEGIN_INSTR(UShr) + MOTH_END_INSTR(UShr) + + MOTH_BEGIN_INSTR(Shr) + MOTH_END_INSTR(Shr) + + MOTH_BEGIN_INSTR(Shl) + MOTH_END_INSTR(Shl) + + MOTH_BEGIN_INSTR(BitAndConst) + MOTH_END_INSTR(BitAndConst) + + MOTH_BEGIN_INSTR(BitOrConst) + MOTH_END_INSTR(BitOr) + + MOTH_BEGIN_INSTR(BitXorConst) + MOTH_END_INSTR(BitXor) + + MOTH_BEGIN_INSTR(UShrConst) + MOTH_END_INSTR(UShrConst) + + MOTH_BEGIN_INSTR(ShrConst) + MOTH_END_INSTR(ShrConst) + + MOTH_BEGIN_INSTR(ShlConst) + MOTH_END_INSTR(ShlConst) + + MOTH_BEGIN_INSTR(Mul) + MOTH_END_INSTR(Mul) + + MOTH_BEGIN_INSTR(Div) + MOTH_END_INSTR(Div) + + MOTH_BEGIN_INSTR(Mod) + MOTH_END_INSTR(Mod) + + MOTH_BEGIN_INSTR(Sub) + MOTH_END_INSTR(Sub) + + MOTH_BEGIN_INSTR(Ret) + MOTH_END_INSTR(Ret) + +#ifndef QT_NO_QML_DEBUGGER + MOTH_BEGIN_INSTR(Debug) + MOTH_END_INSTR(Debug) +#endif // QT_NO_QML_DEBUGGER + + MOTH_BEGIN_INSTR(LoadQmlContext) + MOTH_END_INSTR(LoadQmlContext) + + MOTH_BEGIN_INSTR(LoadQmlImportedScripts) + MOTH_END_INSTR(LoadQmlImportedScripts) + + MOTH_BEGIN_INSTR(LoadQmlSingleton) + MOTH_END_INSTR(LoadQmlSingleton) + } +} +#undef MOTH_BEGIN_INSTR +#undef MOTH_END_INSTR + +#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4jit_p.h new file mode 100644 index 0000000000..dfa2a79c48 --- /dev/null +++ b/src/qml/jit/qv4jit_p.h @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QV4JIT_P_H +#define QV4JIT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qv4global_p.h> +#include <private/qv4function_p.h> +#include <private/qv4instr_moth_p.h> + +//QT_REQUIRE_CONFIG(qml_jit); + +#define JIT_DEFINE_ARGS(nargs, ...) \ + MOTH_EXPAND_FOR_MSVC(JIT_DEFINE_ARGS##nargs(__VA_ARGS__)) + +#define JIT_DEFINE_ARGS0() +#define JIT_DEFINE_ARGS1(arg) \ + int arg +#define JIT_DEFINE_ARGS2(arg1, arg2) \ + int arg1, \ + int arg2 +#define JIT_DEFINE_ARGS3(arg1, arg2, arg3) \ + int arg1, \ + int arg2, \ + int arg3 +#define JIT_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \ + int arg1, \ + int arg2, \ + int arg3, \ + int arg4 + +#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \ + virtual void generate_##name( \ + JIT_DEFINE_ARGS(nargs, __VA_ARGS__) \ + ) = 0; + +#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \ + INSTR_##instr(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER) + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace JIT { + +class Assembler; + +class ByteCodeHandler +{ +public: + virtual ~ByteCodeHandler(); + + void decode(const char *code, uint len); + + int instructionOffset() const { return _offset; } + +protected: + FOR_EACH_MOTH_INSTR(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER) + + virtual void startInstruction(Moth::Instr::Type instr) = 0; + virtual void endInstruction(Moth::Instr::Type instr) = 0; + +private: + int _offset = 0; +}; + +#ifdef V4_ENABLE_JIT +class BaselineJIT final: public ByteCodeHandler +{ +public: + BaselineJIT(QV4::Function *); + virtual ~BaselineJIT(); + + void generate(); + + void generate_Ret() Q_DECL_OVERRIDE; + void generate_Debug() Q_DECL_OVERRIDE; + void generate_LoadConst(int index) Q_DECL_OVERRIDE; + void generate_LoadZero() Q_DECL_OVERRIDE; + void generate_LoadTrue() Q_DECL_OVERRIDE; + void generate_LoadFalse() Q_DECL_OVERRIDE; + void generate_LoadNull() Q_DECL_OVERRIDE; + void generate_LoadUndefined() Q_DECL_OVERRIDE; + void generate_LoadInt(int value) Q_DECL_OVERRIDE; + void generate_MoveConst(int constIndex, int destTemp) Q_DECL_OVERRIDE; + void generate_LoadReg(int reg) Q_DECL_OVERRIDE; + void generate_StoreReg(int reg) Q_DECL_OVERRIDE; + void generate_MoveReg(int srcReg, int destReg) Q_DECL_OVERRIDE; + void generate_LoadLocal(int index) Q_DECL_OVERRIDE; + void generate_StoreLocal(int index) Q_DECL_OVERRIDE; + void generate_LoadScopedLocal(int scope, int index) Q_DECL_OVERRIDE; + void generate_StoreScopedLocal(int scope, int index) Q_DECL_OVERRIDE; + void generate_LoadRuntimeString(int stringId) Q_DECL_OVERRIDE; + void generate_LoadRegExp(int regExpId) Q_DECL_OVERRIDE; + void generate_LoadClosure(int value) Q_DECL_OVERRIDE; + void generate_LoadName(int name) Q_DECL_OVERRIDE; + void generate_LoadGlobalLookup(int index) Q_DECL_OVERRIDE; + void generate_StoreNameSloppy(int name) Q_DECL_OVERRIDE; + void generate_StoreNameStrict(int name) Q_DECL_OVERRIDE; + void generate_LoadElement(int base, int index) Q_DECL_OVERRIDE; + void generate_LoadElementA(int base) Q_DECL_OVERRIDE; + void generate_StoreElement(int base, int index) Q_DECL_OVERRIDE; + void generate_LoadProperty(int name, int base) Q_DECL_OVERRIDE; + void generate_LoadPropertyA(int name) Q_DECL_OVERRIDE; + void generate_GetLookup(int index, int base) Q_DECL_OVERRIDE; + void generate_GetLookupA(int index) Q_DECL_OVERRIDE; + void generate_StoreProperty(int name, int base) Q_DECL_OVERRIDE; + void generate_SetLookup(int index, int base) Q_DECL_OVERRIDE; + void generate_StoreScopeObjectProperty(int base, + int propertyIndex) Q_DECL_OVERRIDE; + void generate_StoreContextObjectProperty(int base, + int propertyIndex) Q_DECL_OVERRIDE; + void generate_LoadScopeObjectProperty(int propertyIndex, int base, + int captureRequired) Q_DECL_OVERRIDE; + void generate_LoadContextObjectProperty(int propertyIndex, int base, + int captureRequired) Q_DECL_OVERRIDE; + void generate_LoadIdObject(int index, int base) Q_DECL_OVERRIDE; + void generate_CallValue(int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallProperty(int name, int base, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallElement(int base, int index, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallName(int name, int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallPossiblyDirectEval(int argc, int argv) Q_DECL_OVERRIDE; + void generate_CallGlobalLookup(int index, int argc, int argv) Q_DECL_OVERRIDE; + void generate_SetExceptionHandler(int offset) Q_DECL_OVERRIDE; + void generate_ThrowException() Q_DECL_OVERRIDE; + void generate_GetException() Q_DECL_OVERRIDE; + void generate_SetException() Q_DECL_OVERRIDE; + void generate_CreateCallContext() Q_DECL_OVERRIDE; + void generate_PushCatchContext(int name, int reg) Q_DECL_OVERRIDE; + void generate_PushWithContext(int reg) Q_DECL_OVERRIDE; + void generate_PopContext(int reg) Q_DECL_OVERRIDE; + void generate_ForeachIteratorObject() Q_DECL_OVERRIDE; + void generate_ForeachNextPropertyName() Q_DECL_OVERRIDE; + void generate_DeleteMember(int member, int base) Q_DECL_OVERRIDE; + void generate_DeleteSubscript(int base, int index) Q_DECL_OVERRIDE; + void generate_DeleteName(int name) Q_DECL_OVERRIDE; + void generate_TypeofName(int name) Q_DECL_OVERRIDE; + void generate_TypeofValue() Q_DECL_OVERRIDE; + void generate_DeclareVar(int varName, int isDeletable) Q_DECL_OVERRIDE; + void generate_DefineArray(int argc, int args) Q_DECL_OVERRIDE; + void generate_DefineObjectLiteral(int internalClassId, int arrayValueCount, + int arrayGetterSetterCountAndFlags, + int args) Q_DECL_OVERRIDE; + void generate_CreateMappedArgumentsObject() Q_DECL_OVERRIDE; + void generate_CreateUnmappedArgumentsObject() Q_DECL_OVERRIDE; + void generate_ConvertThisToObject() Q_DECL_OVERRIDE; + void generate_Construct(int func, int argc, int argv) Q_DECL_OVERRIDE; + void generate_Jump(int offset) Q_DECL_OVERRIDE; + void generate_JumpTrue(int offset) Q_DECL_OVERRIDE; + void generate_JumpFalse(int offset) Q_DECL_OVERRIDE; + void generate_CmpEqNull() Q_DECL_OVERRIDE; + void generate_CmpNeNull() Q_DECL_OVERRIDE; + void generate_CmpEqInt(int lhs) Q_DECL_OVERRIDE; + void generate_CmpNeInt(int lhs) Q_DECL_OVERRIDE; + void generate_CmpEq(int lhs) Q_DECL_OVERRIDE; + void generate_CmpNe(int lhs) Q_DECL_OVERRIDE; + void generate_CmpGt(int lhs) Q_DECL_OVERRIDE; + void generate_CmpGe(int lhs) Q_DECL_OVERRIDE; + void generate_CmpLt(int lhs) Q_DECL_OVERRIDE; + void generate_CmpLe(int lhs) Q_DECL_OVERRIDE; + void generate_CmpStrictEqual(int lhs) Q_DECL_OVERRIDE; + void generate_CmpStrictNotEqual(int lhs) Q_DECL_OVERRIDE; + void generate_CmpIn(int lhs) Q_DECL_OVERRIDE; + void generate_CmpInstanceOf(int lhs) Q_DECL_OVERRIDE; + void generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, + int offset) Q_DECL_OVERRIDE; + void generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, + int offset) Q_DECL_OVERRIDE; + void generate_UNot() Q_DECL_OVERRIDE; + void generate_UPlus() Q_DECL_OVERRIDE; + void generate_UMinus() Q_DECL_OVERRIDE; + void generate_UCompl() Q_DECL_OVERRIDE; + void generate_Increment() Q_DECL_OVERRIDE; + void generate_Decrement() Q_DECL_OVERRIDE; + void generate_Add(int lhs) Q_DECL_OVERRIDE; + void generate_BitAnd(int lhs) Q_DECL_OVERRIDE; + void generate_BitOr(int lhs) Q_DECL_OVERRIDE; + void generate_BitXor(int lhs) Q_DECL_OVERRIDE; + void generate_UShr(int lhs) Q_DECL_OVERRIDE; + void generate_Shr(int lhs) Q_DECL_OVERRIDE; + void generate_Shl(int lhs) Q_DECL_OVERRIDE; + void generate_BitAndConst(int rhs) Q_DECL_OVERRIDE; + void generate_BitOrConst(int rhs) Q_DECL_OVERRIDE; + void generate_BitXorConst(int rhs) Q_DECL_OVERRIDE; + void generate_UShrConst(int rhs) Q_DECL_OVERRIDE; + void generate_ShrConst(int rhs) Q_DECL_OVERRIDE; + void generate_ShlConst(int rhs) Q_DECL_OVERRIDE; + void generate_Mul(int lhs) Q_DECL_OVERRIDE; + void generate_Div(int lhs) Q_DECL_OVERRIDE; + void generate_Mod(int lhs) Q_DECL_OVERRIDE; + void generate_Sub(int lhs) Q_DECL_OVERRIDE; + void generate_LoadQmlContext(int result) Q_DECL_OVERRIDE; + void generate_LoadQmlImportedScripts(int result) Q_DECL_OVERRIDE; + void generate_LoadQmlSingleton(int name) Q_DECL_OVERRIDE; + + void startInstruction(Moth::Instr::Type instr) Q_DECL_OVERRIDE; + void endInstruction(Moth::Instr::Type instr) Q_DECL_OVERRIDE; + +protected: + bool hasLabel() const + { return std::find(labels.cbegin(), labels.cend(), instructionOffset()) != labels.cend(); } + +private: + void collectLabelsInBytecode(); + +private: + QV4::Function *function; + QScopedPointer<Assembler> as; + std::vector<int> labels; +}; +#endif // V4_ENABLE_JIT + +} // namespace JIT +} // namespace QV4 + +QT_END_NAMESPACE + +#endif // QV4JIT_P_H diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index b92bd3992d..beb856c2f6 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -793,7 +793,7 @@ int CppStackFrame::lineNumber() const }; const QV4::CompiledData::Function *cf = v4Function->compiledFunction; - uint offset = static_cast<uint>(instructionPointer - v4Function->codeData); + uint offset = instructionPointer; const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); uint nLineNumbers = cf->nLineNumbers; const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; @@ -1545,6 +1545,15 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } +bool ExecutionEngine::canJIT() +{ +#ifdef V4_ENABLE_JIT + return true; +#else + return false; +#endif +} + // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index d165294a87..148dec44bd 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -94,7 +94,7 @@ struct Q_QML_EXPORT CppStackFrame { CallData *jsFrame; const Value *originalArguments; int originalArgumentsCount; - const uchar *instructionPointer; + int instructionPointer; QString source() const; QString function() const; @@ -477,6 +477,8 @@ public: bool checkStackLimits(); + static bool canJIT(); + private: #ifndef QT_NO_QML_DEBUGGER QScopedPointer<QV4::Debugging::Debugger> m_debugger; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 32a72fda3f..83e861138b 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -46,6 +46,7 @@ #include "qv4lookup_p.h" #include <private/qv4mm_p.h> #include <private/qv4identifiertable_p.h> +#include <assembler/MacroAssemblerCodeRef.h> QT_BEGIN_NAMESPACE @@ -56,6 +57,8 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, , compilationUnit(unit) , code(codePtr) , codeData(function->code()) + , jittedCode(nullptr) + , codeRef(nullptr) , hasQmlDependencies(function->hasQmlDependencies()) { Q_UNUSED(engine); @@ -76,6 +79,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, Function::~Function() { + delete codeRef; } void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 6c62881998..0e61be5115 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -56,6 +56,10 @@ #include <private/qv4context_p.h> #include <private/qv4vme_moth_p.h> +namespace JSC { +class MacroAssemblerCodeRef; +} + QT_BEGIN_NAMESPACE namespace QV4 { @@ -72,6 +76,10 @@ struct Q_QML_EXPORT Function { Code code; const uchar *codeData; + typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *); + JittedCode jittedCode; + JSC::MacroAssemblerCodeRef *codeRef; + // first nArguments names in internalClass are the actual arguments InternalClass *internalClass; uint nFormals; diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 6bdeda3313..090a164ef6 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -107,8 +107,8 @@ inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } # if defined(Q_OS_LINUX) # define V4_ENABLE_JIT # endif -#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) -# define V4_ENABLE_JIT +//#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) +//# define V4_ENABLE_JIT #endif // Black list some platforms diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 9159f55245..e2071986a5 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -60,6 +60,8 @@ #include "qv4alloca_p.h" +#include <private/qv4jit_p.h> + #undef COUNT_INSTRUCTIONS extern "C" { @@ -477,7 +479,7 @@ static bool compareEqualInt(Value &accumulator, Value lhs, int rhs) } } -#define STORE_IP() frame.instructionPointer = code; +#define STORE_IP() frame.instructionPointer = int(code - codeStart); #define STORE_ACC() accumulator = acc; #define ACC Primitive::fromReturnedValue(acc) #define VALUE_TO_INT(i, val) \ @@ -542,22 +544,37 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const Value *thisObject, frame.parent = engine->currentStackFrame; frame.v4Function = function; - frame.instructionPointer = function->codeData; + frame.instructionPointer = 0; frame.jsFrame = callData; engine->currentStackFrame = &frame; } CHECK_STACK_LIMITS(engine); - Profiling::FunctionCallProfiler profiler(engine, function); - if (QV4::Debugging::Debugger *debugger = engine->debugger()) - debugger->enteringFunction(); + Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling + QV4::Debugging::Debugger *debugger = engine->debugger(); const uchar *exceptionHandler = 0; QV4::Value &accumulator = frame.jsFrame->accumulator; QV4::ReturnedValue acc = Encode::undefined(); +#ifdef V4_ENABLE_JIT + static const bool forceInterpreter = qEnvironmentVariableIsSet("QV4_FORCE_INTERPRETER"); + if (function->jittedCode == nullptr) { + if (ExecutionEngine::canJIT() && debugger == nullptr && !forceInterpreter) + QV4::JIT::BaselineJIT(function).generate(); + } +#endif // V4_ENABLE_JIT + + if (debugger) + debugger->enteringFunction(); + + if (function->jittedCode != nullptr && debugger == nullptr) { + acc = function->jittedCode(&frame, engine); + } else { + // interpreter const uchar *code = function->codeData; + const uchar *codeStart = code; MOTH_JUMP_TABLE; @@ -660,7 +677,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const Value *thisObject, STORE_ACC(); Runtime::method_storeNameStrict(engine, name, accumulator); CHECK_EXCEPTION; - MOTH_END_INSTR(StoreNameSloppy) + MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(StoreNameSloppy) STORE_IP(); @@ -835,7 +852,8 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const Value *thisObject, MOTH_END_INSTR(ThrowException) MOTH_BEGIN_INSTR(GetException) - acc = engine->hasException ? engine->exceptionValue->asReturnedValue() : Primitive::emptyValue().asReturnedValue(); + acc = engine->hasException ? engine->exceptionValue->asReturnedValue() + : Primitive::emptyValue().asReturnedValue(); engine->hasException = false; MOTH_END_INSTR(HasException) @@ -1352,6 +1370,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const Value *thisObject, } code = exceptionHandler; } + } functionExit: if (QV4::Debugging::Debugger *debugger = engine->debugger()) diff --git a/src/qml/qml.pro b/src/qml/qml.pro index fb50588177..c13227d8fe 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -58,6 +58,7 @@ include(parser/parser.pri) include(compiler/compiler.pri) include(jsapi/jsapi.pri) include(jsruntime/jsruntime.pri) +include(jit/jit.pri) include(qml/qml.pri) include(debugger/debugger.pri) qtConfig(animation) { |