/**************************************************************************** ** ** 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 #include #include #include 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(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 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 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 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 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 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 void generateFunctionCallImp(ArgRet r, const char* functionName, FunctionPtr function, Arg1 arg1, Arg2 arg2) { generateFunctionCallImp(r, functionName, function, arg1, arg2, VoidType(), VoidType(), VoidType()); } template 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 _addrs; QHash > _patches; QList _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