diff options
author | Lars Knoll <lars.knoll@digia.com> | 2014-02-10 15:58:11 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-02-22 18:47:21 +0100 |
commit | 185dac2d11beadec50e6ac4e1b806585d45de3b7 (patch) | |
tree | 2ceb047a67afce8332dcc43e02af2d424c51f9a1 /src/qml/jit/qv4assembler_p.h | |
parent | 5e085f4883953ebb54ed8abba0468ad17d352682 (diff) |
Move the Assembler class into it's own file
Change-Id: I9968b3ae5ad5fbad3490e08e173c22e4a643c91f
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'src/qml/jit/qv4assembler_p.h')
-rw-r--r-- | src/qml/jit/qv4assembler_p.h | 1478 |
1 files changed, 1478 insertions, 0 deletions
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h new file mode 100644 index 0000000000..ace1aab7b5 --- /dev/null +++ b/src/qml/jit/qv4assembler_p.h @@ -0,0 +1,1478 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ASSEMBLER_P_H +#define QV4ASSEMBLER_P_H + +#include "private/qv4global_p.h" +#include "private/qv4jsir_p.h" +#include "private/qv4isel_p.h" +#include "private/qv4isel_util_p.h" +#include "private/qv4value_p.h" +#include "private/qv4lookup_p.h" + +#include <QtCore/QHash> +#include <QtCore/QStack> +#include <config.h> +#include <wtf/Vector.h> + +#if ENABLE(ASSEMBLER) + +#include <assembler/MacroAssembler.h> +#include <assembler/MacroAssemblerCodeRef.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { +namespace MASM { + +#define OP(op) \ + { isel_stringIfy(op), op, 0, 0, 0 } +#define OPCONTEXT(op) \ + { isel_stringIfy(op), 0, op, 0, 0 } + +class InstructionSelection; + +struct CompilationUnit : public QV4::CompiledData::CompilationUnit +{ + virtual ~CompilationUnit(); + + virtual void linkBackendToEngine(QV4::ExecutionEngine *engine); + + virtual QV4::ExecutableAllocator::ChunkOfPages *chunkForFunction(int functionIndex); + + // Coderef + execution engine + + QVector<JSC::MacroAssemblerCodeRef> codeRefs; + QList<QVector<QV4::Primitive> > constantValues; + QVector<int> codeSizes; // corresponding to the endOfCode labels. MacroAssemblerCodeRef's size may + // be larger, as for example on ARM we append the exception handling table. +}; + +struct RelativeCall { + JSC::MacroAssembler::Address addr; + + explicit RelativeCall(const JSC::MacroAssembler::Address &addr) + : addr(addr) + {} +}; + + +template <typename T> +struct ExceptionCheck { + enum { NeedsCheck = 1 }; +}; +// push_catch and pop context methods shouldn't check for exceptions +template <> +struct ExceptionCheck<QV4::ExecutionContext *(*)(QV4::ExecutionContext *)> { + enum { NeedsCheck = 0 }; +}; +template <typename A> +struct ExceptionCheck<QV4::ExecutionContext *(*)(QV4::ExecutionContext *, A)> { + enum { NeedsCheck = 0 }; +}; +template <> +struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowContext *)> { + enum { NeedsCheck = 0 }; +}; +template <typename A> +struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowContext *, A)> { + enum { NeedsCheck = 0 }; +}; +template <typename A, typename B> +struct ExceptionCheck<QV4::ReturnedValue (*)(QV4::NoThrowContext *, A, B)> { + enum { NeedsCheck = 0 }; +}; +template <typename A, typename B, typename C> +struct ExceptionCheck<void (*)(QV4::NoThrowContext *, A, B, C)> { + enum { NeedsCheck = 0 }; +}; + +class Assembler : public JSC::MacroAssembler +{ +public: + Assembler(InstructionSelection *isel, V4IR::Function* function, QV4::ExecutableAllocator *executableAllocator, + int maxArgCountForBuiltins); + +#if CPU(X86) + +#undef VALUE_FITS_IN_REGISTER +#undef ARGUMENTS_IN_REGISTERS + +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::edi; + static const RegisterID ContextRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + + static const int RegisterSize = 4; + + static const int RegisterArgumentCount = 0; + static RegisterID registerForArgument(int) + { + assert(false); + // Not reached. + return JSC::X86Registers::eax; + } + + // Return address is pushed onto stack by the CPU. + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; + static const int StackShadowSpace = 0; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(X86_64) + +#define VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 + + static const RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID ContextRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + + static const int RegisterSize = 8; + +#if OS(WINDOWS) + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::ecx, + JSC::X86Registers::edx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + static const int StackShadowSpace = 32; +#else // Unix + static const int RegisterArgumentCount = 6; + static RegisterID registerForArgument(int index) + { + static RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::edi, + JSC::X86Registers::esi, + JSC::X86Registers::edx, + JSC::X86Registers::ecx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + assert(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + static const int StackShadowSpace = 0; +#endif + + // Return address is pushed onto stack by the CPU. + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; + inline void platformEnterStandardStackFrame() {} + inline void platformLeaveStandardStackFrame() {} +#elif CPU(ARM) + +#undef VALUE_FITS_IN_REGISTER +#define ARGUMENTS_IN_REGISTERS +#undef HAVE_ALU_OPS_WITH_MEM_OPERAND + + static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4; + static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; + static const RegisterID ContextRegister = JSC::ARMRegisters::r5; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + + static const int RegisterSize = 4; + + static const RegisterID RegisterArgument1 = JSC::ARMRegisters::r0; + static const RegisterID RegisterArgument2 = JSC::ARMRegisters::r1; + static const RegisterID RegisterArgument3 = JSC::ARMRegisters::r2; + static const RegisterID RegisterArgument4 = JSC::ARMRegisters::r3; + + static const int RegisterArgumentCount = 4; + static RegisterID registerForArgument(int index) + { + assert(index >= 0 && index < RegisterArgumentCount); + return static_cast<RegisterID>(JSC::ARMRegisters::r0 + index); + }; + + // Registers saved in platformEnterStandardStackFrame below. + static const int StackSpaceAllocatedUponFunctionEntry = 5 * RegisterSize; + static const int StackShadowSpace = 0; + inline void platformEnterStandardStackFrame() + { + // Move the register arguments onto the stack as if they were + // pushed by the caller, just like on ia32. This gives us consistent + // access to the parameters if we need to. + push(JSC::ARMRegisters::r3); + push(JSC::ARMRegisters::r2); + push(JSC::ARMRegisters::r1); + push(JSC::ARMRegisters::r0); + push(JSC::ARMRegisters::lr); + } + inline void platformLeaveStandardStackFrame() + { + pop(JSC::ARMRegisters::lr); + addPtr(TrustedImm32(4 * RegisterSize), StackPointerRegister); + } +#else +#error The JIT needs to be ported to this platform. +#endif + static const int calleeSavedRegisterCount; + +#if CPU(X86) || CPU(X86_64) + static const int StackAlignment = 16; +#elif CPU(ARM) + // Per AAPCS + static const int StackAlignment = 8; +#else +#error Stack alignment unknown for this platform. +#endif + + // Explicit type to allow distinguishing between + // pushing an address itself or the value it points + // to onto the stack when calling functions. + struct Pointer : public Address + { + explicit Pointer(const Address& addr) + : Address(addr) + {} + explicit Pointer(RegisterID reg, int32_t offset) + : Address(reg, offset) + {} + }; + + // V4 uses two stacks: one stack with QV4::Value items, which is checked by the garbage + // collector, and one stack used by the native C/C++/ABI code. This C++ stack is not scanned + // by the garbage collector, so if any JS object needs to be retained, it should be put on the + // JS stack. + // + // The "saved reg arg X" are on the C++ stack is used to store values in registers that need to + // be passed by reference to native functions. It is fine to use the C++ stack, because only + // non-object values can be stored in registers. + // + // Stack layout for the C++ stack: + // return address + // old FP <- FP + // callee saved reg n + // ... + // callee saved reg 0 + // saved reg arg 0 + // ... + // saved reg arg n <- SP + // + // Stack layout for the JS stack: + // function call argument n <- LocalsRegister + // ... + // function call argument 0 + // local 0 + // ... + // local n + class StackLayout + { + public: + StackLayout(V4IR::Function *function, int maxArgCountForBuiltins) + : calleeSavedRegCount(Assembler::calleeSavedRegisterCount + 1) + , maxOutgoingArgumentCount(function->maxNumberOfArguments) + , localCount(function->tempCount) + , savedRegCount(maxArgCountForBuiltins) + { +#if 0 // debug code + qDebug("calleeSavedRegCount.....: %d",calleeSavedRegCount); + qDebug("maxOutgoingArgumentCount: %d",maxOutgoingArgumentCount); + qDebug("localCount..............: %d",localCount); + qDebug("savedConstCount.........: %d",savedRegCount); + for (int i = 0; i < maxOutgoingArgumentCount; ++i) + qDebug("argumentAddressForCall(%d) = 0x%x / -0x%x", i, + argumentAddressForCall(i).offset, -argumentAddressForCall(i).offset); + for (int i = 0; i < localCount; ++i) + qDebug("local(%d) = 0x%x / -0x%x", i, stackSlotPointer(i).offset, + -stackSlotPointer(i).offset); + qDebug("savedReg(0) = 0x%x / -0x%x", savedRegPointer(0).offset, -savedRegPointer(0).offset); + qDebug("savedReg(1) = 0x%x / -0x%x", savedRegPointer(1).offset, -savedRegPointer(1).offset); + qDebug("savedReg(2) = 0x%x / -0x%x", savedRegPointer(2).offset, -savedRegPointer(2).offset); + qDebug("savedReg(3) = 0x%x / -0x%x", savedRegPointer(3).offset, -savedRegPointer(3).offset); + qDebug("savedReg(4) = 0x%x / -0x%x", savedRegPointer(4).offset, -savedRegPointer(4).offset); + qDebug("savedReg(5) = 0x%x / -0x%x", savedRegPointer(5).offset, -savedRegPointer(5).offset); + + qDebug("callDataAddress(0) = 0x%x", callDataAddress(0).offset); +#endif + } + + int calculateStackFrameSize() const + { + const int stackSpaceAllocatedOtherwise = StackSpaceAllocatedUponFunctionEntry + + RegisterSize; // saved StackFrameRegister + + // space for the callee saved registers + int frameSize = RegisterSize * calleeSavedRegisterCount; + frameSize += savedRegCount * sizeof(QV4::Value); // these get written out as Values, not as native registers + + frameSize = WTF::roundUpToMultipleOf(StackAlignment, frameSize + stackSpaceAllocatedOtherwise); + frameSize -= stackSpaceAllocatedOtherwise; + + return frameSize; + } + + int calculateJSStackFrameSize() const + { + const int locals = (localCount + sizeof(QV4::CallData)/sizeof(QV4::Value) - 1 + maxOutgoingArgumentCount) + 1; + int frameSize = locals * sizeof(QV4::Value); + return frameSize; + } + + Address stackSlotPointer(int idx) const + { + Q_ASSERT(idx >= 0); + Q_ASSERT(idx < localCount); + + Pointer addr = callDataAddress(0); + addr.offset -= sizeof(QV4::Value) * (idx + 1); + return addr; + } + + // Some run-time functions take (Value* args, int argc). This function is for populating + // the args. + Pointer argumentAddressForCall(int argument) const + { + Q_ASSERT(argument >= 0); + Q_ASSERT(argument < maxOutgoingArgumentCount); + + const int index = maxOutgoingArgumentCount - argument; + return Pointer(Assembler::LocalsRegister, sizeof(QV4::Value) * (-index)); + } + + Pointer callDataAddress(int offset = 0) const { + return Pointer(Assembler::LocalsRegister, -(sizeof(QV4::CallData) + sizeof(QV4::Value) * (maxOutgoingArgumentCount - 1)) + offset); + } + + Address savedRegPointer(int offset) const + { + Q_ASSERT(offset >= 0); + Q_ASSERT(offset < savedRegCount); + + const int off = offset * sizeof(QV4::Value); + return Address(Assembler::StackFrameRegister, - calleeSavedRegisterSpace() - off); + } + + int calleeSavedRegisterSpace() const + { + // plus 1 for the old FP + return RegisterSize * (calleeSavedRegCount + 1); + } + + private: + int calleeSavedRegCount; + + /// arg count for calls to JS functions + int maxOutgoingArgumentCount; + + /// the number of spill slots needed by this function + int localCount; + + /// used by built-ins to save arguments (e.g. constants) to the stack when they need to be + /// passed by reference. + int savedRegCount; + }; + + class ConstantTable + { + public: + ConstantTable(Assembler *as): _as(as) {} + + int add(const QV4::Primitive &v); + ImplicitAddress loadValueAddress(V4IR::Const *c, RegisterID baseReg); + ImplicitAddress loadValueAddress(const QV4::Primitive &v, RegisterID baseReg); + void finalize(JSC::LinkBuffer &linkBuffer, InstructionSelection *isel); + + private: + Assembler *_as; + QVector<QV4::Primitive> _values; + QVector<DataLabelPtr> _toPatch; + }; + + struct VoidType { VoidType() {} }; + static const VoidType Void; + + typedef JSC::FunctionPtr FunctionPtr; + + struct CallToLink { + Call call; + FunctionPtr externalFunction; + const char* functionName; + }; + struct PointerToValue { + PointerToValue(V4IR::Expr *value) + : value(value) + {} + V4IR::Expr *value; + }; + struct PointerToString { + explicit PointerToString(const QString &string) : string(string) {} + QString string; + }; + struct Reference { + Reference(V4IR::Temp *value) : value(value) {} + V4IR::Temp *value; + }; + + struct ReentryBlock { + ReentryBlock(V4IR::BasicBlock *b) : block(b) {} + V4IR::BasicBlock *block; + }; + + void callAbsolute(const char* functionName, FunctionPtr function) { + CallToLink ctl; + ctl.call = call(); + ctl.externalFunction = function; + ctl.functionName = functionName; + _callsToLink.append(ctl); + } + + void callAbsolute(const char* /*functionName*/, Address addr) { + call(addr); + } + + void callAbsolute(const char* /*functionName*/, const RelativeCall &relativeCall) + { + call(relativeCall.addr); + } + + void registerBlock(V4IR::BasicBlock*, V4IR::BasicBlock *nextBlock); + V4IR::BasicBlock *nextBlock() const { return _nextBlock; } + void jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target); + void addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump); + void addPatch(DataLabelPtr patch, Label target); + void addPatch(DataLabelPtr patch, V4IR::BasicBlock *target); + void generateCJumpOnNonZero(RegisterID reg, V4IR::BasicBlock *currentBlock, + V4IR::BasicBlock *trueBlock, V4IR::BasicBlock *falseBlock); + void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, + V4IR::BasicBlock *currentBlock, V4IR::BasicBlock *trueBlock, + V4IR::BasicBlock *falseBlock); + void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, + V4IR::BasicBlock *currentBlock, V4IR::BasicBlock *trueBlock, + V4IR::BasicBlock *falseBlock); + + Pointer loadTempAddress(RegisterID baseReg, V4IR::Temp *t); + Pointer loadStringAddress(RegisterID reg, const QString &string); + void loadStringRef(RegisterID reg, const QString &string); + Pointer stackSlotPointer(V4IR::Temp *t) const + { + Q_ASSERT(t->kind == V4IR::Temp::StackSlot); + Q_ASSERT(t->scope == 0); + + return Pointer(_stackLayout.stackSlotPointer(t->index)); + } + + template <int argumentNumber> + void saveOutRegister(PointerToValue arg) + { + if (!arg.value) + return; + if (V4IR::Temp *t = arg.value->asTemp()) { + if (t->kind == V4IR::Temp::PhysicalRegister) { + Pointer addr(_stackLayout.savedRegPointer(argumentNumber)); + switch (t->type) { + case V4IR::BoolType: + storeBool((RegisterID) t->index, addr); + break; + case V4IR::SInt32Type: + storeInt32((RegisterID) t->index, addr); + break; + case V4IR::UInt32Type: + storeUInt32((RegisterID) t->index, addr); + break; + case V4IR::DoubleType: + storeDouble((FPRegisterID) t->index, addr); + break; + default: + Q_UNIMPLEMENTED(); + } + } + } + } + + template <int, typename ArgType> + void saveOutRegister(ArgType) + {} + + void loadArgumentInRegister(RegisterID source, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + move(source, dest); + } + + void loadArgumentInRegister(TrustedImmPtr ptr, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + move(TrustedImmPtr(ptr), dest); + } + + void loadArgumentInRegister(const Pointer& ptr, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + addPtr(TrustedImm32(ptr.offset), ptr.base, dest); + } + + void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber) + { + if (!temp.value) { + loadArgumentInRegister(TrustedImmPtr(0), dest, argumentNumber); + } else { + Pointer addr = toAddress(dest, temp.value, argumentNumber); + loadArgumentInRegister(addr, dest, argumentNumber); + } + } + void loadArgumentInRegister(PointerToString temp, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + loadStringRef(dest, temp.string); + } + + void loadArgumentInRegister(Reference temp, RegisterID dest, int argumentNumber) + { + assert(temp.value); + Pointer addr = loadTempAddress(dest, temp.value); + loadArgumentInRegister(addr, dest, argumentNumber); + } + + void loadArgumentInRegister(ReentryBlock block, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), dest); + addPatch(patch, block.block); + } + +#ifdef VALUE_FITS_IN_REGISTER + void loadArgumentInRegister(V4IR::Temp* temp, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (!temp) { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else { + Pointer addr = loadTempAddress(dest, temp); + load64(addr, dest); + } + } + + void loadArgumentInRegister(V4IR::Const* c, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + QV4::Value v = convertToValue(c); + move(TrustedImm64(v.val), dest); + } + + void loadArgumentInRegister(V4IR::Expr* expr, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (!expr) { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + move(TrustedImm64(undefined.val), dest); + } else if (expr->asTemp()){ + loadArgumentInRegister(expr->asTemp(), dest, argumentNumber); + } else if (expr->asConst()) { + loadArgumentInRegister(expr->asConst(), dest, argumentNumber); + } else { + assert(!"unimplemented expression type in loadArgument"); + } + } +#else + void loadArgumentInRegister(V4IR::Expr*, RegisterID) + { + assert(!"unimplemented: expression in loadArgument"); + } +#endif + + void loadArgumentInRegister(QV4::String* string, RegisterID dest, int argumentNumber) + { + loadArgumentInRegister(TrustedImmPtr(string), dest, argumentNumber); + } + + void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + xorPtr(dest, dest); + if (imm32.m_value) + move(imm32, dest); + } + + void storeReturnValue(RegisterID dest) + { + move(ReturnValueRegister, dest); + } + + void storeUInt32ReturnValue(RegisterID dest) + { + Pointer tmp(StackPointerRegister, -int(sizeof(QV4::Value))); + storeReturnValue(tmp); + toUInt32Register(tmp, dest); + } + + void storeReturnValue(FPRegisterID dest) + { +#ifdef VALUE_FITS_IN_REGISTER + move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); + xor64(ScratchRegister, ReturnValueRegister); + move64ToDouble(ReturnValueRegister, dest); +#else + Pointer tmp(StackPointerRegister, -int(sizeof(QV4::Value))); + storeReturnValue(tmp); + loadDouble(tmp, dest); +#endif + } + +#ifdef VALUE_FITS_IN_REGISTER + void storeReturnValue(const Pointer &dest) + { + store64(ReturnValueRegister, dest); + } +#elif defined(Q_PROCESSOR_X86) + void storeReturnValue(const Pointer &dest) + { + Pointer destination = dest; + store32(JSC::X86Registers::eax, destination); + destination.offset += 4; + store32(JSC::X86Registers::edx, destination); + } +#elif defined(Q_PROCESSOR_ARM) + void storeReturnValue(const Pointer &dest) + { + Pointer destination = dest; + store32(JSC::ARMRegisters::r0, destination); + destination.offset += 4; + store32(JSC::ARMRegisters::r1, destination); + } +#endif + + void storeReturnValue(V4IR::Temp *temp) + { + if (!temp) + return; + + if (temp->kind == V4IR::Temp::PhysicalRegister) { + if (temp->type == V4IR::DoubleType) + storeReturnValue((FPRegisterID) temp->index); + else if (temp->type == V4IR::UInt32Type) + storeUInt32ReturnValue((RegisterID) temp->index); + else + storeReturnValue((RegisterID) temp->index); + } else { + Pointer addr = loadTempAddress(ScratchRegister, temp); + storeReturnValue(addr); + } + } + + void storeReturnValue(VoidType) + { + } + + template <int StackSlot> + void loadArgumentOnStack(RegisterID reg, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + poke(reg, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(TrustedImm32 value, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + poke(value, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(const Pointer& ptr, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister); + poke(ScratchRegister, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(PointerToValue temp, int argumentNumber) + { + if (temp.value) { + Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber); + loadArgumentOnStack<StackSlot>(ptr, argumentNumber); + } else { + poke(TrustedImmPtr(0), StackSlot); + } + } + + template <int StackSlot> + void loadArgumentOnStack(PointerToString temp, int argumentNumber) + { + Q_UNUSED(argumentNumber); + loadStringRef(ScratchRegister, temp.string); + poke(ScratchRegister, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(Reference temp, int argumentNumber) + { + assert (temp.value); + + Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + loadArgumentOnStack<StackSlot>(ptr, argumentNumber); + } + + template <int StackSlot> + void loadArgumentOnStack(ReentryBlock block, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + assert(block.block); + DataLabelPtr patch = moveWithPatch(TrustedImmPtr(0), ScratchRegister); + poke(ScratchRegister, StackSlot); + addPatch(patch, block.block); + } + + template <int StackSlot> + void loadArgumentOnStack(TrustedImmPtr ptr, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + move(TrustedImmPtr(ptr), ScratchRegister); + poke(ScratchRegister, StackSlot); + } + + template <int StackSlot> + void loadArgumentOnStack(QV4::String* name, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + poke(TrustedImmPtr(name), StackSlot); + } + + void loadDouble(V4IR::Temp* temp, FPRegisterID dest) + { + if (temp->kind == V4IR::Temp::PhysicalRegister) { + moveDouble((FPRegisterID) temp->index, dest); + return; + } + Pointer ptr = loadTempAddress(ScratchRegister, temp); + loadDouble(ptr, dest); + } + + void storeDouble(FPRegisterID source, V4IR::Temp* temp) + { + if (temp->kind == V4IR::Temp::PhysicalRegister) { + moveDouble(source, (FPRegisterID) temp->index); + return; + } +#if QT_POINTER_SIZE == 8 + moveDoubleTo64(source, ReturnValueRegister); + move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); + xor64(ScratchRegister, ReturnValueRegister); + Pointer ptr = loadTempAddress(ScratchRegister, temp); + store64(ReturnValueRegister, ptr); +#else + Pointer ptr = loadTempAddress(ScratchRegister, temp); + storeDouble(source, ptr); +#endif + } +#if QT_POINTER_SIZE == 8 + // We need to (de)mangle the double + void loadDouble(Address addr, FPRegisterID dest) + { + load64(addr, ReturnValueRegister); + move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); + xor64(ScratchRegister, ReturnValueRegister); + move64ToDouble(ReturnValueRegister, dest); + } + + void storeDouble(FPRegisterID source, Address addr) + { + moveDoubleTo64(source, ReturnValueRegister); + move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); + xor64(ScratchRegister, ReturnValueRegister); + store64(ReturnValueRegister, addr); + } +#else + using JSC::MacroAssembler::loadDouble; + using JSC::MacroAssembler::storeDouble; +#endif + + template <typename Result, typename Source> + void copyValue(Result result, Source source); + template <typename Result> + void copyValue(Result result, V4IR::Expr* source); + + // The scratch register is used to calculate the temp address for the source. + void memcopyValue(Pointer target, V4IR::Temp *sourceTemp, RegisterID scratchRegister) + { + Q_ASSERT(sourceTemp->kind != V4IR::Temp::PhysicalRegister); + Q_ASSERT(target.base != scratchRegister); + JSC::MacroAssembler::loadDouble(loadTempAddress(scratchRegister, sourceTemp), FPGpr0); + JSC::MacroAssembler::storeDouble(FPGpr0, target); + } + + void storeValue(QV4::Primitive value, RegisterID destination) + { + Q_UNUSED(value); + Q_UNUSED(destination); + Q_UNREACHABLE(); + } + + void storeValue(QV4::Primitive value, Address destination) + { +#ifdef VALUE_FITS_IN_REGISTER + store64(TrustedImm64(value.val), destination); +#else + store32(TrustedImm32(value.int_32), destination); + destination.offset += 4; + store32(TrustedImm32(value.tag), destination); +#endif + } + + void storeValue(QV4::Primitive value, V4IR::Temp* temp); + + void enterStandardStackFrame(); + void leaveStandardStackFrame(); + + void checkException() { + loadPtr(Address(ContextRegister, qOffsetOf(QV4::ExecutionContext, engine)), ScratchRegister); + load32(Address(ScratchRegister, qOffsetOf(QV4::ExecutionEngine, hasException)), ScratchRegister); + Jump exceptionThrown = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); + if (catchBlock) + addPatch(catchBlock, exceptionThrown); + else + exceptionPropagationJumps.append(exceptionThrown); + } + void jumpToExceptionHandler() { + Jump exceptionThrown = jump(); + if (catchBlock) + addPatch(catchBlock, exceptionThrown); + else + exceptionPropagationJumps.append(exceptionThrown); + } + + template <int argumentNumber, typename T> + void loadArgumentOnStackOrRegister(const T &value) + { + if (argumentNumber < RegisterArgumentCount) + loadArgumentInRegister(value, registerForArgument(argumentNumber), argumentNumber); + else +#if OS(WINDOWS) && CPU(X86_64) + loadArgumentOnStack<argumentNumber>(value, argumentNumber); +#else // Sanity: + loadArgumentOnStack<argumentNumber - RegisterArgumentCount>(value, argumentNumber); +#endif + } + + template <int argumentNumber> + void loadArgumentOnStackOrRegister(const VoidType &value) + { + Q_UNUSED(value); + } + + template <bool selectFirst, int First, int Second> + struct Select + { + enum { Chosen = First }; + }; + + template <int First, int Second> + struct Select<false, First, Second> + { + enum { Chosen = Second }; + }; + + template <int ArgumentIndex, typename Parameter> + struct SizeOnStack + { + enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, QT_POINTER_SIZE, 0>::Chosen }; + }; + + template <int ArgumentIndex> + struct SizeOnStack<ArgumentIndex, VoidType> + { + enum { Size = 0 }; + }; + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) + { + int stackSpaceNeeded = SizeOnStack<0, Arg1>::Size + + SizeOnStack<1, Arg2>::Size + + SizeOnStack<2, Arg3>::Size + + SizeOnStack<3, Arg4>::Size + + SizeOnStack<4, Arg5>::Size + + SizeOnStack<5, Arg6>::Size + + StackShadowSpace; + + if (stackSpaceNeeded) { + stackSpaceNeeded = WTF::roundUpToMultipleOf(StackAlignment, stackSpaceNeeded); + sub32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + } + + // First save any arguments that reside in registers, because they could be overwritten + // if that register is also used to pass arguments. + saveOutRegister<5>(arg6); + saveOutRegister<4>(arg5); + saveOutRegister<3>(arg4); + saveOutRegister<2>(arg3); + saveOutRegister<1>(arg2); + saveOutRegister<0>(arg1); + + loadArgumentOnStackOrRegister<5>(arg6); + loadArgumentOnStackOrRegister<4>(arg5); + loadArgumentOnStackOrRegister<3>(arg4); + loadArgumentOnStackOrRegister<2>(arg3); + loadArgumentOnStackOrRegister<1>(arg2); + + prepareRelativeCall(function, this); + loadArgumentOnStackOrRegister<0>(arg1); + +#if OS(LINUX) && CPU(X86) && (defined(__PIC__) || defined(__PIE__)) + load32(Address(StackFrameRegister, -sizeof(void*)), JSC::X86Registers::ebx); // restore the GOT ptr +#endif + + callAbsolute(functionName, function); + + if (stackSpaceNeeded) + add32(TrustedImm32(stackSpaceNeeded), StackPointerRegister); + + if (ExceptionCheck<Callable>::NeedsCheck) { + checkException(); + } + + storeReturnValue(r); + + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, arg5, VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2) + { + generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); + } + + template <typename ArgRet, typename Callable, typename Arg1> + void generateFunctionCallImp(ArgRet r, const char* functionName, Callable function, Arg1 arg1) + { + generateFunctionCallImp(r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType()); + } + + typedef Jump (Assembler::*MemRegBinOp)(Address, RegisterID); + typedef Jump (Assembler::*ImmRegBinOp)(TrustedImm32, RegisterID); + + struct BinaryOperationInfo { + const char *name; + QV4::BinOp fallbackImplementation; + QV4::BinOpContext contextImplementation; + MemRegBinOp inlineMemRegOp; + ImmRegBinOp inlineImmRegOp; + }; + + static const BinaryOperationInfo binaryOperations[QQmlJS::V4IR::LastAluOp + 1]; + static const BinaryOperationInfo &binaryOperation(V4IR::AluOp operation) + { return binaryOperations[operation]; } + + Jump inline_add32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchAdd32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchAdd32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_add32(TrustedImm32 imm, RegisterID reg) + { + return branchAdd32(Overflow, imm, reg); + } + + Jump inline_sub32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchSub32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchSub32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_sub32(TrustedImm32 imm, RegisterID reg) + { + return branchSub32(Overflow, imm, reg); + } + + Jump inline_mul32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + return branchMul32(Overflow, addr, reg); +#else + load32(addr, ScratchRegister); + return branchMul32(Overflow, ScratchRegister, reg); +#endif + } + + Jump inline_mul32(TrustedImm32 imm, RegisterID reg) + { + return branchMul32(Overflow, imm, reg, reg); + } + + Jump inline_shl32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + lshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shl32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + lshift32(imm, reg); + return Jump(); + } + + Jump inline_shr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + rshift32(ScratchRegister, reg); + return Jump(); + } + + Jump inline_shr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + rshift32(imm, reg); + return Jump(); + } + + Jump inline_ushr32(Address addr, RegisterID reg) + { + load32(addr, ScratchRegister); + and32(TrustedImm32(0x1f), ScratchRegister); + urshift32(ScratchRegister, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_ushr32(TrustedImm32 imm, RegisterID reg) + { + imm.m_value &= 0x1f; + urshift32(imm, reg); + return branchTest32(Signed, reg, reg); + } + + Jump inline_and32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + and32(addr, reg); +#else + load32(addr, ScratchRegister); + and32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_and32(TrustedImm32 imm, RegisterID reg) + { + and32(imm, reg); + return Jump(); + } + + Jump inline_or32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + or32(addr, reg); +#else + load32(addr, ScratchRegister); + or32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_or32(TrustedImm32 imm, RegisterID reg) + { + or32(imm, reg); + return Jump(); + } + + Jump inline_xor32(Address addr, RegisterID reg) + { +#if HAVE(ALU_OPS_WITH_MEM_OPERAND) + xor32(addr, reg); +#else + load32(addr, ScratchRegister); + xor32(ScratchRegister, reg); +#endif + return Jump(); + } + + Jump inline_xor32(TrustedImm32 imm, RegisterID reg) + { + xor32(imm, reg); + return Jump(); + } + + Pointer toAddress(RegisterID tmpReg, V4IR::Expr *e, int offset) + { + if (V4IR::Const *c = e->asConst()) { + Address addr = _stackLayout.savedRegPointer(offset); + Address tagAddr = addr; + tagAddr.offset += 4; + + QV4::Primitive v = convertToValue(c); + store32(TrustedImm32(v.int_32), addr); + store32(TrustedImm32(v.tag), tagAddr); + return Pointer(addr); + } + + V4IR::Temp *t = e->asTemp(); + Q_ASSERT(t); + if (t->kind != V4IR::Temp::PhysicalRegister) + return loadTempAddress(tmpReg, t); + + + return Pointer(_stackLayout.savedRegPointer(offset)); + } + + void storeBool(RegisterID reg, Pointer addr) + { + store32(reg, addr); + addr.offset += 4; + store32(TrustedImm32(QV4::Primitive::fromBoolean(0).tag), addr); + } + + void storeBool(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void storeBool(RegisterID reg, V4IR::Temp *target) + { + if (target->kind == V4IR::Temp::PhysicalRegister) { + move(reg, (RegisterID) target->index); + } else { + Pointer addr = loadTempAddress(ScratchRegister, target); + storeBool(reg, addr); + } + } + + void storeBool(bool value, V4IR::Temp *target) { + TrustedImm32 trustedValue(value ? 1 : 0); + if (target->kind == V4IR::Temp::PhysicalRegister) { + move(trustedValue, (RegisterID) target->index); + } else { + move(trustedValue, ScratchRegister); + storeBool(ScratchRegister, target); + } + } + + void storeInt32(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void storeInt32(RegisterID reg, Pointer addr) + { + store32(reg, addr); + addr.offset += 4; + store32(TrustedImm32(QV4::Primitive::fromInt32(0).tag), addr); + } + + void storeInt32(RegisterID reg, V4IR::Temp *target) + { + if (target->kind == V4IR::Temp::PhysicalRegister) { + move(reg, (RegisterID) target->index); + } else { + Pointer addr = loadTempAddress(ScratchRegister, target); + storeInt32(reg, addr); + } + } + + void storeUInt32(RegisterID src, RegisterID dest) + { + move(src, dest); + } + + void storeUInt32(RegisterID reg, Pointer addr) + { + // The UInt32 representation in QV4::Value is really convoluted. See also toUInt32Register. + Jump intRange = branch32(GreaterThanOrEqual, reg, TrustedImm32(0)); + convertUInt32ToDouble(reg, FPGpr0, ReturnValueRegister); + storeDouble(FPGpr0, addr); + Jump done = jump(); + intRange.link(this); + storeInt32(reg, addr); + done.link(this); + } + + void storeUInt32(RegisterID reg, V4IR::Temp *target) + { + if (target->kind == V4IR::Temp::PhysicalRegister) { + move(reg, (RegisterID) target->index); + } else { + Pointer addr = loadTempAddress(ScratchRegister, target); + storeUInt32(reg, addr); + } + } + + FPRegisterID toDoubleRegister(V4IR::Expr *e, FPRegisterID target = FPGpr0) + { + if (V4IR::Const *c = e->asConst()) { +#if QT_POINTER_SIZE == 8 + union { + double d; + int64_t i; + } u; + u.d = c->value; + move(TrustedImm64(u.i), ReturnValueRegister); + move64ToDouble(ReturnValueRegister, target); +#else + JSC::MacroAssembler::loadDouble(constantTable().loadValueAddress(c, ScratchRegister), target); +#endif + return target; + } + + V4IR::Temp *t = e->asTemp(); + Q_ASSERT(t); + if (t->kind == V4IR::Temp::PhysicalRegister) + return (FPRegisterID) t->index; + + loadDouble(t, target); + return target; + } + + RegisterID toBoolRegister(V4IR::Expr *e, RegisterID scratchReg) + { + return toInt32Register(e, scratchReg); + } + + RegisterID toInt32Register(V4IR::Expr *e, RegisterID scratchReg) + { + if (V4IR::Const *c = e->asConst()) { + move(TrustedImm32(convertToValue(c).int_32), scratchReg); + return scratchReg; + } + + V4IR::Temp *t = e->asTemp(); + Q_ASSERT(t); + if (t->kind == V4IR::Temp::PhysicalRegister) + return (RegisterID) t->index; + + return toInt32Register(loadTempAddress(scratchReg, t), scratchReg); + } + + RegisterID toInt32Register(Pointer addr, RegisterID scratchReg) + { + load32(addr, scratchReg); + return scratchReg; + } + + RegisterID toUInt32Register(V4IR::Expr *e, RegisterID scratchReg) + { + if (V4IR::Const *c = e->asConst()) { + move(TrustedImm32(unsigned(c->value)), scratchReg); + return scratchReg; + } + + V4IR::Temp *t = e->asTemp(); + Q_ASSERT(t); + if (t->kind == V4IR::Temp::PhysicalRegister) + return (RegisterID) t->index; + + return toUInt32Register(loadTempAddress(scratchReg, t), scratchReg); + } + + RegisterID toUInt32Register(Pointer addr, RegisterID scratchReg) + { + Q_ASSERT(addr.base != scratchReg); + + // The UInt32 representation in QV4::Value is really convoluted. See also storeUInt32. + Pointer tagAddr = addr; + tagAddr.offset += 4; + load32(tagAddr, scratchReg); + Jump inIntRange = branch32(Equal, scratchReg, TrustedImm32(QV4::Value::_Integer_Type)); + + // it's not in signed int range, so load it as a double, and truncate it down + loadDouble(addr, FPGpr0); + static const double magic = double(INT_MAX) + 1; + move(TrustedImmPtr(&magic), scratchReg); + subDouble(Address(scratchReg, 0), FPGpr0); + Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); + canNeverHappen.link(this); + or32(TrustedImm32(1 << 31), scratchReg); + Jump done = jump(); + + inIntRange.link(this); + load32(addr, scratchReg); + + done.link(this); + return scratchReg; + } + + JSC::MacroAssemblerCodeRef link(int *codeSize); + + const StackLayout stackLayout() const { return _stackLayout; } + ConstantTable &constantTable() { return _constTable; } + + Label exceptionReturnLabel; + V4IR::BasicBlock * catchBlock; + QVector<Jump> exceptionPropagationJumps; +private: + const StackLayout _stackLayout; + ConstantTable _constTable; + V4IR::Function *_function; + QHash<V4IR::BasicBlock *, Label> _addrs; + QHash<V4IR::BasicBlock *, QVector<Jump> > _patches; + QList<CallToLink> _callsToLink; + + struct DataLabelPatch { + DataLabelPtr dataLabel; + Label target; + }; + QList<DataLabelPatch> _dataLabelPatches; + + QHash<V4IR::BasicBlock *, QVector<DataLabelPtr> > _labelPatches; + V4IR::BasicBlock *_nextBlock; + + QV4::ExecutableAllocator *_executableAllocator; + InstructionSelection *_isel; +}; + +template <typename Result, typename Source> +void Assembler::copyValue(Result result, Source source) +{ +#ifdef VALUE_FITS_IN_REGISTER + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(source, ReturnValueRegister, 0); + storeReturnValue(result); +#else + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); +#endif +} + +template <typename Result> +void Assembler::copyValue(Result result, V4IR::Expr* source) +{ + if (source->type == V4IR::BoolType) { + RegisterID reg = toInt32Register(source, ScratchRegister); + storeBool(reg, result); + } else if (source->type == V4IR::SInt32Type) { + RegisterID reg = toInt32Register(source, ScratchRegister); + storeInt32(reg, result); + } else if (source->type == V4IR::UInt32Type) { + RegisterID reg = toUInt32Register(source, ScratchRegister); + storeUInt32(reg, result); + } else if (source->type == V4IR::DoubleType) { + storeDouble(toDoubleRegister(source), result); + } else if (V4IR::Temp *temp = source->asTemp()) { +#ifdef VALUE_FITS_IN_REGISTER + Q_UNUSED(temp); + + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(source, ReturnValueRegister, 0); + storeReturnValue(result); +#else + loadDouble(temp, FPGpr0); + storeDouble(FPGpr0, result); +#endif + } else if (V4IR::Const *c = source->asConst()) { + QV4::Primitive v = convertToValue(c); + storeValue(v, result); + } else { + Q_UNREACHABLE(); + } +} + + + +template <typename T> inline void prepareRelativeCall(const T &, Assembler *){} +template <> inline void prepareRelativeCall(const RelativeCall &relativeCall, Assembler *as) +{ + as->loadPtr(Assembler::Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, lookups)), + relativeCall.addr.base); +} + +} // end of namespace MASM +} // end of namespace QQmlJS + +QT_END_NAMESPACE + +#endif // ENABLE(ASSEMBLER) + +#endif // QV4ISEL_MASM_P_H |