aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jit/qv4assembler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jit/qv4assembler.cpp')
-rw-r--r--src/qml/jit/qv4assembler.cpp2251
1 files changed, 1744 insertions, 507 deletions
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index d062f3bbb2..e357211074 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -37,510 +37,1105 @@
**
****************************************************************************/
-#include "qv4isel_masm_p.h"
-#include "qv4runtime_p.h"
-#include "qv4ssa_p.h"
-#include "qv4regalloc_p.h"
+#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>
-#if !defined(V4_BOOTSTRAP)
-#include "qv4function_p.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
-#include <iostream>
-#include <QBuffer>
-#include <QCoreApplication>
+#ifdef V4_ENABLE_JIT
-#if ENABLE(ASSEMBLER)
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace JIT {
-#if USE(UDIS86)
-# include <udis86.h>
-#endif
+#define callHelper(x) PlatformAssemblerCommon::callRuntime(#x, reinterpret_cast<void *>(&x))
-using namespace QV4;
-using namespace QV4::JIT;
+const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer;
-CompilationUnit::~CompilationUnit()
+static ReturnedValue toNumberHelper(ReturnedValue v)
{
+ return Encode(Value::fromReturnedValue(v).toNumber());
}
-#if !defined(V4_BOOTSTRAP)
+static ReturnedValue toInt32Helper(ReturnedValue v)
+{
+ return Encode(Value::fromReturnedValue(v).toInt32());
+}
-void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
+#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>
{
- runtimeFunctions.resize(data->functionTableSize);
- runtimeFunctions.fill(0);
- for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
- const CompiledData::Function *compiledFunction = data->functionAt(i);
+ 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 ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg5Reg, so do not use while setting up a call!
+ 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);
+ }
- QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction,
- (ReturnedValue (*)(QV4::ExecutionEngine *, const uchar *)) codeRefs[i].code().executableAddress());
- runtimeFunctions[i] = runtimeFunction;
+ 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);
}
-}
-bool CompilationUnit::memoryMapCode(QString *errorString)
-{
- Q_UNUSED(errorString);
- codeRefs.resize(data->functionTableSize);
+ void generatePlatformFunctionExit()
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ ret();
+ }
- const char *basePtr = reinterpret_cast<const char *>(data);
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
- for (uint i = 0; i < data->functionTableSize; ++i) {
- const CompiledData::Function *compiledFunction = data->functionAt(i);
- void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset));
- JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr));
- JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize);
- codeRefs[i] = codeRef;
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
- if (showCode) {
- WTF::dataLogF("Mapped JIT code for %s\n", qPrintable(stringAt(compiledFunction->nameIndex)));
- disassemble(codeRef.code(), compiledFunction->codeSize, " ", WTF::dataFile());
- }
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
}
+};
- return true;
-}
+typedef PlatformAssembler_X86_64_SysV PlatformAssemblerBase;
-#endif // !defined(V4_BOOTSTRAP)
+#endif
+#if defined(Q_OS_WIN)
-void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit)
+struct PlatformAssembler_Win64 : JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
{
- const int codeAlignment = 16;
- quint64 offset = WTF::roundUpToMultipleOf(codeAlignment, unit->unitSize);
- Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
- for (int i = 0; i < codeRefs.size(); ++i) {
- CompiledData::Function *compiledFunction = const_cast<CompiledData::Function *>(unit->functionAt(i));
- compiledFunction->codeOffset = offset;
- compiledFunction->codeSize = codeRefs.at(i).size();
- offset = WTF::roundUpToMultipleOf(codeAlignment, offset + compiledFunction->codeSize);
+ 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 ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg3Reg, so do not use while setting up a call!
+ 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);
}
-}
-bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString)
+ 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>
{
- Q_ASSERT(device->pos() == unit->unitSize);
- Q_ASSERT(device->atEnd());
- Q_ASSERT(int(unit->functionTableSize) == codeRefs.size());
+ 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);
+ }
- QByteArray padding;
+ 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);
+ }
- for (int i = 0; i < codeRefs.size(); ++i) {
- const CompiledData::Function *compiledFunction = unit->functionAt(i);
+ void generatePlatformFunctionExit()
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ ret();
+ }
- if (device->pos() > qint64(compiledFunction->codeOffset)) {
- *errorString = QStringLiteral("Invalid state of cache file to write.");
- return false;
- }
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
- const quint64 paddingSize = compiledFunction->codeOffset - device->pos();
- padding.fill(0, paddingSize);
- qint64 written = device->write(padding);
- if (written != padding.size()) {
- *errorString = device->errorString();
- return false;
- }
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
- const void *undecoratedCodePtr = codeRefs.at(i).code().dataLocation();
- written = device->write(reinterpret_cast<const char *>(undecoratedCodePtr), compiledFunction->codeSize);
- if (written != qint64(compiledFunction->codeSize)) {
- *errorString = device->errorString();
- return false;
- }
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
}
- return true;
-}
+};
-template <typename TargetConfiguration>
-Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator)
- : _function(function)
- , _nextBlock(0)
- , _executableAllocator(executableAllocator)
- , _jsGenerator(jsGenerator)
-{
- _addrs.resize(_function->basicBlockCount());
- _patches.resize(_function->basicBlockCount());
- _labelPatches.resize(_function->basicBlockCount());
-}
+typedef PlatformAssembler_X86_All PlatformAssemblerBase;
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock)
-{
- _addrs[block->index()] = label();
- catchBlock = block->catchBlock;
- _nextBlock = nextBlock;
-}
+#endif
+
+#if defined(Q_PROCESSOR_ARM_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target)
+struct PlatformAssembler_ARM64 : JSC::MacroAssembler<JSC::MacroAssemblerARM64>
{
- Q_UNUSED(current);
+ 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 ScratchRegister2 = JSC::ARM64Registers::x7; // Note: overlaps with Arg7Reg, so do not use while setting up a call!
+ 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);
+ }
- if (target != _nextBlock)
- _patches[target->index()].push_back(jump());
-}
+ void pop(RegisterID dest)
+ {
+ popToRestore(dest);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::addPatch(IR::BasicBlock* targetBlock, Jump targetJump)
-{
- _patches[targetBlock->index()].push_back(targetJump);
-}
+ void pop()
+ {
+ add64(TrustedImm32(16), stackPointerRegister);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, Label target)
-{
- DataLabelPatch p;
- p.dataLabel = patch;
- p.target = target;
- _dataLabelPatches.push_back(p);
-}
+ void popValue()
+ {
+ pop();
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, IR::BasicBlock *target)
-{
- _labelPatches[target->index()].push_back(patch);
-}
+ 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);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock)
-{
- generateCJumpOnCompare(RelationalCondition::NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock);
-}
+ void generatePlatformFunctionExit()
+ {
+ move(AccumulatorRegister, ReturnValueRegister);
+ popPair(EngineRegister, CppStackFrameRegister);
+ popPair(JSStackFrameRegister, AccumulatorRegister);
+ popPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
+ ret();
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond,
- RegisterID left,
- TrustedImm32 right,
- IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- if (trueBlock == _nextBlock) {
- Jump target = branch32(invert(cond), left, right);
- addPatch(falseBlock, target);
- } else {
- Jump target = branch32(cond, left, right);
- addPatch(trueBlock, target);
- jumpToBlock(currentBlock, falseBlock);
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
}
-}
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond,
- RegisterID left,
- RegisterID right,
- IR::BasicBlock *currentBlock,
- IR::BasicBlock *trueBlock,
- IR::BasicBlock *falseBlock)
-{
- if (trueBlock == _nextBlock) {
- Jump target = branch32(invert(cond), left, right);
- addPatch(falseBlock, target);
- } else {
- Jump target = branch32(cond, left, right);
- addPatch(trueBlock, target);
- jumpToBlock(currentBlock, falseBlock);
+ void pushAligned(RegisterID reg)
+ {
+ pushToSave(reg);
}
-}
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Pointer
-Assembler<TargetConfiguration>::loadAddressForWriting(RegisterID tmp, IR::Expr *e, WriteBarrier::Type *barrier)
-{
- if (barrier)
- *barrier = WriteBarrier::NoBarrier;
- IR::Temp *t = e->asTemp();
- if (t)
- return loadTempAddress(t);
- else
- return loadArgLocalAddressForWriting(tmp, e->asArgLocal(), barrier);
-}
+ void popAligned(RegisterID reg)
+ {
+ popToRestore(reg);
+ }
+};
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadTempAddress(IR::Temp *t)
+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>
{
- if (t->kind == IR::Temp::StackSlot)
- return stackSlotPointer(t);
- else
- Q_UNREACHABLE();
-}
+ 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);
+ }
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Pointer
-Assembler<TargetConfiguration>::loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier)
+ 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
{
- if (barrier)
- *barrier = _function->argLocalRequiresWriteBarrier(al) ? WriteBarrier::Barrier : WriteBarrier::NoBarrier;
+ 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);
+ }
- int32_t offset = 0;
- int scope = al->scope;
- loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(EngineBase, current))), baseReg);
+ Address contextAddress() const
+ {
+ return Address(JSStackFrameRegister, offsetof(CallData, context));
+ }
- const qint32 outerOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, outer));
- const qint32 localsOffset = targetStructureOffset(Heap::CallContextData::baseOffset + offsetof(Heap::CallContextData, function))
- + 8 // locals is always 8 bytes away from function, regardless of pointer size.
- + offsetof(ValueArray<0>, values);
+ 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();
+ }
+ }
- while (scope) {
- loadPtr(Address(baseReg, outerOffset), baseReg);
- --scope;
+ void callRuntime(const char *functionName, const void *funcPtr)
+ {
+ functions.insert(funcPtr, functionName);
+ callAbsolute(funcPtr);
}
- switch (al->kind) {
- case IR::ArgLocal::Formal:
- case IR::ArgLocal::ScopedFormal: {
- if (barrier && *barrier == WriteBarrier::Barrier) {
- // if we need a barrier, the baseReg has to point to the ExecutionContext
- // callData comes directly after locals, calculate the offset using that
- offset = localsOffset + _function->localsCountForScope(al) * sizeof(Value);
- offset += sizeof(CallData) + (al->index - 1) * sizeof(Value);
- } else {
- const qint32 callDataOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData));
- loadPtr(Address(baseReg, callDataOffset), baseReg);
- offset = sizeof(CallData) + (al->index - 1) * sizeof(Value);
+
+ 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;
}
- } break;
- case IR::ArgLocal::Local:
- case IR::ArgLocal::ScopedLocal: {
- offset = localsOffset + al->index * sizeof(Value);
- } break;
- default:
- Q_UNREACHABLE();
+
+ functionExit = label();
+ generatePlatformFunctionExit();
}
- return Pointer(baseReg, offset);
-}
+};
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadStringAddress(RegisterID reg, const QString &string)
+#if QT_POINTER_SIZE == 8 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+struct PlatformAssembler64 : PlatformAssemblerCommon
{
- loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), Assembler::ScratchRegister);
- loadPtr(Address(Assembler::ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, compilationUnit))), Assembler::ScratchRegister);
- loadPtr(Address(Assembler::ScratchRegister, offsetof(CompiledData::CompilationUnitBase, runtimeStrings)), reg);
- const int id = _jsGenerator->registerString(string);
- return Pointer(reg, id * RegisterSize);
-}
+ void callRuntime(const char *functionName, const void *funcPtr,
+ Assembler::CallResultDestination dest)
+ {
+ PlatformAssemblerCommon::callRuntime(functionName, funcPtr);
+ if (dest == Assembler::ResultInAccumulator)
+ move(ReturnValueRegister, AccumulatorRegister);
+ }
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg)
-{
- return loadConstant(convertToValue<TargetPrimitive>(c), baseReg);
-}
+ void loadUndefined(RegisterID dest = AccumulatorRegister)
+ {
+ move(TrustedImm64(0), dest);
+ }
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const TargetPrimitive &v, RegisterID baseReg)
-{
- loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), baseReg);
- loadPtr(Address(baseReg, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, constantTable))), baseReg);
- const int index = _jsGenerator->registerConstant(v.rawValue());
- return Address(baseReg, index * sizeof(QV4::Value));
-}
+ void copyConst(int constIndex, Address dest)
+ {
+ //###
+ if (constantTable[constIndex].isUndefined()) {
+ loadUndefined(ScratchRegister);
+ } else {
+ load64(loadConstAddress(constIndex, ScratchRegister), ScratchRegister);
+ }
+ store64(ScratchRegister, dest);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString &string)
-{
- const int id = _jsGenerator->registerString(string);
- move(TrustedImm32(id), reg);
-}
+ void copyReg(Address src, Address dst)
+ {
+ loadReg(src, ScratchRegister);
+ store64(ScratchRegister, dst);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::storeValue(TargetPrimitive value, IR::Expr *destination)
-{
- WriteBarrier::Type barrier;
- Address addr = loadAddressForWriting(ScratchRegister, destination, &barrier);
- storeValue(value, addr, barrier);
-}
+ void loadReg(Address addr, RegisterID dest = AccumulatorRegister)
+ {
+ load64(addr, dest);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::enterStandardStackFrame(const RegisterInformation &regularRegistersToSave,
- const RegisterInformation &fpRegistersToSave)
-{
- platformEnterStandardStackFrame(this);
+ void loadAccumulator(Address addr)
+ {
+ load64(addr, AccumulatorRegister);
+ }
- move(StackPointerRegister, JITTargetPlatform::FramePointerRegister);
+ void storeAccumulator(Address addr)
+ {
+ store64(AccumulatorRegister, addr);
+ }
- const int frameSize = _stackLayout->calculateStackFrameSize();
- subPtr(TrustedImm32(frameSize), StackPointerRegister);
+ void loadString(int stringId)
+ {
+ loadAccumulator(loadStringAddress(stringId));
+ }
- Address slotAddr(JITTargetPlatform::FramePointerRegister, 0);
- for (int i = 0, ei = fpRegistersToSave.size(); i < ei; ++i) {
- Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint());
- slotAddr.offset -= sizeof(double);
- TargetConfiguration::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr);
+ void loadValue(ReturnedValue value)
+ {
+ move(TrustedImm64(value), AccumulatorRegister);
}
- for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) {
- Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister());
- slotAddr.offset -= RegisterSize;
- storePtr(regularRegistersToSave.at(i).reg<RegisterID>(), slotAddr);
+
+ void generateCatchTrampoline()
+ {
+ PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
}
- platformFinishEnteringStandardStackFrame(this);
-}
+ 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);
+ }
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::leaveStandardStackFrame(const RegisterInformation &regularRegistersToSave,
- const RegisterInformation &fpRegistersToSave)
-{
- Address slotAddr(JITTargetPlatform::FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double));
+ void toNumber()
+ {
+ move(AccumulatorRegister, registerForArg(0));
+ callHelper(toNumberHelper);
+ move(ReturnValueRegister, AccumulatorRegister);
+ }
- // restore the callee saved registers
- for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) {
- Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister());
- loadPtr(slotAddr, regularRegistersToSave.at(i).reg<RegisterID>());
- slotAddr.offset += RegisterSize;
+ void toInt32()
+ {
+ move(AccumulatorRegister, registerForArg(0));
+ callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper),
+ Assembler::ResultInAccumulator);
}
- for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) {
- Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint());
- TargetConfiguration::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>());
- slotAddr.offset += sizeof(double);
+
+ void regToInt32(Address srcReg, RegisterID targetReg)
+ {
+ pushAligned(AccumulatorRegister);
+ load64(srcReg, registerForArg(0));
+ callHelper(toInt32Helper);
+ move(ReturnValueRegister, targetReg);
+ popAligned(AccumulatorRegister);
}
- Q_ASSERT(slotAddr.offset == 0);
+ void isNullOrUndefined()
+ {
+ move(AccumulatorRegister, ScratchRegister);
+ compare64(Equal, ScratchRegister, TrustedImm32(0), AccumulatorRegister);
+ Jump isUndef = branch32(NotEqual, TrustedImm32(0), AccumulatorRegister);
- const int frameSize = _stackLayout->calculateStackFrameSize();
- platformLeaveStandardStackFrame(this, frameSize);
-}
+ // 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 });
+ }
-// Try to load the source expression into the destination FP register. This assumes that two
-// general purpose (integer) registers are available: the ScratchRegister and the
-// ReturnValueRegister. It returns a Jump if no conversion can be performed.
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::genTryDoubleConversion(IR::Expr *src, FPRegisterID dest)
-{
- switch (src->type) {
- case IR::DoubleType:
- moveDouble(toDoubleRegister(src, dest), dest);
- return Assembler::Jump();
- case IR::SInt32Type:
- convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister),
- dest);
- return Assembler::Jump();
- case IR::UInt32Type:
- convertUInt32ToDouble(toUInt32Register(src, Assembler::ScratchRegister),
- dest, Assembler::ReturnValueRegister);
- return Assembler::Jump();
- case IR::NullType:
- case IR::UndefinedType:
- case IR::BoolType:
- // TODO?
- case IR::StringType:
- return jump();
- default:
- break;
+ void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
+ {
+ if (sourceReg == NoRegister)
+ or64(TrustedImm64(int64_t(tag) << 32), AccumulatorRegister);
+ else
+ or64(TrustedImm64(int64_t(tag) << 32), sourceReg, AccumulatorRegister);
}
- Q_ASSERT(src->asTemp() || src->asArgLocal());
+ void encodeDoubleIntoAccumulator(FPRegisterID src)
+ {
+ moveDoubleTo64(src, AccumulatorRegister);
+ move(TrustedImm64(Value::NaNEncodeMask), ScratchRegister);
+ xor64(ScratchRegister, AccumulatorRegister);
+ }
- // It's not a number type, so it cannot be in a register.
- Q_ASSERT(src->asArgLocal() || src->asTemp()->kind != IR::Temp::PhysicalRegister || src->type == IR::BoolType);
+ void pushValue(ReturnedValue v)
+ {
+ loadValue(v);
+ push(AccumulatorRegister);
+ }
- Assembler::Pointer tagAddr = loadAddressForReading(Assembler::ScratchRegister, src);
- tagAddr.offset += 4;
- load32(tagAddr, Assembler::ScratchRegister);
+ void pushValueAligned(ReturnedValue v)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ pushValue(v);
+ }
- // check if it's an int32:
- Assembler::Jump isNoInt = branch32(Assembler::NotEqual, Assembler::ScratchRegister,
- Assembler::TrustedImm32(quint32(ValueTypeInternal::Integer)));
- convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest);
- Assembler::Jump intDone = jump();
+ void popValueAligned()
+ {
+ addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
+ }
- // not an int, check if it's a double:
- isNoInt.link(this);
- Assembler::Jump isNoDbl = RegisterSizeDependentOps::checkIfTagRegisterIsDouble(this, ScratchRegister);
- toDoubleRegister(src, dest);
- intDone.link(this);
+ Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath)
+ {
+ urshift64(AccumulatorRegister, TrustedImm32(32), ScratchRegister);
+ Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister);
+ load64(lhsAddr, ScratchRegister);
+ urshift64(ScratchRegister, TrustedImm32(32), ScratchRegister2);
+ Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister2);
+
+ // both integer
+ Jump failure = fastPath();
+ Jump done = jump();
+
+ // all other cases
+ if (failure.isSet())
+ failure.link(this);
+ accNotInt.link(this);
+ lhsNotInt.link(this);
+
+ return done;
+ }
+};
- return isNoDbl;
-}
+typedef PlatformAssembler64 PlatformAssembler;
+#endif
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchDouble(bool invertCondition, IR::AluOp op,
- IR::Expr *left, IR::Expr *right)
+#if QT_POINTER_SIZE == 4 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+struct PlatformAssembler32 : PlatformAssemblerCommon
{
- DoubleCondition cond;
- switch (op) {
- case IR::OpGt: cond = Assembler::DoubleGreaterThan; break;
- case IR::OpLt: cond = Assembler::DoubleLessThan; break;
- case IR::OpGe: cond = Assembler::DoubleGreaterThanOrEqual; break;
- case IR::OpLe: cond = Assembler::DoubleLessThanOrEqual; break;
- case IR::OpEqual:
- case IR::OpStrictEqual: cond = Assembler::DoubleEqual; break;
- case IR::OpNotEqual:
- case IR::OpStrictNotEqual: cond = Assembler::DoubleNotEqualOrUnordered; break; // No, the inversion of DoubleEqual is NOT DoubleNotEqual.
- default:
- Q_UNREACHABLE();
+ 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);
+ }
}
- if (invertCondition)
- cond = TargetConfiguration::MacroAssembler::invert(cond);
- return TargetConfiguration::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, JITTargetPlatform::FPGpr1));
-}
+ void loadUndefined()
+ {
+ move(TrustedImm32(0), AccumulatorRegisterValue);
+ move(TrustedImm32(0), AccumulatorRegisterTag);
+ }
-template <typename TargetConfiguration>
-typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right)
-{
- Assembler::RelationalCondition cond;
- switch (op) {
- case IR::OpGt: cond = Assembler::GreaterThan; break;
- case IR::OpLt: cond = Assembler::LessThan; break;
- case IR::OpGe: cond = Assembler::GreaterThanOrEqual; break;
- case IR::OpLe: cond = Assembler::LessThanOrEqual; break;
- case IR::OpEqual:
- case IR::OpStrictEqual: cond = Assembler::Equal; break;
- case IR::OpNotEqual:
- case IR::OpStrictNotEqual: cond = Assembler::NotEqual; break;
- default:
- Q_UNREACHABLE();
+ 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);
+ }
}
- if (invertCondition)
- cond = TargetConfiguration::MacroAssembler::invert(cond);
-
- return TargetConfiguration::MacroAssembler::branch32(cond,
- toInt32Register(left, Assembler::ScratchRegister),
- toInt32Register(right, Assembler::ReturnValueRegister));
-}
-
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave)
-{
- _stackLayout.reset(new StackLayout(_function, maxArgCountForBuiltins, regularRegistersToSave, fpRegistersToSave));
-}
-
-template <typename TargetConfiguration>
-void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave)
-{
- if (!s) {
- // this only happens if the method doesn't have a return statement and can
- // only exit through an exception
- } else if (IR::Temp *t = s->expr->asTemp()) {
- RegisterSizeDependentOps::setFunctionReturnValueFromTemp(this, t);
- } else if (IR::Const *c = s->expr->asConst()) {
- auto retVal = convertToValue<TargetPrimitive>(c);
- RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal);
- } else {
- Q_UNREACHABLE();
- Q_UNUSED(s);
+
+ 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, RegisterID sourceReg = NoRegister)
+ {
+ if (sourceReg != NoRegister)
+ move(sourceReg, AccumulatorRegisterValue);
+ move(TrustedImm32(int(tag)), AccumulatorRegisterTag);
+ }
+
+ void encodeDoubleIntoAccumulator(FPRegisterID src)
+ {
+ moveDoubleToInts(src, AccumulatorRegisterValue, AccumulatorRegisterTag);
+ xor32(TrustedImm32(Value::NaNEncodeMask >> 32), AccumulatorRegisterTag);
+ }
+
+ void pushValueAligned(ReturnedValue v)
+ {
+ pushValue(v);
}
- Label leaveStackFrame = label();
+ void popValueAligned()
+ {
+ popValue();
+ }
+
+ Jump binopBothIntPath(Address lhsAddr, std::function<Jump(void)> fastPath)
+ {
+ Jump accNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), AccumulatorRegisterTag);
+ Address lhsAddrTag = lhsAddr; lhsAddrTag.offset += Value::tagOffset();
+ load32(lhsAddrTag, ScratchRegister);
+ Jump lhsNotInt = branch32(NotEqual, TrustedImm32(int(IntegerTag)), ScratchRegister);
+
+ // both integer
+ Address lhsAddrValue = lhsAddr; lhsAddrValue.offset += Value::valueOffset();
+ load32(lhsAddrValue, ScratchRegister);
+ Jump failure = fastPath();
+ Jump done = jump();
+
+ // all other cases
+ if (failure.isSet())
+ failure.link(this);
+ accNotInt.link(this);
+ lhsNotInt.link(this);
+
+ return done;
+ }
+};
+
+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;
- const int locals = stackLayout().calculateJSStackFrameSize();
- subPtr(TrustedImm32(sizeof(QV4::Value)*locals), JITTargetPlatform::LocalsRegister);
- storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop))));
+#define pasm() reinterpret_cast<PlatformAssembler *>(this->d)
- leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave);
- ret();
+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;
+}
- exceptionReturnLabel = label();
- auto retVal = TargetPrimitive::undefinedValue();
- RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal);
- jump(leaveStackFrame);
+Assembler::~Assembler()
+{
+ delete pasm();
+}
+
+void Assembler::generatePrologue()
+{
+ pasm()->generateFunctionEntry();
+}
+
+void Assembler::generateEpilogue()
+{
+ pasm()->generateCatchTrampoline();
}
namespace {
@@ -560,7 +1155,7 @@ public:
~QIODevicePrintStream()
{}
- void vprintf(const char* format, va_list argList) override WTF_ATTRIBUTE_PRINTF(2, 0)
+ 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)
@@ -568,7 +1163,7 @@ public:
memset(buf.data(), 0, qMin(written, buf.size()));
}
- void flush() override
+ void flush()
{}
private:
@@ -577,150 +1172,792 @@ private:
};
} // anonymous namespace
-static void printDisassembledOutputWithCalls(QByteArray processedOutput, const QHash<void*, const char*>& functions)
+static void printDisassembledOutputWithCalls(QByteArray processedOutput,
+ const QHash<const void*, const char*>& functions)
{
- for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
+ 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 = processedOutput.indexOf(ptrString);
- if (idx < 0)
- continue;
- idx = processedOutput.lastIndexOf('\n', idx);
- if (idx < 0)
- continue;
- processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; call ") + it.value());
+ 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());
}
-#if defined(Q_OS_LINUX)
-static FILE *pmap;
+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();
+}
-static void qt_closePmap()
+void Assembler::loadConst(int constIndex)
{
- if (pmap) {
- fclose(pmap);
- pmap = 0;
+ //###
+ if (pasm()->constantTable[constIndex].isUndefined()) {
+ pasm()->loadUndefined();
+ } else {
+ pasm()->loadAccumulator(pasm()->loadConstAddress(constIndex));
}
}
-#endif
+void Assembler::copyConst(int constIndex, int destReg)
+{
+ pasm()->copyConst(constIndex, regAddr(destReg));
+}
-template <typename TargetConfiguration>
-JSC::MacroAssemblerCodeRef Assembler<TargetConfiguration>::link(int *codeSize)
+void Assembler::loadReg(int reg)
{
- Label endOfCode = label();
+ pasm()->loadReg(regAddr(reg));
+}
- {
- for (size_t i = 0, ei = _patches.size(); i != ei; ++i) {
- Label target = _addrs.at(i);
- Q_ASSERT(target.isSet());
- for (Jump jump : qAsConst(_patches.at(i)))
- jump.linkTo(target, this);
- }
- }
+void Assembler::storeReg(int reg)
+{
+ pasm()->storeAccumulator(regAddr(reg));
+}
- JSC::JSGlobalData dummy(_executableAllocator);
- JSC::LinkBuffer<typename TargetConfiguration::MacroAssembler> linkBuffer(dummy, this, 0);
+void Assembler::loadString(int stringId)
+{
+ pasm()->loadString(stringId);
+}
- for (const DataLabelPatch &p : qAsConst(_dataLabelPatches))
- linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target));
+void Assembler::loadValue(ReturnedValue value)
+{
+ pasm()->loadValue(value);
+}
- // link exception handlers
- for (Jump jump : qAsConst(exceptionPropagationJumps))
- linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel));
+void Assembler::toNumber()
+{
+ pasm()->toNumber();
+}
- {
- for (size_t i = 0, ei = _labelPatches.size(); i != ei; ++i) {
- Label target = _addrs.at(i);
- Q_ASSERT(target.isSet());
- for (DataLabelPtr label : _labelPatches.at(i))
- linkBuffer.patch(label, linkBuffer.locationOf(target));
- }
- }
+void Assembler::uminus()
+{
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(1);
+ passAccumulatorAsArg(0);
+ IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, ResultInAccumulator);
+ checkException();
+}
- *codeSize = linkBuffer.offsetOf(endOfCode);
+void Assembler::ucompl()
+{
+ pasm()->toInt32();
+ pasm()->xor32(TrustedImm32(-1), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
- QByteArray name;
+static ReturnedValue incHelper(const Value &v)
+{
+ return Encode(v.toNumber() + 1.);
+}
- JSC::MacroAssemblerCodeRef codeRef;
+void Assembler::inc()
+{
+// auto done = pasm()->incFastPath();
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
- if (showCode) {
- QHash<void*, const char*> functions;
+ // 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)
+{
+ auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
+ auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
+ PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->setAccumulatorTag(IntegerTag,
+ PlatformAssembler::ScratchRegister);
+ return overflowed;
+ });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ 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(IntegerTag);
+}
+
+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(IntegerTag);
+}
+
+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(IntegerTag);
+}
+
+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(IntegerTag);
+ 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(IntegerTag);
+}
+
+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(IntegerTag);
+}
+
+void Assembler::bitAndConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->and32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::bitOrConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->or32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+void Assembler::bitXorConst(int rhs)
+{
+ pasm()->toInt32();
+ pasm()->xor32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
+ pasm()->setAccumulatorTag(IntegerTag);
+}
+
+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(IntegerTag);
+ 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(IntegerTag);
+}
+
+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(IntegerTag);
+}
+
+void Assembler::mul(int lhs)
+{
+ auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
+ auto overflowed = pasm()->branchMul32(PlatformAssembler::Overflow,
+ PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->setAccumulatorTag(IntegerTag,
+ PlatformAssembler::ScratchRegister);
+ return overflowed;
+ });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ 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)
+{
+ auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
+ auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
+ PlatformAssembler::AccumulatorRegisterValue,
+ PlatformAssembler::ScratchRegister);
+ pasm()->setAccumulatorTag(IntegerTag,
+ PlatformAssembler::ScratchRegister);
+ return overflowed;
+ });
+
+ // slow path:
+ saveAccumulatorInFrame();
+ 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
- for (CallInfo call : qAsConst(_callInfos))
- functions[linkBuffer.locationOf(call.label).dataLocation()] = call.functionName;
+ Q_ASSERT(remainingArgcForCall == NoCall);
+ remainingArgcForCall = argc;
#endif
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- WTF::setDataFile(new QIODevicePrintStream(&buf));
+ if (argc > PlatformAssembler::ArgInRegCount) {
+ argcOnStackForCall = int(WTF::roundUpToMultipleOf(16, size_t(argc - PlatformAssembler::ArgInRegCount) * PlatformAssembler::PointerSize));
+ pasm()->subPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister);
+ }
+}
- name = _function->name->toUtf8();
- if (name.isEmpty())
- name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')';
- codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
+void Assembler::storeInstructionPointer(int instructionOffset)
+{
+ PlatformAssembler::Address addr(PlatformAssembler::CppStackFrameRegister,
+ offsetof(QV4::CppStackFrame, instructionPointer));
+ pasm()->store32(TrustedImm32(instructionOffset), addr);
+}
- WTF::setDataFile(stderr);
- printDisassembledOutputWithCalls(buf.data(), functions);
+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 {
- codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
+ pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)),
+ PlatformAssembler::JSStackFrameRegister,
+ PlatformAssembler::ScratchRegister);
+ if (push)
+ pasm()->push(PlatformAssembler::ScratchRegister);
+ else
+ pasm()->storePtr(PlatformAssembler::ScratchRegister,
+ argStackAddress(arg));
}
+}
-#if defined(Q_OS_LINUX)
- // This implements writing of JIT'd addresses so that perf can find the
- // symbol names.
- //
- // Perf expects the mapping to be in a certain place and have certain
- // content, for more information, see:
- // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
- static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP");
- static bool profileInitialized = false;
- if (doProfile && !profileInitialized) {
- profileInitialized = true;
+void Assembler::passFunctionAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
- char pname[PATH_MAX];
- snprintf(pname, PATH_MAX - 1, "/tmp/perf-%lu.map",
- (unsigned long)QCoreApplication::applicationPid());
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->loadFunctionPtr(pasm()->registerForArg(arg));
+ } else {
+ pasm()->loadFunctionPtr(PlatformAssembler::ScratchRegister);
+ pasm()->storePtr(PlatformAssembler::ScratchRegister,
+ argStackAddress(arg));
+ }
+}
- pmap = fopen(pname, "w");
- if (!pmap)
- qWarning("QV4: Can't write %s, call stacks will not contain JavaScript function names", pname);
+void Assembler::passEngineAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
- // make sure we clean up nicely
- std::atexit(qt_closePmap);
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->move(PlatformAssembler::EngineRegister, pasm()->registerForArg(arg));
+ } else {
+ pasm()->storePtr(PlatformAssembler::EngineRegister, argStackAddress(arg));
}
+}
- if (pmap) {
- // this may have been pre-populated, if QV4_SHOW_ASM was on
- if (name.isEmpty()) {
- name = _function->name->toUtf8();
- if (name.isEmpty())
- name = "IR::Function(0x" + QByteArray::number(quintptr(_function), 16) + ')';
- }
+void Assembler::passRegAsArg(int reg, int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
- fprintf(pmap, "%llx %x %.*s\n",
- (long long unsigned int)codeRef.code().executableAddress(),
- *codeSize,
- name.length(),
- name.constData());
- fflush(pmap);
+ 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
- return codeRef;
+ if (arg < PlatformAssembler::ArgInRegCount) {
+ pasm()->move(PlatformAssembler::CppStackFrameRegister, pasm()->registerForArg(arg));
+ } else {
+ pasm()->store32(PlatformAssembler::CppStackFrameRegister, argStackAddress(arg));
+ }
}
-template class QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>;
-#if defined(V4_BOOTSTRAP)
-#if !CPU(ARM_THUMB2)
-template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>;
-#endif
-#if !CPU(ARM64)
-template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>;
-#endif
+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