730 lines
23 KiB
C++
730 lines
23 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
|
|
** Contact: http://www.qt-project.org/legal
|
|
**
|
|
** This file is part of the V4VM 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 QV4ISEL_MASM_P_H
|
|
#define QV4ISEL_MASM_P_H
|
|
|
|
#include "qv4ir_p.h"
|
|
#include "qv4isel_p.h"
|
|
#include "qv4isel_util_p.h"
|
|
#include "qmljs_objects.h"
|
|
#include "qmljs_runtime.h"
|
|
|
|
#include <QtCore/QHash>
|
|
#include <config.h>
|
|
#include <wtf/Vector.h>
|
|
#include <assembler/MacroAssembler.h>
|
|
|
|
namespace QQmlJS {
|
|
namespace MASM {
|
|
|
|
class Assembler : public JSC::MacroAssembler
|
|
{
|
|
public:
|
|
Assembler(IR::Function* function);
|
|
#if CPU(X86)
|
|
|
|
#undef VALUE_FITS_IN_REGISTER
|
|
|
|
static const RegisterID StackFrameRegister = JSC::X86Registers::ebp;
|
|
static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
|
|
static const RegisterID ContextRegister = JSC::X86Registers::esi;
|
|
static const RegisterID ReturnValueRegister = JSC::X86Registers::eax;
|
|
static const RegisterID ScratchRegister = JSC::X86Registers::ecx;
|
|
static const RegisterID CalleeSavedFirstRegister = ScratchRegister;
|
|
static const RegisterID CalleeSavedLastRegister = ScratchRegister;
|
|
static const RegisterID IntegerOpRegister = JSC::X86Registers::eax;
|
|
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;
|
|
}
|
|
#elif CPU(X86_64)
|
|
|
|
#define VALUE_FITS_IN_REGISTER
|
|
|
|
static const RegisterID StackFrameRegister = JSC::X86Registers::ebp;
|
|
static const RegisterID StackPointerRegister = JSC::X86Registers::esp;
|
|
static const RegisterID ContextRegister = JSC::X86Registers::r14;
|
|
static const RegisterID ReturnValueRegister = JSC::X86Registers::eax;
|
|
static const RegisterID ScratchRegister = JSC::X86Registers::r10;
|
|
static const RegisterID IntegerOpRegister = JSC::X86Registers::eax;
|
|
static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0;
|
|
|
|
static const int RegisterSize = 8;
|
|
|
|
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];
|
|
};
|
|
#elif CPU(ARM)
|
|
|
|
#undef VALUE_FITS_IN_REGISTER
|
|
|
|
static const RegisterID StackFrameRegister = JSC::ARMRegisters::r4;
|
|
static const RegisterID StackPointerRegister = JSC::ARMRegisters::sp;
|
|
static const RegisterID ContextRegister = JSC::ARMRegisters::r5;
|
|
static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0;
|
|
static const RegisterID ScratchRegister = JSC::ARMRegisters::r6;
|
|
static const RegisterID CalleeSavedFirstRegister = JSC::ARMRegisters::r4;
|
|
static const RegisterID CalleeSavedLastRegister = JSC::ARMRegisters::r11;
|
|
static const RegisterID IntegerOpRegister = JSC::X86Registers::r0;
|
|
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);
|
|
};
|
|
#else
|
|
#error Argh.
|
|
#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)
|
|
{}
|
|
};
|
|
|
|
struct VoidType {};
|
|
static const VoidType Void;
|
|
|
|
|
|
typedef JSC::FunctionPtr FunctionPtr;
|
|
|
|
struct CallToLink {
|
|
Call call;
|
|
FunctionPtr externalFunction;
|
|
const char* functionName;
|
|
};
|
|
|
|
void callAbsolute(const char* functionName, FunctionPtr function) {
|
|
CallToLink ctl;
|
|
ctl.call = call();
|
|
ctl.externalFunction = function;
|
|
ctl.functionName = functionName;
|
|
_callsToLink.append(ctl);
|
|
}
|
|
|
|
void registerBlock(IR::BasicBlock*);
|
|
void jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target);
|
|
void addPatch(IR::BasicBlock* targetBlock, Jump targetJump);
|
|
|
|
Pointer loadTempAddress(RegisterID reg, IR::Temp *t);
|
|
|
|
void loadArgument(RegisterID source, RegisterID dest)
|
|
{
|
|
move(source, dest);
|
|
}
|
|
|
|
void loadArgument(TrustedImmPtr ptr, RegisterID dest)
|
|
{
|
|
move(TrustedImmPtr(ptr), dest);
|
|
}
|
|
|
|
void loadArgument(const Pointer& ptr, RegisterID dest)
|
|
{
|
|
addPtr(TrustedImm32(ptr.offset), ptr.base, dest);
|
|
}
|
|
|
|
#ifdef VALUE_FITS_IN_REGISTER
|
|
void loadArgument(IR::Temp* temp, RegisterID dest)
|
|
{
|
|
if (!temp) {
|
|
VM::Value undefined = VM::Value::undefinedValue();
|
|
move(TrustedImm64(undefined.val), dest);
|
|
} else {
|
|
Pointer addr = loadTempAddress(dest, temp);
|
|
load64(addr, dest);
|
|
}
|
|
}
|
|
|
|
void loadArgument(IR::Const* c, RegisterID dest)
|
|
{
|
|
VM::Value v = convertToValue(c);
|
|
move(TrustedImm64(v.val), dest);
|
|
}
|
|
|
|
void loadArgument(IR::Expr* expr, RegisterID dest)
|
|
{
|
|
if (!expr) {
|
|
VM::Value undefined = VM::Value::undefinedValue();
|
|
move(TrustedImm64(undefined.val), dest);
|
|
} else if (expr->asTemp()){
|
|
loadArgument(expr->asTemp(), dest);
|
|
} else if (expr->asConst()) {
|
|
loadArgument(expr->asConst(), dest);
|
|
} else {
|
|
assert(!"unimplemented expression type in loadArgument");
|
|
}
|
|
}
|
|
#else
|
|
void loadArgument(IR::Expr*, RegisterID)
|
|
{
|
|
assert(!"unimplemented: expression in loadArgument");
|
|
}
|
|
#endif
|
|
|
|
void loadArgument(VM::String* string, RegisterID dest)
|
|
{
|
|
loadArgument(TrustedImmPtr(string), dest);
|
|
}
|
|
|
|
void loadArgument(TrustedImm32 imm32, RegisterID dest)
|
|
{
|
|
xorPtr(dest, dest);
|
|
if (imm32.m_value)
|
|
move(imm32, dest);
|
|
}
|
|
|
|
void storeArgument(RegisterID src, IR::Temp *temp)
|
|
{
|
|
if (temp) {
|
|
Pointer addr = loadTempAddress(ScratchRegister, temp);
|
|
#ifdef VALUE_FITS_IN_REGISTER
|
|
store64(src, addr);
|
|
#else
|
|
// If the value doesn't fit into a register, then the
|
|
// register contains the address to where the argument
|
|
// (return value) is stored. Copy it from there.
|
|
copyValue(addr, Pointer(src, 0));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef VALUE_FITS_IN_REGISTER
|
|
void storeArgument(RegisterID src, const Pointer &dest)
|
|
{
|
|
store64(src, dest);
|
|
}
|
|
#endif
|
|
|
|
void storeArgument(RegisterID src, RegisterID dest)
|
|
{
|
|
move(src, dest);
|
|
}
|
|
|
|
void storeArgument(RegisterID, VoidType)
|
|
{
|
|
}
|
|
|
|
using JSC::MacroAssembler::push;
|
|
|
|
void push(const Pointer& ptr)
|
|
{
|
|
addPtr(TrustedImm32(ptr.offset), ptr.base, ScratchRegister);
|
|
push(ScratchRegister);
|
|
}
|
|
|
|
void push(VM::Value value)
|
|
{
|
|
#ifdef VALUE_FITS_IN_REGISTER
|
|
move(TrustedImm64(value.val), ScratchRegister);
|
|
push(ScratchRegister);
|
|
#else
|
|
move(TrustedImm32(value.tag), ScratchRegister);
|
|
push(ScratchRegister);
|
|
move(TrustedImm32(value.int_32), ScratchRegister);
|
|
push(ScratchRegister);
|
|
#endif
|
|
}
|
|
|
|
void push(IR::Temp* temp)
|
|
{
|
|
if (temp) {
|
|
Address addr = loadTempAddress(ScratchRegister, temp);
|
|
addr.offset += 4;
|
|
push(addr);
|
|
addr.offset -= 4;
|
|
push(addr);
|
|
} else {
|
|
VM::Value undefined = VM::Value::undefinedValue();
|
|
push(undefined);
|
|
}
|
|
}
|
|
|
|
void push(IR::Const* c)
|
|
{
|
|
VM::Value v = convertToValue(c);
|
|
push(v);
|
|
}
|
|
|
|
void push(IR::Expr* e)
|
|
{
|
|
if (!e) {
|
|
VM::Value undefined = VM::Value::undefinedValue();
|
|
push(undefined);
|
|
} else if (IR::Const *c = e->asConst())
|
|
push(c);
|
|
else if (IR::Temp *t = e->asTemp()) {
|
|
push(t);
|
|
} else {
|
|
assert(!"Trying to push an expression that is not a Temp or Const");
|
|
}
|
|
}
|
|
|
|
void push(TrustedImmPtr ptr)
|
|
{
|
|
move(TrustedImmPtr(ptr), ScratchRegister);
|
|
push(ScratchRegister);
|
|
}
|
|
|
|
void push(VM::String* name)
|
|
{
|
|
push(TrustedImmPtr(name));
|
|
}
|
|
|
|
using JSC::MacroAssembler::loadDouble;
|
|
void loadDouble(IR::Temp* temp, FPRegisterID dest)
|
|
{
|
|
Pointer ptr = loadTempAddress(ScratchRegister, temp);
|
|
loadDouble(ptr, dest);
|
|
}
|
|
|
|
using JSC::MacroAssembler::storeDouble;
|
|
void storeDouble(FPRegisterID source, IR::Temp* temp)
|
|
{
|
|
Pointer ptr = loadTempAddress(ScratchRegister, temp);
|
|
storeDouble(source, ptr);
|
|
}
|
|
|
|
template <typename Result, typename Source>
|
|
void copyValue(Result result, Source source);
|
|
|
|
void storeValue(VM::Value 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(VM::Value value, IR::Temp* temp);
|
|
|
|
void enterStandardStackFrame(int locals);
|
|
void leaveStandardStackFrame(int locals);
|
|
|
|
void callFunctionPrologue()
|
|
{
|
|
#if CPU(X86)
|
|
// Callee might clobber it :(
|
|
push(ContextRegister);
|
|
#endif
|
|
}
|
|
void callFunctionEpilogue()
|
|
{
|
|
#if CPU(X86)
|
|
pop(ContextRegister);
|
|
#endif
|
|
}
|
|
|
|
static inline int sizeOfArgument(VoidType)
|
|
{ return 0; }
|
|
static inline int sizeOfArgument(RegisterID)
|
|
{ return RegisterSize; }
|
|
static inline int sizeOfArgument(IR::Temp*)
|
|
{ return 8; } // Size of value
|
|
static inline int sizeOfArgument(IR::Expr*)
|
|
{ return 8; } // Size of value
|
|
static inline int sizeOfArgument(const Pointer&)
|
|
{ return sizeof(void*); }
|
|
static inline int sizeOfArgument(VM::String* string)
|
|
{ return sizeof(string); }
|
|
static inline int sizeOfArgument(TrustedImmPtr)
|
|
{ return sizeof(void*); }
|
|
static inline int sizeOfArgument(TrustedImm32)
|
|
{ return 4; }
|
|
|
|
struct ArgumentLoader
|
|
{
|
|
ArgumentLoader(Assembler* _assembler, int totalNumberOfArguments)
|
|
: assembler(_assembler)
|
|
, stackSpaceForArguments(0)
|
|
, currentRegisterIndex(qMin(totalNumberOfArguments - 1, RegisterArgumentCount - 1))
|
|
{
|
|
}
|
|
|
|
template <typename T>
|
|
void load(T argument)
|
|
{
|
|
if (currentRegisterIndex >= 0) {
|
|
assembler->loadArgument(argument, registerForArgument(currentRegisterIndex));
|
|
--currentRegisterIndex;
|
|
} else {
|
|
assembler->push(argument);
|
|
stackSpaceForArguments += sizeOfArgument(argument);
|
|
}
|
|
}
|
|
|
|
void load(VoidType)
|
|
{
|
|
if (currentRegisterIndex >= 0)
|
|
--currentRegisterIndex;
|
|
}
|
|
|
|
Assembler *assembler;
|
|
int stackSpaceForArguments;
|
|
int currentRegisterIndex;
|
|
};
|
|
|
|
template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
|
|
void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
|
|
{
|
|
callFunctionPrologue();
|
|
|
|
int totalNumberOfArgs = 5;
|
|
|
|
// If necessary reserve space for the return value on the stack and
|
|
// pass the pointer to it as the first hidden parameter.
|
|
bool returnValueOnStack = false;
|
|
int sizeOfReturnValueOnStack = sizeOfArgument(r);
|
|
if (sizeOfReturnValueOnStack > RegisterSize) {
|
|
sub32(TrustedImm32(sizeOfReturnValueOnStack), StackPointerRegister);
|
|
++totalNumberOfArgs;
|
|
returnValueOnStack = true;
|
|
}
|
|
|
|
ArgumentLoader l(this, totalNumberOfArgs);
|
|
l.load(arg5);
|
|
l.load(arg4);
|
|
l.load(arg3);
|
|
l.load(arg2);
|
|
l.load(arg1);
|
|
|
|
if (returnValueOnStack) {
|
|
// Load address of return value
|
|
l.load(Pointer(StackPointerRegister, l.stackSpaceForArguments));
|
|
}
|
|
|
|
callAbsolute(functionName, function);
|
|
|
|
int stackSizeToCorrect = l.stackSpaceForArguments;
|
|
if (returnValueOnStack) {
|
|
stackSizeToCorrect -= sizeof(void*); // Callee removed the hidden argument (address of return value)
|
|
stackSizeToCorrect += sizeOfReturnValueOnStack;
|
|
}
|
|
|
|
storeArgument(ReturnValueRegister, r);
|
|
|
|
if (stackSizeToCorrect)
|
|
add32(TrustedImm32(stackSizeToCorrect), StackPointerRegister);
|
|
|
|
callFunctionEpilogue();
|
|
}
|
|
|
|
template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
|
|
void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
|
|
{
|
|
generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, arg4, VoidType());
|
|
}
|
|
|
|
template <typename ArgRet, typename Arg1, typename Arg2, typename Arg3>
|
|
void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2, Arg3 arg3)
|
|
{
|
|
generateFunctionCallImp(r, functionName, function, arg1, arg2, arg3, VoidType(), VoidType());
|
|
}
|
|
|
|
template <typename ArgRet, typename Arg1, typename Arg2>
|
|
void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2)
|
|
{
|
|
generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType());
|
|
}
|
|
|
|
template <typename ArgRet, typename Arg1>
|
|
void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr 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;
|
|
VM::Value (*fallbackImplementation)(const VM::Value, const VM::Value, VM::ExecutionContext *);
|
|
MemRegBinOp inlineMemRegOp;
|
|
ImmRegBinOp inlineImmRegOp;
|
|
};
|
|
|
|
static const BinaryOperationInfo binaryOperations[QQmlJS::IR::LastAluOp + 1];
|
|
|
|
void generateBinOp(IR::AluOp operation, IR::Temp* target, IR::Expr* left, IR::Expr* right);
|
|
|
|
Jump inline_add32(Address addr, RegisterID reg)
|
|
{
|
|
return branchAdd32(Overflow, addr, reg);
|
|
}
|
|
|
|
Jump inline_add32(TrustedImm32 imm, RegisterID reg)
|
|
{
|
|
return branchAdd32(Overflow, imm, reg);
|
|
}
|
|
|
|
Jump inline_sub32(Address addr, RegisterID reg)
|
|
{
|
|
return branchSub32(Overflow, addr, reg);
|
|
}
|
|
|
|
Jump inline_sub32(TrustedImm32 imm, RegisterID reg)
|
|
{
|
|
return branchSub32(Overflow, imm, reg);
|
|
}
|
|
|
|
Jump inline_mul32(Address addr, RegisterID reg)
|
|
{
|
|
return branchMul32(Overflow, addr, reg);
|
|
}
|
|
|
|
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 Jump();
|
|
}
|
|
|
|
Jump inline_ushr32(TrustedImm32 imm, RegisterID reg)
|
|
{
|
|
imm.m_value &= 0x1f;
|
|
urshift32(imm, reg);
|
|
return Jump();
|
|
}
|
|
|
|
Jump inline_and32(Address addr, RegisterID reg)
|
|
{
|
|
and32(addr, reg);
|
|
return Jump();
|
|
}
|
|
|
|
Jump inline_and32(TrustedImm32 imm, RegisterID reg)
|
|
{
|
|
and32(imm, reg);
|
|
return Jump();
|
|
}
|
|
|
|
Jump inline_or32(Address addr, RegisterID reg)
|
|
{
|
|
or32(addr, reg);
|
|
return Jump();
|
|
}
|
|
|
|
Jump inline_or32(TrustedImm32 imm, RegisterID reg)
|
|
{
|
|
or32(imm, reg);
|
|
return Jump();
|
|
}
|
|
|
|
Jump inline_xor32(Address addr, RegisterID reg)
|
|
{
|
|
xor32(addr, reg);
|
|
return Jump();
|
|
}
|
|
|
|
Jump inline_xor32(TrustedImm32 imm, RegisterID reg)
|
|
{
|
|
xor32(imm, reg);
|
|
return Jump();
|
|
}
|
|
|
|
void link(VM::Function *vmFunc);
|
|
|
|
private:
|
|
IR::Function* _function;
|
|
QHash<IR::BasicBlock *, Label> _addrs;
|
|
QHash<IR::BasicBlock *, QVector<Jump> > _patches;
|
|
QList<CallToLink> _callsToLink;
|
|
};
|
|
|
|
class InstructionSelection: protected IR::StmtVisitor, public EvalInstructionSelection
|
|
{
|
|
public:
|
|
InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module);
|
|
~InstructionSelection();
|
|
|
|
virtual void run(VM::Function *vmFunction, IR::Function *function);
|
|
|
|
protected:
|
|
typedef Assembler::Address Address;
|
|
typedef Assembler::Pointer Pointer;
|
|
|
|
Address addressForArgument(int index) const
|
|
{
|
|
if (index < Assembler::RegisterArgumentCount)
|
|
return Address(_asm->registerForArgument(index), 0);
|
|
|
|
// StackFrameRegister points to its old value on the stack, and above
|
|
// it we have the return address, hence the need to step over two
|
|
// values before reaching the first argument.
|
|
return Address(Assembler::StackFrameRegister, (index - Assembler::RegisterArgumentCount + 2) * sizeof(void*));
|
|
}
|
|
|
|
// Some run-time functions take (Value* args, int argc). This function is for populating
|
|
// the args.
|
|
Pointer argumentAddressForCall(int argument)
|
|
{
|
|
const int index = _function->maxNumberOfArguments - argument;
|
|
return Pointer(Assembler::StackFrameRegister, sizeof(VM::Value) * (-index)
|
|
- sizeof(void*) // size of ebp
|
|
);
|
|
}
|
|
Pointer baseAddressForCallArguments()
|
|
{
|
|
return argumentAddressForCall(0);
|
|
}
|
|
|
|
VM::String *identifier(const QString &s);
|
|
void callActivationProperty(IR::Call *call, IR::Temp *result);
|
|
void callProperty(IR::Call *call, IR::Temp *result);
|
|
void constructActivationProperty(IR::New *call, IR::Temp *result);
|
|
void constructProperty(IR::New *ctor, IR::Temp *result);
|
|
void callValue(IR::Call *call, IR::Temp *result);
|
|
void constructValue(IR::New *call, IR::Temp *result);
|
|
|
|
virtual void visitExp(IR::Exp *);
|
|
virtual void visitEnter(IR::Enter *);
|
|
virtual void visitLeave(IR::Leave *);
|
|
virtual void visitMove(IR::Move *s);
|
|
virtual void visitJump(IR::Jump *);
|
|
virtual void visitCJump(IR::CJump *);
|
|
virtual void visitRet(IR::Ret *);
|
|
|
|
private:
|
|
#define isel_stringIfyx(s) #s
|
|
#define isel_stringIfy(s) isel_stringIfyx(s)
|
|
|
|
#define generateFunctionCall(t, function, ...) \
|
|
_asm->generateFunctionCallImp(t, isel_stringIfy(function), function, __VA_ARGS__)
|
|
|
|
int prepareVariableArguments(IR::ExprList* args);
|
|
|
|
typedef VM::Value (*ActivationMethod)(VM::ExecutionContext *, VM::String *name, VM::Value *args, int argc);
|
|
typedef VM::Value (*BuiltinMethod)(VM::ExecutionContext *, VM::Value *args, int argc);
|
|
void callRuntimeMethodImp(IR::Temp *result, const char* name, ActivationMethod method, IR::Expr *base, IR::ExprList *args);
|
|
void callRuntimeMethodImp(IR::Temp *result, const char* name, BuiltinMethod method, IR::ExprList *args);
|
|
#define callRuntimeMethod(result, function, ...) \
|
|
callRuntimeMethodImp(result, isel_stringIfy(function), function, __VA_ARGS__)
|
|
|
|
IR::BasicBlock *_block;
|
|
IR::Function* _function;
|
|
Assembler* _asm;
|
|
};
|
|
|
|
class ISelFactory: public EvalISelFactory
|
|
{
|
|
public:
|
|
virtual ~ISelFactory() {}
|
|
virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module)
|
|
{ return new InstructionSelection(engine, module); }
|
|
};
|
|
|
|
} // end of namespace MASM
|
|
} // end of namespace QQmlJS
|
|
|
|
#endif // QV4ISEL_MASM_P_H
|