aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jit
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jit')
-rw-r--r--src/qml/jit/jit.pri2
-rw-r--r--src/qml/jit/qv4assembler.cpp131
-rw-r--r--src/qml/jit/qv4assembler_p.h373
-rw-r--r--src/qml/jit/qv4binop.cpp50
-rw-r--r--src/qml/jit/qv4binop_p.h8
-rw-r--r--src/qml/jit/qv4isel_masm.cpp437
-rw-r--r--src/qml/jit/qv4isel_masm_p.h139
-rw-r--r--src/qml/jit/qv4regalloc.cpp1292
-rw-r--r--src/qml/jit/qv4regalloc_p.h51
-rw-r--r--src/qml/jit/qv4registerinfo_p.h99
-rw-r--r--src/qml/jit/qv4targetplatform_p.h390
-rw-r--r--src/qml/jit/qv4unop.cpp35
-rw-r--r--src/qml/jit/qv4unop_p.h8
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 &registerInformation)
+ : 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 &registerInformation)
+ : _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 &regInfo = registerInformation.at(i);
+ if (regInfo.useForRegAlloc()) {
+ if (regInfo.isRegularRegister())
+ _normalRegisters.append(&regInfo);
+ else
+ _fpRegisters.append(&regInfo);
+ }
+ }
+ 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 &current, 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 &reg, 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 &current)
+{
+ 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 &current, 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 &current, const int position)
+// This gets called when all registers are currently in use.
+void RegisterAllocator::allocateBlockedReg(LifeTimeInterval &current)
{
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(&current);
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()] = &it;
- } 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()] = &it;
- }
+ 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()] = &it;
+ } 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()] = &it;
}
}
- 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()] = &it;
- }
+ 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()] = &it;
}
}
}
}
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 &reg,
- 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 &current,
- 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 &currentRanges = 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 &current,
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 &current, int beforePosition,
@@ -1544,28 +1737,21 @@ void RegisterAllocator::split(LifeTimeInterval &current, 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 &current, 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 &registerInformation);
~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 &current, const int position);
- void allocateBlockedReg(LifeTimeInterval &current, const int position);
- void longestAvailableReg(const QVector<int> &nextUses, int &reg, int &nextUsePos_reg,
- int lastUse) const;
- int nextIntersection(const LifeTimeInterval &current, const LifeTimeInterval &another,
- const int position) const;
+ void tryAllocateFreeReg(LifeTimeInterval &current);
+ void allocateBlockedReg(LifeTimeInterval &current);
+ int nextIntersection(const LifeTimeInterval &current, const LifeTimeInterval &another) const;
int nextUse(const IR::Temp &t, int startPosition) const;
void split(LifeTimeInterval &current, 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;