summaryrefslogtreecommitdiff
path: root/src/qml/compiler/qv4isel_masm.cpp
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@digia.com>2014-02-10 15:46:28 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-22 18:47:14 +0100
commit5e085f4883953ebb54ed8abba0468ad17d352682 (patch)
treeda69ca46d485b24848b45266cd9139de92cede27 /src/qml/compiler/qv4isel_masm.cpp
parentf11561f0eb3f084cd6e10dd7984b865a548403ac (diff)
downloadqtdeclarative-5e085f4883953ebb54ed8abba0468ad17d352682.tar.gz
Move JIT infrastructure into it's own directory
This is a starting point to clean up some of the code base for the JIT. Change-Id: I388ce8df7d4ab87095e227cf36643f4be2594b7d Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
Diffstat (limited to 'src/qml/compiler/qv4isel_masm.cpp')
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp2603
1 files changed, 0 insertions, 2603 deletions
diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp
deleted file mode 100644
index 157cbd3634..0000000000
--- a/src/qml/compiler/qv4isel_masm.cpp
+++ /dev/null
@@ -1,2603 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4isel_masm_p.h"
-#include "qv4runtime_p.h"
-#include "qv4object_p.h"
-#include "qv4functionobject_p.h"
-#include "qv4regexpobject_p.h"
-#include "qv4lookup_p.h"
-#include "qv4function_p.h"
-#include "qv4ssa_p.h"
-#include "qv4regalloc_p.h"
-
-#include <assembler/LinkBuffer.h>
-#include <WTFStubs.h>
-
-#include <iostream>
-#include <cassert>
-
-#if ENABLE(ASSEMBLER)
-
-#if USE(UDIS86)
-# include <udis86.h>
-#endif
-
-using namespace QQmlJS;
-using namespace QQmlJS::MASM;
-using namespace QV4;
-
-CompilationUnit::~CompilationUnit()
-{
- foreach (Function *f, runtimeFunctions)
- engine->allFunctions.remove(reinterpret_cast<quintptr>(f->code));
-}
-
-void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
-{
- runtimeFunctions.resize(data->functionTableSize);
- runtimeFunctions.fill(0);
- for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
- const CompiledData::Function *compiledFunction = data->functionAt(i);
-
- QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction,
- (ReturnedValue (*)(QV4::ExecutionContext *, const uchar *)) codeRefs[i].code().executableAddress(),
- codeSizes[i]);
- runtimeFunctions[i] = runtimeFunction;
- }
-
- foreach (Function *f, runtimeFunctions)
- engine->allFunctions.insert(reinterpret_cast<quintptr>(f->code), f);
-}
-
-QV4::ExecutableAllocator::ChunkOfPages *CompilationUnit::chunkForFunction(int functionIndex)
-{
- if (functionIndex < 0 || functionIndex >= codeRefs.count())
- return 0;
- JSC::ExecutableMemoryHandle *handle = codeRefs[functionIndex].executableMemory();
- if (!handle)
- return 0;
- return handle->chunk();
-}
-
-namespace {
-inline bool isPregOrConst(V4IR::Expr *e)
-{
- if (V4IR::Temp *t = e->asTemp())
- return t->kind == V4IR::Temp::PhysicalRegister;
- return e->asConst() != 0;
-}
-} // anonymous namespace
-
-/* Platform/Calling convention/Architecture specific section */
-
-#if CPU(X86_64)
-static const Assembler::RegisterID calleeSavedRegisters[] = {
- // Not used: JSC::X86Registers::rbx,
- // Not used: JSC::X86Registers::r10,
- JSC::X86Registers::r12, // LocalsRegister
- // Not used: JSC::X86Registers::r13,
- JSC::X86Registers::r14 // ContextRegister
- // Not used: JSC::X86Registers::r15,
-};
-#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, V4IR::Function* function, QV4::ExecutableAllocator *executableAllocator,
- int maxArgCountForBuiltins)
- : _stackLayout(function, maxArgCountForBuiltins)
- , _constTable(this)
- , _function(function)
- , _nextBlock(0)
- , _executableAllocator(executableAllocator)
- , _isel(isel)
-{
-}
-
-void Assembler::registerBlock(V4IR::BasicBlock* block, V4IR::BasicBlock *nextBlock)
-{
- _addrs[block] = label();
- catchBlock = block->catchBlock;
- _nextBlock = nextBlock;
-}
-
-void Assembler::jumpToBlock(V4IR::BasicBlock* current, V4IR::BasicBlock *target)
-{
- Q_UNUSED(current);
-
- if (target != _nextBlock)
- _patches[target].append(jump());
-}
-
-void Assembler::addPatch(V4IR::BasicBlock* targetBlock, Jump targetJump)
-{
- _patches[targetBlock].append(targetJump);
-}
-
-void Assembler::addPatch(DataLabelPtr patch, Label target)
-{
- DataLabelPatch p;
- p.dataLabel = patch;
- p.target = target;
- _dataLabelPatches.append(p);
-}
-
-void Assembler::addPatch(DataLabelPtr patch, V4IR::BasicBlock *target)
-{
- _labelPatches[target].append(patch);
-}
-
-void Assembler::generateCJumpOnNonZero(RegisterID reg, V4IR::BasicBlock *currentBlock,
- V4IR::BasicBlock *trueBlock, V4IR::BasicBlock *falseBlock)
-{
- generateCJumpOnCompare(NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock);
-}
-
-void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left,TrustedImm32 right,
- V4IR::BasicBlock *currentBlock, V4IR::BasicBlock *trueBlock,
- V4IR::BasicBlock *falseBlock)
-{
- if (trueBlock == _nextBlock) {
- Jump target = branch32(invert(cond), left, right);
- addPatch(falseBlock, target);
- } else {
- Jump target = branch32(cond, left, right);
- addPatch(trueBlock, target);
- jumpToBlock(currentBlock, falseBlock);
- }
-}
-
-void Assembler::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right,
- V4IR::BasicBlock *currentBlock, V4IR::BasicBlock *trueBlock,
- V4IR::BasicBlock *falseBlock)
-{
- if (trueBlock == _nextBlock) {
- Jump target = branch32(invert(cond), left, right);
- addPatch(falseBlock, target);
- } else {
- Jump target = branch32(cond, left, right);
- addPatch(trueBlock, target);
- jumpToBlock(currentBlock, falseBlock);
- }
-}
-
-Assembler::Pointer Assembler::loadTempAddress(RegisterID baseReg, V4IR::Temp *t)
-{
- int32_t offset = 0;
- int scope = t->scope;
- RegisterID context = ContextRegister;
- if (scope) {
- loadPtr(Address(ContextRegister, qOffsetOf(ExecutionContext, outer)), baseReg);
- --scope;
- context = baseReg;
- while (scope) {
- loadPtr(Address(context, qOffsetOf(ExecutionContext, outer)), context);
- --scope;
- }
- }
- switch (t->kind) {
- case V4IR::Temp::Formal:
- case V4IR::Temp::ScopedFormal: {
- loadPtr(Address(context, qOffsetOf(ExecutionContext, callData)), baseReg);
- offset = sizeof(CallData) + (t->index - 1) * sizeof(Value);
- } break;
- case V4IR::Temp::Local:
- case V4IR::Temp::ScopedLocal: {
- loadPtr(Address(context, qOffsetOf(CallContext, locals)), baseReg);
- offset = t->index * sizeof(Value);
- } break;
- case V4IR::Temp::StackSlot: {
- return stackSlotPointer(t);
- } break;
- default:
- Q_UNREACHABLE();
- }
- return Pointer(baseReg, offset);
-}
-
-Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &string)
-{
- loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, 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));
-}
-
-void Assembler::loadStringRef(RegisterID reg, const QString &string)
-{
- loadPtr(Address(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, compilationUnit)), reg);
- loadPtr(Address(reg, qOffsetOf(QV4::CompiledData::CompilationUnit, runtimeStrings)), reg);
- const int id = _isel->registerString(string);
- addPtr(TrustedImmPtr(id * sizeof(QV4::StringValue)), reg);
-}
-
-template <typename Result, typename Source>
-void Assembler::copyValue(Result result, Source source)
-{
-#ifdef VALUE_FITS_IN_REGISTER
- // Use ReturnValueRegister as "scratch" register because loadArgument
- // and storeArgument are functions that may need a scratch register themselves.
- loadArgumentInRegister(source, ReturnValueRegister, 0);
- storeReturnValue(result);
-#else
- loadDouble(source, FPGpr0);
- storeDouble(FPGpr0, result);
-#endif
-}
-
-template <typename Result>
-void Assembler::copyValue(Result result, V4IR::Expr* source)
-{
- if (source->type == V4IR::BoolType) {
- RegisterID reg = toInt32Register(source, ScratchRegister);
- storeBool(reg, result);
- } else if (source->type == V4IR::SInt32Type) {
- RegisterID reg = toInt32Register(source, ScratchRegister);
- storeInt32(reg, result);
- } else if (source->type == V4IR::UInt32Type) {
- RegisterID reg = toUInt32Register(source, ScratchRegister);
- storeUInt32(reg, result);
- } else if (source->type == V4IR::DoubleType) {
- storeDouble(toDoubleRegister(source), result);
- } else if (V4IR::Temp *temp = source->asTemp()) {
-#ifdef VALUE_FITS_IN_REGISTER
- Q_UNUSED(temp);
-
- // Use ReturnValueRegister as "scratch" register because loadArgument
- // and storeArgument are functions that may need a scratch register themselves.
- loadArgumentInRegister(source, ReturnValueRegister, 0);
- storeReturnValue(result);
-#else
- loadDouble(temp, FPGpr0);
- storeDouble(FPGpr0, result);
-#endif
- } else if (V4IR::Const *c = source->asConst()) {
- QV4::Primitive v = convertToValue(c);
- storeValue(v, result);
- } else {
- Q_UNREACHABLE();
- }
-}
-
-void Assembler::storeValue(QV4::Primitive value, V4IR::Temp* destination)
-{
- Address addr = loadTempAddress(ScratchRegister, destination);
- storeValue(value, addr);
-}
-
-void Assembler::enterStandardStackFrame()
-{
- platformEnterStandardStackFrame();
-
- // ### FIXME: Handle through calleeSavedRegisters mechanism
- // or eliminate StackFrameRegister altogether.
- push(StackFrameRegister);
- move(StackPointerRegister, StackFrameRegister);
-
- int frameSize = _stackLayout.calculateStackFrameSize();
-
- subPtr(TrustedImm32(frameSize), StackPointerRegister);
-
- for (int i = 0; i < calleeSavedRegisterCount; ++i)
- storePtr(calleeSavedRegisters[i], 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]);
-
- int frameSize = _stackLayout.calculateStackFrameSize();
- // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't
- // work well for large immediates.
-#if CPU(ARM_THUMB2)
- move(TrustedImm32(frameSize), JSC::ARMRegisters::r3);
- add32(JSC::ARMRegisters::r3, StackPointerRegister);
-#else
- addPtr(TrustedImm32(frameSize), StackPointerRegister);
-#endif
-
- pop(StackFrameRegister);
- platformLeaveStandardStackFrame();
-}
-
-
-
-#define OP(op) \
- { isel_stringIfy(op), op, 0, 0, 0 }
-#define OPCONTEXT(op) \
- { isel_stringIfy(op), 0, op, 0, 0 }
-
-#define INLINE_OP(op, memOp, immOp) \
- { isel_stringIfy(op), op, 0, memOp, immOp }
-#define INLINE_OPCONTEXT(op, memOp, immOp) \
- { isel_stringIfy(op), 0, op, memOp, immOp }
-
-#define NULL_OP \
- { 0, 0, 0, 0, 0 }
-
-const Assembler::BinaryOperationInfo Assembler::binaryOperations[QQmlJS::V4IR::LastAluOp + 1] = {
- NULL_OP, // OpInvalid
- NULL_OP, // OpIfTrue
- NULL_OP, // OpNot
- NULL_OP, // OpUMinus
- NULL_OP, // OpUPlus
- NULL_OP, // OpCompl
- NULL_OP, // OpIncrement
- NULL_OP, // OpDecrement
-
- INLINE_OP(__qmljs_bit_and, &Assembler::inline_and32, &Assembler::inline_and32), // OpBitAnd
- INLINE_OP(__qmljs_bit_or, &Assembler::inline_or32, &Assembler::inline_or32), // OpBitOr
- INLINE_OP(__qmljs_bit_xor, &Assembler::inline_xor32, &Assembler::inline_xor32), // OpBitXor
-
- INLINE_OPCONTEXT(__qmljs_add, &Assembler::inline_add32, &Assembler::inline_add32), // OpAdd
- INLINE_OP(__qmljs_sub, &Assembler::inline_sub32, &Assembler::inline_sub32), // OpSub
- INLINE_OP(__qmljs_mul, &Assembler::inline_mul32, &Assembler::inline_mul32), // OpMul
-
- OP(__qmljs_div), // OpDiv
- OP(__qmljs_mod), // OpMod
-
- INLINE_OP(__qmljs_shl, &Assembler::inline_shl32, &Assembler::inline_shl32), // OpLShift
- INLINE_OP(__qmljs_shr, &Assembler::inline_shr32, &Assembler::inline_shr32), // OpRShift
- INLINE_OP(__qmljs_ushr, &Assembler::inline_ushr32, &Assembler::inline_ushr32), // OpURShift
-
- OP(__qmljs_gt), // OpGt
- OP(__qmljs_lt), // OpLt
- OP(__qmljs_ge), // OpGe
- OP(__qmljs_le), // OpLe
- OP(__qmljs_eq), // OpEqual
- OP(__qmljs_ne), // OpNotEqual
- OP(__qmljs_se), // OpStrictEqual
- OP(__qmljs_sne), // OpStrictNotEqual
-
- OPCONTEXT(__qmljs_instanceof), // OpInstanceof
- OPCONTEXT(__qmljs_in), // OpIn
-
- NULL_OP, // OpAnd
- NULL_OP // OpOr
-};
-
-#if OS(LINUX) || OS(MAC_OS_X)
-static void printDisassembledOutputWithCalls(const char* output, const QHash<void*, const char*>& functions)
-{
- QByteArray processedOutput(output);
- for (QHash<void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
- it != end; ++it) {
- QByteArray ptrString = QByteArray::number(quintptr(it.key()), 16);
- ptrString.prepend("0x");
- processedOutput = processedOutput.replace(ptrString, it.value());
- }
- fprintf(stderr, "%s\n", processedOutput.constData());
-}
-#endif
-
-JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize)
-{
- Label endOfCode = label();
-
- {
- QHashIterator<V4IR::BasicBlock *, QVector<Jump> > it(_patches);
- while (it.hasNext()) {
- it.next();
- V4IR::BasicBlock *block = it.key();
- Label target = _addrs.value(block);
- assert(target.isSet());
- foreach (Jump jump, it.value())
- jump.linkTo(target, this);
- }
- }
-
- JSC::JSGlobalData dummy(_executableAllocator);
- JSC::LinkBuffer linkBuffer(dummy, this, 0);
-
- QHash<void*, const char*> functions;
- foreach (CallToLink ctl, _callsToLink) {
- linkBuffer.link(ctl.call, ctl.externalFunction);
- functions[ctl.externalFunction.value()] = ctl.functionName;
- }
-
- foreach (const DataLabelPatch &p, _dataLabelPatches)
- linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target));
-
- // link exception handlers
- foreach(Jump jump, exceptionPropagationJumps)
- linkBuffer.link(jump, linkBuffer.locationOf(exceptionReturnLabel));
-
- {
- QHashIterator<V4IR::BasicBlock *, QVector<DataLabelPtr> > it(_labelPatches);
- while (it.hasNext()) {
- it.next();
- V4IR::BasicBlock *block = it.key();
- Label target = _addrs.value(block);
- assert(target.isSet());
- foreach (DataLabelPtr label, it.value())
- linkBuffer.patch(label, linkBuffer.locationOf(target));
- }
- }
- _constTable.finalize(linkBuffer, _isel);
-
- *codeSize = linkBuffer.offsetOf(endOfCode);
-
- JSC::MacroAssemblerCodeRef codeRef;
-
- static bool showCode = !qgetenv("QV4_SHOW_ASM").isNull();
- if (showCode) {
-#if OS(LINUX) && !defined(Q_OS_ANDROID)
- char* disasmOutput = 0;
- size_t disasmLength = 0;
- FILE* disasmStream = open_memstream(&disasmOutput, &disasmLength);
- WTF::setDataFile(disasmStream);
-#elif OS(MAC_OS_X)
- struct MemStream {
- QByteArray buf;
- static int write(void *cookie, const char *buf, int len) {
- MemStream *stream = reinterpret_cast<MemStream *>(cookie);
- stream->buf.append(buf, len);
- return len;
- }
- };
- MemStream memStream;
-
- FILE* disasmStream = fwopen(&memStream, MemStream::write);
- WTF::setDataFile(disasmStream);
-#endif
-
- QByteArray name = _function->name->toUtf8();
- if (name.isEmpty()) {
- name = QByteArray::number(quintptr(_function), 16);
- name.prepend("IR::Function(0x");
- name.append(")");
- }
- codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
-
- WTF::setDataFile(stderr);
-#if (OS(LINUX) && !defined(Q_OS_ANDROID)) || OS(MAC_OS_X)
- fclose(disasmStream);
-# if OS(MAC_OS_X)
- char *disasmOutput = memStream.buf.data();
-# endif
-# if CPU(X86) || CPU(X86_64) || CPU(ARM)
- QHash<void*, String*> idents;
- printDisassembledOutputWithCalls(disasmOutput, functions);
-# endif
-# if OS(LINUX)
- free(disasmOutput);
-# endif
-#endif
- } else {
- codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
- }
-
- return codeRef;
-}
-
-InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, Compiler::JSUnitGenerator *jsGenerator)
- : EvalInstructionSelection(execAllocator, module, jsGenerator)
- , _block(0)
- , _as(0)
- , qmlEngine(qmlEngine)
-{
- compilationUnit = new CompilationUnit;
- compilationUnit->codeRefs.resize(module->functions.size());
- compilationUnit->codeSizes.resize(module->functions.size());
-}
-
-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::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::r8
- << JSC::ARMRegisters::r9
- << JSC::ARMRegisters::r10
- << JSC::ARMRegisters::r11;
- return intRegisters;
-}
-
-static QVector<int> getFpRegisters()
-{
- static const QVector<int> fpRegisters = QVector<int>()
- << JSC::ARMRegisters::d1
- << JSC::ARMRegisters::d2
- << JSC::ARMRegisters::d3
- << JSC::ARMRegisters::d4
- << JSC::ARMRegisters::d5
- << JSC::ARMRegisters::d6;
- return fpRegisters;
-}
-#endif
-
-void InstructionSelection::run(int functionIndex)
-{
- V4IR::Function *function = irModule->functions[functionIndex];
- QVector<Lookup> lookups;
- qSwap(_function, function);
-
- V4IR::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 (opt.isInSSA())
- // No register allocator available for this platform, or env. var was set, so:
- opt.convertOutOfSSA();
- ConvertTemps().toStackSlots(_function);
- }
- V4IR::Optimizer::showMeTheCode(_function);
- QSet<V4IR::Jump *> removableJumps = opt.calculateOptionalJumps();
- qSwap(_removableJumps, removableJumps);
-
- Assembler* oldAssembler = _as;
- _as = new Assembler(this, _function, executableAllocator, 6); // 6 == max argc for calls to built-ins with an argument array
-
- _as->enterStandardStackFrame();
-
-#ifdef ARGUMENTS_IN_REGISTERS
- _as->move(_as->registerForArgument(0), Assembler::ContextRegister);
-#else
- _as->loadPtr(addressForArgument(0), Assembler::ContextRegister);
-#endif
-
- const int locals = _as->stackLayout().calculateJSStackFrameSize();
- _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, 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)));
-
- int lastLine = -1;
- for (int i = 0, ei = _function->basicBlocks.size(); i != ei; ++i) {
- V4IR::BasicBlock *nextBlock = (i < ei - 1) ? _function->basicBlocks[i + 1] : 0;
- _block = _function->basicBlocks[i];
- _as->registerBlock(_block, nextBlock);
-
- foreach (V4IR::Stmt *s, _block->statements) {
- if (s->location.isValid()) {
- if (int(s->location.startLine) != lastLine) {
- Assembler::Address lineAddr(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, lineNumber));
- _as->store32(Assembler::TrustedImm32(s->location.startLine), lineAddr);
- lastLine = s->location.startLine;
- }
- }
- s->accept(this);
- }
- }
-
- if (!_as->exceptionReturnLabel.isSet())
- visitRet(0);
-
- JSC::MacroAssemblerCodeRef codeRef =_as->link(&compilationUnit->codeSizes[functionIndex]);
- compilationUnit->codeRefs[functionIndex] = codeRef;
-
- qSwap(_function, function);
- delete _as;
- _as = oldAssembler;
- qSwap(_removableJumps, removableJumps);
-}
-
-void *InstructionSelection::addConstantTable(QVector<Primitive> *values)
-{
- compilationUnit->constantValues.append(*values);
- values->clear();
-
- QVector<QV4::Primitive> &finalValues = compilationUnit->constantValues.last();
- finalValues.squeeze();
- return finalValues.data();
-}
-
-QV4::CompiledData::CompilationUnit *InstructionSelection::backendCompileStep()
-{
- return compilationUnit;
-}
-
-void InstructionSelection::callBuiltinInvalid(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
-{
- prepareCallData(args, 0);
-
- if (useFastLookups && func->global) {
- uint index = registerGlobalGetterLookup(*func->id);
- generateFunctionCall(result, __qmljs_call_global_lookup,
- Assembler::ContextRegister,
- Assembler::TrustedImm32(index),
- baseAddressForCallData());
- } else {
- generateFunctionCall(result, __qmljs_call_activation_property,
- Assembler::ContextRegister,
- Assembler::PointerToString(*func->id),
- baseAddressForCallData());
- }
-}
-
-void InstructionSelection::callBuiltinTypeofMember(V4IR::Expr *base, const QString &name,
- V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_builtin_typeof_member, Assembler::ContextRegister,
- Assembler::PointerToValue(base), Assembler::PointerToString(name));
-}
-
-void InstructionSelection::callBuiltinTypeofSubscript(V4IR::Expr *base, V4IR::Expr *index,
- V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_builtin_typeof_element,
- Assembler::ContextRegister,
- Assembler::PointerToValue(base), Assembler::PointerToValue(index));
-}
-
-void InstructionSelection::callBuiltinTypeofName(const QString &name, V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_builtin_typeof_name, Assembler::ContextRegister,
- Assembler::PointerToString(name));
-}
-
-void InstructionSelection::callBuiltinTypeofValue(V4IR::Expr *value, V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_builtin_typeof, Assembler::ContextRegister,
- Assembler::PointerToValue(value));
-}
-
-void InstructionSelection::callBuiltinDeleteMember(V4IR::Temp *base, const QString &name, V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_delete_member, Assembler::ContextRegister,
- Assembler::Reference(base), Assembler::PointerToString(name));
-}
-
-void InstructionSelection::callBuiltinDeleteSubscript(V4IR::Temp *base, V4IR::Expr *index,
- V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_delete_subscript, Assembler::ContextRegister,
- Assembler::Reference(base), Assembler::PointerToValue(index));
-}
-
-void InstructionSelection::callBuiltinDeleteName(const QString &name, V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_delete_name, Assembler::ContextRegister,
- Assembler::PointerToString(name));
-}
-
-void InstructionSelection::callBuiltinDeleteValue(V4IR::Temp *result)
-{
- _as->storeValue(Primitive::fromBoolean(false), result);
-}
-
-void InstructionSelection::callBuiltinThrow(V4IR::Expr *arg)
-{
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_throw, Assembler::ContextRegister,
- Assembler::PointerToValue(arg));
-}
-
-void InstructionSelection::callBuiltinReThrow()
-{
- _as->jumpToExceptionHandler();
-}
-
-void InstructionSelection::callBuiltinUnwindException(V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_builtin_unwind_exception, Assembler::ContextRegister);
-
-}
-
-void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName)
-{
- Assembler::Pointer s = _as->loadStringAddress(Assembler::ScratchRegister, exceptionName);
- generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_catch_scope, Assembler::ContextRegister, s);
-}
-
-void InstructionSelection::callBuiltinForeachIteratorObject(V4IR::Temp *arg, V4IR::Temp *result)
-{
- Q_ASSERT(arg);
- Q_ASSERT(result);
-
- generateFunctionCall(result, __qmljs_foreach_iterator_object, Assembler::ContextRegister, Assembler::Reference(arg));
-}
-
-void InstructionSelection::callBuiltinForeachNextPropertyname(V4IR::Temp *arg, V4IR::Temp *result)
-{
- Q_ASSERT(arg);
- Q_ASSERT(result);
-
- generateFunctionCall(result, __qmljs_foreach_next_property_name, Assembler::Reference(arg));
-}
-
-void InstructionSelection::callBuiltinPushWithScope(V4IR::Temp *arg)
-{
- Q_ASSERT(arg);
-
- generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with_scope, Assembler::Reference(arg), Assembler::ContextRegister);
-}
-
-void InstructionSelection::callBuiltinPopScope()
-{
- generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_scope, Assembler::ContextRegister);
-}
-
-void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
-{
- generateFunctionCall(Assembler::Void, __qmljs_builtin_declare_var, Assembler::ContextRegister,
- Assembler::TrustedImm32(deletable), Assembler::PointerToString(name));
-}
-
-void InstructionSelection::callBuiltinDefineGetterSetter(V4IR::Temp *object, const QString &name, V4IR::Temp *getter, V4IR::Temp *setter)
-{
- Q_ASSERT(object);
- Q_ASSERT(getter);
- Q_ASSERT(setter);
- generateFunctionCall(Assembler::Void, __qmljs_builtin_define_getter_setter, Assembler::ContextRegister,
- Assembler::Reference(object), Assembler::PointerToString(name), Assembler::PointerToValue(getter), Assembler::PointerToValue(setter));
-}
-
-void InstructionSelection::callBuiltinDefineProperty(V4IR::Temp *object, const QString &name,
- V4IR::Expr *value)
-{
- Q_ASSERT(object);
- Q_ASSERT(value->asTemp() || value->asConst());
-
- generateFunctionCall(Assembler::Void, __qmljs_builtin_define_property,
- Assembler::ContextRegister, Assembler::Reference(object),
- Assembler::PointerToString(name),
- Assembler::PointerToValue(value));
-}
-
-void InstructionSelection::callBuiltinDefineArray(V4IR::Temp *result, V4IR::ExprList *args)
-{
- Q_ASSERT(result);
-
- int length = prepareVariableArguments(args);
- generateFunctionCall(result, __qmljs_builtin_define_array, Assembler::ContextRegister,
- baseAddressForCallArguments(), Assembler::TrustedImm32(length));
-}
-
-void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4IR::ExprList *args)
-{
- Q_ASSERT(result);
-
- int argc = 0;
-
- const int classId = registerJSClass(args);
-
- V4IR::ExprList *it = args;
- while (it) {
- it = it->next;
-
- bool isData = it->expr->asConst()->value;
- it = it->next;
-
- _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr);
-
- if (!isData) {
- it = it->next;
- _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr);
- }
-
- it = it->next;
- }
-
- generateFunctionCall(result, __qmljs_builtin_define_object_literal, Assembler::ContextRegister,
- baseAddressForCallArguments(), Assembler::TrustedImm32(classId));
-}
-
-void InstructionSelection::callBuiltinSetupArgumentObject(V4IR::Temp *result)
-{
- generateFunctionCall(result, __qmljs_builtin_setup_arguments_object, Assembler::ContextRegister);
-}
-
-void InstructionSelection::callBuiltinConvertThisToObject()
-{
- generateFunctionCall(Assembler::Void, __qmljs_builtin_convert_this_to_object, Assembler::ContextRegister);
-}
-
-void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
-{
- Q_ASSERT(value);
-
- prepareCallData(args, 0);
- generateFunctionCall(result, __qmljs_call_value, Assembler::ContextRegister,
- Assembler::Reference(value),
- baseAddressForCallData());
-}
-
-void InstructionSelection::loadThisObject(V4IR::Temp *temp)
-{
- _as->loadPtr(Address(Assembler::ContextRegister, qOffsetOf(ExecutionContext, callData)), Assembler::ScratchRegister);
-#if defined(VALUE_FITS_IN_REGISTER)
- _as->load64(Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject)),
- Assembler::ReturnValueRegister);
- _as->storeReturnValue(temp);
-#else
- _as->copyValue(temp, Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject)));
-#endif
-}
-
-void InstructionSelection::loadQmlIdArray(V4IR::Temp *temp)
-{
- generateFunctionCall(temp, __qmljs_get_id_array, Assembler::ContextRegister);
-}
-
-void InstructionSelection::loadQmlImportedScripts(V4IR::Temp *temp)
-{
- generateFunctionCall(temp, __qmljs_get_imported_scripts, Assembler::ContextRegister);
-}
-
-void InstructionSelection::loadQmlContextObject(V4IR::Temp *temp)
-{
- generateFunctionCall(temp, __qmljs_get_context_object, Assembler::ContextRegister);
-}
-
-void InstructionSelection::loadQmlScopeObject(V4IR::Temp *temp)
-{
- generateFunctionCall(temp, __qmljs_get_scope_object, Assembler::ContextRegister);
-}
-
-void InstructionSelection::loadQmlSingleton(const QString &name, V4IR::Temp *temp)
-{
- generateFunctionCall(temp, __qmljs_get_qml_singleton, Assembler::ContextRegister, Assembler::PointerToString(name));
-}
-
-void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp)
-{
- if (targetTemp->kind == V4IR::Temp::PhysicalRegister) {
- if (targetTemp->type == V4IR::DoubleType) {
- Q_ASSERT(sourceConst->type == V4IR::DoubleType);
- _as->toDoubleRegister(sourceConst, (Assembler::FPRegisterID) targetTemp->index);
- } else if (targetTemp->type == V4IR::SInt32Type) {
- Q_ASSERT(sourceConst->type == V4IR::SInt32Type);
- _as->toInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index);
- } else if (targetTemp->type == V4IR::UInt32Type) {
- Q_ASSERT(sourceConst->type == V4IR::UInt32Type);
- _as->toUInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index);
- } else if (targetTemp->type == V4IR::BoolType) {
- Q_ASSERT(sourceConst->type == V4IR::BoolType);
- _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32),
- (Assembler::RegisterID) targetTemp->index);
- } else {
- Q_UNREACHABLE();
- }
- } else {
- _as->storeValue(convertToValue(sourceConst), targetTemp);
- }
-}
-
-void InstructionSelection::loadString(const QString &str, V4IR::Temp *targetTemp)
-{
- Pointer srcAddr = _as->loadStringAddress(Assembler::ReturnValueRegister, str);
- _as->loadPtr(srcAddr, Assembler::ReturnValueRegister);
- Pointer destAddr = _as->loadTempAddress(Assembler::ScratchRegister, targetTemp);
-#if QT_POINTER_SIZE == 8
- _as->store64(Assembler::ReturnValueRegister, destAddr);
-#else
- _as->store32(Assembler::ReturnValueRegister, destAddr);
- destAddr.offset += 4;
- _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type), destAddr);
-#endif
-}
-
-void InstructionSelection::loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp)
-{
- int id = registerRegExp(sourceRegexp);
- generateFunctionCall(targetTemp, __qmljs_lookup_runtime_regexp, Assembler::ContextRegister, Assembler::TrustedImm32(id));
-}
-
-void InstructionSelection::getActivationProperty(const V4IR::Name *name, V4IR::Temp *temp)
-{
- if (useFastLookups && name->global) {
- uint index = registerGlobalGetterLookup(*name->id);
- generateLookupCall(temp, index, qOffsetOf(QV4::Lookup, globalGetter), Assembler::ContextRegister, Assembler::Void);
- return;
- }
- generateFunctionCall(temp, __qmljs_get_activation_property, Assembler::ContextRegister, Assembler::PointerToString(*name->id));
-}
-
-void InstructionSelection::setActivationProperty(V4IR::Expr *source, const QString &targetName)
-{
- // ### should use a lookup call here
- generateFunctionCall(Assembler::Void, __qmljs_set_activation_property,
- Assembler::ContextRegister, Assembler::PointerToString(targetName), Assembler::PointerToValue(source));
-}
-
-void InstructionSelection::initClosure(V4IR::Closure *closure, V4IR::Temp *target)
-{
- int id = closure->value;
- generateFunctionCall(target, __qmljs_init_closure, Assembler::ContextRegister, Assembler::TrustedImm32(id));
-}
-
-void InstructionSelection::getProperty(V4IR::Expr *base, const QString &name, V4IR::Temp *target)
-{
- if (useFastLookups) {
- uint index = registerGetterLookup(name);
- generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), Assembler::PointerToValue(base), Assembler::Void);
- } else {
- generateFunctionCall(target, __qmljs_get_property, Assembler::ContextRegister,
- Assembler::PointerToValue(base), Assembler::PointerToString(name));
- }
-}
-
-void InstructionSelection::getQObjectProperty(V4IR::Expr *base, int propertyIndex, bool captureRequired, int attachedPropertiesId, V4IR::Temp *target)
-{
- if (attachedPropertiesId != 0)
- generateFunctionCall(target, __qmljs_get_attached_property, Assembler::ContextRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex));
- else
- generateFunctionCall(target, __qmljs_get_qobject_property, Assembler::ContextRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex),
- Assembler::TrustedImm32(captureRequired));
-}
-
-void InstructionSelection::setProperty(V4IR::Expr *source, V4IR::Expr *targetBase,
- const QString &targetName)
-{
- if (useFastLookups) {
- uint index = registerSetterLookup(targetName);
- generateLookupCall(Assembler::Void, index, qOffsetOf(QV4::Lookup, setter),
- Assembler::PointerToValue(targetBase),
- Assembler::PointerToValue(source));
- } else {
- generateFunctionCall(Assembler::Void, __qmljs_set_property, Assembler::ContextRegister,
- Assembler::PointerToValue(targetBase), Assembler::PointerToString(targetName),
- Assembler::PointerToValue(source));
- }
-}
-
-void InstructionSelection::setQObjectProperty(V4IR::Expr *source, V4IR::Expr *targetBase, int propertyIndex)
-{
- generateFunctionCall(Assembler::Void, __qmljs_set_qobject_property, Assembler::ContextRegister, Assembler::PointerToValue(targetBase),
- Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source));
-}
-
-void InstructionSelection::getElement(V4IR::Expr *base, V4IR::Expr *index, V4IR::Temp *target)
-{
- if (useFastLookups) {
- uint lookup = registerIndexedGetterLookup();
- generateLookupCall(target, lookup, qOffsetOf(QV4::Lookup, indexedGetter),
- Assembler::PointerToValue(base),
- Assembler::PointerToValue(index));
- return;
- }
-
- generateFunctionCall(target, __qmljs_get_element, Assembler::ContextRegister,
- Assembler::PointerToValue(base), Assembler::PointerToValue(index));
-}
-
-void InstructionSelection::setElement(V4IR::Expr *source, V4IR::Expr *targetBase, V4IR::Expr *targetIndex)
-{
- if (useFastLookups) {
- uint lookup = registerIndexedSetterLookup();
- generateLookupCall(Assembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter),
- Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex),
- Assembler::PointerToValue(source));
- return;
- }
- generateFunctionCall(Assembler::Void, __qmljs_set_element, Assembler::ContextRegister,
- Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex),
- Assembler::PointerToValue(source));
-}
-
-void InstructionSelection::copyValue(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
-{
- if (*sourceTemp == *targetTemp)
- return;
-
- if (sourceTemp->kind == V4IR::Temp::PhysicalRegister) {
- if (targetTemp->kind == V4IR::Temp::PhysicalRegister) {
- if (sourceTemp->type == V4IR::DoubleType)
- _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index,
- (Assembler::FPRegisterID) targetTemp->index);
- else
- _as->move((Assembler::RegisterID) sourceTemp->index,
- (Assembler::RegisterID) targetTemp->index);
- return;
- } else {
- switch (sourceTemp->type) {
- case V4IR::DoubleType:
- _as->storeDouble((Assembler::FPRegisterID) sourceTemp->index, targetTemp);
- break;
- case V4IR::SInt32Type:
- _as->storeInt32((Assembler::RegisterID) sourceTemp->index, targetTemp);
- break;
- case V4IR::UInt32Type:
- _as->storeUInt32((Assembler::RegisterID) sourceTemp->index, targetTemp);
- break;
- case V4IR::BoolType:
- _as->storeBool((Assembler::RegisterID) sourceTemp->index, targetTemp);
- break;
- default:
- Q_ASSERT(!"Unreachable");
- break;
- }
- return;
- }
- } else if (targetTemp->kind == V4IR::Temp::PhysicalRegister) {
- switch (targetTemp->type) {
- case V4IR::DoubleType:
- Q_ASSERT(sourceTemp->type == V4IR::DoubleType);
- _as->toDoubleRegister(sourceTemp, (Assembler::FPRegisterID) targetTemp->index);
- return;
- case V4IR::BoolType:
- Q_ASSERT(sourceTemp->type == V4IR::BoolType);
- _as->toInt32Register(sourceTemp, (Assembler::RegisterID) targetTemp->index);
- return;
- case V4IR::SInt32Type:
- Q_ASSERT(sourceTemp->type == V4IR::SInt32Type);
- _as->toInt32Register(sourceTemp, (Assembler::RegisterID) targetTemp->index);
- return;
- case V4IR::UInt32Type:
- Q_ASSERT(sourceTemp->type == V4IR::UInt32Type);
- _as->toUInt32Register(sourceTemp, (Assembler::RegisterID) targetTemp->index);
- return;
- default:
- Q_ASSERT(!"Unreachable");
- break;
- }
- }
-
- // 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);
-}
-
-void InstructionSelection::swapValues(V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
-{
- if (sourceTemp->kind == V4IR::Temp::PhysicalRegister) {
- if (targetTemp->kind == V4IR::Temp::PhysicalRegister) {
- Q_ASSERT(sourceTemp->type == targetTemp->type);
-
- if (sourceTemp->type == V4IR::DoubleType) {
- _as->moveDouble((Assembler::FPRegisterID) targetTemp->index, Assembler::FPGpr0);
- _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index,
- (Assembler::FPRegisterID) targetTemp->index);
- _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) sourceTemp->index);
- } else {
- _as->swap((Assembler::RegisterID) sourceTemp->index,
- (Assembler::RegisterID) targetTemp->index);
- }
- return;
- }
- } else if (sourceTemp->kind == V4IR::Temp::StackSlot) {
- if (targetTemp->kind == V4IR::Temp::StackSlot) {
- // Note: a swap for two stack-slots can involve different types.
-#if CPU(X86_64)
- _as->load64(_as->stackSlotPointer(targetTemp), Assembler::ReturnValueRegister);
- _as->load64(_as->stackSlotPointer(sourceTemp), Assembler::ScratchRegister);
- _as->store64(Assembler::ScratchRegister, _as->stackSlotPointer(targetTemp));
- _as->store64(Assembler::ReturnValueRegister, _as->stackSlotPointer(sourceTemp));
-#else
- Assembler::FPRegisterID tReg = _as->toDoubleRegister(targetTemp);
- Assembler::Pointer sAddr = _as->stackSlotPointer(sourceTemp);
- Assembler::Pointer tAddr = _as->stackSlotPointer(targetTemp);
- _as->load32(sAddr, Assembler::ScratchRegister);
- _as->store32(Assembler::ScratchRegister, tAddr);
- sAddr.offset += 4;
- tAddr.offset += 4;
- _as->load32(sAddr, Assembler::ScratchRegister);
- _as->store32(Assembler::ScratchRegister, tAddr);
- _as->storeDouble(tReg, _as->stackSlotPointer(sourceTemp));
-#endif
- return;
- }
- }
-
- V4IR::Temp *stackTemp = sourceTemp->kind == V4IR::Temp::StackSlot ? sourceTemp : targetTemp;
- V4IR::Temp *registerTemp = sourceTemp->kind == V4IR::Temp::PhysicalRegister ? sourceTemp
- : targetTemp;
- Assembler::Pointer addr = _as->stackSlotPointer(stackTemp);
- if (registerTemp->type == V4IR::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 == V4IR::UInt32Type) {
- _as->toUInt32Register(addr, Assembler::ScratchRegister);
- _as->storeUInt32((Assembler::RegisterID) registerTemp->index, addr);
- _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) registerTemp->index);
- } else {
- _as->load32(addr, Assembler::ScratchRegister);
- _as->store32((Assembler::RegisterID) registerTemp->index, addr);
- if (registerTemp->type != stackTemp->type) {
- addr.offset += 4;
- quint32 tag;
- switch (registerTemp->type) {
- case V4IR::BoolType:
- tag = QV4::Value::_Boolean_Type;
- break;
- case V4IR::SInt32Type:
- tag = QV4::Value::_Integer_Type;
- break;
- default:
- tag = QV4::Value::Undefined_Type;
- Q_UNREACHABLE();
- }
- _as->store32(Assembler::TrustedImm32(tag), addr);
- }
- _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) registerTemp->index);
- }
-}
-
-#define setOp(op, opName, operation) \
- do { op = operation; opName = isel_stringIfy(operation); } while (0)
-#define setOpContext(op, opName, operation) \
- do { opContext = operation; opName = isel_stringIfy(operation); } while (0)
-
-void InstructionSelection::unop(V4IR::AluOp oper, V4IR::Temp *sourceTemp, V4IR::Temp *targetTemp)
-{
- UnaryOpName op = 0;
- const char *opName = 0;
- switch (oper) {
- case V4IR::OpIfTrue: assert(!"unreachable"); break;
- case V4IR::OpNot:
- if (sourceTemp->type == V4IR::BoolType && targetTemp->type == V4IR::BoolType) {
- Assembler::RegisterID tReg = Assembler::ScratchRegister;
- if (targetTemp->kind == V4IR::Temp::PhysicalRegister)
- tReg = (Assembler::RegisterID) targetTemp->index;
- _as->xor32(Assembler::TrustedImm32(0x1),
- _as->toInt32Register(sourceTemp, Assembler::ScratchRegister),
- tReg);
- if (targetTemp->kind != V4IR::Temp::PhysicalRegister)
- _as->storeBool(tReg, targetTemp);
- return;
- } else {
- setOp(op, opName, __qmljs_not); break;
- }
- case V4IR::OpUMinus: setOp(op, opName, __qmljs_uminus); break;
- case V4IR::OpUPlus: setOp(op, opName, __qmljs_uplus); break;
- case V4IR::OpCompl: setOp(op, opName, __qmljs_compl); break;
- case V4IR::OpIncrement: setOp(op, opName, __qmljs_increment); break;
- case V4IR::OpDecrement: setOp(op, opName, __qmljs_decrement); break;
- default: assert(!"unreachable"); break;
- } // switch
-
- if (op) {
- _as->generateFunctionCallImp(targetTemp, opName, op,
- Assembler::PointerToValue(sourceTemp));
- }
-}
-
-static inline Assembler::FPRegisterID getFreeFPReg(V4IR::Expr *shouldNotOverlap, unsigned hint)
-{
- if (V4IR::Temp *t = shouldNotOverlap->asTemp())
- if (t->type == V4IR::DoubleType)
- if (t->kind == V4IR::Temp::PhysicalRegister)
- if (t->index == hint)
- return Assembler::FPRegisterID(hint + 1);
- return Assembler::FPRegisterID(hint);
-}
-
-Assembler::Jump InstructionSelection::genInlineBinop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target)
-{
- Assembler::Jump done;
-
- // Try preventing a call for a few common binary operations. This is used in two cases:
- // - no register allocation was performed (not available for the platform, or the IR was
- // not transformed into SSA)
- // - type inference found that either or both operands can be of non-number type, and the
- // register allocator will have prepared for a call (meaning: all registers that do not
- // hold operands are spilled to the stack, which makes them available here)
- // Note: FPGPr0 can still not be used, because uint32->double conversion uses it as a scratch
- // register.
- switch (oper) {
- case V4IR::OpAdd: {
- Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2);
- Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4);
- Assembler::Jump leftIsNoDbl = genTryDoubleConversion(leftSource, lReg);
- Assembler::Jump rightIsNoDbl = genTryDoubleConversion(rightSource, rReg);
-
- _as->addDouble(rReg, lReg);
- _as->storeDouble(lReg, target);
- done = _as->jump();
-
- if (leftIsNoDbl.isSet())
- leftIsNoDbl.link(_as);
- if (rightIsNoDbl.isSet())
- rightIsNoDbl.link(_as);
- } break;
- case V4IR::OpMul: {
- Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2);
- Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4);
- Assembler::Jump leftIsNoDbl = genTryDoubleConversion(leftSource, lReg);
- Assembler::Jump rightIsNoDbl = genTryDoubleConversion(rightSource, rReg);
-
- _as->mulDouble(rReg, lReg);
- _as->storeDouble(lReg, target);
- done = _as->jump();
-
- if (leftIsNoDbl.isSet())
- leftIsNoDbl.link(_as);
- if (rightIsNoDbl.isSet())
- rightIsNoDbl.link(_as);
- } break;
- case V4IR::OpSub: {
- Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2);
- Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4);
- Assembler::Jump leftIsNoDbl = genTryDoubleConversion(leftSource, lReg);
- Assembler::Jump rightIsNoDbl = genTryDoubleConversion(rightSource, rReg);
-
- _as->subDouble(rReg, lReg);
- _as->storeDouble(lReg, target);
- done = _as->jump();
-
- if (leftIsNoDbl.isSet())
- leftIsNoDbl.link(_as);
- if (rightIsNoDbl.isSet())
- rightIsNoDbl.link(_as);
- } break;
- case V4IR::OpDiv: {
- Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2);
- Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4);
- Assembler::Jump leftIsNoDbl = genTryDoubleConversion(leftSource, lReg);
- Assembler::Jump rightIsNoDbl = genTryDoubleConversion(rightSource, rReg);
-
- _as->divDouble(rReg, lReg);
- _as->storeDouble(lReg, target);
- done = _as->jump();
-
- if (leftIsNoDbl.isSet())
- leftIsNoDbl.link(_as);
- if (rightIsNoDbl.isSet())
- rightIsNoDbl.link(_as);
- } break;
- default:
- break;
- }
-
- return done;
-}
-
-void InstructionSelection::binop(V4IR::AluOp oper, V4IR::Expr *leftSource, V4IR::Expr *rightSource, V4IR::Temp *target)
-{
- if (oper != V4IR::OpMod
- && leftSource->type == V4IR::DoubleType && rightSource->type == V4IR::DoubleType
- && isPregOrConst(leftSource) && isPregOrConst(rightSource)) {
- doubleBinop(oper, leftSource, rightSource, target);
- return;
- }
- if (leftSource->type == V4IR::SInt32Type && rightSource->type == V4IR::SInt32Type) {
- if (int32Binop(oper, leftSource, rightSource, target))
- return;
- }
-
- Assembler::Jump done;
- if (leftSource->type != V4IR::StringType && rightSource->type != V4IR::StringType)
- done = genInlineBinop(oper, leftSource, rightSource, target);
-
- // TODO: inline var===null and var!==null
- Assembler::BinaryOperationInfo info = Assembler::binaryOperation(oper);
-
- if (oper == V4IR::OpAdd &&
- (leftSource->type == V4IR::StringType || rightSource->type == V4IR::StringType)) {
- const Assembler::BinaryOperationInfo stringAdd = OPCONTEXT(__qmljs_add_string);
- info = stringAdd;
- }
-
- if (info.fallbackImplementation) {
- _as->generateFunctionCallImp(target, info.name, info.fallbackImplementation,
- Assembler::PointerToValue(leftSource),
- Assembler::PointerToValue(rightSource));
- } else if (info.contextImplementation) {
- _as->generateFunctionCallImp(target, info.name, info.contextImplementation,
- Assembler::ContextRegister,
- Assembler::PointerToValue(leftSource),
- Assembler::PointerToValue(rightSource));
- } else {
- assert(!"unreachable");
- }
-
- if (done.isSet())
- done.link(_as);
-}
-
-void InstructionSelection::callProperty(V4IR::Expr *base, const QString &name, V4IR::ExprList *args,
- V4IR::Temp *result)
-{
- assert(base != 0);
-
- prepareCallData(args, base);
-
- if (useFastLookups) {
- uint index = registerGetterLookup(name);
- generateFunctionCall(result, __qmljs_call_property_lookup,
- Assembler::ContextRegister,
- Assembler::TrustedImm32(index),
- baseAddressForCallData());
- } else
- {
- generateFunctionCall(result, __qmljs_call_property, Assembler::ContextRegister,
- Assembler::PointerToString(name),
- baseAddressForCallData());
- }
-}
-
-void InstructionSelection::callSubscript(V4IR::Expr *base, V4IR::Expr *index, V4IR::ExprList *args,
- V4IR::Temp *result)
-{
- assert(base != 0);
-
- prepareCallData(args, base);
- generateFunctionCall(result, __qmljs_call_element, Assembler::ContextRegister,
- Assembler::PointerToValue(index),
- baseAddressForCallData());
-}
-
-void InstructionSelection::convertType(V4IR::Temp *source, V4IR::Temp *target)
-{
- switch (target->type) {
- case V4IR::DoubleType:
- convertTypeToDouble(source, target);
- break;
- case V4IR::BoolType:
- convertTypeToBool(source, target);
- break;
- case V4IR::SInt32Type:
- convertTypeToSInt32(source, target);
- break;
- case V4IR::UInt32Type:
- convertTypeToUInt32(source, target);
- break;
- default:
- convertTypeSlowPath(source, target);
- break;
- }
-}
-
-void InstructionSelection::convertTypeSlowPath(V4IR::Temp *source, V4IR::Temp *target)
-{
- Q_ASSERT(target->type != V4IR::BoolType);
-
- if (target->type & V4IR::NumberType)
- unop(V4IR::OpUPlus, source, target);
- else
- copyValue(source, target);
-}
-
-void InstructionSelection::convertTypeToDouble(V4IR::Temp *source, V4IR::Temp *target)
-{
- switch (source->type) {
- case V4IR::SInt32Type:
- case V4IR::BoolType:
- case V4IR::NullType:
- convertIntToDouble(source, target);
- break;
- case V4IR::UInt32Type:
- convertUIntToDouble(source, target);
- break;
- case V4IR::UndefinedType:
- _as->loadDouble(_as->loadTempAddress(Assembler::ScratchRegister, source), Assembler::FPGpr0);
- _as->storeDouble(Assembler::FPGpr0, target);
- break;
- case V4IR::StringType:
- case V4IR::VarType: {
- // load the tag:
- Assembler::Pointer tagAddr = _as->loadTempAddress(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));
- convertIntToDouble(source, target);
- Assembler::Jump intDone = _as->jump();
-
- // not an int, check if it's NOT a double:
- isNoInt.link(_as);
-#if QT_POINTER_SIZE == 8
- _as->and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister);
- Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister,
- Assembler::TrustedImm32(0));
-#else
- _as->and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister);
- Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister,
- Assembler::TrustedImm32(Value::NotDouble_Mask));
-#endif
-
- generateFunctionCall(target, __qmljs_value_to_double, Assembler::PointerToValue(source));
- Assembler::Jump noDoubleDone = _as->jump();
-
- // it is a double:
- isDbl.link(_as);
- Assembler::Pointer addr2 = _as->loadTempAddress(Assembler::ScratchRegister, source);
- if (target->kind == V4IR::Temp::StackSlot) {
-#if QT_POINTER_SIZE == 8
- _as->load64(addr2, Assembler::ScratchRegister);
- _as->store64(Assembler::ScratchRegister, _as->stackSlotPointer(target));
-#else
- _as->loadDouble(addr2, Assembler::FPGpr0);
- _as->storeDouble(Assembler::FPGpr0, _as->stackSlotPointer(target));
-#endif
- } else {
- _as->loadDouble(addr2, (Assembler::FPRegisterID) target->index);
- }
-
- noDoubleDone.link(_as);
- intDone.link(_as);
- } break;
- default:
- convertTypeSlowPath(source, target);
- break;
- }
-}
-
-void InstructionSelection::convertTypeToBool(V4IR::Temp *source, V4IR::Temp *target)
-{
- switch (source->type) {
- case V4IR::SInt32Type:
- case V4IR::UInt32Type:
- convertIntToBool(source, target);
- break;
- case V4IR::DoubleType: {
- // The source is in a register if the register allocator is used. If the register
- // 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 == V4IR::Temp::PhysicalRegister)
- reg = (Assembler::FPRegisterID) source->index;
- else
- reg = _as->toDoubleRegister(source, (Assembler::FPRegisterID) 1);
- Assembler::Jump nonZero = _as->branchDoubleNonZero(reg, Assembler::FPGpr0);
-
- // it's 0, so false:
- _as->storeBool(false, target);
- Assembler::Jump done = _as->jump();
-
- // it's non-zero, so true:
- nonZero.link(_as);
- _as->storeBool(true, target);
-
- // done:
- done.link(_as);
- } break;
- case V4IR::UndefinedType:
- case V4IR::NullType:
- _as->storeBool(false, target);
- break;
- case V4IR::StringType:
- case V4IR::VarType:
- default:
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean,
- Assembler::PointerToValue(source));
- _as->storeBool(Assembler::ReturnValueRegister, target);
- break;
- }
-}
-
-void InstructionSelection::convertTypeToSInt32(V4IR::Temp *source, V4IR::Temp *target)
-{
- switch (source->type) {
- case V4IR::VarType: {
-
-#if QT_POINTER_SIZE == 8
- Assembler::Pointer addr = _as->loadTempAddress(Assembler::ScratchRegister, source);
- _as->load64(addr, Assembler::ScratchRegister);
- _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
-
- // check if it's a number
- _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsNumber_Shift), Assembler::ScratchRegister);
- Assembler::Jump isInt = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(1));
- Assembler::Jump fallback = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(0));
-
- // it's a double
- _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister);
- _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
- _as->move64ToDouble(Assembler::ReturnValueRegister, Assembler::FPGpr0);
- Assembler::Jump success =
- _as->branchTruncateDoubleToInt32(Assembler::FPGpr0, Assembler::ReturnValueRegister,
- Assembler::BranchIfTruncateSuccessful);
-
- // not an int:
- fallback.link(_as);
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_value_to_int32,
- _as->loadTempAddress(Assembler::ScratchRegister, source));
-
- isInt.link(_as);
- success.link(_as);
- if (target->kind == V4IR::Temp::StackSlot) {
- Assembler::Pointer targetAddr = _as->stackSlotPointer(target);
- _as->store32(Assembler::ReturnValueRegister, targetAddr);
- targetAddr.offset += 4;
- _as->store32(Assembler::TrustedImm32(Value::_Integer_Type), targetAddr);
- } else {
- _as->storeInt32(Assembler::ReturnValueRegister, target);
- }
-#else
- // load the tag:
- Assembler::Pointer addr = _as->loadTempAddress(Assembler::ScratchRegister, source);
- Assembler::Pointer tagAddr = addr;
- tagAddr.offset += 4;
- _as->load32(tagAddr, Assembler::ReturnValueRegister);
-
- // check if it's an int32:
- Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister,
- Assembler::TrustedImm32(Value::_Integer_Type));
- if (target->kind == V4IR::Temp::StackSlot) {
- _as->load32(addr, Assembler::ScratchRegister);
- Assembler::Pointer targetAddr = _as->stackSlotPointer(target);
- _as->store32(Assembler::ScratchRegister, targetAddr);
- targetAddr.offset += 4;
- _as->store32(Assembler::TrustedImm32(Value::_Integer_Type), targetAddr);
- } else {
- _as->load32(addr, (Assembler::RegisterID) target->index);
- }
- Assembler::Jump intDone = _as->jump();
-
- // not an int:
- fallback.link(_as);
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_value_to_int32,
- _as->loadTempAddress(Assembler::ScratchRegister, source));
- _as->storeInt32(Assembler::ReturnValueRegister, target);
-
- intDone.link(_as);
-#endif
-
- } break;
- case V4IR::DoubleType: {
- Assembler::Jump success =
- _as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source),
- Assembler::ReturnValueRegister,
- Assembler::BranchIfTruncateSuccessful);
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_double_to_int32,
- Assembler::PointerToValue(source));
- success.link(_as);
- _as->storeInt32(Assembler::ReturnValueRegister, target);
- } break;
- case V4IR::UInt32Type:
- _as->storeInt32(_as->toUInt32Register(source, Assembler::ReturnValueRegister), target);
- break;
- case V4IR::NullType:
- case V4IR::UndefinedType:
- _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister);
- _as->storeInt32(Assembler::ReturnValueRegister, target);
- break;
- case V4IR::BoolType:
- _as->storeInt32(_as->toInt32Register(source, Assembler::ReturnValueRegister), target);
- break;
- case V4IR::StringType:
- default:
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_value_to_int32,
- _as->loadTempAddress(Assembler::ScratchRegister, source));
- _as->storeInt32(Assembler::ReturnValueRegister, target);
- break;
- } // switch (source->type)
-}
-
-void InstructionSelection::convertTypeToUInt32(V4IR::Temp *source, V4IR::Temp *target)
-{
- switch (source->type) {
- case V4IR::VarType: {
- // load the tag:
- Assembler::Pointer tagAddr = _as->loadTempAddress(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);
- _as->storeUInt32(_as->toInt32Register(addr, Assembler::ScratchRegister), target);
- Assembler::Jump intDone = _as->jump();
-
- // not an int:
- isNoInt.link(_as);
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_value_to_uint32,
- _as->loadTempAddress(Assembler::ScratchRegister, source));
- _as->storeInt32(Assembler::ReturnValueRegister, target);
-
- intDone.link(_as);
- } break;
- case V4IR::DoubleType: {
- Assembler::FPRegisterID reg = _as->toDoubleRegister(source);
- Assembler::Jump success =
- _as->branchTruncateDoubleToUint32(reg, Assembler::ReturnValueRegister,
- Assembler::BranchIfTruncateSuccessful);
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_double_to_uint32,
- Assembler::PointerToValue(source));
- success.link(_as);
- _as->storeUInt32(Assembler::ReturnValueRegister, target);
- } break;
- case V4IR::NullType:
- case V4IR::UndefinedType:
- _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister);
- _as->storeUInt32(Assembler::ReturnValueRegister, target);
- break;
- case V4IR::StringType:
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_value_to_uint32,
- Assembler::PointerToValue(source));
- _as->storeUInt32(Assembler::ReturnValueRegister, target);
- break;
- case V4IR::SInt32Type:
- case V4IR::BoolType:
- _as->storeUInt32(_as->toInt32Register(source, Assembler::ReturnValueRegister), target);
- break;
- default:
- break;
- } // switch (source->type)
-}
-
-void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::ExprList *args, V4IR::Temp *result)
-{
- assert(func != 0);
- prepareCallData(args, 0);
-
- if (useFastLookups && func->global) {
- uint index = registerGlobalGetterLookup(*func->id);
- generateFunctionCall(result, __qmljs_construct_global_lookup,
- Assembler::ContextRegister,
- Assembler::TrustedImm32(index), baseAddressForCallData());
- return;
- }
-
- generateFunctionCall(result, __qmljs_construct_activation_property,
- Assembler::ContextRegister,
- Assembler::PointerToString(*func->id),
- baseAddressForCallData());
-}
-
-
-void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result)
-{
- prepareCallData(args, base);
- if (useFastLookups) {
- uint index = registerGetterLookup(name);
- generateFunctionCall(result, __qmljs_construct_property_lookup,
- Assembler::ContextRegister,
- Assembler::TrustedImm32(index),
- baseAddressForCallData());
- return;
- }
-
- generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister,
- Assembler::PointerToString(name),
- baseAddressForCallData());
-}
-
-void InstructionSelection::constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
-{
- assert(value != 0);
-
- prepareCallData(args, 0);
- generateFunctionCall(result, __qmljs_construct_value,
- Assembler::ContextRegister,
- Assembler::Reference(value),
- baseAddressForCallData());
-}
-
-void InstructionSelection::visitJump(V4IR::Jump *s)
-{
- if (!_removableJumps.contains(s))
- _as->jumpToBlock(_block, s->target);
-}
-
-void InstructionSelection::visitCJump(V4IR::CJump *s)
-{
- if (V4IR::Temp *t = s->cond->asTemp()) {
- Assembler::RegisterID reg;
- if (t->kind == V4IR::Temp::PhysicalRegister) {
- Q_ASSERT(t->type == V4IR::BoolType);
- reg = (Assembler::RegisterID) t->index;
- } else if (t->kind == V4IR::Temp::StackSlot && t->type == V4IR::BoolType) {
- reg = Assembler::ReturnValueRegister;
- _as->toInt32Register(t, reg);
- } else {
- Address temp = _as->loadTempAddress(Assembler::ScratchRegister, t);
- Address tag = temp;
- tag.offset += qOffsetOf(QV4::Value, tag);
- Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type));
-
- Address data = temp;
- data.offset += qOffsetOf(QV4::Value, int_32);
- _as->load32(data, Assembler::ReturnValueRegister);
- Assembler::Jump testBoolean = _as->jump();
-
- booleanConversion.link(_as);
- reg = Assembler::ReturnValueRegister;
- generateFunctionCall(reg, __qmljs_to_boolean, Assembler::Reference(t));
-
- testBoolean.link(_as);
- }
-
- _as->generateCJumpOnNonZero(reg, _block, s->iftrue, s->iffalse);
- return;
- } else if (V4IR::Const *c = s->cond->asConst()) {
- // TODO: SSA optimization for constant condition evaluation should remove this.
- // See also visitCJump() in RegAllocInfo.
- generateFunctionCall(Assembler::ReturnValueRegister, __qmljs_to_boolean,
- Assembler::PointerToValue(c));
- _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse);
- return;
- } else if (V4IR::Binop *b = s->cond->asBinop()) {
- if (b->left->type == V4IR::DoubleType && b->right->type == V4IR::DoubleType
- && visitCJumpDouble(b->op, b->left, b->right, s->iftrue, s->iffalse))
- return;
-
- if (b->op == V4IR::OpStrictEqual || b->op == V4IR::OpStrictNotEqual) {
- visitCJumpStrict(b, s->iftrue, s->iffalse);
- return;
- }
- if (b->op == V4IR::OpEqual || b->op == V4IR::OpNotEqual) {
- visitCJumpEqual(b, s->iftrue, s->iffalse);
- return;
- }
-
- CmpOp op = 0;
- CmpOpContext opContext = 0;
- const char *opName = 0;
- switch (b->op) {
- default: Q_UNREACHABLE(); assert(!"todo"); break;
- case V4IR::OpGt: setOp(op, opName, __qmljs_cmp_gt); break;
- case V4IR::OpLt: setOp(op, opName, __qmljs_cmp_lt); break;
- case V4IR::OpGe: setOp(op, opName, __qmljs_cmp_ge); break;
- case V4IR::OpLe: setOp(op, opName, __qmljs_cmp_le); break;
- case V4IR::OpEqual: setOp(op, opName, __qmljs_cmp_eq); break;
- case V4IR::OpNotEqual: setOp(op, opName, __qmljs_cmp_ne); break;
- case V4IR::OpStrictEqual: setOp(op, opName, __qmljs_cmp_se); break;
- case V4IR::OpStrictNotEqual: setOp(op, opName, __qmljs_cmp_sne); break;
- case V4IR::OpInstanceof: setOpContext(op, opName, __qmljs_cmp_instanceof); break;
- case V4IR::OpIn: setOpContext(op, opName, __qmljs_cmp_in); break;
- } // switch
-
- // TODO: in SSA optimization, do constant expression evaluation.
- // The case here is, for example:
- // if (true === true) .....
- // Of course, after folding the CJUMP to a JUMP, dead-code (dead-basic-block)
- // elimination (which isn't there either) would remove the whole else block.
- if (opContext)
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, opContext,
- Assembler::ContextRegister,
- Assembler::PointerToValue(b->left),
- Assembler::PointerToValue(b->right));
- else
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, opName, op,
- Assembler::PointerToValue(b->left),
- Assembler::PointerToValue(b->right));
-
- _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse);
- return;
- }
- Q_UNREACHABLE();
-}
-
-void InstructionSelection::visitRet(V4IR::Ret *s)
-{
- if (!s) {
- // this only happens if the method doesn't have a return statement and can
- // only exit through an exception
- } else if (V4IR::Temp *t = s->expr->asTemp()) {
-#if CPU(X86) || CPU(ARM)
-
-# if CPU(X86)
- Assembler::RegisterID lowReg = JSC::X86Registers::eax;
- Assembler::RegisterID highReg = JSC::X86Registers::edx;
-# else // CPU(ARM)
- Assembler::RegisterID lowReg = JSC::ARMRegisters::r0;
- Assembler::RegisterID highReg = JSC::ARMRegisters::r1;
-# endif
-
- if (t->kind == V4IR::Temp::PhysicalRegister) {
- switch (t->type) {
- case V4IR::DoubleType:
- _as->moveDoubleToInts((Assembler::FPRegisterID) t->index, lowReg, highReg);
- break;
- case V4IR::UInt32Type: {
- Assembler::RegisterID srcReg = (Assembler::RegisterID) t->index;
- Assembler::Jump intRange = _as->branch32(Assembler::GreaterThanOrEqual, srcReg, Assembler::TrustedImm32(0));
- _as->convertUInt32ToDouble(srcReg, Assembler::FPGpr0, Assembler::ReturnValueRegister);
- _as->moveDoubleToInts(Assembler::FPGpr0, lowReg, highReg);
- Assembler::Jump done = _as->jump();
- intRange.link(_as);
- _as->move(srcReg, lowReg);
- _as->move(Assembler::TrustedImm32(QV4::Value::_Integer_Type), highReg);
- done.link(_as);
- } break;
- case V4IR::SInt32Type:
- _as->move((Assembler::RegisterID) t->index, lowReg);
- _as->move(Assembler::TrustedImm32(QV4::Value::_Integer_Type), highReg);
- break;
- case V4IR::BoolType:
- _as->move((Assembler::RegisterID) t->index, lowReg);
- _as->move(Assembler::TrustedImm32(QV4::Value::_Boolean_Type), highReg);
- break;
- default:
- Q_UNREACHABLE();
- }
- } else {
- Pointer addr = _as->loadTempAddress(Assembler::ScratchRegister, t);
- _as->load32(addr, lowReg);
- addr.offset += 4;
- _as->load32(addr, highReg);
- }
-#else
- if (t->kind == V4IR::Temp::PhysicalRegister) {
- if (t->type == V4IR::DoubleType) {
- _as->moveDoubleTo64((Assembler::FPRegisterID) t->index,
- Assembler::ReturnValueRegister);
- _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask),
- Assembler::ScratchRegister);
- _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
- } else if (t->type == V4IR::UInt32Type) {
- Assembler::RegisterID srcReg = (Assembler::RegisterID) t->index;
- Assembler::Jump intRange = _as->branch32(Assembler::GreaterThanOrEqual, srcReg, Assembler::TrustedImm32(0));
- _as->convertUInt32ToDouble(srcReg, Assembler::FPGpr0, Assembler::ReturnValueRegister);
- _as->moveDoubleTo64(Assembler::FPGpr0, Assembler::ReturnValueRegister);
- _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister);
- _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister);
- Assembler::Jump done = _as->jump();
- intRange.link(_as);
- _as->zeroExtend32ToPtr(srcReg, Assembler::ReturnValueRegister);
- quint64 tag = QV4::Value::_Integer_Type;
- _as->or64(Assembler::TrustedImm64(tag << 32),
- Assembler::ReturnValueRegister);
- done.link(_as);
- } else {
- _as->zeroExtend32ToPtr((Assembler::RegisterID) t->index, Assembler::ReturnValueRegister);
- quint64 tag;
- switch (t->type) {
- case V4IR::SInt32Type:
- tag = QV4::Value::_Integer_Type;
- break;
- case V4IR::BoolType:
- tag = QV4::Value::_Boolean_Type;
- break;
- default:
- tag = QV4::Value::Undefined_Type;
- Q_UNREACHABLE();
- }
- _as->or64(Assembler::TrustedImm64(tag << 32),
- Assembler::ReturnValueRegister);
- }
- } else {
- _as->copyValue(Assembler::ReturnValueRegister, t);
- }
-#endif
- } else if (V4IR::Const *c = s->expr->asConst()) {
- QV4::Primitive retVal = convertToValue(c);
-#if CPU(X86)
- _as->move(Assembler::TrustedImm32(retVal.int_32), JSC::X86Registers::eax);
- _as->move(Assembler::TrustedImm32(retVal.tag), JSC::X86Registers::edx);
-#elif CPU(ARM)
- _as->move(Assembler::TrustedImm32(retVal.int_32), JSC::ARMRegisters::r0);
- _as->move(Assembler::TrustedImm32(retVal.tag), JSC::ARMRegisters::r1);
-#else
- _as->move(Assembler::TrustedImm64(retVal.val), Assembler::ReturnValueRegister);
-#endif
- } else {
- Q_UNREACHABLE();
- Q_UNUSED(s);
- }
-
- _as->exceptionReturnLabel = _as->label();
-
- 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->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop)));
-
- _as->leaveStandardStackFrame();
- _as->ret();
-}
-
-int InstructionSelection::prepareVariableArguments(V4IR::ExprList* args)
-{
- int argc = 0;
- for (V4IR::ExprList *it = args; it; it = it->next) {
- ++argc;
- }
-
- int i = 0;
- for (V4IR::ExprList *it = args; it; it = it->next, ++i) {
- V4IR::Expr *arg = it->expr;
- Q_ASSERT(arg != 0);
- Pointer dst(_as->stackLayout().argumentAddressForCall(i));
- if (arg->asTemp() && arg->asTemp()->kind != V4IR::Temp::PhysicalRegister)
- _as->memcopyValue(dst, arg->asTemp(), Assembler::ScratchRegister);
- else
- _as->copyValue(dst, arg);
- }
-
- return argc;
-}
-
-int InstructionSelection::prepareCallData(V4IR::ExprList* args, V4IR::Expr *thisObject)
-{
- int argc = 0;
- for (V4IR::ExprList *it = args; it; it = it->next) {
- ++argc;
- }
-
- Pointer p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, tag));
- _as->store32(Assembler::TrustedImm32(QV4::Value::_Integer_Type), p);
- p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, argc));
- _as->store32(Assembler::TrustedImm32(argc), p);
- p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, thisObject));
- if (!thisObject)
- _as->storeValue(QV4::Primitive::undefinedValue(), p);
- else
- _as->copyValue(p, thisObject);
-
- int i = 0;
- for (V4IR::ExprList *it = args; it; it = it->next, ++i) {
- V4IR::Expr *arg = it->expr;
- Q_ASSERT(arg != 0);
- Pointer dst(_as->stackLayout().argumentAddressForCall(i));
- if (arg->asTemp() && arg->asTemp()->kind != V4IR::Temp::PhysicalRegister)
- _as->memcopyValue(dst, arg->asTemp(), Assembler::ScratchRegister);
- else
- _as->copyValue(dst, arg);
- }
- return argc;
-}
-
-
-QT_BEGIN_NAMESPACE
-namespace QV4 {
-bool operator==(const Primitive &v1, const Primitive &v2)
-{
- return v1.rawValue() == v2.rawValue();
-}
-} // QV4 namespace
-QT_END_NAMESPACE
-
-int Assembler::ConstantTable::add(const Primitive &v)
-{
- int idx = _values.indexOf(v);
- if (idx == -1) {
- idx = _values.size();
- _values.append(v);
- }
- return idx;
-}
-
-Assembler::ImplicitAddress Assembler::ConstantTable::loadValueAddress(V4IR::Const *c,
- RegisterID baseReg)
-{
- return loadValueAddress(convertToValue(c), baseReg);
-}
-
-Assembler::ImplicitAddress Assembler::ConstantTable::loadValueAddress(const Primitive &v,
- RegisterID baseReg)
-{
- _toPatch.append(_as->moveWithPatch(TrustedImmPtr(0), baseReg));
- ImplicitAddress addr(baseReg);
- addr.offset = add(v) * sizeof(QV4::Primitive);
- Q_ASSERT(addr.offset >= 0);
- return addr;
-}
-
-void Assembler::ConstantTable::finalize(JSC::LinkBuffer &linkBuffer, InstructionSelection *isel)
-{
- void *tablePtr = isel->addConstantTable(&_values);
-
- foreach (DataLabelPtr label, _toPatch)
- linkBuffer.patch(label, tablePtr);
-}
-
-// Try to load the source expression into the destination FP register. This assumes that two
-// general purpose (integer) registers are available: the ScratchRegister and the
-// ReturnValueRegister. It returns a Jump if no conversion can be performed.
-Assembler::Jump InstructionSelection::genTryDoubleConversion(V4IR::Expr *src,
- Assembler::FPRegisterID dest)
-{
- switch (src->type) {
- case V4IR::DoubleType:
- _as->moveDouble(_as->toDoubleRegister(src, dest), dest);
- return Assembler::Jump();
- case V4IR::SInt32Type:
- _as->convertInt32ToDouble(_as->toInt32Register(src, Assembler::ScratchRegister),
- dest);
- return Assembler::Jump();
- case V4IR::UInt32Type:
- _as->convertUInt32ToDouble(_as->toUInt32Register(src, Assembler::ScratchRegister),
- dest, Assembler::ReturnValueRegister);
- return Assembler::Jump();
- case V4IR::BoolType:
- // TODO?
- return _as->jump();
- default:
- break;
- }
-
- V4IR::Temp *sourceTemp = src->asTemp();
- Q_ASSERT(sourceTemp);
-
- // It's not a number type, so it cannot be in a register.
- Q_ASSERT(sourceTemp->kind != V4IR::Temp::PhysicalRegister || sourceTemp->type == V4IR::BoolType);
-
- Assembler::Pointer tagAddr = _as->loadTempAddress(Assembler::ScratchRegister, sourceTemp);
- 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));
- _as->convertInt32ToDouble(_as->toInt32Register(src, Assembler::ScratchRegister), dest);
- Assembler::Jump intDone = _as->jump();
-
- // not an int, check if it's a double:
- isNoInt.link(_as);
-#if QT_POINTER_SIZE == 8
- _as->and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister);
- Assembler::Jump isNoDbl = _as->branch32(Assembler::Equal, Assembler::ScratchRegister,
- Assembler::TrustedImm32(0));
-#else
- _as->and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister);
- Assembler::Jump isNoDbl = _as->branch32(Assembler::Equal, Assembler::ScratchRegister,
- Assembler::TrustedImm32(Value::NotDouble_Mask));
-#endif
- _as->toDoubleRegister(src, dest);
- intDone.link(_as);
-
- return isNoDbl;
-}
-
-void InstructionSelection::doubleBinop(V4IR::AluOp oper, V4IR::Expr *leftSource,
- V4IR::Expr *rightSource, V4IR::Temp *target)
-{
- Q_ASSERT(leftSource->asConst() == 0 || rightSource->asConst() == 0);
- Q_ASSERT(isPregOrConst(leftSource));
- Q_ASSERT(isPregOrConst(rightSource));
- Assembler::FPRegisterID targetReg;
- if (target->kind == V4IR::Temp::PhysicalRegister)
- targetReg = (Assembler::FPRegisterID) target->index;
- else
- targetReg = Assembler::FPGpr0;
-
- switch (oper) {
- case V4IR::OpAdd:
- _as->addDouble(_as->toDoubleRegister(leftSource), _as->toDoubleRegister(rightSource),
- targetReg);
- break;
- case V4IR::OpMul:
- _as->mulDouble(_as->toDoubleRegister(leftSource), _as->toDoubleRegister(rightSource),
- targetReg);
- break;
- case V4IR::OpSub:
-#if CPU(X86) || CPU(X86_64)
- if (V4IR::Temp *rightTemp = rightSource->asTemp()) {
- if (rightTemp->kind == V4IR::Temp::PhysicalRegister && rightTemp->index == targetReg) {
- _as->moveDouble(targetReg, Assembler::FPGpr0);
- _as->moveDouble(_as->toDoubleRegister(leftSource, targetReg), targetReg);
- _as->subDouble(Assembler::FPGpr0, targetReg);
- break;
- }
- } else if (rightSource->asConst() && targetReg == Assembler::FPGpr0) {
- Q_ASSERT(leftSource->asTemp());
- Q_ASSERT(leftSource->asTemp()->kind == V4IR::Temp::PhysicalRegister);
- _as->moveDouble(_as->toDoubleRegister(leftSource, targetReg), targetReg);
- Assembler::FPRegisterID reg = (Assembler::FPRegisterID) leftSource->asTemp()->index;
- _as->moveDouble(_as->toDoubleRegister(rightSource, reg), reg);
- _as->subDouble(reg, targetReg);
- break;
- }
-#endif
-
- _as->subDouble(_as->toDoubleRegister(leftSource), _as->toDoubleRegister(rightSource),
- targetReg);
- break;
- case V4IR::OpDiv:
-#if CPU(X86) || CPU(X86_64)
- if (V4IR::Temp *rightTemp = rightSource->asTemp()) {
- if (rightTemp->kind == V4IR::Temp::PhysicalRegister && rightTemp->index == targetReg) {
- _as->moveDouble(targetReg, Assembler::FPGpr0);
- _as->moveDouble(_as->toDoubleRegister(leftSource, targetReg), targetReg);
- _as->divDouble(Assembler::FPGpr0, targetReg);
- break;
- }
- } else if (rightSource->asConst() && targetReg == Assembler::FPGpr0) {
- Q_ASSERT(leftSource->asTemp());
- Q_ASSERT(leftSource->asTemp()->kind == V4IR::Temp::PhysicalRegister);
- _as->moveDouble(_as->toDoubleRegister(leftSource, targetReg), targetReg);
- Assembler::FPRegisterID reg = (Assembler::FPRegisterID) leftSource->asTemp()->index;
- _as->moveDouble(_as->toDoubleRegister(rightSource, reg), reg);
- _as->divDouble(reg, targetReg);
- break;
- }
-#endif
- _as->divDouble(_as->toDoubleRegister(leftSource), _as->toDoubleRegister(rightSource),
- targetReg);
- break;
- default: {
- Q_ASSERT(target->type == V4IR::BoolType);
- Assembler::Jump trueCase = branchDouble(false, oper, leftSource, rightSource);
- _as->storeBool(false, target);
- Assembler::Jump done = _as->jump();
- trueCase.link(_as);
- _as->storeBool(true, target);
- done.link(_as);
- } return;
- }
-
- if (target->kind != V4IR::Temp::PhysicalRegister)
- _as->storeDouble(Assembler::FPGpr0, target);
-}
-
-Assembler::Jump InstructionSelection::branchDouble(bool invertCondition, V4IR::AluOp op,
- V4IR::Expr *left, V4IR::Expr *right)
-{
- Q_ASSERT(isPregOrConst(left));
- Q_ASSERT(isPregOrConst(right));
- Q_ASSERT(left->asConst() == 0 || right->asConst() == 0);
-
- Assembler::DoubleCondition cond;
- switch (op) {
- case V4IR::OpGt: cond = Assembler::DoubleGreaterThan; break;
- case V4IR::OpLt: cond = Assembler::DoubleLessThan; break;
- case V4IR::OpGe: cond = Assembler::DoubleGreaterThanOrEqual; break;
- case V4IR::OpLe: cond = Assembler::DoubleLessThanOrEqual; break;
- case V4IR::OpEqual:
- case V4IR::OpStrictEqual: cond = Assembler::DoubleEqual; break;
- case V4IR::OpNotEqual:
- case V4IR::OpStrictNotEqual: cond = Assembler::DoubleNotEqualOrUnordered; break; // No, the inversion of DoubleEqual is NOT DoubleNotEqual.
- default:
- Q_UNREACHABLE();
- }
- if (invertCondition)
- cond = JSC::MacroAssembler::invert(cond);
-
- return _as->branchDouble(cond, _as->toDoubleRegister(left), _as->toDoubleRegister(right));
-}
-
-bool InstructionSelection::visitCJumpDouble(V4IR::AluOp op, V4IR::Expr *left, V4IR::Expr *right,
- V4IR::BasicBlock *iftrue, V4IR::BasicBlock *iffalse)
-{
- if (!isPregOrConst(left) || !isPregOrConst(right))
- return false;
-
- if (_as->nextBlock() == iftrue) {
- Assembler::Jump target = branchDouble(true, op, left, right);
- _as->addPatch(iffalse, target);
- } else {
- Assembler::Jump target = branchDouble(false, op, left, right);
- _as->addPatch(iftrue, target);
- _as->jumpToBlock(_block, iffalse);
- }
- return true;
-}
-
-void InstructionSelection::visitCJumpStrict(V4IR::Binop *binop, V4IR::BasicBlock *trueBlock,
- V4IR::BasicBlock *falseBlock)
-{
- Q_ASSERT(binop->op == V4IR::OpStrictEqual || binop->op == V4IR::OpStrictNotEqual);
-
- if (visitCJumpStrictNullUndefined(V4IR::NullType, binop, trueBlock, falseBlock))
- return;
- if (visitCJumpStrictNullUndefined(V4IR::UndefinedType, binop, trueBlock, falseBlock))
- return;
- if (visitCJumpStrictBool(binop, trueBlock, falseBlock))
- return;
-
- V4IR::Expr *left = binop->left;
- V4IR::Expr *right = binop->right;
-
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, "__qmljs_cmp_se", __qmljs_cmp_se,
- Assembler::PointerToValue(left), Assembler::PointerToValue(right));
- _as->generateCJumpOnCompare(binop->op == V4IR::OpStrictEqual ? Assembler::NotEqual : Assembler::Equal,
- Assembler::ReturnValueRegister, Assembler::TrustedImm32(0),
- _block, trueBlock, falseBlock);
-}
-
-// Only load the non-null temp.
-bool InstructionSelection::visitCJumpStrictNullUndefined(V4IR::Type nullOrUndef, V4IR::Binop *binop,
- V4IR::BasicBlock *trueBlock,
- V4IR::BasicBlock *falseBlock)
-{
- Q_ASSERT(nullOrUndef == V4IR::NullType || nullOrUndef == V4IR::UndefinedType);
-
- V4IR::Expr *varSrc = 0;
- if (binop->left->type == V4IR::VarType && binop->right->type == nullOrUndef)
- varSrc = binop->left;
- else if (binop->left->type == nullOrUndef && binop->right->type == V4IR::VarType)
- varSrc = binop->right;
- if (!varSrc)
- return false;
-
- if (varSrc->asTemp() && varSrc->asTemp()->kind == V4IR::Temp::PhysicalRegister) {
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- if (V4IR::Const *c = varSrc->asConst()) {
- if (c->type == nullOrUndef)
- _as->jumpToBlock(_block, trueBlock);
- else
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- V4IR::Temp *t = varSrc->asTemp();
- Q_ASSERT(t);
-
- Assembler::Pointer tagAddr = _as->loadTempAddress(Assembler::ScratchRegister, t);
- tagAddr.offset += 4;
- const Assembler::RegisterID tagReg = Assembler::ScratchRegister;
- _as->load32(tagAddr, tagReg);
-
- Assembler::RelationalCondition cond = binop->op == V4IR::OpStrictEqual ? Assembler::Equal
- : Assembler::NotEqual;
- const Assembler::TrustedImm32 tag(nullOrUndef == V4IR::NullType ? int(QV4::Value::_Null_Type)
- : int(QV4::Value::Undefined_Type));
- _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock);
- return true;
-}
-
-bool InstructionSelection::visitCJumpStrictBool(V4IR::Binop *binop, V4IR::BasicBlock *trueBlock,
- V4IR::BasicBlock *falseBlock)
-{
- V4IR::Expr *boolSrc = 0, *otherSrc = 0;
- if (binop->left->type == V4IR::BoolType) {
- boolSrc = binop->left;
- otherSrc = binop->right;
- } else if (binop->right->type == V4IR::BoolType) {
- boolSrc = binop->right;
- otherSrc = binop->left;
- } else {
- // neither operands are statically typed as bool, so bail out.
- return false;
- }
-
- Assembler::RelationalCondition cond = binop->op == V4IR::OpStrictEqual ? Assembler::Equal
- : Assembler::NotEqual;
-
- if (otherSrc->type == V4IR::BoolType) { // both are boolean
- Assembler::RegisterID one = _as->toBoolRegister(boolSrc, Assembler::ReturnValueRegister);
- Assembler::RegisterID two = _as->toBoolRegister(otherSrc, Assembler::ScratchRegister);
- _as->generateCJumpOnCompare(cond, one, two, _block, trueBlock, falseBlock);
- return true;
- }
-
- if (otherSrc->type != V4IR::VarType) {
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- V4IR::Temp *otherTemp = otherSrc->asTemp();
- Q_ASSERT(otherTemp); // constants cannot have "var" type
- Q_ASSERT(otherTemp->kind != V4IR::Temp::PhysicalRegister);
-
- Assembler::Pointer otherAddr = _as->loadTempAddress(Assembler::ReturnValueRegister, otherTemp);
- otherAddr.offset += 4; // tag address
-
- // check if the tag of the var operand is indicates 'boolean'
- _as->load32(otherAddr, Assembler::ScratchRegister);
- Assembler::Jump noBool = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister,
- Assembler::TrustedImm32(QV4::Value::_Boolean_Type));
- if (binop->op == V4IR::OpStrictEqual)
- _as->addPatch(falseBlock, noBool);
- else
- _as->addPatch(trueBlock, noBool);
-
- // ok, both are boolean, so let's load them and compare them.
- otherAddr.offset -= 4; // int_32 address
- _as->load32(otherAddr, Assembler::ReturnValueRegister);
- Assembler::RegisterID boolReg = _as->toBoolRegister(boolSrc, Assembler::ScratchRegister);
- _as->generateCJumpOnCompare(cond, boolReg, Assembler::ReturnValueRegister, _block, trueBlock,
- falseBlock);
- return true;
-}
-
-bool InstructionSelection::visitCJumpNullUndefined(V4IR::Type nullOrUndef, V4IR::Binop *binop,
- V4IR::BasicBlock *trueBlock,
- V4IR::BasicBlock *falseBlock)
-{
- Q_ASSERT(nullOrUndef == V4IR::NullType || nullOrUndef == V4IR::UndefinedType);
-
- V4IR::Expr *varSrc = 0;
- if (binop->left->type == V4IR::VarType && binop->right->type == nullOrUndef)
- varSrc = binop->left;
- else if (binop->left->type == nullOrUndef && binop->right->type == V4IR::VarType)
- varSrc = binop->right;
- if (!varSrc)
- return false;
-
- if (varSrc->asTemp() && varSrc->asTemp()->kind == V4IR::Temp::PhysicalRegister) {
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- if (V4IR::Const *c = varSrc->asConst()) {
- if (c->type == nullOrUndef)
- _as->jumpToBlock(_block, trueBlock);
- else
- _as->jumpToBlock(_block, falseBlock);
- return true;
- }
-
- V4IR::Temp *t = varSrc->asTemp();
- Q_ASSERT(t);
-
- Assembler::Pointer tagAddr = _as->loadTempAddress(Assembler::ScratchRegister, t);
- tagAddr.offset += 4;
- const Assembler::RegisterID tagReg = Assembler::ScratchRegister;
- _as->load32(tagAddr, tagReg);
-
- if (binop->op == V4IR::OpNotEqual)
- qSwap(trueBlock, falseBlock);
- Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::_Null_Type)));
- Assembler::Jump isUndefined = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Undefined_Type)));
- _as->addPatch(trueBlock, isNull);
- _as->addPatch(trueBlock, isUndefined);
- _as->jumpToBlock(_block, falseBlock);
-
- return true;
-}
-
-
-void InstructionSelection::visitCJumpEqual(V4IR::Binop *binop, V4IR::BasicBlock *trueBlock,
- V4IR::BasicBlock *falseBlock)
-{
- Q_ASSERT(binop->op == V4IR::OpEqual || binop->op == V4IR::OpNotEqual);
-
- if (visitCJumpNullUndefined(V4IR::NullType, binop, trueBlock, falseBlock))
- return;
-
- V4IR::Expr *left = binop->left;
- V4IR::Expr *right = binop->right;
-
- _as->generateFunctionCallImp(Assembler::ReturnValueRegister, "__qmljs_cmp_eq", __qmljs_cmp_eq,
- Assembler::PointerToValue(left), Assembler::PointerToValue(right));
- _as->generateCJumpOnCompare(binop->op == V4IR::OpEqual ? Assembler::NotEqual : Assembler::Equal,
- Assembler::ReturnValueRegister, Assembler::TrustedImm32(0),
- _block, trueBlock, falseBlock);
-}
-
-
-bool InstructionSelection::int32Binop(V4IR::AluOp oper, V4IR::Expr *leftSource,
- V4IR::Expr *rightSource, V4IR::Temp *target)
-{
- Q_ASSERT(leftSource->type == V4IR::SInt32Type);
- Assembler::RegisterID targetReg;
- if (target->kind == V4IR::Temp::PhysicalRegister)
- targetReg = (Assembler::RegisterID) target->index;
- else
- targetReg = Assembler::ReturnValueRegister;
-
- switch (oper) {
- case V4IR::OpBitAnd: {
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
- if (rightSource->asTemp() && rightSource->asTemp()->kind == V4IR::Temp::PhysicalRegister
- && target->kind == V4IR::Temp::PhysicalRegister
- && target->index == rightSource->asTemp()->index) {
- _as->and32(_as->toInt32Register(leftSource, Assembler::ScratchRegister),
- (Assembler::RegisterID) target->index);
- return true;
- }
-
- _as->and32(_as->toInt32Register(leftSource, targetReg),
- _as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- _as->storeInt32(targetReg, target);
- } return true;
- case V4IR::OpBitOr: {
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
- if (rightSource->asTemp() && rightSource->asTemp()->kind == V4IR::Temp::PhysicalRegister
- && target->kind == V4IR::Temp::PhysicalRegister
- && target->index == rightSource->asTemp()->index) {
- _as->or32(_as->toInt32Register(leftSource, Assembler::ScratchRegister),
- (Assembler::RegisterID) target->index);
- return true;
- }
-
- _as->or32(_as->toInt32Register(leftSource, targetReg),
- _as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- _as->storeInt32(targetReg, target);
- } return true;
- case V4IR::OpBitXor: {
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
- if (rightSource->asTemp() && rightSource->asTemp()->kind == V4IR::Temp::PhysicalRegister
- && target->kind == V4IR::Temp::PhysicalRegister
- && target->index == rightSource->asTemp()->index) {
- _as->xor32(_as->toInt32Register(leftSource, Assembler::ScratchRegister),
- (Assembler::RegisterID) target->index);
- return true;
- }
-
- _as->xor32(_as->toInt32Register(leftSource, targetReg),
- _as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- _as->storeInt32(targetReg, target);
- } return true;
- case V4IR::OpLShift: {
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
-
- if (V4IR::Const *c = rightSource->asConst()) {
- _as->lshift32(_as->toInt32Register(leftSource, Assembler::ReturnValueRegister),
- Assembler::TrustedImm32(int(c->value) & 0x1f), targetReg);
- } else {
- _as->move(_as->toInt32Register(rightSource, Assembler::ScratchRegister),
- Assembler::ScratchRegister);
- if (!rightSource->asConst())
- _as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister);
- _as->lshift32(_as->toInt32Register(leftSource, targetReg), Assembler::ScratchRegister, targetReg);
- }
- _as->storeInt32(targetReg, target);
- } return true;
- case V4IR::OpRShift: {
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
-
- if (V4IR::Const *c = rightSource->asConst()) {
- _as->rshift32(_as->toInt32Register(leftSource, Assembler::ReturnValueRegister),
- Assembler::TrustedImm32(int(c->value) & 0x1f), targetReg);
- } else {
- _as->move(_as->toInt32Register(rightSource, Assembler::ScratchRegister),
- Assembler::ScratchRegister);
- _as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister);
- _as->rshift32(_as->toInt32Register(leftSource, targetReg), Assembler::ScratchRegister, targetReg);
- }
- _as->storeInt32(targetReg, target);
- } return true;
- case V4IR::OpURShift:
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
-
- if (V4IR::Const *c = rightSource->asConst()) {
- _as->urshift32(_as->toInt32Register(leftSource, Assembler::ReturnValueRegister),
- Assembler::TrustedImm32(int(c->value) & 0x1f), targetReg);
- } else {
- _as->move(_as->toInt32Register(rightSource, Assembler::ScratchRegister),
- Assembler::ScratchRegister);
- _as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister);
- _as->urshift32(_as->toInt32Register(leftSource, targetReg), Assembler::ScratchRegister, targetReg);
- }
- _as->storeUInt32(targetReg, target);
- return true;
- case V4IR::OpAdd: {
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
-
- Assembler::RegisterID targetReg;
- if (target->kind == V4IR::Temp::PhysicalRegister)
- targetReg = (Assembler::RegisterID) target->index;
- else
- targetReg = Assembler::ReturnValueRegister;
-
- _as->add32(_as->toInt32Register(leftSource, targetReg),
- _as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- _as->storeInt32(targetReg, target);
- } return true;
- case V4IR::OpSub: {
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
-
- if (rightSource->asTemp() && rightSource->asTemp()->kind == V4IR::Temp::PhysicalRegister
- && target->kind == V4IR::Temp::PhysicalRegister
- && target->index == rightSource->asTemp()->index) {
- Assembler::RegisterID targetReg = (Assembler::RegisterID) target->index;
- _as->move(targetReg, Assembler::ScratchRegister);
- _as->move(_as->toInt32Register(leftSource, targetReg), targetReg);
- _as->sub32(Assembler::ScratchRegister, targetReg);
- _as->storeInt32(targetReg, target);
- return true;
- }
-
- Assembler::RegisterID targetReg;
- if (target->kind == V4IR::Temp::PhysicalRegister)
- targetReg = (Assembler::RegisterID) target->index;
- else
- targetReg = Assembler::ReturnValueRegister;
-
- _as->move(_as->toInt32Register(leftSource, targetReg), targetReg);
- _as->sub32(_as->toInt32Register(rightSource, Assembler::ScratchRegister), targetReg);
- _as->storeInt32(targetReg, target);
- } return true;
- case V4IR::OpMul: {
- Q_ASSERT(rightSource->type == V4IR::SInt32Type);
-
- Assembler::RegisterID targetReg;
- if (target->kind == V4IR::Temp::PhysicalRegister)
- targetReg = (Assembler::RegisterID) target->index;
- else
- targetReg = Assembler::ReturnValueRegister;
-
- _as->mul32(_as->toInt32Register(leftSource, targetReg),
- _as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- _as->storeInt32(targetReg, target);
- } return true;
- default:
- return false;
- }
-}
-
-#endif // ENABLE(ASSEMBLER)