diff options
Diffstat (limited to 'src/qml/jit')
-rw-r--r-- | src/qml/jit/jit.pri | 2 | ||||
-rw-r--r-- | src/qml/jit/qv4assembler.cpp | 131 | ||||
-rw-r--r-- | src/qml/jit/qv4assembler_p.h | 373 | ||||
-rw-r--r-- | src/qml/jit/qv4binop.cpp | 50 | ||||
-rw-r--r-- | src/qml/jit/qv4binop_p.h | 8 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm.cpp | 437 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm_p.h | 139 | ||||
-rw-r--r-- | src/qml/jit/qv4regalloc.cpp | 1292 | ||||
-rw-r--r-- | src/qml/jit/qv4regalloc_p.h | 51 | ||||
-rw-r--r-- | src/qml/jit/qv4registerinfo_p.h | 99 | ||||
-rw-r--r-- | src/qml/jit/qv4targetplatform_p.h | 390 | ||||
-rw-r--r-- | src/qml/jit/qv4unop.cpp | 35 | ||||
-rw-r--r-- | src/qml/jit/qv4unop_p.h | 8 |
13 files changed, 1753 insertions, 1262 deletions
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri index 151ff32df9..7ea4e951d5 100644 --- a/src/qml/jit/jit.pri +++ b/src/qml/jit/jit.pri @@ -6,9 +6,11 @@ INCLUDEPATH += $$OUT_PWD HEADERS += \ $$PWD/qv4assembler_p.h \ $$PWD/qv4regalloc_p.h \ + $$PWD/qv4targetplatform_p.h \ $$PWD/qv4isel_masm_p.h \ $$PWD/qv4binop_p.h \ $$PWD/qv4unop_p.h \ + $$PWD/qv4registerinfo_p.h SOURCES += \ $$PWD/qv4assembler.cpp \ diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index cd44b537df..cb2279b336 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 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. @@ -91,58 +91,6 @@ QV4::ExecutableAllocator::ChunkOfPages *CompilationUnit::chunkForFunction(int fu return handle->chunk(); } - - -/* Platform/Calling convention/Architecture specific section */ - -#if CPU(X86_64) -# if OS(LINUX) || OS(MAC_OS_X) -static const Assembler::RegisterID calleeSavedRegisters[] = { - JSC::X86Registers::ebx, - JSC::X86Registers::r12, // LocalsRegister - JSC::X86Registers::r13, - JSC::X86Registers::r14, // ContextRegister - JSC::X86Registers::r15 -}; -# elif OS(WINDOWS) -static const Assembler::RegisterID calleeSavedRegisters[] = { - JSC::X86Registers::ebx, - JSC::X86Registers::esi, - JSC::X86Registers::edi, - JSC::X86Registers::r12, // LocalsRegister - JSC::X86Registers::r13, - JSC::X86Registers::r14, // ContextRegister - JSC::X86Registers::r15 -}; -# endif -#endif - -#if CPU(X86) -static const Assembler::RegisterID calleeSavedRegisters[] = { - JSC::X86Registers::ebx, // temporary register - JSC::X86Registers::esi, // ContextRegister - JSC::X86Registers::edi // LocalsRegister -}; -#endif - -#if CPU(ARM) -static const Assembler::RegisterID calleeSavedRegisters[] = { - JSC::ARMRegisters::r11, - JSC::ARMRegisters::r10, - JSC::ARMRegisters::r9, - JSC::ARMRegisters::r8, - JSC::ARMRegisters::r7, - JSC::ARMRegisters::r6, - JSC::ARMRegisters::r5, - JSC::ARMRegisters::r4 -}; -#endif - -const int Assembler::calleeSavedRegisterCount = sizeof(calleeSavedRegisters) / sizeof(calleeSavedRegisters[0]); - -/* End of platform/calling convention/architecture specific section */ - - const Assembler::VoidType Assembler::Void; Assembler::Assembler(InstructionSelection *isel, IR::Function* function, QV4::ExecutableAllocator *executableAllocator, @@ -223,33 +171,47 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left } } -Assembler::Pointer Assembler::loadTempAddress(RegisterID baseReg, IR::Temp *t) +Assembler::Pointer Assembler::loadAddress(RegisterID tmp, IR::Expr *e) +{ + IR::Temp *t = e->asTemp(); + if (t) + return loadTempAddress(t); + else + return loadArgLocalAddress(tmp, e->asArgLocal()); +} + +Assembler::Pointer Assembler::loadTempAddress(IR::Temp *t) +{ + if (t->kind == IR::Temp::StackSlot) + return stackSlotPointer(t); + else + Q_UNREACHABLE(); +} + +Assembler::Pointer Assembler::loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al) { int32_t offset = 0; - int scope = t->scope; + int scope = al->scope; RegisterID context = ContextRegister; if (scope) { - loadPtr(Address(ContextRegister, qOffsetOf(ExecutionContext, outer)), baseReg); + loadPtr(Address(ContextRegister, qOffsetOf(ExecutionContext::Data, outer)), baseReg); --scope; context = baseReg; while (scope) { - loadPtr(Address(context, qOffsetOf(ExecutionContext, outer)), context); + loadPtr(Address(context, qOffsetOf(ExecutionContext::Data, outer)), context); --scope; } } - switch (t->kind) { - case IR::Temp::Formal: - case IR::Temp::ScopedFormal: { - loadPtr(Address(context, qOffsetOf(ExecutionContext, callData)), baseReg); - offset = sizeof(CallData) + (t->index - 1) * sizeof(Value); + switch (al->kind) { + case IR::ArgLocal::Formal: + case IR::ArgLocal::ScopedFormal: { + loadPtr(Address(context, qOffsetOf(ExecutionContext::Data, callData)), baseReg); + offset = sizeof(CallData) + (al->index - 1) * sizeof(Value); } break; - case IR::Temp::Local: - case IR::Temp::ScopedLocal: { - loadPtr(Address(context, qOffsetOf(CallContext, locals)), baseReg); - offset = t->index * sizeof(Value); - } break; - case IR::Temp::StackSlot: { - return stackSlotPointer(t); + case IR::ArgLocal::Local: + case IR::ArgLocal::ScopedLocal: { + loadPtr(Address(context, qOffsetOf(CallContext::Data, locals)), baseReg); + offset = al->index * sizeof(Value); } break; default: Q_UNREACHABLE(); @@ -259,7 +221,7 @@ Assembler::Pointer Assembler::loadTempAddress(RegisterID baseReg, IR::Temp *t) Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &string) { - loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, compilationUnit)), Assembler::ScratchRegister); + loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext::Data, compilationUnit)), Assembler::ScratchRegister); loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(QV4::CompiledData::CompilationUnit, runtimeStrings)), reg); const int id = _isel->registerString(string); return Pointer(reg, id * sizeof(QV4::StringValue)); @@ -267,21 +229,21 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s void Assembler::loadStringRef(RegisterID reg, const QString &string) { - loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, compilationUnit)), reg); + loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext::Data, compilationUnit)), reg); loadPtr(Address(reg, qOffsetOf(QV4::CompiledData::CompilationUnit, runtimeStrings)), reg); const int id = _isel->registerString(string); - addPtr(TrustedImmPtr(id * sizeof(QV4::StringValue)), reg); + loadPtr(Address(reg, id * sizeof(QV4::StringValue)), reg); } -void Assembler::storeValue(QV4::Primitive value, IR::Temp* destination) +void Assembler::storeValue(QV4::Primitive value, IR::Expr *destination) { - Address addr = loadTempAddress(ScratchRegister, destination); + Address addr = loadAddress(ScratchRegister, destination); storeValue(value, addr); } void Assembler::enterStandardStackFrame() { - platformEnterStandardStackFrame(); + platformEnterStandardStackFrame(this); // ### FIXME: Handle through calleeSavedRegisters mechanism // or eliminate StackFrameRegister altogether. @@ -292,16 +254,18 @@ void Assembler::enterStandardStackFrame() subPtr(TrustedImm32(frameSize), StackPointerRegister); - for (int i = 0; i < calleeSavedRegisterCount; ++i) - storePtr(calleeSavedRegisters[i], Address(StackFrameRegister, -(i + 1) * sizeof(void*))); + const RegisterInformation &calleeSavedRegisters = getCalleeSavedRegisters(); + for (int i = 0; i < calleeSavedRegisterCount(); ++i) + storePtr(calleeSavedRegisters[i].reg<RegisterID>(), Address(StackFrameRegister, -(i + 1) * sizeof(void*))); } void Assembler::leaveStandardStackFrame() { // restore the callee saved registers - for (int i = calleeSavedRegisterCount - 1; i >= 0; --i) - loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i]); + const RegisterInformation &calleeSavedRegisters = getCalleeSavedRegisters(); + for (int i = calleeSavedRegisterCount() - 1; i >= 0; --i) + loadPtr(Address(StackFrameRegister, -(i + 1) * sizeof(void*)), calleeSavedRegisters[i].reg<RegisterID>()); int frameSize = _stackLayout.calculateStackFrameSize(); // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't @@ -314,7 +278,7 @@ void Assembler::leaveStandardStackFrame() #endif pop(StackFrameRegister); - platformLeaveStandardStackFrame(); + platformLeaveStandardStackFrame(this); } @@ -347,13 +311,12 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe break; } - IR::Temp *sourceTemp = src->asTemp(); - Q_ASSERT(sourceTemp); + Q_ASSERT(src->asTemp() || src->asArgLocal()); // It's not a number type, so it cannot be in a register. - Q_ASSERT(sourceTemp->kind != IR::Temp::PhysicalRegister || sourceTemp->type == IR::BoolType); + Q_ASSERT(src->asArgLocal() || src->asTemp()->kind != IR::Temp::PhysicalRegister || src->type == IR::BoolType); - Assembler::Pointer tagAddr = loadTempAddress(Assembler::ScratchRegister, sourceTemp); + Assembler::Pointer tagAddr = loadAddress(Assembler::ScratchRegister, src); tagAddr.offset += 4; load32(tagAddr, Assembler::ScratchRegister); diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 6fde517e1f..5e1f162f39 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 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. @@ -47,6 +47,7 @@ #include "private/qv4isel_util_p.h" #include "private/qv4value_p.h" #include "private/qv4lookup_p.h" +#include "qv4targetplatform_p.h" #include <QtCore/QHash> #include <QtCore/QStack> @@ -123,158 +124,12 @@ struct ExceptionCheck<void (*)(QV4::NoThrowContext *, A, B, C)> { enum { NeedsCheck = 0 }; }; -class Assembler : public JSC::MacroAssembler +class Assembler : public JSC::MacroAssembler, public TargetPlatform { public: Assembler(InstructionSelection *isel, IR::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 FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - - static const int RegisterSize = 4; - - static const int RegisterArgumentCount = 0; - static RegisterID registerForArgument(int) - { - Q_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 - }; - Q_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 - }; - Q_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 StackPointerRegister = JSC::ARMRegisters::sp; // r13 - static const RegisterID StackFrameRegister = JSC::ARMRegisters::fp; // r11 - static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; - static const RegisterID ScratchRegister = JSC::ARMRegisters::r6; - static const RegisterID ContextRegister = JSC::ARMRegisters::r5; - static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; - static const FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; - - 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) - { - Q_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. @@ -318,7 +173,7 @@ public: { public: StackLayout(IR::Function *function, int maxArgCountForBuiltins) - : calleeSavedRegCount(Assembler::calleeSavedRegisterCount + 1) + : calleeSavedRegCount(Assembler::calleeSavedRegisterCount() + 1) , maxOutgoingArgumentCount(function->maxNumberOfArguments) , localCount(function->tempCount) , savedRegCount(maxArgCountForBuiltins) @@ -351,7 +206,7 @@ public: + RegisterSize; // saved StackFrameRegister // space for the callee saved registers - int frameSize = RegisterSize * calleeSavedRegisterCount; + int frameSize = RegisterSize * calleeSavedRegisterCount(); frameSize += savedRegCount * sizeof(QV4::Value); // these get written out as Values, not as native registers Q_ASSERT(frameSize + stackSpaceAllocatedOtherwise < INT_MAX); @@ -460,8 +315,10 @@ public: QString string; }; struct Reference { - Reference(IR::Temp *value) : value(value) {} - IR::Temp *value; + Reference(IR::Expr *value) : value(value) { + Q_ASSERT(value->asTemp() || value->asArgLocal()); + } + IR::Expr *value; }; struct ReentryBlock { @@ -504,13 +361,14 @@ public: Jump genTryDoubleConversion(IR::Expr *src, Assembler::FPRegisterID dest); Assembler::Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - Pointer loadTempAddress(RegisterID baseReg, IR::Temp *t); + Pointer loadAddress(RegisterID tmp, IR::Expr *t); + Pointer loadTempAddress(IR::Temp *t); + Pointer loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al); Pointer loadStringAddress(RegisterID reg, const QString &string); void loadStringRef(RegisterID reg, const QString &string); Pointer stackSlotPointer(IR::Temp *t) const { Q_ASSERT(t->kind == IR::Temp::StackSlot); - Q_ASSERT(t->scope == 0); return Pointer(_stackLayout.stackSlotPointer(t->index)); } @@ -585,7 +443,7 @@ public: void loadArgumentInRegister(Reference temp, RegisterID dest, int argumentNumber) { Q_ASSERT(temp.value); - Pointer addr = loadTempAddress(dest, temp.value); + Pointer addr = loadAddress(dest, temp.value); loadArgumentInRegister(addr, dest, argumentNumber); } @@ -603,12 +461,25 @@ public: { Q_UNUSED(argumentNumber); - if (!temp) { + if (temp) { + Pointer addr = loadTempAddress(temp); + load64(addr, dest); + } else { QV4::Value undefined = QV4::Primitive::undefinedValue(); move(TrustedImm64(undefined.val), dest); - } else { - Pointer addr = loadTempAddress(dest, temp); + } + } + + void loadArgumentInRegister(IR::ArgLocal* al, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (al) { + Pointer addr = loadArgLocalAddress(dest, al); load64(addr, dest); + } else { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + move(TrustedImm64(undefined.val), dest); } } @@ -627,10 +498,12 @@ public: 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 if (IR::Temp *t = expr->asTemp()){ + loadArgumentInRegister(t, dest, argumentNumber); + } else if (IR::ArgLocal *al = expr->asArgLocal()) { + loadArgumentInRegister(al, dest, argumentNumber); + } else if (IR::Const *c = expr->asConst()) { + loadArgumentInRegister(c, dest, argumentNumber); } else { Q_ASSERT(!"unimplemented expression type in loadArgument"); } @@ -704,20 +577,26 @@ public: } #endif - void storeReturnValue(IR::Temp *temp) + void storeReturnValue(IR::Expr *target) { - if (!temp) + if (!target) return; - if (temp->kind == IR::Temp::PhysicalRegister) { - if (temp->type == IR::DoubleType) - storeReturnValue((FPRegisterID) temp->index); - else if (temp->type == IR::UInt32Type) - storeUInt32ReturnValue((RegisterID) temp->index); - else - storeReturnValue((RegisterID) temp->index); - } else { - Pointer addr = loadTempAddress(ScratchRegister, temp); + if (IR::Temp *temp = target->asTemp()) { + if (temp->kind == IR::Temp::PhysicalRegister) { + if (temp->type == IR::DoubleType) + storeReturnValue((FPRegisterID) temp->index); + else if (temp->type == IR::UInt32Type) + storeUInt32ReturnValue((RegisterID) temp->index); + else + storeReturnValue((RegisterID) temp->index); + return; + } else { + Pointer addr = loadTempAddress(temp); + storeReturnValue(addr); + } + } else if (IR::ArgLocal *al = target->asArgLocal()) { + Pointer addr = loadArgLocalAddress(ScratchRegister, al); storeReturnValue(addr); } } @@ -775,7 +654,7 @@ public: { Q_ASSERT (temp.value); - Pointer ptr = loadTempAddress(ScratchRegister, temp.value); + Pointer ptr = loadAddress(ScratchRegister, temp.value); loadArgumentOnStack<StackSlot>(ptr, argumentNumber); } @@ -807,30 +686,32 @@ public: poke(TrustedImmPtr(name), StackSlot); } - void loadDouble(IR::Temp* temp, FPRegisterID dest) + void loadDouble(IR::Expr *source, FPRegisterID dest) { - if (temp->kind == IR::Temp::PhysicalRegister) { - moveDouble((FPRegisterID) temp->index, dest); + IR::Temp *sourceTemp = source->asTemp(); + if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { + moveDouble((FPRegisterID) sourceTemp->index, dest); return; } - Pointer ptr = loadTempAddress(ScratchRegister, temp); + Pointer ptr = loadAddress(ScratchRegister, source); loadDouble(ptr, dest); } - void storeDouble(FPRegisterID source, IR::Temp* temp) + void storeDouble(FPRegisterID source, IR::Expr* target) { - if (temp->kind == IR::Temp::PhysicalRegister) { - moveDouble(source, (FPRegisterID) temp->index); + IR::Temp *targetTemp = target->asTemp(); + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { + moveDouble(source, (FPRegisterID) targetTemp->index); return; } #if QT_POINTER_SIZE == 8 moveDoubleTo64(source, ReturnValueRegister); move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); xor64(ScratchRegister, ReturnValueRegister); - Pointer ptr = loadTempAddress(ScratchRegister, temp); + Pointer ptr = loadAddress(ScratchRegister, target); store64(ReturnValueRegister, ptr); #else - Pointer ptr = loadTempAddress(ScratchRegister, temp); + Pointer ptr = loadAddress(ScratchRegister, target); storeDouble(source, ptr); #endif } @@ -862,11 +743,11 @@ public: void copyValue(Result result, IR::Expr* source); // The scratch register is used to calculate the temp address for the source. - void memcopyValue(Pointer target, IR::Temp *sourceTemp, RegisterID scratchRegister) + void memcopyValue(Pointer target, IR::Expr *source, RegisterID scratchRegister) { - Q_ASSERT(sourceTemp->kind != IR::Temp::PhysicalRegister); + Q_ASSERT(!source->asTemp() || source->asTemp()->kind != IR::Temp::PhysicalRegister); Q_ASSERT(target.base != scratchRegister); - JSC::MacroAssembler::loadDouble(loadTempAddress(scratchRegister, sourceTemp), FPGpr0); + JSC::MacroAssembler::loadDouble(loadAddress(scratchRegister, source), FPGpr0); JSC::MacroAssembler::storeDouble(FPGpr0, target); } @@ -888,13 +769,13 @@ public: #endif } - void storeValue(QV4::Primitive value, IR::Temp* temp); + void storeValue(QV4::Primitive value, IR::Expr* temp); void enterStandardStackFrame(); void leaveStandardStackFrame(); void checkException() { - loadPtr(Address(ContextRegister, qOffsetOf(QV4::ExecutionContext, engine)), ScratchRegister); + loadPtr(Address(ContextRegister, qOffsetOf(QV4::ExecutionContext::Data, engine)), ScratchRegister); load32(Address(ScratchRegister, qOffsetOf(QV4::ExecutionEngine, hasException)), ScratchRegister); Jump exceptionThrown = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); if (catchBlock) @@ -988,10 +869,8 @@ public: prepareRelativeCall(function, this); loadArgumentOnStackOrRegister<0>(arg1); -#if (OS(LINUX) && CPU(X86) && (defined(__PIC__) || defined(__PIE__))) || \ - (OS(WINDOWS) && CPU(X86)) - load32(Address(StackFrameRegister, -int(sizeof(void*))), - JSC::X86Registers::ebx); // restore the GOT ptr +#ifdef RESTORE_EBX_ON_CALL + load32(ebxAddressOnStack(), JSC::X86Registers::ebx); // restore the GOT ptr #endif callAbsolute(functionName, function); @@ -1050,13 +929,11 @@ public: return Pointer(addr); } - IR::Temp *t = e->asTemp(); - Q_ASSERT(t); - if (t->kind != IR::Temp::PhysicalRegister) - return loadTempAddress(tmpReg, t); - + if (IR::Temp *t = e->asTemp()) + if (t->kind == IR::Temp::PhysicalRegister) + return Pointer(_stackLayout.savedRegPointer(offset)); - return Pointer(_stackLayout.savedRegPointer(offset)); + return loadAddress(tmpReg, e); } void storeBool(RegisterID reg, Pointer addr) @@ -1071,24 +948,31 @@ public: move(src, dest); } - void storeBool(RegisterID reg, IR::Temp *target) + void storeBool(RegisterID reg, IR::Expr *target) { - if (target->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) target->index); - } else { - Pointer addr = loadTempAddress(ScratchRegister, target); - storeBool(reg, addr); + if (IR::Temp *targetTemp = target->asTemp()) { + if (targetTemp->kind == IR::Temp::PhysicalRegister) { + move(reg, (RegisterID) targetTemp->index); + return; + } } + + Pointer addr = loadAddress(ScratchRegister, target); + storeBool(reg, addr); } - void storeBool(bool value, IR::Temp *target) { + void storeBool(bool value, IR::Expr *target) { TrustedImm32 trustedValue(value ? 1 : 0); - if (target->kind == IR::Temp::PhysicalRegister) { - move(trustedValue, (RegisterID) target->index); - } else { - move(trustedValue, ScratchRegister); - storeBool(ScratchRegister, target); + + if (IR::Temp *targetTemp = target->asTemp()) { + if (targetTemp->kind == IR::Temp::PhysicalRegister) { + move(trustedValue, (RegisterID) targetTemp->index); + return; + } } + + move(trustedValue, ScratchRegister); + storeBool(ScratchRegister, target); } void storeInt32(RegisterID src, RegisterID dest) @@ -1103,12 +987,17 @@ public: store32(TrustedImm32(QV4::Primitive::fromInt32(0).tag), addr); } - void storeInt32(RegisterID reg, IR::Temp *target) + void storeInt32(RegisterID reg, IR::Expr *target) { - if (target->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) target->index); - } else { - Pointer addr = loadTempAddress(ScratchRegister, target); + if (IR::Temp *targetTemp = target->asTemp()) { + if (targetTemp->kind == IR::Temp::PhysicalRegister) { + move(reg, (RegisterID) targetTemp->index); + } else { + Pointer addr = loadTempAddress(targetTemp); + storeInt32(reg, addr); + } + } else if (IR::ArgLocal *al = target->asArgLocal()) { + Pointer addr = loadArgLocalAddress(ScratchRegister, al); storeInt32(reg, addr); } } @@ -1130,12 +1019,13 @@ public: done.link(this); } - void storeUInt32(RegisterID reg, IR::Temp *target) + void storeUInt32(RegisterID reg, IR::Expr *target) { - if (target->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) target->index); + IR::Temp *targetTemp = target->asTemp(); + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { + move(reg, (RegisterID) targetTemp->index); } else { - Pointer addr = loadTempAddress(ScratchRegister, target); + Pointer addr = loadAddress(ScratchRegister, target); storeUInt32(reg, addr); } } @@ -1157,12 +1047,11 @@ public: return target; } - IR::Temp *t = e->asTemp(); - Q_ASSERT(t); - if (t->kind == IR::Temp::PhysicalRegister) - return (FPRegisterID) t->index; + if (IR::Temp *t = e->asTemp()) + if (t->kind == IR::Temp::PhysicalRegister) + return (FPRegisterID) t->index; - loadDouble(t, target); + loadDouble(e, target); return target; } @@ -1178,12 +1067,11 @@ public: return scratchReg; } - IR::Temp *t = e->asTemp(); - Q_ASSERT(t); - if (t->kind == IR::Temp::PhysicalRegister) - return (RegisterID) t->index; + if (IR::Temp *t = e->asTemp()) + if (t->kind == IR::Temp::PhysicalRegister) + return (RegisterID) t->index; - return toInt32Register(loadTempAddress(scratchReg, t), scratchReg); + return toInt32Register(loadAddress(scratchReg, e), scratchReg); } RegisterID toInt32Register(Pointer addr, RegisterID scratchReg) @@ -1199,12 +1087,11 @@ public: return scratchReg; } - IR::Temp *t = e->asTemp(); - Q_ASSERT(t); - if (t->kind == IR::Temp::PhysicalRegister) - return (RegisterID) t->index; + if (IR::Temp *t = e->asTemp()) + if (t->kind == IR::Temp::PhysicalRegister) + return (RegisterID) t->index; - return toUInt32Register(loadTempAddress(scratchReg, t), scratchReg); + return toUInt32Register(loadAddress(scratchReg, e), scratchReg); } RegisterID toUInt32Register(Pointer addr, RegisterID scratchReg) @@ -1291,17 +1178,15 @@ void Assembler::copyValue(Result result, IR::Expr* source) storeUInt32(reg, result); } else if (source->type == IR::DoubleType) { storeDouble(toDoubleRegister(source), result); - } else if (IR::Temp *temp = source->asTemp()) { + } else if (source->asTemp() || source->asArgLocal()) { #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); + // 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); + loadDouble(source, FPGpr0); + storeDouble(FPGpr0, result); #endif } else if (IR::Const *c = source->asConst()) { QV4::Primitive v = convertToValue(c); @@ -1316,7 +1201,7 @@ void Assembler::copyValue(Result result, IR::Expr* source) 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)), + as->loadPtr(Assembler::Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext::Data, lookups)), relativeCall.addr.base); } diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index 344bbf56e0..a19072f52e 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -112,7 +112,7 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { -void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Temp *target) +void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) { if (op != IR::OpMod && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType @@ -156,14 +156,15 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Temp *target) } -void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Temp *target) +void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) { Q_ASSERT(lhs->asConst() == 0 || rhs->asConst() == 0); Q_ASSERT(isPregOrConst(lhs)); Q_ASSERT(isPregOrConst(rhs)); + IR::Temp *targetTemp = target->asTemp(); Assembler::FPRegisterID targetReg; - if (target->kind == IR::Temp::PhysicalRegister) - targetReg = (Assembler::FPRegisterID) target->index; + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) + targetReg = (Assembler::FPRegisterID) targetTemp->index; else targetReg = Assembler::FPGpr0; @@ -232,31 +233,33 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Temp *target) } return; } - if (target->kind != IR::Temp::PhysicalRegister) + if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) as->storeDouble(Assembler::FPGpr0, target); } -bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { Q_ASSERT(leftSource->type == IR::SInt32Type); + IR::Temp *targetTemp = target->asTemp(); Assembler::RegisterID targetReg = Assembler::ReturnValueRegister; - if (target->kind == IR::Temp::PhysicalRegister) { + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { // We try to load leftSource into the target's register, but we can't do that if // the target register is the same as rightSource. IR::Temp *rhs = rightSource->asTemp(); - if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != target->index) - targetReg = (Assembler::RegisterID) target->index; + if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != targetTemp->index) + targetReg = (Assembler::RegisterID) targetTemp->index; } switch (op) { case IR::OpBitAnd: { Q_ASSERT(rightSource->type == IR::SInt32Type); if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister - && target->kind == IR::Temp::PhysicalRegister - && target->index == rightSource->asTemp()->index) { + && targetTemp + && targetTemp->kind == IR::Temp::PhysicalRegister + && targetTemp->index == rightSource->asTemp()->index) { as->and32(as->toInt32Register(leftSource, Assembler::ScratchRegister), - (Assembler::RegisterID) target->index); + (Assembler::RegisterID) targetTemp->index); return true; } @@ -268,10 +271,11 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *ta case IR::OpBitOr: { Q_ASSERT(rightSource->type == IR::SInt32Type); if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister - && target->kind == IR::Temp::PhysicalRegister - && target->index == rightSource->asTemp()->index) { + && targetTemp + && targetTemp->kind == IR::Temp::PhysicalRegister + && targetTemp->index == rightSource->asTemp()->index) { as->or32(as->toInt32Register(leftSource, Assembler::ScratchRegister), - (Assembler::RegisterID) target->index); + (Assembler::RegisterID) targetTemp->index); return true; } @@ -283,10 +287,11 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *ta case IR::OpBitXor: { Q_ASSERT(rightSource->type == IR::SInt32Type); if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister - && target->kind == IR::Temp::PhysicalRegister - && target->index == rightSource->asTemp()->index) { + && targetTemp + && targetTemp->kind == IR::Temp::PhysicalRegister + && targetTemp->index == rightSource->asTemp()->index) { as->xor32(as->toInt32Register(leftSource, Assembler::ScratchRegister), - (Assembler::RegisterID) target->index); + (Assembler::RegisterID) targetTemp->index); return true; } @@ -370,9 +375,10 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *ta Q_ASSERT(rightSource->type == IR::SInt32Type); if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister - && target->kind == IR::Temp::PhysicalRegister - && target->index == rightSource->asTemp()->index) { - Assembler::RegisterID targetReg = (Assembler::RegisterID) target->index; + && targetTemp + && targetTemp->kind == IR::Temp::PhysicalRegister + && targetTemp->index == rightSource->asTemp()->index) { + Assembler::RegisterID targetReg = (Assembler::RegisterID) targetTemp->index; as->move(targetReg, Assembler::ScratchRegister); as->move(as->toInt32Register(leftSource, targetReg), targetReg); as->sub32(Assembler::ScratchRegister, targetReg); @@ -407,7 +413,7 @@ static inline Assembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, u return Assembler::FPRegisterID(hint); } -Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { Assembler::Jump done; diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index a6292e6fb5..8c7fa4337a 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -58,10 +58,10 @@ struct Binop { , op(operation) {} - void generate(IR::Expr *lhs, IR::Expr *rhs, IR::Temp *target); - void doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Temp *target); - bool int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); - Assembler::Jump genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + void generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); + void doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); + bool int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target); + Assembler::Jump genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target); typedef Assembler::Jump (Binop::*MemRegOp)(Assembler::Address, Assembler::RegisterID); typedef Assembler::Jump (Binop::*ImmRegOp)(Assembler::TrustedImm32, Assembler::RegisterID); diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 17e2730669..353a29425d 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 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. @@ -203,9 +203,9 @@ InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::Ex : EvalInstructionSelection(execAllocator, module, jsGenerator) , _block(0) , _as(0) + , compilationUnit(new CompilationUnit) , qmlEngine(qmlEngine) { - compilationUnit = new CompilationUnit; compilationUnit->codeRefs.resize(module->functions.size()); } @@ -214,90 +214,6 @@ InstructionSelection::~InstructionSelection() delete _as; } -#if (CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX))) || (CPU(X86) && OS(LINUX)) -# define REGALLOC_IS_SUPPORTED -static QVector<int> getIntRegisters() -{ -# if CPU(X86) && OS(LINUX) // x86 with linux - static const QVector<int> intRegisters = QVector<int>() - << JSC::X86Registers::edx - << JSC::X86Registers::ebx; -# else // x86_64 with linux or with macos - static const QVector<int> intRegisters = QVector<int>() - << JSC::X86Registers::ebx - << JSC::X86Registers::edi - << JSC::X86Registers::esi - << JSC::X86Registers::edx - << JSC::X86Registers::r9 - << JSC::X86Registers::r8 - << JSC::X86Registers::r13 - << JSC::X86Registers::r15; -# endif - return intRegisters; -} - -static QVector<int> getFpRegisters() -{ -// linux/x86_64, linux/x86, and macos/x86_64: - static const QVector<int> fpRegisters = QVector<int>() - << JSC::X86Registers::xmm2 - << JSC::X86Registers::xmm3 - << JSC::X86Registers::xmm4 - << JSC::X86Registers::xmm5 - << JSC::X86Registers::xmm6 - << JSC::X86Registers::xmm7; - return fpRegisters; -} - -#elif CPU(ARM) && OS(LINUX) - // Note: this is not generic for all ARM platforms. Specifically, r9 is platform dependent - // (e.g. iOS reserves it). See the ARM GNU Linux abi for details. -# define REGALLOC_IS_SUPPORTED -static QVector<int> getIntRegisters() -{ - static const QVector<int> intRegisters = QVector<int>() - << JSC::ARMRegisters::r1 - << JSC::ARMRegisters::r2 - << JSC::ARMRegisters::r3 - << JSC::ARMRegisters::r4 - << JSC::ARMRegisters::r8 - << JSC::ARMRegisters::r9; - return intRegisters; -} - -static QVector<int> getFpRegisters() -{ - static const QVector<int> fpRegisters = QVector<int>() - << JSC::ARMRegisters::d2 - << JSC::ARMRegisters::d3 - << JSC::ARMRegisters::d4 - << JSC::ARMRegisters::d5 - << JSC::ARMRegisters::d6; - return fpRegisters; -} -#elif CPU(X86) && OS(WINDOWS) -# define REGALLOC_IS_SUPPORTED -static QVector<int> getIntRegisters() -{ - static const QVector<int> intRegisters = QVector<int>() - << JSC::X86Registers::edx - << JSC::X86Registers::ebx; - return intRegisters; -} - -static QVector<int> getFpRegisters() -{ - static const QVector<int> fpRegisters = QVector<int>() - << JSC::X86Registers::xmm2 - << JSC::X86Registers::xmm3 - << JSC::X86Registers::xmm4 - << JSC::X86Registers::xmm5 - << JSC::X86Registers::xmm6 - << JSC::X86Registers::xmm7; - return fpRegisters; -} -#endif - void InstructionSelection::run(int functionIndex) { IR::Function *function = irModule->functions[functionIndex]; @@ -307,19 +223,16 @@ void InstructionSelection::run(int functionIndex) IR::Optimizer opt(_function); opt.run(qmlEngine); -#ifdef REGALLOC_IS_SUPPORTED static const bool withRegisterAllocator = qgetenv("QV4_NO_REGALLOC").isEmpty(); - if (opt.isInSSA() && withRegisterAllocator) { - RegisterAllocator(getIntRegisters(), getFpRegisters()).run(_function, opt); - } else -#endif // REGALLOC_IS_SUPPORTED - { + if (Assembler::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { + RegisterAllocator(Assembler::getRegisterInfo()).run(_function, opt); + } else { if (opt.isInSSA()) // No register allocator available for this platform, or env. var was set, so: opt.convertOutOfSSA(); ConvertTemps().toStackSlots(_function); + IR::Optimizer::showMeTheCode(_function); } - IR::Optimizer::showMeTheCode(_function); QSet<IR::Jump *> removableJumps = opt.calculateOptionalJumps(); qSwap(_removableJumps, removableJumps); @@ -335,7 +248,7 @@ void InstructionSelection::run(int functionIndex) #endif const int locals = _as->stackLayout().calculateJSStackFrameSize(); - _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, engine)), Assembler::ScratchRegister); + _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext::Data, engine)), Assembler::ScratchRegister); _as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop)), Assembler::LocalsRegister); _as->addPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister); _as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); @@ -351,7 +264,7 @@ void InstructionSelection::run(int functionIndex) foreach (IR::Stmt *s, _block->statements()) { if (s->location.isValid()) { if (int(s->location.startLine) != lastLine) { - Assembler::Address lineAddr(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, lineNumber)); + Assembler::Address lineAddr(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext::Data, lineNumber)); _as->store32(Assembler::TrustedImm32(s->location.startLine), lineAddr); lastLine = s->location.startLine; } @@ -385,10 +298,10 @@ const void *InstructionSelection::addConstantTable(QVector<Primitive> *values) QV4::CompiledData::CompilationUnit *InstructionSelection::backendCompileStep() { - return compilationUnit; + return compilationUnit.take(); } -void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) { prepareCallData(args, 0); @@ -407,52 +320,52 @@ void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args } void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name, - IR::Temp *result) + IR::Expr *result) { generateFunctionCall(result, Runtime::typeofMember, Assembler::ContextRegister, Assembler::PointerToValue(base), Assembler::PointerToString(name)); } void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, - IR::Temp *result) + IR::Expr *result) { generateFunctionCall(result, Runtime::typeofElement, Assembler::ContextRegister, Assembler::PointerToValue(base), Assembler::PointerToValue(index)); } -void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Temp *result) +void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result) { generateFunctionCall(result, Runtime::typeofName, Assembler::ContextRegister, Assembler::PointerToString(name)); } -void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Temp *result) +void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) { generateFunctionCall(result, Runtime::typeofValue, Assembler::ContextRegister, Assembler::PointerToValue(value)); } -void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) { generateFunctionCall(result, Runtime::deleteMember, Assembler::ContextRegister, Assembler::Reference(base), Assembler::PointerToString(name)); } -void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Expr *index, - IR::Temp *result) +void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, + IR::Expr *result) { generateFunctionCall(result, Runtime::deleteElement, Assembler::ContextRegister, Assembler::Reference(base), Assembler::PointerToValue(index)); } -void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Temp *result) +void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result) { generateFunctionCall(result, Runtime::deleteName, Assembler::ContextRegister, Assembler::PointerToString(name)); } -void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result) { _as->storeValue(Primitive::fromBoolean(false), result); } @@ -468,7 +381,7 @@ void InstructionSelection::callBuiltinReThrow() _as->jumpToExceptionHandler(); } -void InstructionSelection::callBuiltinUnwindException(IR::Temp *result) +void InstructionSelection::callBuiltinUnwindException(IR::Expr *result) { generateFunctionCall(result, Runtime::unwindException, Assembler::ContextRegister); @@ -476,11 +389,10 @@ void InstructionSelection::callBuiltinUnwindException(IR::Temp *result) void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName) { - Assembler::Pointer s = _as->loadStringAddress(Assembler::ScratchRegister, exceptionName); - generateFunctionCall(Assembler::ContextRegister, Runtime::pushCatchScope, Assembler::ContextRegister, s); + generateFunctionCall(Assembler::ContextRegister, Runtime::pushCatchScope, Assembler::ContextRegister, Assembler::PointerToString(exceptionName)); } -void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Temp *result) +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) { Q_ASSERT(arg); Q_ASSERT(result); @@ -488,7 +400,7 @@ void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::T generateFunctionCall(result, Runtime::foreachIterator, Assembler::ContextRegister, Assembler::PointerToValue(arg)); } -void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) { Q_ASSERT(arg); Q_ASSERT(result); @@ -496,7 +408,7 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR: generateFunctionCall(result, Runtime::foreachNextPropertyName, Assembler::Reference(arg)); } -void InstructionSelection::callBuiltinPushWithScope(IR::Temp *arg) +void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) { Q_ASSERT(arg); @@ -514,7 +426,7 @@ void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString & Assembler::TrustedImm32(deletable), Assembler::PointerToString(name)); } -void InstructionSelection::callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args) +void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) { Q_ASSERT(result); @@ -523,7 +435,7 @@ void InstructionSelection::callBuiltinDefineArray(IR::Temp *result, IR::ExprList baseAddressForCallArguments(), Assembler::TrustedImm32(length)); } -void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Temp *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) +void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) { Q_ASSERT(result); @@ -604,7 +516,7 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Temp *result, int Assembler::TrustedImm32(arrayValueCount), Assembler::TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); } -void InstructionSelection::callBuiltinSetupArgumentObject(IR::Temp *result) +void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) { generateFunctionCall(result, Runtime::setupArgumentsObject, Assembler::ContextRegister); } @@ -614,19 +526,24 @@ void InstructionSelection::callBuiltinConvertThisToObject() generateFunctionCall(Assembler::Void, Runtime::convertThisToObject, Assembler::ContextRegister); } -void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(value); prepareCallData(args, 0); - generateFunctionCall(result, Runtime::callValue, Assembler::ContextRegister, - Assembler::Reference(value), - baseAddressForCallData()); + if (value->asConst()) + generateFunctionCall(result, Runtime::callValue, Assembler::ContextRegister, + Assembler::PointerToValue(value), + baseAddressForCallData()); + else + generateFunctionCall(result, Runtime::callValue, Assembler::ContextRegister, + Assembler::Reference(value), + baseAddressForCallData()); } -void InstructionSelection::loadThisObject(IR::Temp *temp) +void InstructionSelection::loadThisObject(IR::Expr *temp) { - _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, callData)), Assembler::ScratchRegister); + _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext::Data, callData)), Assembler::ScratchRegister); #if defined(VALUE_FITS_IN_REGISTER) _as->load64(Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject)), Assembler::ReturnValueRegister); @@ -636,60 +553,63 @@ void InstructionSelection::loadThisObject(IR::Temp *temp) #endif } -void InstructionSelection::loadQmlIdArray(IR::Temp *temp) +void InstructionSelection::loadQmlIdArray(IR::Expr *temp) { generateFunctionCall(temp, Runtime::getQmlIdArray, Assembler::ContextRegister); } -void InstructionSelection::loadQmlImportedScripts(IR::Temp *temp) +void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) { generateFunctionCall(temp, Runtime::getQmlImportedScripts, Assembler::ContextRegister); } -void InstructionSelection::loadQmlContextObject(IR::Temp *temp) +void InstructionSelection::loadQmlContextObject(IR::Expr *temp) { generateFunctionCall(temp, Runtime::getQmlContextObject, Assembler::ContextRegister); } -void InstructionSelection::loadQmlScopeObject(IR::Temp *temp) +void InstructionSelection::loadQmlScopeObject(IR::Expr *temp) { generateFunctionCall(temp, Runtime::getQmlScopeObject, Assembler::ContextRegister); } -void InstructionSelection::loadQmlSingleton(const QString &name, IR::Temp *temp) +void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *temp) { generateFunctionCall(temp, Runtime::getQmlSingleton, Assembler::ContextRegister, Assembler::PointerToString(name)); } -void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) -{ - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp->type == IR::DoubleType) { - Q_ASSERT(sourceConst->type == IR::DoubleType); - _as->toDoubleRegister(sourceConst, (Assembler::FPRegisterID) targetTemp->index); - } else if (targetTemp->type == IR::SInt32Type) { - Q_ASSERT(sourceConst->type == IR::SInt32Type); - _as->toInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); - } else if (targetTemp->type == IR::UInt32Type) { - Q_ASSERT(sourceConst->type == IR::UInt32Type); - _as->toUInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); - } else if (targetTemp->type == IR::BoolType) { - Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32), - (Assembler::RegisterID) targetTemp->index); - } else { - Q_UNREACHABLE(); +void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) +{ + if (IR::Temp *targetTemp = target->asTemp()) { + if (targetTemp->kind == IR::Temp::PhysicalRegister) { + if (targetTemp->type == IR::DoubleType) { + Q_ASSERT(sourceConst->type == IR::DoubleType); + _as->toDoubleRegister(sourceConst, (Assembler::FPRegisterID) targetTemp->index); + } else if (targetTemp->type == IR::SInt32Type) { + Q_ASSERT(sourceConst->type == IR::SInt32Type); + _as->toInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); + } else if (targetTemp->type == IR::UInt32Type) { + Q_ASSERT(sourceConst->type == IR::UInt32Type); + _as->toUInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); + } else if (targetTemp->type == IR::BoolType) { + Q_ASSERT(sourceConst->type == IR::BoolType); + _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32), + (Assembler::RegisterID) targetTemp->index); + } else { + Q_UNREACHABLE(); + } + return; } - } else { - _as->storeValue(convertToValue(sourceConst), targetTemp); } + + _as->storeValue(convertToValue(sourceConst), target); } -void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +void InstructionSelection::loadString(const QString &str, IR::Expr *target) { Pointer srcAddr = _as->loadStringAddress(Assembler::ReturnValueRegister, str); _as->loadPtr(srcAddr, Assembler::ReturnValueRegister); - Pointer destAddr = _as->loadTempAddress(Assembler::ScratchRegister, targetTemp); + Pointer destAddr = _as->loadAddress(Assembler::ScratchRegister, target); #if QT_POINTER_SIZE == 8 _as->store64(Assembler::ReturnValueRegister, destAddr); #else @@ -699,20 +619,20 @@ void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) #endif } -void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) { int id = registerRegExp(sourceRegexp); - generateFunctionCall(targetTemp, Runtime::regexpLiteral, Assembler::ContextRegister, Assembler::TrustedImm32(id)); + generateFunctionCall(target, Runtime::regexpLiteral, Assembler::ContextRegister, Assembler::TrustedImm32(id)); } -void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Temp *temp) +void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target) { if (useFastLookups && name->global) { uint index = registerGlobalGetterLookup(*name->id); - generateLookupCall(temp, index, qOffsetOf(QV4::Lookup, globalGetter), Assembler::ContextRegister, Assembler::Void); + generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), Assembler::ContextRegister, Assembler::Void); return; } - generateFunctionCall(temp, Runtime::getActivationProperty, Assembler::ContextRegister, Assembler::PointerToString(*name->id)); + generateFunctionCall(target, Runtime::getActivationProperty, Assembler::ContextRegister, Assembler::PointerToString(*name->id)); } void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) @@ -722,13 +642,13 @@ void InstructionSelection::setActivationProperty(IR::Expr *source, const QString Assembler::ContextRegister, Assembler::PointerToString(targetName), Assembler::PointerToValue(source)); } -void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target) { int id = closure->value; generateFunctionCall(target, Runtime::closure, Assembler::ContextRegister, Assembler::TrustedImm32(id)); } -void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Temp *target) +void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) { if (useFastLookups) { uint index = registerGetterLookup(name); @@ -739,7 +659,7 @@ void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR:: } } -void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, IR::Temp *target) +void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, IR::Expr *target) { if (attachedPropertiesId != 0) generateFunctionCall(target, Runtime::getQmlAttachedProperty, Assembler::ContextRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex)); @@ -769,7 +689,7 @@ void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *target Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); } -void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Temp *target) +void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) { if (useFastLookups) { uint lookup = registerIndexedGetterLookup(); @@ -797,13 +717,20 @@ void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR Assembler::PointerToValue(source)); } -void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) { - if (*sourceTemp == *targetTemp) + IR::Temp *sourceTemp = source->asTemp(); + IR::Temp *targetTemp = target->asTemp(); + + if (sourceTemp && targetTemp && *sourceTemp == *targetTemp) return; + if (IR::ArgLocal *sal = source->asArgLocal()) + if (IR::ArgLocal *tal = target->asArgLocal()) + if (*sal == *tal) + return; - if (sourceTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { + if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { if (sourceTemp->type == IR::DoubleType) _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index, (Assembler::FPRegisterID) targetTemp->index); @@ -814,16 +741,16 @@ void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) } else { switch (sourceTemp->type) { case IR::DoubleType: - _as->storeDouble((Assembler::FPRegisterID) sourceTemp->index, targetTemp); + _as->storeDouble((Assembler::FPRegisterID) sourceTemp->index, target); break; case IR::SInt32Type: - _as->storeInt32((Assembler::RegisterID) sourceTemp->index, targetTemp); + _as->storeInt32((Assembler::RegisterID) sourceTemp->index, target); break; case IR::UInt32Type: - _as->storeUInt32((Assembler::RegisterID) sourceTemp->index, targetTemp); + _as->storeUInt32((Assembler::RegisterID) sourceTemp->index, target); break; case IR::BoolType: - _as->storeBool((Assembler::RegisterID) sourceTemp->index, targetTemp); + _as->storeBool((Assembler::RegisterID) sourceTemp->index, target); break; default: Q_ASSERT(!"Unreachable"); @@ -831,23 +758,23 @@ void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) } return; } - } else if (targetTemp->kind == IR::Temp::PhysicalRegister) { + } else if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { switch (targetTemp->type) { case IR::DoubleType: - Q_ASSERT(sourceTemp->type == IR::DoubleType); - _as->toDoubleRegister(sourceTemp, (Assembler::FPRegisterID) targetTemp->index); + Q_ASSERT(source->type == IR::DoubleType); + _as->toDoubleRegister(source, (Assembler::FPRegisterID) targetTemp->index); return; case IR::BoolType: - Q_ASSERT(sourceTemp->type == IR::BoolType); - _as->toInt32Register(sourceTemp, (Assembler::RegisterID) targetTemp->index); + Q_ASSERT(source->type == IR::BoolType); + _as->toInt32Register(source, (Assembler::RegisterID) targetTemp->index); return; case IR::SInt32Type: - Q_ASSERT(sourceTemp->type == IR::SInt32Type); - _as->toInt32Register(sourceTemp, (Assembler::RegisterID) targetTemp->index); + Q_ASSERT(source->type == IR::SInt32Type); + _as->toInt32Register(source, (Assembler::RegisterID) targetTemp->index); return; case IR::UInt32Type: - Q_ASSERT(sourceTemp->type == IR::UInt32Type); - _as->toUInt32Register(sourceTemp, (Assembler::RegisterID) targetTemp->index); + Q_ASSERT(source->type == IR::UInt32Type); + _as->toUInt32Register(source, (Assembler::RegisterID) targetTemp->index); return; default: Q_ASSERT(!"Unreachable"); @@ -856,14 +783,16 @@ void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) } // The target is not a physical register, nor is the source. So we can do a memory-to-memory copy: - _as->memcopyValue(_as->loadTempAddress(Assembler::ReturnValueRegister, targetTemp), sourceTemp, - Assembler::ScratchRegister); + _as->memcopyValue(_as->loadAddress(Assembler::ReturnValueRegister, target), source, Assembler::ScratchRegister); } -void InstructionSelection::swapValues(IR::Temp *sourceTemp, IR::Temp *targetTemp) +void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) { - if (sourceTemp->kind == IR::Temp::PhysicalRegister) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { + IR::Temp *sourceTemp = source->asTemp(); + IR::Temp *targetTemp = target->asTemp(); + + if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { Q_ASSERT(sourceTemp->type == targetTemp->type); if (sourceTemp->type == IR::DoubleType) { @@ -877,11 +806,11 @@ void InstructionSelection::swapValues(IR::Temp *sourceTemp, IR::Temp *targetTemp } return; } - } else if (sourceTemp->kind == IR::Temp::StackSlot) { - if (targetTemp->kind == IR::Temp::StackSlot) { + } else if (!sourceTemp || sourceTemp->kind == IR::Temp::StackSlot) { + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { // Note: a swap for two stack-slots can involve different types. - Assembler::Pointer sAddr = _as->stackSlotPointer(sourceTemp); - Assembler::Pointer tAddr = _as->stackSlotPointer(targetTemp); + Assembler::Pointer sAddr = _as->loadAddress(Assembler::ScratchRegister, source); + Assembler::Pointer tAddr = _as->loadAddress(Assembler::ReturnValueRegister, target); // use the implementation in JSC::MacroAssembler, as it doesn't do bit swizzling _as->JSC::MacroAssembler::loadDouble(sAddr, Assembler::FPGpr0); _as->JSC::MacroAssembler::loadDouble(tAddr, Assembler::FPGpr1); @@ -891,25 +820,28 @@ void InstructionSelection::swapValues(IR::Temp *sourceTemp, IR::Temp *targetTemp } } - IR::Temp *stackTemp = sourceTemp->kind == IR::Temp::StackSlot ? sourceTemp : targetTemp; - IR::Temp *registerTemp = sourceTemp->kind == IR::Temp::PhysicalRegister ? sourceTemp - : targetTemp; - Assembler::Pointer addr = _as->stackSlotPointer(stackTemp); - if (registerTemp->type == IR::DoubleType) { + IR::Expr *memExpr = !sourceTemp || sourceTemp->kind == IR::Temp::StackSlot ? source : target; + IR::Temp *regTemp = sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister ? sourceTemp + : targetTemp; + Q_ASSERT(memExpr); + Q_ASSERT(regTemp); + + Assembler::Pointer addr = _as->loadAddress(Assembler::ReturnValueRegister, memExpr); + if (regTemp->type == IR::DoubleType) { _as->loadDouble(addr, Assembler::FPGpr0); - _as->storeDouble((Assembler::FPRegisterID) registerTemp->index, addr); - _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) registerTemp->index); - } else if (registerTemp->type == IR::UInt32Type) { + _as->storeDouble((Assembler::FPRegisterID) regTemp->index, addr); + _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) regTemp->index); + } else if (regTemp->type == IR::UInt32Type) { _as->toUInt32Register(addr, Assembler::ScratchRegister); - _as->storeUInt32((Assembler::RegisterID) registerTemp->index, addr); - _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) registerTemp->index); + _as->storeUInt32((Assembler::RegisterID) regTemp->index, addr); + _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) regTemp->index); } else { _as->load32(addr, Assembler::ScratchRegister); - _as->store32((Assembler::RegisterID) registerTemp->index, addr); - if (registerTemp->type != stackTemp->type) { + _as->store32((Assembler::RegisterID) regTemp->index, addr); + if (regTemp->type != memExpr->type) { addr.offset += 4; quint32 tag; - switch (registerTemp->type) { + switch (regTemp->type) { case IR::BoolType: tag = QV4::Value::_Boolean_Type; break; @@ -922,7 +854,7 @@ void InstructionSelection::swapValues(IR::Temp *sourceTemp, IR::Temp *targetTemp } _as->store32(Assembler::TrustedImm32(tag), addr); } - _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) registerTemp->index); + _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) regTemp->index); } } @@ -931,21 +863,21 @@ void InstructionSelection::swapValues(IR::Temp *sourceTemp, IR::Temp *targetTemp #define setOpContext(op, opName, operation) \ do { opContext = operation; opName = isel_stringIfy(operation); } while (0) -void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) { QV4::JIT::Unop unop(_as, oper); - unop.generate(sourceTemp, targetTemp); + unop.generate(source, target); } -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { QV4::JIT::Binop binop(_as, oper); binop.generate(leftSource, rightSource, target); } void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, - IR::Temp *result) + IR::Expr *result) { Q_ASSERT(base != 0); @@ -965,7 +897,7 @@ void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR: } void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, - IR::Temp *result) + IR::Expr *result) { Q_ASSERT(base != 0); @@ -975,7 +907,7 @@ void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::Ex baseAddressForCallData()); } -void InstructionSelection::convertType(IR::Temp *source, IR::Temp *target) +void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target) { switch (target->type) { case IR::DoubleType: @@ -996,7 +928,7 @@ void InstructionSelection::convertType(IR::Temp *source, IR::Temp *target) } } -void InstructionSelection::convertTypeSlowPath(IR::Temp *source, IR::Temp *target) +void InstructionSelection::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) { Q_ASSERT(target->type != IR::BoolType); @@ -1006,7 +938,7 @@ void InstructionSelection::convertTypeSlowPath(IR::Temp *source, IR::Temp *targe copyValue(source, target); } -void InstructionSelection::convertTypeToDouble(IR::Temp *source, IR::Temp *target) +void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::SInt32Type: @@ -1018,13 +950,13 @@ void InstructionSelection::convertTypeToDouble(IR::Temp *source, IR::Temp *targe convertUIntToDouble(source, target); break; case IR::UndefinedType: - _as->loadDouble(_as->loadTempAddress(Assembler::ScratchRegister, source), Assembler::FPGpr0); + _as->loadDouble(_as->loadAddress(Assembler::ScratchRegister, source), Assembler::FPGpr0); _as->storeDouble(Assembler::FPGpr0, target); break; case IR::StringType: case IR::VarType: { // load the tag: - Assembler::Pointer tagAddr = _as->loadTempAddress(Assembler::ScratchRegister, source); + Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, source); tagAddr.offset += 4; _as->load32(tagAddr, Assembler::ScratchRegister); @@ -1051,17 +983,18 @@ void InstructionSelection::convertTypeToDouble(IR::Temp *source, IR::Temp *targe // it is a double: isDbl.link(_as); - Assembler::Pointer addr2 = _as->loadTempAddress(Assembler::ScratchRegister, source); - if (target->kind == IR::Temp::StackSlot) { + Assembler::Pointer addr2 = _as->loadAddress(Assembler::ScratchRegister, source); + IR::Temp *targetTemp = target->asTemp(); + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { #if QT_POINTER_SIZE == 8 _as->load64(addr2, Assembler::ScratchRegister); - _as->store64(Assembler::ScratchRegister, _as->stackSlotPointer(target)); + _as->store64(Assembler::ScratchRegister, _as->loadAddress(Assembler::ReturnValueRegister, target)); #else _as->loadDouble(addr2, Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, _as->stackSlotPointer(target)); + _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(Assembler::ReturnValueRegister, target)); #endif } else { - _as->loadDouble(addr2, (Assembler::FPRegisterID) target->index); + _as->loadDouble(addr2, (Assembler::FPRegisterID) targetTemp->index); } noDoubleDone.link(_as); @@ -1073,8 +1006,9 @@ void InstructionSelection::convertTypeToDouble(IR::Temp *source, IR::Temp *targe } } -void InstructionSelection::convertTypeToBool(IR::Temp *source, IR::Temp *target) +void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) { + IR::Temp *sourceTemp = source->asTemp(); switch (source->type) { case IR::SInt32Type: case IR::UInt32Type: @@ -1085,8 +1019,8 @@ void InstructionSelection::convertTypeToBool(IR::Temp *source, IR::Temp *target) // allocator was not used, then that means that we can use any register for to // load the double into. Assembler::FPRegisterID reg; - if (source->kind == IR::Temp::PhysicalRegister) - reg = (Assembler::FPRegisterID) source->index; + if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) + reg = (Assembler::FPRegisterID) sourceTemp->index; else reg = _as->toDoubleRegister(source, (Assembler::FPRegisterID) 1); Assembler::Jump nonZero = _as->branchDoubleNonZero(reg, Assembler::FPGpr0); @@ -1116,13 +1050,13 @@ void InstructionSelection::convertTypeToBool(IR::Temp *source, IR::Temp *target) } } -void InstructionSelection::convertTypeToSInt32(IR::Temp *source, IR::Temp *target) +void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::VarType: { #if QT_POINTER_SIZE == 8 - Assembler::Pointer addr = _as->loadTempAddress(Assembler::ScratchRegister, source); + Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); _as->load64(addr, Assembler::ScratchRegister); _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister); @@ -1142,12 +1076,13 @@ void InstructionSelection::convertTypeToSInt32(IR::Temp *source, IR::Temp *targe // not an int: fallback.link(_as); generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt, - _as->loadTempAddress(Assembler::ScratchRegister, source)); + _as->loadAddress(Assembler::ScratchRegister, source)); isInt.link(_as); success.link(_as); - if (target->kind == IR::Temp::StackSlot) { - Assembler::Pointer targetAddr = _as->stackSlotPointer(target); + IR::Temp *targetTemp = target->asTemp(); + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { + Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); _as->store32(Assembler::ReturnValueRegister, targetAddr); targetAddr.offset += 4; _as->store32(Assembler::TrustedImm32(Value::_Integer_Type), targetAddr); @@ -1156,7 +1091,7 @@ void InstructionSelection::convertTypeToSInt32(IR::Temp *source, IR::Temp *targe } #else // load the tag: - Assembler::Pointer addr = _as->loadTempAddress(Assembler::ScratchRegister, source); + Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); Assembler::Pointer tagAddr = addr; tagAddr.offset += 4; _as->load32(tagAddr, Assembler::ReturnValueRegister); @@ -1164,21 +1099,22 @@ void InstructionSelection::convertTypeToSInt32(IR::Temp *source, IR::Temp *targe // check if it's an int32: Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, Assembler::TrustedImm32(Value::_Integer_Type)); - if (target->kind == IR::Temp::StackSlot) { - _as->load32(addr, Assembler::ScratchRegister); - Assembler::Pointer targetAddr = _as->stackSlotPointer(target); - _as->store32(Assembler::ScratchRegister, targetAddr); + IR::Temp *targetTemp = target->asTemp(); + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { + _as->load32(addr, Assembler::ReturnValueRegister); + Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); + _as->store32(Assembler::ReturnValueRegister, targetAddr); targetAddr.offset += 4; _as->store32(Assembler::TrustedImm32(Value::_Integer_Type), targetAddr); } else { - _as->load32(addr, (Assembler::RegisterID) target->index); + _as->load32(addr, (Assembler::RegisterID) targetTemp->index); } Assembler::Jump intDone = _as->jump(); // not an int: fallback.link(_as); generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt, - _as->loadTempAddress(Assembler::ScratchRegister, source)); + _as->loadAddress(Assembler::ScratchRegister, source)); _as->storeInt32(Assembler::ReturnValueRegister, target); intDone.link(_as); @@ -1209,32 +1145,32 @@ void InstructionSelection::convertTypeToSInt32(IR::Temp *source, IR::Temp *targe case IR::StringType: default: generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toInt, - _as->loadTempAddress(Assembler::ScratchRegister, source)); + _as->loadAddress(Assembler::ScratchRegister, source)); _as->storeInt32(Assembler::ReturnValueRegister, target); break; } // switch (source->type) } -void InstructionSelection::convertTypeToUInt32(IR::Temp *source, IR::Temp *target) +void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::VarType: { // load the tag: - Assembler::Pointer tagAddr = _as->loadTempAddress(Assembler::ScratchRegister, source); + Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, source); tagAddr.offset += 4; _as->load32(tagAddr, Assembler::ScratchRegister); // check if it's an int32: Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, Assembler::TrustedImm32(Value::_Integer_Type)); - Assembler::Pointer addr = _as->loadTempAddress(Assembler::ScratchRegister, source); + Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); _as->storeUInt32(_as->toInt32Register(addr, Assembler::ScratchRegister), target); Assembler::Jump intDone = _as->jump(); // not an int: isNoInt.link(_as); generateFunctionCall(Assembler::ReturnValueRegister, Runtime::toUInt, - _as->loadTempAddress(Assembler::ScratchRegister, source)); + _as->loadAddress(Assembler::ScratchRegister, source)); _as->storeInt32(Assembler::ReturnValueRegister, target); intDone.link(_as); @@ -1268,7 +1204,7 @@ void InstructionSelection::convertTypeToUInt32(IR::Temp *source, IR::Temp *targe } // switch (source->type) } -void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result) +void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(func != 0); prepareCallData(args, 0); @@ -1288,7 +1224,7 @@ void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprL } -void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { prepareCallData(args, base); if (useFastLookups) { @@ -1305,7 +1241,7 @@ void InstructionSelection::constructProperty(IR::Temp *base, const QString &name baseAddressForCallData()); } -void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(value != 0); @@ -1324,16 +1260,17 @@ void InstructionSelection::visitJump(IR::Jump *s) void InstructionSelection::visitCJump(IR::CJump *s) { - if (IR::Temp *t = s->cond->asTemp()) { + IR::Temp *t = s->cond->asTemp(); + if (t || s->cond->asArgLocal()) { Assembler::RegisterID reg; - if (t->kind == IR::Temp::PhysicalRegister) { + if (t && t->kind == IR::Temp::PhysicalRegister) { Q_ASSERT(t->type == IR::BoolType); reg = (Assembler::RegisterID) t->index; - } else if (t->kind == IR::Temp::StackSlot && t->type == IR::BoolType) { + } else if (t && t->kind == IR::Temp::StackSlot && t->type == IR::BoolType) { reg = Assembler::ReturnValueRegister; _as->toInt32Register(t, reg); } else { - Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t); + Address temp = _as->loadAddress(Assembler::ScratchRegister, s->cond); Address tag = temp; tag.offset += qOffsetOf(QV4::Value, tag); Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type)); @@ -1345,7 +1282,7 @@ void InstructionSelection::visitCJump(IR::CJump *s) booleanConversion.link(_as); reg = Assembler::ReturnValueRegister; - generateFunctionCall(reg, Runtime::toBoolean, Assembler::Reference(t)); + generateFunctionCall(reg, Runtime::toBoolean, Assembler::Reference(s->cond)); testBoolean.link(_as); } @@ -1455,7 +1392,7 @@ void InstructionSelection::visitRet(IR::Ret *s) Q_UNREACHABLE(); } } else { - Pointer addr = _as->loadTempAddress(Assembler::ScratchRegister, t); + Pointer addr = _as->loadAddress(Assembler::ScratchRegister, t); _as->load32(addr, lowReg); addr.offset += 4; _as->load32(addr, highReg); @@ -1523,7 +1460,7 @@ void InstructionSelection::visitRet(IR::Ret *s) const int locals = _as->stackLayout().calculateJSStackFrameSize(); _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister); - _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, engine)), Assembler::ScratchRegister); + _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext::Data, engine)), Assembler::ScratchRegister); _as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); _as->leaveStandardStackFrame(); @@ -1692,10 +1629,7 @@ bool InstructionSelection::visitCJumpStrictNullUndefined(IR::Type nullOrUndef, I return true; } - IR::Temp *t = varSrc->asTemp(); - Q_ASSERT(t); - - Assembler::Pointer tagAddr = _as->loadTempAddress(Assembler::ScratchRegister, t); + Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); tagAddr.offset += 4; const Assembler::RegisterID tagReg = Assembler::ScratchRegister; _as->load32(tagAddr, tagReg); @@ -1738,11 +1672,7 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock return true; } - IR::Temp *otherTemp = otherSrc->asTemp(); - Q_ASSERT(otherTemp); // constants cannot have "var" type - Q_ASSERT(otherTemp->kind != IR::Temp::PhysicalRegister); - - Assembler::Pointer otherAddr = _as->loadTempAddress(Assembler::ReturnValueRegister, otherTemp); + Assembler::Pointer otherAddr = _as->loadAddress(Assembler::ReturnValueRegister, otherSrc); otherAddr.offset += 4; // tag address // check if the tag of the var operand is indicates 'boolean' @@ -1790,10 +1720,7 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin return true; } - IR::Temp *t = varSrc->asTemp(); - Q_ASSERT(t); - - Assembler::Pointer tagAddr = _as->loadTempAddress(Assembler::ScratchRegister, t); + Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); tagAddr.offset += 4; const Assembler::RegisterID tagReg = Assembler::ScratchRegister; _as->load32(tagAddr, tagReg); diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index d589223d7e..9c0bc73a65 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -76,54 +76,54 @@ public: protected: virtual QV4::CompiledData::CompilationUnit *backendCompileStep(); - virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Temp *result); - virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Temp *result); - virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); - virtual void callBuiltinTypeofValue(IR::Expr *value, IR::Temp *result); - virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); - virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Expr *index, IR::Temp *result); - virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); - virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result); + virtual void callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result); + virtual void callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Expr *result); + virtual void callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result); + virtual void callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result); + virtual void callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Expr *result); + virtual void callBuiltinDeleteValue(IR::Expr *result); virtual void callBuiltinThrow(IR::Expr *arg); virtual void callBuiltinReThrow(); - virtual void callBuiltinUnwindException(IR::Temp *); + virtual void callBuiltinUnwindException(IR::Expr *); virtual void callBuiltinPushCatchScope(const QString &exceptionName); - virtual void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Temp *result); - virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); - virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result); + virtual void callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result); + virtual void callBuiltinPushWithScope(IR::Expr *arg); virtual void callBuiltinPopScope(); virtual void callBuiltinDeclareVar(bool deletable, const QString &name); - virtual void callBuiltinDefineArray(IR::Temp *result, IR::ExprList *args); - virtual void callBuiltinDefineObjectLiteral(IR::Temp *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray); - virtual void callBuiltinSetupArgumentObject(IR::Temp *result); + virtual void callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args); + virtual void callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray); + virtual void callBuiltinSetupArgumentObject(IR::Expr *result); virtual void callBuiltinConvertThisToObject(); - virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); - virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Temp *result); - virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Temp *result); - virtual void convertType(IR::Temp *source, IR::Temp *target); - virtual void loadThisObject(IR::Temp *temp); - virtual void loadQmlIdArray(IR::Temp *temp); - virtual void loadQmlImportedScripts(IR::Temp *temp); - virtual void loadQmlContextObject(IR::Temp *temp); - virtual void loadQmlScopeObject(IR::Temp *temp); - virtual void loadQmlSingleton(const QString &name, IR::Temp *temp); - virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); - virtual void loadString(const QString &str, IR::Temp *targetTemp); - virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); - virtual void getActivationProperty(const IR::Name *name, IR::Temp *temp); + virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result); + virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result); + virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result); + virtual void convertType(IR::Expr *source, IR::Expr *target); + virtual void loadThisObject(IR::Expr *temp); + virtual void loadQmlIdArray(IR::Expr *target); + virtual void loadQmlImportedScripts(IR::Expr *target); + virtual void loadQmlContextObject(IR::Expr *target); + virtual void loadQmlScopeObject(IR::Expr *target); + virtual void loadQmlSingleton(const QString &name, IR::Expr *target); + virtual void loadConst(IR::Const *sourceConst, IR::Expr *target); + virtual void loadString(const QString &str, IR::Expr *target); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target); + virtual void getActivationProperty(const IR::Name *name, IR::Expr *target); virtual void setActivationProperty(IR::Expr *source, const QString &targetName); - virtual void initClosure(IR::Closure *closure, IR::Temp *target); - virtual void getProperty(IR::Expr *base, const QString &name, IR::Temp *target); + virtual void initClosure(IR::Closure *closure, IR::Expr *target); + virtual void getProperty(IR::Expr *base, const QString &name, IR::Expr *target); + virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, IR::Expr *target); virtual void setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName); virtual void setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex); - virtual void getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, IR::Temp *target); - virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Temp *target); + virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target); virtual void setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex); - virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void swapValues(IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); - virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void copyValue(IR::Expr *source, IR::Expr *target); + virtual void swapValues(IR::Expr *source, IR::Expr *target); + virtual void unop(IR::AluOp oper, IR::Expr *sourceTemp, IR::Expr *target); + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target); typedef Assembler::Address Address; typedef Assembler::Pointer Pointer; @@ -148,9 +148,9 @@ protected: return _as->stackLayout().callDataAddress(); } - virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); - virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); - virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result); + virtual void constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr*result); + virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result); virtual void visitJump(IR::Jump *); virtual void visitCJump(IR::CJump *); @@ -167,44 +167,51 @@ protected: void visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); private: - void convertTypeSlowPath(IR::Temp *source, IR::Temp *target); - void convertTypeToDouble(IR::Temp *source, IR::Temp *target); - void convertTypeToBool(IR::Temp *source, IR::Temp *target); - void convertTypeToSInt32(IR::Temp *source, IR::Temp *target); - void convertTypeToUInt32(IR::Temp *source, IR::Temp *target); + void convertTypeSlowPath(IR::Expr *source, IR::Expr *target); + void convertTypeToDouble(IR::Expr *source, IR::Expr *target); + void convertTypeToBool(IR::Expr *source, IR::Expr *target); + void convertTypeToSInt32(IR::Expr *source, IR::Expr *target); + void convertTypeToUInt32(IR::Expr *source, IR::Expr *target); - void convertIntToDouble(IR::Temp *source, IR::Temp *target) + void convertIntToDouble(IR::Expr *source, IR::Expr *target) { - if (target->kind == IR::Temp::PhysicalRegister) { - _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), - (Assembler::FPRegisterID) target->index); - } else { - _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), - Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, _as->stackSlotPointer(target)); + if (IR::Temp *targetTemp = target->asTemp()) { + if (targetTemp->kind == IR::Temp::PhysicalRegister) { + _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), + (Assembler::FPRegisterID) targetTemp->index); + return; + } } + + _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), + Assembler::FPGpr0); + _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(Assembler::ScratchRegister, target)); } - void convertUIntToDouble(IR::Temp *source, IR::Temp *target) + void convertUIntToDouble(IR::Expr *source, IR::Expr *target) { Assembler::RegisterID tmpReg = Assembler::ScratchRegister; Assembler::RegisterID reg = _as->toInt32Register(source, tmpReg); - if (target->kind == IR::Temp::PhysicalRegister) { - _as->convertUInt32ToDouble(reg, (Assembler::FPRegisterID) target->index, tmpReg); - } else { - _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg), - Assembler::FPGpr0, tmpReg); - _as->storeDouble(Assembler::FPGpr0, _as->stackSlotPointer(target)); + if (IR::Temp *targetTemp = target->asTemp()) { + if (targetTemp->kind == IR::Temp::PhysicalRegister) { + _as->convertUInt32ToDouble(reg, (Assembler::FPRegisterID) targetTemp->index, tmpReg); + return; + } } + + _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg), + Assembler::FPGpr0, tmpReg); + _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(tmpReg, target)); } - void convertIntToBool(IR::Temp *source, IR::Temp *target) + void convertIntToBool(IR::Expr *source, IR::Expr *target) { - Assembler::RegisterID reg = target->kind == IR::Temp::PhysicalRegister - ? (Assembler::RegisterID) target->index - : Assembler::ScratchRegister; + Assembler::RegisterID reg = Assembler::ScratchRegister; + if (IR::Temp *targetTemp = target->asTemp()) + if (targetTemp->kind == IR::Temp::PhysicalRegister) + reg = (Assembler::RegisterID) targetTemp->index; _as->move(_as->toInt32Register(source, reg), reg); _as->compare32(Assembler::NotEqual, reg, Assembler::TrustedImm32(0), reg); _as->storeBool(reg, target); @@ -247,7 +254,7 @@ private: QSet<IR::Jump *> _removableJumps; Assembler* _as; - CompilationUnit *compilationUnit; + QScopedPointer<CompilationUnit> compilationUnit; QQmlEnginePrivate *qmlEngine; }; diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index b5765cd589..52c2936352 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -44,9 +44,9 @@ #include <algorithm> -//#define DEBUG_REGALLOC - namespace { +enum { DebugRegAlloc = 0 }; + struct Use { enum RegisterFlag { MustHaveRegister = 0, CouldHaveRegister = 1 }; unsigned flag : 1; @@ -68,45 +68,135 @@ using namespace QV4::IR; namespace QV4 { namespace JIT { +namespace { +class IRPrinterWithPositions: public IRPrinter +{ + LifeTimeIntervals::Ptr intervals; + const int positionSize; + +public: + IRPrinterWithPositions(QTextStream *out, const LifeTimeIntervals::Ptr &intervals) + : IRPrinter(out) + , intervals(intervals) + , positionSize(QString::number(intervals->lastPosition()).size()) + {} + +protected: + void addStmtNr(Stmt *s) + { + QString posStr; + int pos = intervals->positionForStatement(s); + if (pos != Stmt::InvalidId) + posStr = QString::number(pos); + *out << posStr.rightJustified(positionSize); + if (pos == Stmt::InvalidId) + *out << " "; + else + *out << ": "; + } +}; + +class IRPrinterWithRegisters: public IRPrinterWithPositions +{ + const RegisterInformation &_registerInformation; + QHash<int, const RegisterInfo *> _infoForRegularRegister; + QHash<int, const RegisterInfo *> _infoForFPRegister; + +public: + IRPrinterWithRegisters(QTextStream *out, const LifeTimeIntervals::Ptr &intervals, + const RegisterInformation ®isterInformation) + : IRPrinterWithPositions(out, intervals) + , _registerInformation(registerInformation) + { + for (int i = 0, ei = _registerInformation.size(); i != ei; ++i) + if (_registerInformation.at(i).isRegularRegister()) + _infoForRegularRegister.insert(_registerInformation.at(i).reg<int>(), + &_registerInformation.at(i)); + else + _infoForFPRegister.insert(_registerInformation.at(i).reg<int>(), + &_registerInformation.at(i)); + } + +protected: + void visitTemp(Temp *e) + { + switch (e->kind) { + case Temp::PhysicalRegister: { + const RegisterInfo *ri = e->type == DoubleType ? _infoForFPRegister.value(e->index, 0) + : _infoForRegularRegister.value(e->index, 0); + if (ri) { + *out << dumpStart(e); + *out << ri->prettyName(); + *out << dumpEnd(e); + break; + } + } + default: + IRPrinterWithPositions::visitTemp(e); + } + } +}; +} + class RegAllocInfo: public IRDecoder { struct Def { - unsigned defStmt : 30; + unsigned valid : 1; unsigned canHaveReg : 1; unsigned isPhiTarget : 1; - Def(): defStmt(0), canHaveReg(0), isPhiTarget(0) {} - Def(int defStmt, bool canHaveReg, bool isPhiTarget) - : defStmt(defStmt), canHaveReg(canHaveReg), isPhiTarget(isPhiTarget) + Def(): valid(0), canHaveReg(0), isPhiTarget(0) {} + Def(bool canHaveReg, bool isPhiTarget) + : valid(1), canHaveReg(canHaveReg), isPhiTarget(isPhiTarget) { - Q_ASSERT(defStmt > 0); - Q_ASSERT(defStmt < (1 << 30)); } - bool isValid() const { return defStmt != 0; } // 0 is invalid, as stmt numbers start at 1. + bool isValid() const { return valid != 0; } }; + IR::LifeTimeIntervals::Ptr _lifeTimeIntervals; + BasicBlock *_currentBB; Stmt *_currentStmt; - QHash<Temp, Def> _defs; - QHash<Temp, QList<Use> > _uses; - QList<int> _calls; - QHash<Temp, QList<Temp> > _hints; + std::vector<Def> _defs; + std::vector<std::vector<Use> > _uses; + std::vector<int> _calls; + std::vector<QList<Temp> > _hints; + + int defPosition(Stmt *s) const + { + return usePosition(s) + 1; + } + + int usePosition(Stmt *s) const + { + return _lifeTimeIntervals->positionForStatement(s); + } public: - RegAllocInfo(): _currentStmt(0) {} + RegAllocInfo(): _currentBB(0), _currentStmt(0) {} - void collect(IR::Function *function) + void collect(IR::Function *function, const IR::LifeTimeIntervals::Ptr &lifeTimeIntervals) { + _lifeTimeIntervals = lifeTimeIntervals; + _defs.resize(function->tempCount); + _uses.resize(function->tempCount); + _calls.reserve(function->statementCount() / 3); + _hints.resize(function->tempCount); + foreach (BasicBlock *bb, function->basicBlocks()) { + _currentBB = bb; foreach (Stmt *s, bb->statements()) { - Q_ASSERT(s->id > 0); _currentStmt = s; s->accept(this); } } } - QList<Use> uses(const Temp &t) const { return _uses[t]; } + const std::vector<Use> &uses(const Temp &t) const + { + return _uses[t.index]; + } + bool useMustHaveReg(const Temp &t, int position) { foreach (const Use &use, uses(t)) if (use.pos == position) @@ -121,45 +211,52 @@ public: return false; } - int def(const Temp &t) const { - Q_ASSERT(_defs[t].isValid()); - return _defs[t].defStmt; - } bool canHaveRegister(const Temp &t) const { - Q_ASSERT(_defs[t].isValid()); - return _defs[t].canHaveReg; + Q_ASSERT(_defs[t.index].isValid()); + return _defs[t.index].canHaveReg; } bool isPhiTarget(const Temp &t) const { - Q_ASSERT(_defs[t].isValid()); - return _defs[t].isPhiTarget; + Q_ASSERT(_defs[t.index].isValid()); + return _defs[t.index].isPhiTarget; } - const QList<int> &calls() const { return _calls; } - QList<Temp> hints(const Temp &t) const { return _hints[t]; } + const std::vector<int> &calls() const { return _calls; } + const QList<Temp> &hints(const Temp &t) const { return _hints[t.index]; } void addHint(const Temp &t, int physicalRegister) + { addHint(t, Temp::PhysicalRegister, physicalRegister); } + + void addHint(const Temp &t, Temp::Kind kind, int hintedIndex) { + QList<Temp> &hints = _hints[t.index]; + foreach (const Temp &hint, hints) + if (hint.index == hintedIndex) + return; + Temp hint; - hint.init(Temp::PhysicalRegister, physicalRegister, 0); - _hints[t].append(hint); + hint.init(kind, hintedIndex); + hints.append(hint); } -#ifdef DEBUG_REGALLOC void dump() const { + if (!DebugRegAlloc) + return; + QTextStream qout(stdout, QIODevice::WriteOnly); + IRPrinterWithPositions printer(&qout, _lifeTimeIntervals); qout << "RegAllocInfo:" << endl << "Defs/uses:" << endl; - QList<Temp> temps = _defs.keys(); - std::sort(temps.begin(), temps.end()); - foreach (const Temp &t, temps) { - t.dump(qout); - qout << " def at " << _defs[t].defStmt << " (" + for (unsigned t = 0; t < _defs.size(); ++t) { + const std::vector<Use> &uses = _uses[t]; + if (uses.empty()) + continue; + qout << "%" << t <<": " + << " (" << (_defs[t].canHaveReg ? "can" : "can NOT") << " have a register, and " - << (isPhiTarget(t) ? "is" : "is NOT") + << (_defs[t].isPhiTarget ? "is" : "is NOT") << " defined by a phi node), uses at: "; - const QList<Use> &uses = _uses[t]; - for (int i = 0; i < uses.size(); ++i) { + for (unsigned i = 0; i < uses.size(); ++i) { if (i > 0) qout << ", "; qout << uses[i].pos; if (uses[i].mustHaveRegister()) qout << "(R)"; else qout << "(S)"; @@ -168,66 +265,62 @@ public: } qout << "Calls at: "; - for (int i = 0; i < _calls.size(); ++i) { + for (unsigned i = 0; i < _calls.size(); ++i) { if (i > 0) qout << ", "; qout << _calls[i]; } qout << endl; qout << "Hints:" << endl; - QList<Temp> hinted = _hints.keys(); - if (hinted.isEmpty()) - qout << "\t(none)" << endl; - std::sort(hinted.begin(), hinted.end()); - foreach (const Temp &t, hinted) { - qout << "\t"; - t.dump(qout); - qout << ": "; + for (unsigned t = 0; t < _hints.size(); ++t) { + if (_uses[t].empty()) + continue; + qout << "\t%" << t << ": "; QList<Temp> hints = _hints[t]; for (int i = 0; i < hints.size(); ++i) { if (i > 0) qout << ", "; - hints[i].dump(qout); + printer.print(hints[i]); } qout << endl; } } -#endif // DEBUG_REGALLOC protected: // IRDecoder - virtual void callBuiltinInvalid(IR::Name *, IR::ExprList *, IR::Temp *) {} - virtual void callBuiltinTypeofMember(IR::Expr *, const QString &, IR::Temp *) {} - virtual void callBuiltinTypeofSubscript(IR::Expr *, IR::Expr *, IR::Temp *) {} - virtual void callBuiltinTypeofName(const QString &, IR::Temp *) {} - virtual void callBuiltinTypeofValue(IR::Expr *, IR::Temp *) {} - virtual void callBuiltinDeleteMember(IR::Temp *, const QString &, IR::Temp *) {} - virtual void callBuiltinDeleteSubscript(IR::Temp *, IR::Expr *, IR::Temp *) {} - virtual void callBuiltinDeleteName(const QString &, IR::Temp *) {} - virtual void callBuiltinDeleteValue(IR::Temp *) {} + virtual void callBuiltinInvalid(IR::Name *, IR::ExprList *, IR::Expr *) {} + virtual void callBuiltinTypeofMember(IR::Expr *, const QString &, IR::Expr *) {} + virtual void callBuiltinTypeofSubscript(IR::Expr *, IR::Expr *, IR::Expr *) {} + virtual void callBuiltinTypeofName(const QString &, IR::Expr *) {} + virtual void callBuiltinTypeofValue(IR::Expr *, IR::Expr *) {} + virtual void callBuiltinDeleteMember(IR::Expr *, const QString &, IR::Expr *) {} + virtual void callBuiltinDeleteSubscript(IR::Expr *, IR::Expr *, IR::Expr *) {} + virtual void callBuiltinDeleteName(const QString &, IR::Expr *) {} + virtual void callBuiltinDeleteValue(IR::Expr *) {} virtual void callBuiltinThrow(IR::Expr *) {} virtual void callBuiltinReThrow() {} - virtual void callBuiltinUnwindException(IR::Temp *) {} + virtual void callBuiltinUnwindException(IR::Expr *) {} virtual void callBuiltinPushCatchScope(const QString &) {}; - virtual void callBuiltinForeachIteratorObject(IR::Expr *, IR::Temp *) {} + virtual void callBuiltinForeachIteratorObject(IR::Expr *, IR::Expr *) {} virtual void callBuiltinForeachNextProperty(IR::Temp *, IR::Temp *) {} - virtual void callBuiltinForeachNextPropertyname(IR::Temp *, IR::Temp *) {} - virtual void callBuiltinPushWithScope(IR::Temp *) {} + virtual void callBuiltinForeachNextPropertyname(IR::Expr *, IR::Expr *) {} + virtual void callBuiltinPushWithScope(IR::Expr *) {} virtual void callBuiltinPopScope() {} virtual void callBuiltinDeclareVar(bool , const QString &) {} - virtual void callBuiltinDefineArray(IR::Temp *, IR::ExprList *) {} - virtual void callBuiltinDefineObjectLiteral(IR::Temp *, int, IR::ExprList *, IR::ExprList *, bool) {} - virtual void callBuiltinSetupArgumentObject(IR::Temp *) {} + virtual void callBuiltinDefineArray(IR::Expr *, IR::ExprList *) {} + virtual void callBuiltinDefineObjectLiteral(IR::Expr *, int, IR::ExprList *, IR::ExprList *, bool) {} + virtual void callBuiltinSetupArgumentObject(IR::Expr *) {} virtual void callBuiltinConvertThisToObject() {} - virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) + virtual void callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { addDef(result); - addUses(value, Use::CouldHaveRegister); + if (IR::Temp *tempValue = value->asTemp()) + addUses(tempValue, Use::CouldHaveRegister); addUses(args, Use::CouldHaveRegister); addCall(); } virtual void callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, - IR::Temp *result) + IR::Expr *result) { Q_UNUSED(name) @@ -238,7 +331,7 @@ protected: // IRDecoder } virtual void callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, - IR::Temp *result) + IR::Expr *result) { addDef(result); addUses(base->asTemp(), Use::CouldHaveRegister); @@ -247,7 +340,7 @@ protected: // IRDecoder addCall(); } - virtual void convertType(IR::Temp *source, IR::Temp *target) + virtual void convertType(IR::Expr *source, IR::Expr *target) { addDef(target); @@ -310,22 +403,24 @@ protected: // IRDecoder break; } - addUses(source, sourceReg); + Temp *sourceTemp = source->asTemp(); + if (sourceTemp) + addUses(sourceTemp, sourceReg); if (needsCall) addCall(); - else - addHint(target, source); + else if (target->asTemp()) + addHint(target->asTemp(), sourceTemp); } - virtual void constructActivationProperty(IR::Name *, IR::ExprList *args, IR::Temp *result) + virtual void constructActivationProperty(IR::Name *, IR::ExprList *args, IR::Expr *result) { addDef(result); addUses(args, Use::CouldHaveRegister); addCall(); } - virtual void constructProperty(IR::Temp *base, const QString &, IR::ExprList *args, IR::Temp *result) + virtual void constructProperty(IR::Expr *base, const QString &, IR::ExprList *args, IR::Expr *result) { addDef(result); addUses(base, Use::CouldHaveRegister); @@ -333,7 +428,7 @@ protected: // IRDecoder addCall(); } - virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) + virtual void constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { addDef(result); addUses(value, Use::CouldHaveRegister); @@ -341,30 +436,30 @@ protected: // IRDecoder addCall(); } - virtual void loadThisObject(IR::Temp *temp) + virtual void loadThisObject(IR::Expr *temp) { addDef(temp); } - virtual void loadQmlIdArray(IR::Temp *temp) + virtual void loadQmlIdArray(IR::Expr *temp) { addDef(temp); addCall(); } - virtual void loadQmlImportedScripts(IR::Temp *temp) + virtual void loadQmlImportedScripts(IR::Expr *temp) { addDef(temp); addCall(); } - virtual void loadQmlContextObject(Temp *temp) + virtual void loadQmlContextObject(Expr *temp) { addDef(temp); addCall(); } - virtual void loadQmlScopeObject(Temp *temp) + virtual void loadQmlScopeObject(Expr *temp) { Q_UNUSED(temp); @@ -372,7 +467,7 @@ protected: // IRDecoder addCall(); } - virtual void loadQmlSingleton(const QString &/*name*/, Temp *temp) + virtual void loadQmlSingleton(const QString &/*name*/, Expr *temp) { Q_UNUSED(temp); @@ -380,21 +475,21 @@ protected: // IRDecoder addCall(); } - virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) + virtual void loadConst(IR::Const *sourceConst, Expr *targetTemp) { Q_UNUSED(sourceConst); addDef(targetTemp); } - virtual void loadString(const QString &str, IR::Temp *targetTemp) + virtual void loadString(const QString &str, Expr *targetTemp) { Q_UNUSED(str); addDef(targetTemp); } - virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) + virtual void loadRegexp(IR::RegExp *sourceRegexp, Expr *targetTemp) { Q_UNUSED(sourceRegexp); @@ -402,7 +497,7 @@ protected: // IRDecoder addCall(); } - virtual void getActivationProperty(const IR::Name *, IR::Temp *temp) + virtual void getActivationProperty(const IR::Name *, Expr *temp) { addDef(temp); addCall(); @@ -414,7 +509,7 @@ protected: // IRDecoder addCall(); } - virtual void initClosure(IR::Closure *closure, IR::Temp *target) + virtual void initClosure(IR::Closure *closure, Expr *target) { Q_UNUSED(closure); @@ -422,7 +517,7 @@ protected: // IRDecoder addCall(); } - virtual void getProperty(IR::Expr *base, const QString &, IR::Temp *target) + virtual void getProperty(IR::Expr *base, const QString &, Expr *target) { addDef(target); addUses(base->asTemp(), Use::CouldHaveRegister); @@ -443,14 +538,14 @@ protected: // IRDecoder addCall(); } - virtual void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, int /*attachedPropertiesId*/, IR::Temp *target) + virtual void getQObjectProperty(IR::Expr *base, int /*propertyIndex*/, bool /*captureRequired*/, int /*attachedPropertiesId*/, IR::Expr *target) { addDef(target); addUses(base->asTemp(), Use::CouldHaveRegister); addCall(); } - virtual void getElement(IR::Expr *base, IR::Expr *index, IR::Temp *target) + virtual void getElement(IR::Expr *base, IR::Expr *index, Expr *target) { addDef(target); addUses(base->asTemp(), Use::CouldHaveRegister); @@ -466,25 +561,30 @@ protected: // IRDecoder addCall(); } - virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) + virtual void copyValue(Expr *source, Expr *target) { - addDef(targetTemp); + addDef(target); + Temp *sourceTemp = source->asTemp(); + if (!sourceTemp) + return; addUses(sourceTemp, Use::CouldHaveRegister); - addHint(targetTemp, sourceTemp); + Temp *targetTemp = target->asTemp(); + if (targetTemp) + addHint(targetTemp, sourceTemp); } - virtual void swapValues(IR::Temp *, IR::Temp *) + virtual void swapValues(Expr *, Expr *) { // Inserted by the register allocator, so it cannot occur here. Q_UNREACHABLE(); } - virtual void unop(AluOp oper, Temp *sourceTemp, Temp *targetTemp) + virtual void unop(AluOp oper, Expr *source, Expr *target) { - addDef(targetTemp); + addDef(target); bool needsCall = true; - if (oper == OpNot && sourceTemp->type == IR::BoolType && targetTemp->type == IR::BoolType) + if (oper == OpNot && source->type == IR::BoolType && target->type == IR::BoolType) needsCall = false; #if 0 // TODO: change masm to generate code @@ -504,15 +604,18 @@ protected: // IRDecoder } #endif + IR::Temp *sourceTemp = source->asTemp(); if (needsCall) { - addUses(sourceTemp, Use::CouldHaveRegister); + if (sourceTemp) + addUses(sourceTemp, Use::CouldHaveRegister); addCall(); } else { - addUses(sourceTemp, Use::MustHaveRegister); + if (sourceTemp) + addUses(sourceTemp, Use::MustHaveRegister); } } - virtual void binop(AluOp oper, Expr *leftSource, Expr *rightSource, Temp *target) + virtual void binop(AluOp oper, Expr *leftSource, Expr *rightSource, Expr *target) { bool needsCall = true; @@ -590,25 +693,31 @@ protected: // IRDecoder if (Temp *t = e->asTemp()) { addUses(t, Use::CouldHaveRegister); addHint(s->targetTemp, t); + addHint(t, s->targetTemp); } } } protected: - virtual void callBuiltin(IR::Call *c, IR::Temp *result) + virtual void callBuiltin(IR::Call *c, IR::Expr *result) { addDef(result); - addUses(c->base->asTemp(), Use::CouldHaveRegister); + addUses(c->base, Use::CouldHaveRegister); addUses(c->args, Use::CouldHaveRegister); addCall(); } private: - void addDef(Temp *t, bool isPhiTarget = false) + void addDef(Expr *e, bool isPhiTarget = false) { + if (!e) + return; + Temp *t = e->asTemp(); + if (!t) + return; if (!t || t->kind != Temp::VirtualRegister) return; - Q_ASSERT(!_defs.contains(*t)); + Q_ASSERT(!_defs[t->index].isValid()); bool canHaveReg = true; switch (t->type) { case QObjectType: @@ -622,25 +731,40 @@ private: break; } - _defs[*t] = Def(_currentStmt->id, canHaveReg, isPhiTarget); + _defs[t->index] = Def(canHaveReg, isPhiTarget); } - void addUses(Temp *t, Use::RegisterFlag flag) + void addUses(Expr *e, Use::RegisterFlag flag) { - Q_ASSERT(_currentStmt->id > 0); + int usePos = usePosition(_currentStmt); + if (usePos == Stmt::InvalidId) + usePos = _lifeTimeIntervals->startPosition(_currentBB); + Q_ASSERT(usePos > 0); + if (!e) + return; + Temp *t = e->asTemp(); + if (!t) + return; if (t && t->kind == Temp::VirtualRegister) - _uses[*t].append(Use(_currentStmt->id, flag)); + _uses[t->index].push_back(Use(usePosition(_currentStmt), flag)); } void addUses(ExprList *l, Use::RegisterFlag flag) { for (ExprList *it = l; it; it = it->next) - addUses(it->expr->asTemp(), flag); + addUses(it->expr, flag); } void addCall() { - _calls.append(_currentStmt->id); + _calls.push_back(usePosition(_currentStmt)); + } + + void addHint(Expr *hinted, Temp *hint1, Temp *hint2 = 0) + { + if (hinted) + if (Temp *hintedTemp = hinted->asTemp()) + addHint(hintedTemp, hint1, hint2); } void addHint(Temp *hinted, Temp *hint1, Temp *hint2 = 0) @@ -648,9 +772,9 @@ private: if (!hinted || hinted->kind != Temp::VirtualRegister) return; if (hint1 && hint1->kind == Temp::VirtualRegister && hinted->type == hint1->type) - _hints[*hinted].append(*hint1); + addHint(*hinted, Temp::VirtualRegister, hint1->index); if (hint2 && hint2->kind == Temp::VirtualRegister && hinted->type == hint2->type) - _hints[*hinted].append(*hint2); + addHint(*hinted, Temp::VirtualRegister, hint2->index); } }; @@ -666,16 +790,15 @@ using namespace QT_PREPEND_NAMESPACE(QV4); namespace { class ResolutionPhase: protected StmtVisitor, protected ExprVisitor { - const QVector<LifeTimeInterval> &_intervals; - QVector<const LifeTimeInterval *> _unprocessed; + Q_DISABLE_COPY(ResolutionPhase) + + LifeTimeIntervals::Ptr _intervals; + QVector<LifeTimeInterval *> _unprocessed; IR::Function *_function; -#if !defined(QT_NO_DEBUG) - RegAllocInfo *_info; -#endif - const QHash<IR::Temp, int> &_assignedSpillSlots; + const std::vector<int> &_assignedSpillSlots; QHash<IR::Temp, const LifeTimeInterval *> _intervalForTemp; - const QVector<int> &_intRegs; - const QVector<int> &_fpRegs; + const QVector<const RegisterInfo *> &_intRegs; + const QVector<const RegisterInfo *> &_fpRegs; Stmt *_currentStmt; QVector<Move *> _loads; @@ -685,54 +808,64 @@ class ResolutionPhase: protected StmtVisitor, protected ExprVisitor { QHash<BasicBlock *, QList<const LifeTimeInterval *> > _liveAtEnd; public: - ResolutionPhase(const QVector<LifeTimeInterval> &intervals, IR::Function *function, RegAllocInfo *info, - const QHash<IR::Temp, int> &assignedSpillSlots, - const QVector<int> &intRegs, const QVector<int> &fpRegs) + ResolutionPhase(const QVector<LifeTimeInterval *> &unprocessed, + const LifeTimeIntervals::Ptr &intervals, + IR::Function *function, + const std::vector<int> &assignedSpillSlots, + const QVector<const RegisterInfo *> &intRegs, + const QVector<const RegisterInfo *> &fpRegs) : _intervals(intervals) , _function(function) -#if !defined(QT_NO_DEBUG) - , _info(info) -#endif , _assignedSpillSlots(assignedSpillSlots) , _intRegs(intRegs) , _fpRegs(fpRegs) { -#if defined(QT_NO_DEBUG) - Q_UNUSED(info) -#endif - - _unprocessed.resize(_intervals.size()); - for (int i = 0, ei = _intervals.size(); i != ei; ++i) - _unprocessed[i] = &_intervals[i]; - + _unprocessed = unprocessed; _liveAtStart.reserve(function->basicBlockCount()); _liveAtEnd.reserve(function->basicBlockCount()); } void run() { renumber(); - Optimizer::showMeTheCode(_function); + if (DebugRegAlloc) { + QTextStream qout(stdout, QIODevice::WriteOnly); + IRPrinterWithPositions(&qout, _intervals).print(_function); + } resolve(); } private: + int defPosition(Stmt *s) const + { + return usePosition(s) + 1; + } + + int usePosition(Stmt *s) const + { + return _intervals->positionForStatement(s); + } + void renumber() { foreach (BasicBlock *bb, _function->basicBlocks()) { + _currentStmt = 0; + QVector<Stmt *> statements = bb->statements(); QVector<Stmt *> newStatements; newStatements.reserve(bb->statements().size() + 7); - bool seenFirstNonPhiStmt = false; + cleanOldIntervals(_intervals->startPosition(bb)); + addNewIntervals(_intervals->startPosition(bb)); + _liveAtStart[bb] = _intervalForTemp.values(); + for (int i = 0, ei = statements.size(); i != ei; ++i) { - _currentStmt = statements[i]; + _currentStmt = statements.at(i); _loads.clear(); _stores.clear(); - addNewIntervals(); - if (!seenFirstNonPhiStmt && !_currentStmt->asPhi()) { - seenFirstNonPhiStmt = true; - _liveAtStart[bb] = _intervalForTemp.values(); - } + if (_currentStmt->asTerminator()) + addNewIntervals(usePosition(_currentStmt)); + else + addNewIntervals(defPosition(_currentStmt)); _currentStmt->accept(this); foreach (Move *load, _loads) newStatements.append(load); @@ -744,90 +877,71 @@ private: newStatements.append(store); } - cleanOldIntervals(); + cleanOldIntervals(_intervals->endPosition(bb)); _liveAtEnd[bb] = _intervalForTemp.values(); -#ifdef DEBUG_REGALLOC - QTextStream os(stdout, QIODevice::WriteOnly); - os << "Intervals live at the start of L" << bb->index << ":" << endl; - if (_liveAtStart[bb].isEmpty()) - os << "\t(none)" << endl; - foreach (const LifeTimeInterval *i, _liveAtStart[bb]) { - os << "\t"; - i->dump(os); - os << endl; - } - os << "Intervals live at the end of L" << bb->index << ":" << endl; - if (_liveAtEnd[bb].isEmpty()) - os << "\t(none)" << endl; - foreach (const LifeTimeInterval *i, _liveAtEnd[bb]) { - os << "\t"; - i->dump(os); - os << endl; + if (DebugRegAlloc) { + QTextStream os(stdout, QIODevice::WriteOnly); + os << "Intervals live at the start of L" << bb->index() << ":" << endl; + if (_liveAtStart[bb].isEmpty()) + os << "\t(none)" << endl; + foreach (const LifeTimeInterval *i, _liveAtStart[bb]) { + os << "\t"; + i->dump(os); + os << endl; + } + os << "Intervals live at the end of L" << bb->index() << ":" << endl; + if (_liveAtEnd[bb].isEmpty()) + os << "\t(none)" << endl; + foreach (const LifeTimeInterval *i, _liveAtEnd[bb]) { + os << "\t"; + i->dump(os); + os << endl; + } } -#endif bb->setStatements(newStatements); } } - void activate(const LifeTimeInterval *i) + void maybeGenerateSpill(Temp *t) { - Q_ASSERT(!i->isFixedInterval()); - _intervalForTemp[i->temp()] = i; + const LifeTimeInterval *i = _intervalForTemp[*t]; + if (i->reg() == LifeTimeInterval::InvalidRegister) + return; - if (i->reg() != LifeTimeInterval::Invalid) { - // check if we need to generate spill/unspill instructions - if (i->start() == _currentStmt->id) { - if (i->isSplitFromInterval()) { - int pReg = platformRegister(*i); - _loads.append(generateUnspill(i->temp(), pReg)); - } else { - int pReg = platformRegister(*i); - int spillSlot = _assignedSpillSlots.value(i->temp(), -1); - if (spillSlot != -1) - _stores.append(generateSpill(spillSlot, i->temp().type, pReg)); - } - } - } + const RegisterInfo *pReg = platformRegister(*i); + Q_ASSERT(pReg); + int spillSlot = _assignedSpillSlots[i->temp().index]; + if (spillSlot != RegisterAllocator::InvalidSpillSlot) + _stores.append(generateSpill(spillSlot, i->temp().type, pReg->reg<int>())); } - void addNewIntervals() + void addNewIntervals(int position) { - if (Phi *phi = _currentStmt->asPhi()) { - // for phi nodes, only activate the range belonging to that node - for (int it = 0, eit = _unprocessed.size(); it != eit; ++it) { - const LifeTimeInterval *i = _unprocessed.at(it); - if (i->start() > _currentStmt->id) - break; - if (i->temp() == *phi->targetTemp) { - activate(i); - _unprocessed.remove(it); - break; - } - } + if (position == Stmt::InvalidId) return; - } while (!_unprocessed.isEmpty()) { const LifeTimeInterval *i = _unprocessed.first(); - if (i->start() > _currentStmt->id) + if (i->start() > position) break; - activate(i); + Q_ASSERT(!i->isFixedInterval()); + _intervalForTemp[i->temp()] = i; +// qDebug() << "-- Activating interval for temp" << i->temp().index; _unprocessed.removeFirst(); } } - void cleanOldIntervals() + void cleanOldIntervals(int position) { - const int id = _currentStmt->id; QMutableHashIterator<Temp, const LifeTimeInterval *> it(_intervalForTemp); while (it.hasNext()) { const LifeTimeInterval *i = it.next().value(); - if (i->end() < id || i->isFixedInterval()) + if (i->end() < position || i->isFixedInterval()) it.remove(); } } @@ -840,77 +954,75 @@ private: } } - void resolveEdge(BasicBlock *predecessor, BasicBlock *successor) + Phi *findDefPhi(const Temp &t, BasicBlock *bb) const { -#ifdef DEBUG_REGALLOC - Optimizer::showMeTheCode(_function); - qDebug() << "Resolving edge" << predecessor->index << "->" << successor->index; -#endif // DEBUG_REGALLOC + foreach (Stmt *s, bb->statements()) { + Phi *phi = s->asPhi(); + if (!phi) + return 0; - MoveMapping mapping; + if (*phi->targetTemp == t) + return phi; + } - const int predecessorEnd = predecessor->terminator()->id; // the terminator is always last and always has an id set... - Q_ASSERT(predecessorEnd > 0); // ... but we verify it anyway for good measure. + Q_UNREACHABLE(); + } - int successorStart = -1; - foreach (Stmt *s, successor->statements()) { - if (s && s->id > 0) { - successorStart = s->id; - break; - } + void resolveEdge(BasicBlock *predecessor, BasicBlock *successor) + { + if (DebugRegAlloc) { + qDebug() << "Resolving edge" << predecessor->index() << "->" << successor->index(); + QTextStream qout(stdout, QIODevice::WriteOnly); + IRPrinterWithPositions printer(&qout, _intervals); + printer.print(predecessor); + printer.print(successor); + qout.flush(); } + MoveMapping mapping; + + const int predecessorEnd = _intervals->endPosition(predecessor); + Q_ASSERT(predecessorEnd > 0); + + int successorStart = _intervals->startPosition(successor); Q_ASSERT(successorStart > 0); foreach (const LifeTimeInterval *it, _liveAtStart[successor]) { - if (it->end() < successorStart) - continue; - bool isPhiTarget = false; Expr *moveFrom = 0; if (it->start() == successorStart) { - foreach (Stmt *s, successor->statements()) { - if (!s || s->id < 1) - continue; - if (Phi *phi = s->asPhi()) { - if (*phi->targetTemp == it->temp()) { - isPhiTarget = true; - Expr *opd = phi->d->incoming[successor->in.indexOf(predecessor)]; - if (opd->asConst()) { - moveFrom = opd; - } else { - Temp *t = opd->asTemp(); - Q_ASSERT(t); - - foreach (const LifeTimeInterval *it2, _liveAtEnd[predecessor]) { - if (it2->temp() == *t - && it2->reg() != LifeTimeInterval::Invalid - && it2->covers(predecessorEnd)) { - moveFrom = createTemp(Temp::PhysicalRegister, - platformRegister(*it2), t->type); - break; - } - } - if (!moveFrom) - moveFrom = createTemp(Temp::StackSlot, - _assignedSpillSlots.value(*t, -1), - t->type); + if (Phi *phi = findDefPhi(it->temp(), successor)) { + isPhiTarget = true; + Expr *opd = phi->d->incoming[successor->in.indexOf(predecessor)]; + if (opd->asConst()) { + moveFrom = opd; + } else { + Temp *t = opd->asTemp(); + Q_ASSERT(t); + + foreach (const LifeTimeInterval *it2, _liveAtEnd[predecessor]) { + if (it2->temp() == *t + && it2->reg() != LifeTimeInterval::InvalidRegister + && it2->covers(predecessorEnd)) { + moveFrom = createPhysicalRegister(it2, t->type); + break; } } - } else { - break; + if (!moveFrom) + moveFrom = createTemp(Temp::StackSlot, + _assignedSpillSlots[t->index], + t->type); } } } else { foreach (const LifeTimeInterval *predIt, _liveAtEnd[predecessor]) { if (predIt->temp() == it->temp()) { - if (predIt->reg() != LifeTimeInterval::Invalid + if (predIt->reg() != LifeTimeInterval::InvalidRegister && predIt->covers(predecessorEnd)) { - moveFrom = createTemp(Temp::PhysicalRegister, platformRegister(*predIt), - predIt->temp().type); + moveFrom = createPhysicalRegister(predIt, predIt->temp().type); } else { - int spillSlot = _assignedSpillSlots.value(predIt->temp(), -1); + int spillSlot = _assignedSpillSlots[predIt->temp().index]; if (spillSlot != -1) moveFrom = createTemp(Temp::StackSlot, spillSlot, predIt->temp().type); } @@ -919,14 +1031,14 @@ private: } } if (!moveFrom) { -#if !defined(QT_NO_DEBUG) +#if !defined(QT_NO_DEBUG) && 0 bool lifeTimeHole = false; if (it->ranges().first().start <= successorStart && it->ranges().last().end >= successorStart) lifeTimeHole = !it->covers(successorStart); Q_ASSERT(!_info->isPhiTarget(it->temp()) || it->isSplitFromInterval() || lifeTimeHole); if (_info->def(it->temp()) != successorStart && !it->isSplitFromInterval()) { - const int successorEnd = successor->terminator()->id; + const int successorEnd = successor->terminator()->id(); const int idx = successor->in.indexOf(predecessor); foreach (const Use &use, _info->uses(it->temp())) { if (use.pos == static_cast<unsigned>(successorStart)) { @@ -956,15 +1068,15 @@ private: } Temp *moveTo; - if (it->reg() == LifeTimeInterval::Invalid || !it->covers(successorStart)) { + if (it->reg() == LifeTimeInterval::InvalidRegister || !it->covers(successorStart)) { if (!isPhiTarget) // if it->temp() is a phi target, skip it. continue; - const int spillSlot = _assignedSpillSlots.value(it->temp(), -1); - if (spillSlot == -1) + const int spillSlot = _assignedSpillSlots[it->temp().index]; + if (spillSlot == RegisterAllocator::InvalidSpillSlot) continue; // it has a life-time hole here. moveTo = createTemp(Temp::StackSlot, spillSlot, it->temp().type); } else { - moveTo = createTemp(Temp::PhysicalRegister, platformRegister(*it), it->temp().type); + moveTo = createPhysicalRegister(it, it->temp().type); } // add move to mapping @@ -972,37 +1084,52 @@ private: } mapping.order(); -#ifdef DEBUG_REGALLOC - mapping.dump(); -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + mapping.dump(); bool insertIntoPredecessor = successor->in.size() > 1; mapping.insertMoves(insertIntoPredecessor ? predecessor : successor, _function, insertIntoPredecessor); + + if (DebugRegAlloc) { + qDebug() << ".. done, result:"; + QTextStream qout(stdout, QIODevice::WriteOnly); + IRPrinterWithPositions printer(&qout, _intervals); + printer.print(predecessor); + printer.print(successor); + qout.flush(); + } } Temp *createTemp(Temp::Kind kind, int index, Type type) const { Q_ASSERT(index >= 0); Temp *t = _function->New<Temp>(); - t->init(kind, index, 0); + t->init(kind, index); t->type = type; return t; } - int platformRegister(const LifeTimeInterval &i) const + Temp *createPhysicalRegister(const LifeTimeInterval *i, Type type) const + { + const RegisterInfo *ri = platformRegister(*i); + Q_ASSERT(ri); + return createTemp(Temp::PhysicalRegister, ri->reg<int>(), type); + } + + const RegisterInfo *platformRegister(const LifeTimeInterval &i) const { if (i.isFP()) - return _fpRegs.value(i.reg(), -1); + return _fpRegs.value(i.reg(), 0); else - return _intRegs.value(i.reg(), -1); + return _intRegs.value(i.reg(), 0); } Move *generateSpill(int spillSlot, Type type, int pReg) const { Q_ASSERT(spillSlot >= 0); - Move *store = _function->New<Move>(); + Move *store = _function->NewStmt<Move>(); store->init(createTemp(Temp::StackSlot, spillSlot, type), createTemp(Temp::PhysicalRegister, pReg, type)); return store; @@ -1011,9 +1138,9 @@ private: Move *generateUnspill(const Temp &t, int pReg) const { Q_ASSERT(pReg >= 0); - int spillSlot = _assignedSpillSlots.value(t, -1); + int spillSlot = _assignedSpillSlots[t.index]; Q_ASSERT(spillSlot != -1); - Move *load = _function->New<Move>(); + Move *load = _function->NewStmt<Move>(); load->init(createTemp(Temp::PhysicalRegister, pReg, t.type), createTemp(Temp::StackSlot, spillSlot, t.type)); return load; @@ -1027,18 +1154,30 @@ protected: const LifeTimeInterval *i = _intervalForTemp[*t]; Q_ASSERT(i->isValid()); - if (i->reg() != LifeTimeInterval::Invalid && i->covers(_currentStmt->id)) { - int pReg = platformRegister(*i); + + if (_currentStmt != 0 && i->start() == usePosition(_currentStmt)) { + Q_ASSERT(i->isSplitFromInterval()); + const RegisterInfo *pReg = platformRegister(*i); + Q_ASSERT(pReg); + _loads.append(generateUnspill(i->temp(), pReg->reg<int>())); + } + + if (i->reg() != LifeTimeInterval::InvalidRegister && + (i->covers(defPosition(_currentStmt)) || + i->covers(usePosition(_currentStmt)))) { + const RegisterInfo *pReg = platformRegister(*i); + Q_ASSERT(pReg); t->kind = Temp::PhysicalRegister; - t->index = pReg; + t->index = pReg->reg<unsigned>(); } else { - int stackSlot = _assignedSpillSlots.value(*t, -1); + int stackSlot = _assignedSpillSlots[t->index]; Q_ASSERT(stackSlot >= 0); t->kind = Temp::StackSlot; t->index = stackSlot; } } + virtual void visitArgLocal(ArgLocal *) {} virtual void visitConst(Const *) {} virtual void visitString(IR::String *) {} virtual void visitRegExp(IR::RegExp *) {} @@ -1063,21 +1202,41 @@ protected: } virtual void visitExp(Exp *s) { s->expr->accept(this); } - virtual void visitMove(Move *s) { s->source->accept(this); s->target->accept(this); } + + virtual void visitMove(Move *s) + { + if (Temp *t = s->target->asTemp()) + maybeGenerateSpill(t); + + s->source->accept(this); + s->target->accept(this); + } + virtual void visitJump(Jump *) {} virtual void visitCJump(CJump *s) { s->cond->accept(this); } virtual void visitRet(Ret *s) { s->expr->accept(this); } - virtual void visitPhi(Phi *) {} + virtual void visitPhi(Phi *s) + { + maybeGenerateSpill(s->targetTemp); + } }; } // anonymous namespace -RegisterAllocator::RegisterAllocator(const QVector<int> &normalRegisters, const QVector<int> &fpRegisters) - : _normalRegisters(normalRegisters) - , _fpRegisters(fpRegisters) +RegisterAllocator::RegisterAllocator(const QV4::JIT::RegisterInformation ®isterInformation) + : _registerInformation(registerInformation) { - Q_ASSERT(normalRegisters.size() >= 2); - Q_ASSERT(fpRegisters.size() >= 2); - _active.reserve((normalRegisters.size() + fpRegisters.size()) * 2); + for (int i = 0, ei = registerInformation.size(); i != ei; ++i) { + const RegisterInfo ®Info = registerInformation.at(i); + if (regInfo.useForRegAlloc()) { + if (regInfo.isRegularRegister()) + _normalRegisters.append(®Info); + else + _fpRegisters.append(®Info); + } + } + Q_ASSERT(_normalRegisters.size() >= 2); + Q_ASSERT(_fpRegisters.size() >= 2); + _active.reserve((_normalRegisters.size() + _fpRegisters.size()) * 2); _inactive.reserve(_active.size()); } @@ -1087,54 +1246,58 @@ RegisterAllocator::~RegisterAllocator() void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) { - _lastAssignedRegister.reserve(function->tempCount); - _assignedSpillSlots.reserve(function->tempCount); + _lastAssignedRegister.assign(function->tempCount, LifeTimeInterval::InvalidRegister); + _assignedSpillSlots.assign(function->tempCount, InvalidSpillSlot); _activeSpillSlots.resize(function->tempCount); -#ifdef DEBUG_REGALLOC - qDebug() << "*** Running regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***"; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** Running regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***"; + + _lifeTimeIntervals = opt.lifeTimeIntervals(); - _unhandled = opt.lifeTimeIntervals(); + _unhandled = _lifeTimeIntervals->intervals(); _handled.reserve(_unhandled.size()); _info.reset(new RegAllocInfo); - _info->collect(function); + _info->collect(function, _lifeTimeIntervals); -#ifdef DEBUG_REGALLOC - { + if (DebugRegAlloc) { QTextStream qout(stdout, QIODevice::WriteOnly); qout << "Ranges:" << endl; - QVector<LifeTimeInterval> intervals = _unhandled; - std::sort(intervals.begin(), intervals.end(), LifeTimeInterval::lessThanForTemp); - foreach (const LifeTimeInterval &r, intervals) { - r.dump(qout); + QVector<LifeTimeInterval *> intervals = _unhandled; + std::reverse(intervals.begin(), intervals.end()); + foreach (const LifeTimeInterval *r, intervals) { + r->dump(qout); qout << endl; } + _info->dump(); } - _info->dump(); -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + qDebug() << "*** Before register allocation:"; + QTextStream qout(stdout, QIODevice::WriteOnly); + IRPrinterWithPositions(&qout, _lifeTimeIntervals).print(function); + } prepareRanges(); - Optimizer::showMeTheCode(function); - linearScan(); -#ifdef DEBUG_REGALLOC - dump(); -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + dump(function); std::sort(_handled.begin(), _handled.end(), LifeTimeInterval::lessThan); - ResolutionPhase(_handled, function, _info.data(), _assignedSpillSlots, _normalRegisters, _fpRegisters).run(); + ResolutionPhase(_handled, _lifeTimeIntervals, function, _assignedSpillSlots, _normalRegisters, _fpRegisters).run(); - function->tempCount = QSet<int>::fromList(_assignedSpillSlots.values()).size(); + function->tempCount = *std::max_element(_assignedSpillSlots.begin(), _assignedSpillSlots.end()) + 1; - Optimizer::showMeTheCode(function); + if (DebugRegAlloc) + qDebug() << "*** Finished regalloc , result:"; -#ifdef DEBUG_REGALLOC - qDebug() << "*** Finished regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***"; -#endif // DEBUG_REGALLOC + static bool showCode = !qgetenv("QV4_SHOW_IR").isNull(); + if (showCode) { + QTextStream qout(stdout, QIODevice::WriteOnly); + IRPrinterWithRegisters(&qout, _lifeTimeIntervals, _registerInformation).print(function); + } } static inline LifeTimeInterval createFixedInterval(int rangeCount) @@ -1143,26 +1306,30 @@ static inline LifeTimeInterval createFixedInterval(int rangeCount) i.setReg(0); Temp t; - t.init(Temp::PhysicalRegister, 0, 0); + t.init(Temp::PhysicalRegister, 0); t.type = IR::SInt32Type; i.setTemp(t); return i; } -static inline LifeTimeInterval cloneFixedInterval(int reg, bool isFP, LifeTimeInterval lti) +LifeTimeInterval *RegisterAllocator::cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original) { - lti.setReg(reg); - lti.setFixedInterval(true); + LifeTimeInterval *lti = new LifeTimeInterval(original); + _lifeTimeIntervals->add(lti); + lti->setReg(reg); + lti->setFixedInterval(true); Temp t; - t.init(Temp::PhysicalRegister, reg, 0); + t.init(Temp::PhysicalRegister, reg); t.type = isFP ? IR::DoubleType : IR::SInt32Type; - lti.setTemp(t); + lti->setTemp(t); return lti; } +// Creates the intervals with fixed ranges. See [Wimmer2]. Note that this only applies to callee- +// saved registers. void RegisterAllocator::prepareRanges() { LifeTimeInterval ltiWithCalls = createFixedInterval(_info->calls().size()); @@ -1172,37 +1339,41 @@ void RegisterAllocator::prepareRanges() const int regCount = _normalRegisters.size(); _fixedRegisterRanges.reserve(regCount); for (int reg = 0; reg < regCount; ++reg) { - LifeTimeInterval lti = cloneFixedInterval(reg, false, ltiWithCalls); - _fixedRegisterRanges.append(lti); - if (lti.isValid()) - _active.append(lti); + if (_normalRegisters.at(reg)->isCallerSaved()) { + LifeTimeInterval *lti = cloneFixedInterval(reg, false, ltiWithCalls); + _fixedRegisterRanges.append(lti); + if (lti->isValid()) + _active.append(lti); + } } const int fpRegCount = _fpRegisters.size(); _fixedFPRegisterRanges.reserve(fpRegCount); for (int fpReg = 0; fpReg < fpRegCount; ++fpReg) { - LifeTimeInterval lti = cloneFixedInterval(fpReg, true, ltiWithCalls); - _fixedFPRegisterRanges.append(lti); - if (lti.isValid()) - _active.append(lti); + if (_fpRegisters.at(fpReg)->isCallerSaved()) { + LifeTimeInterval *lti = cloneFixedInterval(fpReg, true, ltiWithCalls); + _fixedFPRegisterRanges.append(lti); + if (lti->isValid()) + _active.append(lti); + } } } void RegisterAllocator::linearScan() { while (!_unhandled.isEmpty()) { - LifeTimeInterval current = _unhandled.first(); - _unhandled.removeFirst(); - int position = current.start(); + LifeTimeInterval *current = _unhandled.back(); + _unhandled.pop_back(); + const int position = current->start(); // check for intervals in active that are handled or inactive for (int i = 0; i < _active.size(); ) { - const LifeTimeInterval &it = _active.at(i); - if (it.end() < position) { - if (!it.isFixedInterval()) + LifeTimeInterval *it = _active.at(i); + if (it->end() < position) { + if (!it->isFixedInterval()) _handled += it; _active.remove(i); - } else if (!it.covers(position)) { + } else if (!it->covers(position)) { _inactive += it; _active.remove(i); } else { @@ -1212,13 +1383,13 @@ void RegisterAllocator::linearScan() // check for intervals in inactive that are handled or active for (int i = 0; i < _inactive.size(); ) { - const LifeTimeInterval &it = _inactive.at(i); - if (it.end() < position) { - if (!it.isFixedInterval()) + LifeTimeInterval *it = _inactive.at(i); + if (it->end() < position) { + if (!it->isFixedInterval()) _handled += it; _inactive.remove(i); - } else if (it.covers(position)) { - if (it.reg() != LifeTimeInterval::Invalid) { + } else if (it->covers(position)) { + if (it->reg() != LifeTimeInterval::InvalidRegister) { _active += it; _inactive.remove(i); } else { @@ -1231,34 +1402,33 @@ void RegisterAllocator::linearScan() } } - Q_ASSERT(!current.isFixedInterval()); + Q_ASSERT(!current->isFixedInterval()); #ifdef DEBUG_REGALLOC qDebug() << "** Position" << position; #endif // DEBUG_REGALLOC - if (_info->canHaveRegister(current.temp())) { - tryAllocateFreeReg(current, position); - if (current.reg() == LifeTimeInterval::Invalid) - allocateBlockedReg(current, position); - if (current.reg() != LifeTimeInterval::Invalid) + if (_info->canHaveRegister(current->temp())) { + tryAllocateFreeReg(*current); + if (current->reg() == LifeTimeInterval::InvalidRegister) + allocateBlockedReg(*current); + if (current->reg() != LifeTimeInterval::InvalidRegister) _active += current; } else { - assignSpillSlot(current.temp(), current.start(), current.end()); + assignSpillSlot(current->temp(), current->start(), current->end()); _inactive += current; -#ifdef DEBUG_REGALLOC - qDebug() << "*** allocating stack slot" << _assignedSpillSlots[current.temp()] - << "for %" << current.temp().index << "as it cannot be loaded in a register"; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** allocating stack slot" << _assignedSpillSlots[current->temp().index] + << "for %" << current->temp().index << "as it cannot be loaded in a register"; } } - foreach (const LifeTimeInterval &r, _active) - if (!r.isFixedInterval()) + foreach (LifeTimeInterval *r, _active) + if (!r->isFixedInterval()) _handled.append(r); _active.clear(); - foreach (const LifeTimeInterval &r, _inactive) - if (!r.isFixedInterval()) + foreach (LifeTimeInterval *r, _inactive) + if (!r->isFixedInterval()) _handled.append(r); _inactive.clear(); } @@ -1284,45 +1454,75 @@ static inline int intersectionPosition(const LifeTimeInterval::Range &one, const static inline bool isFP(const Temp &t) { return t.type == DoubleType; } -void RegisterAllocator::tryAllocateFreeReg(LifeTimeInterval ¤t, const int position) +static inline bool candidateIsBetterFit(int bestSizeSoFar, int idealSize, int candidateSize) { - Q_ASSERT(!current.isFixedInterval()); - Q_ASSERT(current.reg() == LifeTimeInterval::Invalid); + // If the candidateSize is larger than the current we take it only if the current size does not + // yet fit for the whole interval. + if (bestSizeSoFar < candidateSize && bestSizeSoFar < idealSize) + return true; - const bool needsFPReg = isFP(current.temp()); - QVector<int> freeUntilPos(needsFPReg ? _fpRegisters.size() : _normalRegisters.size(), INT_MAX); - Q_ASSERT(freeUntilPos.size() > 0); + // If the candidateSize is smaller we only take it if it still fits the whole interval. + if (bestSizeSoFar > candidateSize && candidateSize >= idealSize) + return true; - const bool isPhiTarget = _info->isPhiTarget(current.temp()); - foreach (const LifeTimeInterval &it, _active) { - if (it.isFP() == needsFPReg) { - if (!isPhiTarget && it.isFixedInterval() && !current.isSplitFromInterval()) { - const int idx = indexOfRangeCoveringPosition(it.ranges(), position); - if (it.ranges().at(idx).end == current.start()) { - if (it.ranges().size() > idx + 1) - freeUntilPos[it.reg()] = it.ranges().at(idx + 1).start; - continue; - } - } + // Other wise: no luck. + return false; +} - if (isPhiTarget || it.end() >= current.firstPossibleUsePosition(isPhiTarget)) - freeUntilPos[it.reg()] = 0; // mark register as unavailable +// Out of all available registers (with their next-uses), choose the one that fits the requested +// duration best. This can return a register that is not free for the whole interval, but that's +// fine: we just have to split the current interval. +static void longestAvailableReg(int *nextUses, int nextUseCount, int ®, int &freeUntilPos_reg, int lastUse) +{ + reg = LifeTimeInterval::InvalidRegister; + freeUntilPos_reg = 0; + + for (int candidate = 0, candidateEnd = nextUseCount; candidate != candidateEnd; ++candidate) { + int fp = nextUses[candidate]; + if (candidateIsBetterFit(freeUntilPos_reg, lastUse, fp)) { + reg = candidate; + freeUntilPos_reg = fp; } } +} - foreach (const LifeTimeInterval &it, _inactive) { - if (current.isSplitFromInterval() || it.isFixedInterval()) { - if (it.isFP() == needsFPReg && it.reg() != LifeTimeInterval::Invalid) { - const int intersectionPos = nextIntersection(current, it, position); - if (!isPhiTarget && it.isFixedInterval() && current.end() == intersectionPos) - freeUntilPos[it.reg()] = qMin(freeUntilPos[it.reg()], intersectionPos + 1); - else if (intersectionPos != -1) - freeUntilPos[it.reg()] = qMin(freeUntilPos[it.reg()], intersectionPos); - } +#define CALLOC_ON_STACK(ty, ptr, sz, val) \ + Q_ASSERT(sz > 0); \ + ty *ptr = reinterpret_cast<ty *>(alloca(sizeof(ty) * (sz))); \ + for (ty *it = ptr, *eit = ptr + (sz); it != eit; ++it) \ + *it = val; + +// Try to allocate a register that's currently free. +void RegisterAllocator::tryAllocateFreeReg(LifeTimeInterval ¤t) +{ + Q_ASSERT(!current.isFixedInterval()); + Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister); + + const bool needsFPReg = isFP(current.temp()); + const int freeUntilPosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size(); + CALLOC_ON_STACK(int, freeUntilPos, freeUntilPosCount, INT_MAX); + + for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) { + const LifeTimeInterval *it = *i; + if (it->isFP() == needsFPReg) + freeUntilPos[it->reg()] = 0; // mark register as unavailable + } + + for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) { + const LifeTimeInterval *it = *i; + if (it->isFP() != needsFPReg) + continue; // different register type, so not applicable. + if (it->reg() == LifeTimeInterval::InvalidRegister) + continue; // this range does not block a register from being used, as it has no register assigned + + if (current.isSplitFromInterval() || it->isFixedInterval()) { + const int intersectionPos = nextIntersection(current, *it); + if (intersectionPos != -1) + freeUntilPos[it->reg()] = qMin(freeUntilPos[it->reg()], intersectionPos); } } - int reg = LifeTimeInterval::Invalid; + int reg = LifeTimeInterval::InvalidRegister; int freeUntilPos_reg = 0; foreach (const Temp &hint, _info->hints(current.temp())) { @@ -1330,173 +1530,165 @@ void RegisterAllocator::tryAllocateFreeReg(LifeTimeInterval ¤t, const int if (hint.kind == Temp::PhysicalRegister) candidate = hint.index; else - candidate = _lastAssignedRegister.value(hint, LifeTimeInterval::Invalid); + candidate = _lastAssignedRegister[hint.index]; const int end = current.end(); - if (candidate != LifeTimeInterval::Invalid) { - if (current.isFP() == (hint.type == DoubleType)) { - int fp = freeUntilPos[candidate]; - if ((freeUntilPos_reg < end && fp > freeUntilPos_reg) - || (freeUntilPos_reg >= end && fp >= end && freeUntilPos_reg > fp)) { - reg = candidate; - freeUntilPos_reg = fp; - } - } + if (candidate == LifeTimeInterval::InvalidRegister) + continue; // the candidate has no register assigned, so it cannot be (re-)used + if (current.isFP() != isFP(hint)) + continue; // different register type, so not applicable. + + const int fp = freeUntilPos[candidate]; + if (candidateIsBetterFit(freeUntilPos_reg, end, fp)) { + reg = candidate; + freeUntilPos_reg = fp; } } - if (reg == LifeTimeInterval::Invalid) - longestAvailableReg(freeUntilPos, reg, freeUntilPos_reg, current.end()); + // None of the hinted registers could fit the interval, so try all registers next. + if (reg == LifeTimeInterval::InvalidRegister) + longestAvailableReg(freeUntilPos, freeUntilPosCount, reg, freeUntilPos_reg, current.end()); if (freeUntilPos_reg == 0) { // no register available without spilling -#ifdef DEBUG_REGALLOC - qDebug() << "*** no register available for %" << current.temp().index; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** no register available for %" << current.temp().index; return; } else if (current.end() < freeUntilPos_reg) { // register available for the whole interval -#ifdef DEBUG_REGALLOC - qDebug() << "*** allocating register" << reg << "for the whole interval of %" << current.temp().index; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** allocating register" << reg << "for the whole interval of %" << current.temp().index; current.setReg(reg); - _lastAssignedRegister.insert(current.temp(), reg); + _lastAssignedRegister[current.temp().index] = reg; } else { // register available for the first part of the interval + + // TODO: this is slightly inefficient in the following case: + // %1 = something + // some_call(%1) + // %2 = %1 + 1 + // Now %1 will get a register assigned, and will be spilled to the stack immediately. It + // would be better to check if there are actually uses in the range before the split. + current.setReg(reg); - _lastAssignedRegister.insert(current.temp(), reg); -#ifdef DEBUG_REGALLOC - qDebug() << "*** allocating register" << reg << "for the first part of interval of %" << current.temp().index; -#endif // DEBUG_REGALLOC + _lastAssignedRegister[current.temp().index] = reg; + if (DebugRegAlloc) + qDebug() << "*** allocating register" << reg << "for the first part of interval of %" << current.temp().index; split(current, freeUntilPos_reg, true); } } -void RegisterAllocator::allocateBlockedReg(LifeTimeInterval ¤t, const int position) +// This gets called when all registers are currently in use. +void RegisterAllocator::allocateBlockedReg(LifeTimeInterval ¤t) { Q_ASSERT(!current.isFixedInterval()); - Q_ASSERT(current.reg() == LifeTimeInterval::Invalid); + Q_ASSERT(current.reg() == LifeTimeInterval::InvalidRegister); + const int position = current.start(); const bool isPhiTarget = _info->isPhiTarget(current.temp()); if (isPhiTarget && !current.isSplitFromInterval()) { + // Special case: storing to a phi-node's target will result in a single move. So, if we + // would spill another interval to the stack (that's 1 store), and then do the move for the + // phi target (at least 1 move or a load), that would result in 2 instructions. Instead, we + // force the phi-node's target to go to the stack immediately, which is always a single + // store. split(current, position + 1, true); - _inactive.append(current); + _inactive.append(¤t); return; } const bool needsFPReg = isFP(current.temp()); - QVector<int> nextUsePos(needsFPReg ? _fpRegisters.size() : _normalRegisters.size(), INT_MAX); - QVector<LifeTimeInterval *> nextUseRangeForReg(nextUsePos.size(), 0); - Q_ASSERT(nextUsePos.size() > 0); - - const bool definedAtCurrentPosition = !current.isSplitFromInterval() && current.start() == position; - - for (int i = 0, ei = _active.size(); i != ei; ++i) { - LifeTimeInterval &it = _active[i]; - if (it.isFP() == needsFPReg) { - int nu = it.isFixedInterval() ? 0 : nextUse(it.temp(), current.firstPossibleUsePosition(isPhiTarget)); - if (nu == position && !definedAtCurrentPosition) { - nextUsePos[it.reg()] = 0; - } else if (nu != -1 && nu < nextUsePos[it.reg()]) { - nextUsePos[it.reg()] = nu; - nextUseRangeForReg[it.reg()] = ⁢ - } else if (nu == -1 && nextUsePos[it.reg()] == INT_MAX) { - // in a loop, the range can be active, but only used before the current position (e.g. in a loop header or phi node) - nextUseRangeForReg[it.reg()] = ⁢ - } + const int nextUsePosCount = needsFPReg ? _fpRegisters.size() : _normalRegisters.size(); + CALLOC_ON_STACK(int, nextUsePos, nextUsePosCount, INT_MAX); + QVector<LifeTimeInterval *> nextUseRangeForReg(nextUsePosCount, 0); + + for (Intervals::const_iterator i = _active.constBegin(), ei = _active.constEnd(); i != ei; ++i) { + LifeTimeInterval &it = **i; + if (it.isFP() != needsFPReg) + continue; // different register type, so not applicable. + + const int nu = it.isFixedInterval() ? 0 : nextUse(it.temp(), current.start()); + if (nu == position) { + nextUsePos[it.reg()] = 0; + } else if (nu != -1 && nu < nextUsePos[it.reg()]) { + nextUsePos[it.reg()] = nu; + nextUseRangeForReg[it.reg()] = ⁢ + } else if (nu == -1 && nextUsePos[it.reg()] == INT_MAX) { + // in a loop, the range can be active, but the result might only be used before the + // current position (e.g. the induction variable being used in the phi node in the loop + // header). So, we can use this register, but we need to remember to split the interval + // in order to have the edge-resolving generate a load at the edge going back to the + // loop header. + nextUseRangeForReg[it.reg()] = ⁢ } } - for (int i = 0, ei = _inactive.size(); i != ei; ++i) { - LifeTimeInterval &it = _inactive[i]; + for (Intervals::const_iterator i = _inactive.constBegin(), ei = _inactive.constEnd(); i != ei; ++i) { + LifeTimeInterval &it = **i; + if (it.isFP() != needsFPReg) + continue; // different register type, so not applicable. + if (it.reg() == LifeTimeInterval::InvalidRegister) + continue; // this range does not block a register from being used, as it has no register assigned + if (current.isSplitFromInterval() || it.isFixedInterval()) { - if (it.isFP() == needsFPReg && it.reg() != LifeTimeInterval::Invalid) { - if (nextIntersection(current, it, position) != -1) { - int nu = nextUse(it.temp(), current.firstPossibleUsePosition(isPhiTarget)); - if (nu != -1 && nu < nextUsePos[it.reg()]) { - nextUsePos[it.reg()] = nu; - nextUseRangeForReg[it.reg()] = ⁢ - } + if (nextIntersection(current, it) != -1) { + const int nu = nextUse(it.temp(), current.start()); + if (nu != -1 && nu < nextUsePos[it.reg()]) { + nextUsePos[it.reg()] = nu; + nextUseRangeForReg[it.reg()] = ⁢ } } } } int reg, nextUsePos_reg; - longestAvailableReg(nextUsePos, reg, nextUsePos_reg, current.end()); + longestAvailableReg(nextUsePos, nextUsePosCount, reg, nextUsePos_reg, current.end()); - if (current.start() > nextUsePos_reg) { - // all other intervals are used before current, so it is best to spill current itself -#ifdef DEBUG_REGALLOC - QTextStream out(stderr, QIODevice::WriteOnly); - out << "*** splitting current for range ";current.dump(out);out<<endl; -#endif // DEBUG_REGALLOC - Q_ASSERT(!_info->useMustHaveReg(current.temp(), position)); - split(current, position + 1, true); - _inactive.append(current); - } else { - // spill intervals that currently block reg -#ifdef DEBUG_REGALLOC - QTextStream out(stderr, QIODevice::WriteOnly); - out << "*** spilling intervals that block reg "<<reg<<" for interval ";current.dump(out);out<<endl; -#endif // DEBUG_REGALLOC - current.setReg(reg); - _lastAssignedRegister.insert(current.temp(), reg); - LifeTimeInterval *nextUse = nextUseRangeForReg[reg]; - Q_ASSERT(nextUse); - Q_ASSERT(!nextUse->isFixedInterval()); - - if (_info->isUsedAt(nextUse->temp(), position)) { - Q_ASSERT(!_info->isUsedAt(current.temp(), position)); - // the register is used (as an incoming parameter) at the current position, so split - // the interval immediately after the (use at the) current position - split(*nextUse, position + 1); - } else { - // the register was used before the current position - split(*nextUse, position); - } + Q_ASSERT(current.start() <= nextUsePos_reg); - splitInactiveAtEndOfLifetimeHole(reg, needsFPReg, position); - - // make sure that current does not intersect with the fixed interval for reg - const LifeTimeInterval &fixedRegRange = needsFPReg ? _fixedFPRegisterRanges.at(reg) - : _fixedRegisterRanges.at(reg); - int ni = nextIntersection(current, fixedRegRange, position); - if (ni != -1) { -#ifdef DEBUG_REGALLOC - out << "***-- current range intersects with a fixed reg use at "<<ni<<", so splitting it."<<endl; -#endif // DEBUG_REGALLOC - split(current, ni, true); - } - } -} - -void RegisterAllocator::longestAvailableReg(const QVector<int> &nextUses, int ®, - int &freeUntilPos_reg, int lastUse) const -{ - reg = LifeTimeInterval::Invalid; - freeUntilPos_reg = 0; - - for (int candidate = 0, candidateEnd = nextUses.size(); candidate != candidateEnd; ++candidate) { - int fp = nextUses[candidate]; - if ((freeUntilPos_reg < lastUse && fp > freeUntilPos_reg) - || (freeUntilPos_reg >= lastUse && fp >= lastUse && freeUntilPos_reg > fp)) { - reg = candidate; - freeUntilPos_reg = fp; + // spill interval that currently block reg + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "*** spilling intervals that block reg " <<reg<< " for interval "; + current.dump(out); + out << endl; + } + current.setReg(reg); + _lastAssignedRegister[current.temp().index] = reg; + LifeTimeInterval *nextUse = nextUseRangeForReg[reg]; + Q_ASSERT(nextUse); + Q_ASSERT(!nextUse->isFixedInterval()); + + split(*nextUse, position); + + // We might have chosen a register that is used by a range that has a hole in its life time. + // If that's the case, check if the current interval completely fits in the hole. Or rephrased: + // check if the current interval will use the register after that hole ends (so that range, and + // if so, split that interval so that it gets a new register assigned when it needs one. + splitInactiveAtEndOfLifetimeHole(reg, needsFPReg, position); + + // make sure that current does not intersect with the fixed interval for reg + const LifeTimeInterval &fixedRegRange = needsFPReg ? *_fixedFPRegisterRanges.at(reg) + : *_fixedRegisterRanges.at(reg); + int ni = nextIntersection(current, fixedRegRange); + if (ni != -1) { + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "***-- current range intersects with a fixed reg use at " << ni << ", so splitting it." << endl; } + // current does overlap with a fixed interval, so split current before that intersection. + split(current, ni, true); } } int RegisterAllocator::nextIntersection(const LifeTimeInterval ¤t, - const LifeTimeInterval &another, const int position) const + const LifeTimeInterval &another) const { - LifeTimeInterval::Ranges currentRanges = current.ranges(); - int currentIt = indexOfRangeCoveringPosition(currentRanges, position); - if (currentIt == -1) - return -1; + const LifeTimeInterval::Ranges ¤tRanges = current.ranges(); + int currentIt = 0; - LifeTimeInterval::Ranges anotherRanges = another.ranges(); - const int anotherItStart = indexOfRangeCoveringPosition(anotherRanges, position); + const LifeTimeInterval::Ranges &anotherRanges = another.ranges(); + const int anotherItStart = indexOfRangeCoveringPosition(anotherRanges, current.start()); if (anotherItStart == -1) return -1; @@ -1515,11 +1707,12 @@ int RegisterAllocator::nextIntersection(const LifeTimeInterval ¤t, return -1; } +/// Find the first use after the start position for the given temp. int RegisterAllocator::nextUse(const Temp &t, int startPosition) const { - QList<Use> usePositions = _info->uses(t); - for (int i = 0, ei = usePositions.size(); i != ei; ++i) { - int usePos = usePositions[i].pos; + const std::vector<Use> &usePositions = _info->uses(t); + for (int i = 0, ei = usePositions.size(); i != ei; ++i) { //### FIXME: use an iterator + const int usePos = usePositions.at(i).pos; if (usePos >= startPosition) return usePos; } @@ -1527,16 +1720,16 @@ int RegisterAllocator::nextUse(const Temp &t, int startPosition) const return -1; } -static inline void insertSorted(QVector<LifeTimeInterval> &intervals, const LifeTimeInterval &newInterval) +static inline void insertReverseSorted(QVector<LifeTimeInterval *> &intervals, LifeTimeInterval *newInterval) { - newInterval.validate(); - for (int i = 0, ei = intervals.size(); i != ei; ++i) { - if (LifeTimeInterval::lessThan(newInterval, intervals.at(i))) { - intervals.insert(i, newInterval); + newInterval->validate(); + for (int i = intervals.size(); i > 0;) { + if (LifeTimeInterval::lessThan(newInterval, intervals.at(--i))) { + intervals.insert(i + 1, newInterval); return; } } - intervals.append(newInterval); + intervals.insert(0, newInterval); } void RegisterAllocator::split(LifeTimeInterval ¤t, int beforePosition, @@ -1544,28 +1737,21 @@ void RegisterAllocator::split(LifeTimeInterval ¤t, int beforePosition, { // TODO: check if we can always skip the optional register uses Q_ASSERT(!current.isFixedInterval()); -#ifdef DEBUG_REGALLOC - QTextStream out(stderr, QIODevice::WriteOnly); - out << "***** split request for range ";current.dump(out);out<<" before position "<<beforePosition<<" and skipOptionalRegisterUses = "<<skipOptionalRegisterUses<<endl; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "***** split request for range ";current.dump(out);out<<" before position "<<beforePosition<<" and skipOptionalRegisterUses = "<<skipOptionalRegisterUses<<endl; + } assignSpillSlot(current.temp(), current.start(), current.end()); - const int defPosition = _info->def(current.temp()); - if (beforePosition < defPosition) { -#ifdef DEBUG_REGALLOC - out << "***** split before position is before or at definition, so not splitting."<<endl; -#endif // DEBUG_REGALLOC - return; - } + const int firstPosition = current.start(); + Q_ASSERT(beforePosition > firstPosition && "split before start"); - int lastUse = -1; - if (defPosition < beforePosition) - lastUse = defPosition; + int lastUse = firstPosition; int nextUse = -1; - QList<Use> usePositions = _info->uses(current.temp()); + const std::vector<Use> &usePositions = _info->uses(current.temp()); for (int i = 0, ei = usePositions.size(); i != ei; ++i) { - const Use &usePosition = usePositions[i]; + const Use &usePosition = usePositions.at(i); const int usePos = usePosition.pos; if (lastUse < usePos && usePos < beforePosition) { lastUse = usePos; @@ -1576,31 +1762,30 @@ void RegisterAllocator::split(LifeTimeInterval ¤t, int beforePosition, } } } - if (lastUse == -1) - lastUse = beforePosition - 1; - + Q_ASSERT(lastUse != -1); Q_ASSERT(lastUse < beforePosition); -#ifdef DEBUG_REGALLOC - out << "***** last use = "<<lastUse<<", nextUse = " << nextUse<<endl; -#endif // DEBUG_REGALLOC LifeTimeInterval newInterval = current.split(lastUse, nextUse); -#ifdef DEBUG_REGALLOC - out << "***** new interval: "; newInterval.dump(out); out << endl; - out << "***** preceding interval: "; current.dump(out); out << endl; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "***** last use = "<<lastUse<<", nextUse = " << nextUse<<endl; + out << "***** new interval: "; newInterval.dump(out); out << endl; + out << "***** preceding interval: "; current.dump(out); out << endl; + } if (newInterval.isValid()) { - if (current.reg() != LifeTimeInterval::Invalid) + if (current.reg() != LifeTimeInterval::InvalidRegister) _info->addHint(current.temp(), current.reg()); - newInterval.setReg(LifeTimeInterval::Invalid); - insertSorted(_unhandled, newInterval); + newInterval.setReg(LifeTimeInterval::InvalidRegister); + LifeTimeInterval *newIntervalPtr = new LifeTimeInterval(newInterval); + _lifeTimeIntervals->add(newIntervalPtr); + insertReverseSorted(_unhandled, newIntervalPtr); } } void RegisterAllocator::splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position) { for (int i = 0, ei = _inactive.size(); i != ei; ++i) { - LifeTimeInterval &interval = _inactive[i]; + LifeTimeInterval &interval = *_inactive[i]; if (interval.isFixedInterval()) continue; if (isFPReg == interval.isFP() && interval.reg() == reg) { @@ -1618,13 +1803,13 @@ void RegisterAllocator::splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, void RegisterAllocator::assignSpillSlot(const Temp &t, int startPos, int endPos) { - if (_assignedSpillSlots.contains(t)) + if (_assignedSpillSlots[t.index] != InvalidSpillSlot) return; for (int i = 0, ei = _activeSpillSlots.size(); i != ei; ++i) { if (_activeSpillSlots.at(i) < startPos) { _activeSpillSlots[i] = endPos; - _assignedSpillSlots.insert(t, i); + _assignedSpillSlots[t.index] = i; return; } } @@ -1632,32 +1817,33 @@ void RegisterAllocator::assignSpillSlot(const Temp &t, int startPos, int endPos) Q_UNREACHABLE(); } -void RegisterAllocator::dump() const +void RegisterAllocator::dump(IR::Function *function) const { -#ifdef DEBUG_REGALLOC QTextStream qout(stdout, QIODevice::WriteOnly); + IRPrinterWithPositions printer(&qout, _lifeTimeIntervals); - { - qout << "Ranges:" << endl; - QVector<LifeTimeInterval> handled = _handled; - std::sort(handled.begin(), handled.end(), LifeTimeInterval::lessThanForTemp); - foreach (const LifeTimeInterval &r, handled) { - r.dump(qout); - qout << endl; - } + qout << "Ranges:" << endl; + QVector<LifeTimeInterval *> handled = _handled; + std::sort(handled.begin(), handled.end(), LifeTimeInterval::lessThanForTemp); + foreach (const LifeTimeInterval *r, handled) { + r->dump(qout); + qout << endl; } - { - qout << "Spill slots:" << endl; - QList<Temp> temps = _assignedSpillSlots.keys(); - if (temps.isEmpty()) - qout << "\t(none)" << endl; - std::sort(temps.begin(), temps.end()); - foreach (const Temp &t, temps) { - qout << "\t"; - t.dump(qout); - qout << " -> " << _assignedSpillSlots[t] << endl; - } - } -#endif // DEBUG_REGALLOC + qout << "Spill slots:" << endl; + for (unsigned i = 0; i < _assignedSpillSlots.size(); ++i) + if (_assignedSpillSlots[i] != InvalidSpillSlot) + qout << "\t%" << i << " -> " << _assignedSpillSlots[i] << endl; + + printer.print(function); } + +// References: +// [Wimmer1] C. Wimmer and M. Franz. Linear Scan Register Allocation on SSA Form. In Proceedings of +// CGO’10, ACM Press, 2010 +// [Wimmer2] C. Wimmer and H. Mossenbock. Optimized Interval Splitting in a Linear Scan Register +// Allocator. In Proceedings of the ACM/USENIX International Conference on Virtual +// Execution Environments, pages 132–141. ACM Press, 2005. +// [Traub] Omri Traub, Glenn Holloway, and Michael D. Smith. Quality and Speed in Linear-scan +// Register Allocation. In Proceedings of the ACM SIGPLAN 1998 Conference on Programming +// Language Design and Implementation, pages 142–151, June 1998. diff --git a/src/qml/jit/qv4regalloc_p.h b/src/qml/jit/qv4regalloc_p.h index 030fb4bf50..34ae19dfec 100644 --- a/src/qml/jit/qv4regalloc_p.h +++ b/src/qml/jit/qv4regalloc_p.h @@ -44,6 +44,7 @@ #include "qv4global_p.h" #include "qv4isel_p.h" #include "qv4ssa_p.h" +#include "qv4registerinfo_p.h" #include <config.h> @@ -54,46 +55,68 @@ namespace JIT { class RegAllocInfo; +// This class implements a linear-scan register allocator, with a couple of tweaks: +// - Second-chance allocation is used when an interval becomes active after a spill, which results +// in fewer differences between edges, and hence fewer moves before jumps. +// - Use positions are flagged with either "must have" register or "could have" register. This is +// used to decide whether a register is really needed when a temporary is used after a spill +// occurred. +// - Fixed intervals are used to denotate IR positions where certain registers are needed in order +// to implement the operation, and cannot be used by a temporary on that position. An example is +// caller saved registers, where the call will use/clobber those registers. +// - Hints are used to indicate which registers could be used to generate more compact code. An +// example is an addition, where one (or both) operands' life-time ends at that instruction. In +// this case, re-using an operand register for the result will result in an in-place add. +// - SSA form properties are used: +// - to simplify life-times (two temporaries will never interfere as long as their intervals +// are not split), resulting in a slightly faster algorithm; +// - when a temporary needs to be spilled, it is done directly after calculating it, so that +// 1 store is generated even if multiple spills/splits happen. +// - phi-node elimination (SSA form deconstruction) is done when resolving differences between +// CFG edges class RegisterAllocator { typedef IR::LifeTimeInterval LifeTimeInterval; - QVector<int> _normalRegisters; - QVector<int> _fpRegisters; + const RegisterInformation &_registerInformation; + QVector<const RegisterInfo *> _normalRegisters; + QVector<const RegisterInfo *> _fpRegisters; QScopedPointer<RegAllocInfo> _info; - QVector<LifeTimeInterval> _fixedRegisterRanges, _fixedFPRegisterRanges; + QVector<LifeTimeInterval *> _fixedRegisterRanges, _fixedFPRegisterRanges; - QVector<LifeTimeInterval> _unhandled, _active, _inactive, _handled; + IR::LifeTimeIntervals::Ptr _lifeTimeIntervals; + typedef QVector<LifeTimeInterval *> Intervals; + Intervals _unhandled, _active, _inactive, _handled; - QHash<IR::Temp, int> _lastAssignedRegister; - QHash<IR::Temp, int> _assignedSpillSlots; + std::vector<int> _lastAssignedRegister; + std::vector<int> _assignedSpillSlots; QVector<int> _activeSpillSlots; Q_DISABLE_COPY(RegisterAllocator) public: - RegisterAllocator(const QVector<int> &normalRegisters, const QVector<int> &fpRegisters); + enum { InvalidSpillSlot = -1 }; + + RegisterAllocator(const RegisterInformation ®isterInformation); ~RegisterAllocator(); void run(IR::Function *function, const IR::Optimizer &opt); private: + LifeTimeInterval *cloneFixedInterval(int reg, bool isFP, const LifeTimeInterval &original); void prepareRanges(); void linearScan(); - void tryAllocateFreeReg(LifeTimeInterval ¤t, const int position); - void allocateBlockedReg(LifeTimeInterval ¤t, const int position); - void longestAvailableReg(const QVector<int> &nextUses, int ®, int &nextUsePos_reg, - int lastUse) const; - int nextIntersection(const LifeTimeInterval ¤t, const LifeTimeInterval &another, - const int position) const; + void tryAllocateFreeReg(LifeTimeInterval ¤t); + void allocateBlockedReg(LifeTimeInterval ¤t); + int nextIntersection(const LifeTimeInterval ¤t, const LifeTimeInterval &another) const; int nextUse(const IR::Temp &t, int startPosition) const; void split(LifeTimeInterval ¤t, int beforePosition, bool skipOptionalRegisterUses =false); void splitInactiveAtEndOfLifetimeHole(int reg, bool isFPReg, int position); void assignSpillSlot(const IR::Temp &t, int startPos, int endPos); void resolve(IR::Function *function, const IR::Optimizer &opt); - void dump() const; + void dump(IR::Function *function) const; }; } // end of namespace JIT diff --git a/src/qml/jit/qv4registerinfo_p.h b/src/qml/jit/qv4registerinfo_p.h new file mode 100644 index 0000000000..b8701d72f4 --- /dev/null +++ b/src/qml/jit/qv4registerinfo_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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 QV4REGISTERINFO_P_H +#define QV4REGISTERINFO_P_H + +#include <QtCore/QString> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace JIT { + +class RegisterInfo +{ +public: + enum { InvalidRegister = -1 }; + enum SavedBy { CallerSaved, CalleeSaved }; + enum RegisterType { RegularRegister, FloatingPointRegister }; + enum Usage { Predefined, RegAlloc }; + +public: + RegisterInfo() + : _reg(InvalidRegister) + , _type(RegularRegister) + , _savedBy(CallerSaved) + , _usage(Predefined) + {} + + RegisterInfo(int reg, const QString &prettyName, RegisterType type, SavedBy savedBy, Usage usage) + : _reg(reg) + , _prettyName(prettyName) + , _type(type) + , _savedBy(savedBy) + , _usage(usage) + {} + + bool isValid() const { return _reg != InvalidRegister; } + template <typename T> T reg() const { return static_cast<T>(_reg); } + QString prettyName() const { return _prettyName; } + bool isCallerSaved() const { return _savedBy == CallerSaved; } + bool isCalleeSaved() const { return _savedBy == CalleeSaved; } + bool isFloatingPoint() const { return _type == FloatingPointRegister; } + bool isRegularRegister() const { return _type == RegularRegister; } + bool useForRegAlloc() const { return _usage == RegAlloc; } + +private: + int _reg; + QString _prettyName; + RegisterType _type; + SavedBy _savedBy; + Usage _usage; +}; +typedef QVector<RegisterInfo> RegisterInformation; + +} // JIT namespace +} // QV4 namespace + +QT_END_NAMESPACE + +#endif // QV4REGISTERINFO_P_H diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h new file mode 100644 index 0000000000..4b384d2fe9 --- /dev/null +++ b/src/qml/jit/qv4targetplatform_p.h @@ -0,0 +1,390 @@ +/**************************************************************************** +** +** Copyright (C) 2014 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 QV4TARGETPLATFORM_P_H +#define QV4TARGETPLATFORM_P_H + +#include <config.h> + +#if ENABLE(ASSEMBLER) + +#include "qv4registerinfo_p.h" +#include <assembler/MacroAssembler.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace JIT { + +// The TargetPlatform class describes how the stack and the registers work on a CPU+ABI combination. +// +// All combinations have a separate definition, guarded by #ifdefs. The exceptions are: +// - Linux/x86 and win32, which are the same, except that linux non-PIC/PIE code does not need to +// restore ebx (which holds the GOT ptr) before a call +// - All (supported) ARM platforms, where the only variety is the platform specific usage of r9, +// and the frame-pointer in Thumb/Thumb2 v.s. ARM mode. +// +// Specific handling of ebx when it holds the GOT: +// In this case we can use it, but it needs to be restored when doing a call. So, the handling is as +// follows: it is marked as caller saved, meaning the value in it won't survive a call. When +// calculating the list of callee saved registers in getCalleeSavedRegisters (which is used to +// generate push/pop instructions in the prelude/postlude), we add ebx too. Then when synthesizing +// a call, we add a load it right before emitting the call instruction. +// +// NOTE: When adding new architecture, do not forget to whitelist it in qv4global_p.h! +class TargetPlatform +{ +public: +#if CPU(X86) && (OS(LINUX) || OS(WINDOWS) || OS(QNX) || OS(FREEBSD)) + enum { RegAllocIsSupported = 1 }; + + static const JSC::MacroAssembler::RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::edi; + static const JSC::MacroAssembler::RegisterID ContextRegister = JSC::X86Registers::esi; + static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + + static RegisterInformation getPlatformRegisterInfo() + { + typedef RegisterInfo RI; + return RegisterInformation() + << RI(JSC::X86Registers::edx, QStringLiteral("edx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::ebx, QStringLiteral("ebx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::edi, QStringLiteral("edi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::esi, QStringLiteral("esi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + ; + } + +# define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 +# undef VALUE_FITS_IN_REGISTER + static const int RegisterSize = 4; + +# undef ARGUMENTS_IN_REGISTERS + static const int RegisterArgumentCount = 0; + static JSC::MacroAssembler::RegisterID registerForArgument(int) { Q_UNREACHABLE(); } + + static const int StackAlignment = 16; + static const int StackShadowSpace = 0; + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. + static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { Q_UNUSED(as); } + static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { Q_UNUSED(as); } + +#if OS(WINDOWS) || OS(QNX) || \ + ((OS(LINUX) || OS(FREEBSD)) && (defined(__PIC__) || defined(__PIE__))) + +#define RESTORE_EBX_ON_CALL + static JSC::MacroAssembler::Address ebxAddressOnStack() + { + static int ebxIdx = -1; + if (ebxIdx == -1) { + int calleeSaves = 0; + foreach (const RegisterInfo &info, getRegisterInfo()) { + if (info.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) { + ebxIdx = calleeSaves; + break; + } else if (info.isCalleeSaved()) { + ++calleeSaves; + } + } + Q_ASSERT(ebxIdx >= 0); + ebxIdx += 1; + } + return JSC::MacroAssembler::Address(StackFrameRegister, ebxIdx * -int(sizeof(void*))); + } +#endif + +#endif // Windows on x86 + +#if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X) || OS(FREEBSD)) + enum { RegAllocIsSupported = 1 }; + + static const JSC::MacroAssembler::RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::r12; + static const JSC::MacroAssembler::RegisterID ContextRegister = JSC::X86Registers::r14; + static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::r10; + static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + + static RegisterInformation getPlatformRegisterInfo() + { + typedef RegisterInfo RI; + return RegisterInformation() + << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + ; + } + +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 +#define VALUE_FITS_IN_REGISTER + static const int RegisterSize = 8; + +#define ARGUMENTS_IN_REGISTERS + static const int RegisterArgumentCount = 6; + static JSC::MacroAssembler::RegisterID registerForArgument(int index) + { + static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::edi, + JSC::X86Registers::esi, + JSC::X86Registers::edx, + JSC::X86Registers::ecx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + Q_ASSERT(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + + static const int StackAlignment = 16; + static const int StackShadowSpace = 0; + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. + static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { Q_UNUSED(as); } + static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { Q_UNUSED(as); } +#endif // Linux/MacOS on x86_64 + +#if CPU(X86_64) && OS(WINDOWS) + // Register allocation is not (yet) supported on win64, because the ABI related stack handling + // is not completely implemented. Specifically, the saving of xmm registers, and the saving of + // incoming function parameters to the shadow space is missing. + enum { RegAllocIsSupported = 0 }; + + static const JSC::MacroAssembler::RegisterID StackFrameRegister = JSC::X86Registers::ebp; + static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::r12; + static const JSC::MacroAssembler::RegisterID ContextRegister = JSC::X86Registers::r14; + static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::r10; + static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + + static RegisterInformation getPlatformRegisterInfo() + { + typedef RegisterInfo RI; + return RegisterInformation() + << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) + << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) + << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) + << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + ; + } + +#define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 +#define VALUE_FITS_IN_REGISTER + static const int RegisterSize = 8; + +#define ARGUMENTS_IN_REGISTERS + static const int RegisterArgumentCount = 4; + static JSC::MacroAssembler::RegisterID registerForArgument(int index) + { + static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + JSC::X86Registers::ecx, + JSC::X86Registers::edx, + JSC::X86Registers::r8, + JSC::X86Registers::r9 + }; + Q_ASSERT(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + + static const int StackAlignment = 16; + static const int StackShadowSpace = 32; + static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. + static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { Q_UNUSED(as); } + static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { Q_UNUSED(as); } +#endif // Windows on x86_64 + +#if CPU(ARM) + enum { RegAllocIsSupported = 1 }; + + // The AAPCS specifies that the platform ABI has to define the usage of r9. Known are: + // - The GNU/Linux ABI defines it as an additional callee-saved variable register (v6). + // - iOS (for which we cannot JIT, but still...) defines it as having a special use, so we do + // not touch it, nor use it. + // - Any other platform has not been verified, so we conservatively assume we cannot use it. +#if OS(LINUX) +#define CAN_USE_R9 +#endif + + // There are two designated frame-pointer registers on ARM, depending on which instruction set + // is used for the subroutine: r7 for Thumb or Thumb2, and r11 for ARM. We assign the constants + // accordingly, and assign the locals-register to the "other" register. +#if CPU(ARM_THUMB2) + static const JSC::MacroAssembler::RegisterID StackFrameRegister = JSC::ARMRegisters::r7; + static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARMRegisters::r11; +#else // Thumbs down + static const JSC::MacroAssembler::RegisterID StackFrameRegister = JSC::ARMRegisters::r11; + static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARMRegisters::r7; +#endif + static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::ARMRegisters::r13; + static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::ARMRegisters::r6; + static const JSC::MacroAssembler::RegisterID ContextRegister = JSC::ARMRegisters::r5; + static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; + + static RegisterInformation getPlatformRegisterInfo() + { + typedef RegisterInfo RI; + return RegisterInformation() + << RI(JSC::ARMRegisters::r0, QStringLiteral("r0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) + << RI(JSC::ARMRegisters::r1, QStringLiteral("r1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::ARMRegisters::r2, QStringLiteral("r2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::ARMRegisters::r3, QStringLiteral("r3"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::ARMRegisters::r4, QStringLiteral("r4"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::ARMRegisters::r5, QStringLiteral("r5"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::ARMRegisters::r6, QStringLiteral("r6"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) +#if !CPU(ARM_THUMB2) + << RI(JSC::ARMRegisters::r7, QStringLiteral("r7"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) +#endif + << RI(JSC::ARMRegisters::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) +#ifdef CAN_USE_R9 + << RI(JSC::ARMRegisters::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) +#endif + << RI(JSC::ARMRegisters::r10, QStringLiteral("r10"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) +#if CPU(ARM_THUMB2) + << RI(JSC::ARMRegisters::r11, QStringLiteral("r11"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) +#endif + << RI(JSC::ARMRegisters::d2, QStringLiteral("d2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::ARMRegisters::d3, QStringLiteral("d3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::ARMRegisters::d4, QStringLiteral("d4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::ARMRegisters::d5, QStringLiteral("d5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::ARMRegisters::d6, QStringLiteral("d6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + ; + // TODO: someone should check what's up with d8-d15: are they alway available, and are they caller or callee saved? + } + +#undef HAVE_ALU_OPS_WITH_MEM_OPERAND +#undef VALUE_FITS_IN_REGISTER + static const int RegisterSize = 4; + +#define ARGUMENTS_IN_REGISTERS + static const int RegisterArgumentCount = 4; + static JSC::MacroAssembler::RegisterID registerForArgument(int index) + { + static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + JSC::ARMRegisters::r0, + JSC::ARMRegisters::r1, + JSC::ARMRegisters::r2, + JSC::ARMRegisters::r3 + }; + + Q_ASSERT(index >= 0 && index < RegisterArgumentCount); + return regs[index]; + }; + + static const int StackAlignment = 8; // Per AAPCS + static const int StackShadowSpace = 0; + static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. + static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { as->push(JSC::ARMRegisters::lr); } + static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(JSC::ARMRegisters::lr); } +#endif // Linux on ARM (32 bit) + +public: // utility functions + static RegisterInformation getRegisterInfo() + { + static const RegisterInformation info = getPlatformRegisterInfo(); + + return info; + } + + static RegisterInformation &getCalleeSavedRegisters() + { + static RegisterInformation regs; + if (regs.isEmpty()) { + foreach (const RegisterInfo &info, getRegisterInfo()) { +#if defined(RESTORE_EBX_ON_CALL) + if (info.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) { + regs.append(info); + continue; + } +#endif // RESTORE_EBX_ON_CALL + if (info.isCalleeSaved()) + regs.append(info); + } + } + + return regs; + } + + static int calleeSavedRegisterCount() + { + return getCalleeSavedRegisters().size(); + } +}; + +} // JIT namespace +} // QV4 namespace + +QT_END_NAMESPACE + +#endif // ENABLE(ASSEMBLER) + +#endif // QV4TARGETPLATFORM_P_H diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index 40f86f91b5..d92b5797cd 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -51,7 +51,7 @@ using namespace JIT; #define setOp(operation) \ do { call = operation; name = stringIfy(operation); } while (0) -void Unop::generate(IR::Temp *source, IR::Temp *target) +void Unop::generate(IR::Expr *source, IR::Expr *target) { Runtime::UnaryOperation call = 0; const char *name = 0; @@ -77,16 +77,17 @@ void Unop::generate(IR::Temp *source, IR::Temp *target) } } -void Unop::generateUMinus(IR::Temp *source, IR::Temp *target) +void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) { + IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::SInt32Type) { Assembler::RegisterID tReg = Assembler::ScratchRegister; - if (target->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) target->index; + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) + tReg = (Assembler::RegisterID) targetTemp->index; Assembler::RegisterID sReg = as->toInt32Register(source, tReg); as->move(sReg, tReg); as->neg32(tReg); - if (target->kind != IR::Temp::PhysicalRegister) + if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) as->storeInt32(tReg, target); return; } @@ -94,24 +95,25 @@ void Unop::generateUMinus(IR::Temp *source, IR::Temp *target) as->generateFunctionCallImp(target, "Runtime::uMinus", Runtime::uMinus, Assembler::PointerToValue(source)); } -void Unop::generateNot(IR::Temp *source, IR::Temp *target) +void Unop::generateNot(IR::Expr *source, IR::Expr *target) { + IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::BoolType) { Assembler::RegisterID tReg = Assembler::ScratchRegister; - if (target->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) target->index; + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) + tReg = (Assembler::RegisterID) targetTemp->index; as->xor32(Assembler::TrustedImm32(0x1), as->toInt32Register(source, tReg), tReg); - if (target->kind != IR::Temp::PhysicalRegister) + if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) as->storeBool(tReg, target); return; } else if (source->type == IR::SInt32Type) { Assembler::RegisterID tReg = Assembler::ScratchRegister; - if (target->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) target->index; + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) + tReg = (Assembler::RegisterID) targetTemp->index; as->compare32(Assembler::Equal, as->toInt32Register(source, Assembler::ScratchRegister), Assembler::TrustedImm32(0), tReg); - if (target->kind != IR::Temp::PhysicalRegister) + if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) as->storeBool(tReg, target); return; } else if (source->type == IR::DoubleType) { @@ -122,14 +124,15 @@ void Unop::generateNot(IR::Temp *source, IR::Temp *target) as->generateFunctionCallImp(target, "Runtime::uNot", Runtime::uNot, Assembler::PointerToValue(source)); } -void Unop::generateCompl(IR::Temp *source, IR::Temp *target) +void Unop::generateCompl(IR::Expr *source, IR::Expr *target) { + IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::SInt32Type) { Assembler::RegisterID tReg = Assembler::ScratchRegister; - if (target->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) target->index; + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) + tReg = (Assembler::RegisterID) targetTemp->index; as->xor32(Assembler::TrustedImm32(0xffffffff), as->toInt32Register(source, tReg), tReg); - if (target->kind != IR::Temp::PhysicalRegister) + if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) as->storeInt32(tReg, target); return; } diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jit/qv4unop_p.h index a8c62182ad..07fb6e5ad6 100644 --- a/src/qml/jit/qv4unop_p.h +++ b/src/qml/jit/qv4unop_p.h @@ -59,11 +59,11 @@ struct Unop { , op(operation) {} - void generate(IR::Temp *source, IR::Temp *target); + void generate(IR::Expr *source, IR::Expr *target); - void generateUMinus(IR::Temp *source, IR::Temp *target); - void generateNot(IR::Temp *source, IR::Temp *target); - void generateCompl(IR::Temp *source, IR::Temp *target); + void generateUMinus(IR::Expr *source, IR::Expr *target); + void generateNot(IR::Expr *source, IR::Expr *target); + void generateCompl(IR::Expr *source, IR::Expr *target); Assembler *as; IR::AluOp op; |