1693 lines
55 KiB
C++
1693 lines
55 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** 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 The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 2.0 or (at your option) the GNU General
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qv4isel_util_p.h"
|
|
#include "qv4isel_moth_p.h"
|
|
#include "qv4vme_moth_p.h"
|
|
#include "qv4ssa_p.h"
|
|
#include <private/qv4debugging_p.h>
|
|
#include <private/qv4function_p.h>
|
|
#include <private/qv4regexpobject_p.h>
|
|
#include <private/qv4compileddata_p.h>
|
|
#include <private/qqmlengine_p.h>
|
|
#include "qml/qqmlaccessors_p.h"
|
|
#include "qml/qqmlpropertycache_p.h"
|
|
|
|
#undef USE_TYPE_INFO
|
|
|
|
using namespace QV4;
|
|
using namespace QV4::Moth;
|
|
|
|
namespace {
|
|
|
|
inline uint aluOpFunction(IR::AluOp op)
|
|
{
|
|
switch (op) {
|
|
case IR::OpInvalid:
|
|
return 0;
|
|
case IR::OpIfTrue:
|
|
return 0;
|
|
case IR::OpNot:
|
|
return 0;
|
|
case IR::OpUMinus:
|
|
return 0;
|
|
case IR::OpUPlus:
|
|
return 0;
|
|
case IR::OpCompl:
|
|
return 0;
|
|
case IR::OpBitAnd:
|
|
return offsetof(QV4::Runtime, bitAnd);
|
|
case IR::OpBitOr:
|
|
return offsetof(QV4::Runtime, bitOr);
|
|
case IR::OpBitXor:
|
|
return offsetof(QV4::Runtime, bitXor);
|
|
case IR::OpAdd:
|
|
return 0;
|
|
case IR::OpSub:
|
|
return offsetof(QV4::Runtime, sub);
|
|
case IR::OpMul:
|
|
return offsetof(QV4::Runtime, mul);
|
|
case IR::OpDiv:
|
|
return offsetof(QV4::Runtime, div);
|
|
case IR::OpMod:
|
|
return offsetof(QV4::Runtime, mod);
|
|
case IR::OpLShift:
|
|
return offsetof(QV4::Runtime, shl);
|
|
case IR::OpRShift:
|
|
return offsetof(QV4::Runtime, shr);
|
|
case IR::OpURShift:
|
|
return offsetof(QV4::Runtime, ushr);
|
|
case IR::OpGt:
|
|
return offsetof(QV4::Runtime, greaterThan);
|
|
case IR::OpLt:
|
|
return offsetof(QV4::Runtime, lessThan);
|
|
case IR::OpGe:
|
|
return offsetof(QV4::Runtime, greaterEqual);
|
|
case IR::OpLe:
|
|
return offsetof(QV4::Runtime, lessEqual);
|
|
case IR::OpEqual:
|
|
return offsetof(QV4::Runtime, equal);
|
|
case IR::OpNotEqual:
|
|
return offsetof(QV4::Runtime, notEqual);
|
|
case IR::OpStrictEqual:
|
|
return offsetof(QV4::Runtime, strictEqual);
|
|
case IR::OpStrictNotEqual:
|
|
return offsetof(QV4::Runtime, strictNotEqual);
|
|
case IR::OpInstanceof:
|
|
return 0;
|
|
case IR::OpIn:
|
|
return 0;
|
|
case IR::OpAnd:
|
|
return 0;
|
|
case IR::OpOr:
|
|
return 0;
|
|
default:
|
|
Q_ASSERT(!"Unknown AluOp");
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
inline bool isNumberType(IR::Expr *e)
|
|
{
|
|
switch (e->type) {
|
|
case IR::SInt32Type:
|
|
case IR::UInt32Type:
|
|
case IR::DoubleType:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline bool isIntegerType(IR::Expr *e)
|
|
{
|
|
switch (e->type) {
|
|
case IR::SInt32Type:
|
|
case IR::UInt32Type:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline bool isBoolType(IR::Expr *e)
|
|
{
|
|
return (e->type == IR::BoolType);
|
|
}
|
|
|
|
/*
|
|
* stack slot allocation:
|
|
*
|
|
* foreach bb do
|
|
* foreach stmt do
|
|
* if the current statement is not a phi-node:
|
|
* purge ranges that end before the current statement
|
|
* check for life ranges to activate, and if they don't have a stackslot associated then allocate one
|
|
* renumber temps to stack
|
|
* for phi nodes: check if all temps (src+dst) are assigned stack slots and marked as allocated
|
|
* if it's a jump:
|
|
* foreach phi node in the successor:
|
|
* allocate slots for each temp (both sources and targets) if they don't have one allocated already
|
|
* insert moves before the jump
|
|
*/
|
|
class AllocateStackSlots: protected ConvertTemps
|
|
{
|
|
IR::LifeTimeIntervals::Ptr _intervals;
|
|
QVector<IR::LifeTimeInterval *> _unhandled;
|
|
QVector<IR::LifeTimeInterval *> _live;
|
|
QBitArray _slotIsInUse;
|
|
IR::Function *_function;
|
|
|
|
int defPosition(IR::Stmt *s) const
|
|
{
|
|
return usePosition(s) + 1;
|
|
}
|
|
|
|
int usePosition(IR::Stmt *s) const
|
|
{
|
|
return _intervals->positionForStatement(s);
|
|
}
|
|
|
|
public:
|
|
AllocateStackSlots(const IR::LifeTimeIntervals::Ptr &intervals)
|
|
: _intervals(intervals)
|
|
, _slotIsInUse(intervals->size(), false)
|
|
, _function(0)
|
|
{
|
|
_live.reserve(8);
|
|
_unhandled = _intervals->intervals();
|
|
}
|
|
|
|
void forFunction(IR::Function *function)
|
|
{
|
|
IR::Optimizer::showMeTheCode(function, "Before stack slot allocation");
|
|
_function = function;
|
|
toStackSlots(function);
|
|
}
|
|
|
|
protected:
|
|
virtual int allocateFreeSlot()
|
|
{
|
|
for (int i = 0, ei = _slotIsInUse.size(); i != ei; ++i) {
|
|
if (!_slotIsInUse[i]) {
|
|
if (_nextUnusedStackSlot <= i) {
|
|
Q_ASSERT(_nextUnusedStackSlot == i);
|
|
_nextUnusedStackSlot = i + 1;
|
|
}
|
|
_slotIsInUse[i] = true;
|
|
return i;
|
|
}
|
|
}
|
|
|
|
Q_UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
virtual void process(IR::Stmt *s)
|
|
{
|
|
// qDebug("L%d statement %d:", _currentBasicBlock->index, s->id);
|
|
|
|
if (IR::Phi *phi = s->asPhi()) {
|
|
visitPhi(phi);
|
|
} else {
|
|
// purge ranges no longer alive:
|
|
for (int i = 0; i < _live.size(); ) {
|
|
const IR::LifeTimeInterval *lti = _live.at(i);
|
|
if (lti->end() < usePosition(s)) {
|
|
// qDebug() << "\t - moving temp" << lti->temp().index << "to handled, freeing slot" << _stackSlotForTemp[lti->temp().index];
|
|
_live.remove(i);
|
|
Q_ASSERT(_slotIsInUse[_stackSlotForTemp[lti->temp().index]]);
|
|
_slotIsInUse[_stackSlotForTemp[lti->temp().index]] = false;
|
|
continue;
|
|
} else {
|
|
++i;
|
|
}
|
|
}
|
|
|
|
// active new ranges:
|
|
while (!_unhandled.isEmpty()) {
|
|
IR::LifeTimeInterval *lti = _unhandled.last();
|
|
if (lti->start() > defPosition(s))
|
|
break; // we're done
|
|
Q_ASSERT(!_stackSlotForTemp.contains(lti->temp().index));
|
|
_stackSlotForTemp[lti->temp().index] = allocateFreeSlot();
|
|
// qDebug() << "\t - activating temp" << lti->temp().index << "on slot" << _stackSlotForTemp[lti->temp().index];
|
|
_live.append(lti);
|
|
_unhandled.removeLast();
|
|
}
|
|
|
|
s->accept(this);
|
|
}
|
|
|
|
if (IR::Jump *jump = s->asJump()) {
|
|
IR::MoveMapping moves;
|
|
for (IR::Stmt *succStmt : jump->target->statements()) {
|
|
if (IR::Phi *phi = succStmt->asPhi()) {
|
|
forceActivation(*phi->targetTemp);
|
|
for (int i = 0, ei = phi->incoming.size(); i != ei; ++i) {
|
|
IR::Expr *e = phi->incoming[i];
|
|
if (IR::Temp *t = e->asTemp()) {
|
|
forceActivation(*t);
|
|
}
|
|
if (jump->target->in[i] == _currentBasicBlock)
|
|
moves.add(phi->incoming[i], phi->targetTemp);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
moves.order();
|
|
QList<IR::Move *> newMoves = moves.insertMoves(_currentBasicBlock, _function, true);
|
|
foreach (IR::Move *move, newMoves)
|
|
move->accept(this);
|
|
}
|
|
}
|
|
|
|
void forceActivation(const IR::Temp &t)
|
|
{
|
|
if (_stackSlotForTemp.contains(t.index))
|
|
return;
|
|
|
|
int i = _unhandled.size() - 1;
|
|
for (; i >= 0; --i) {
|
|
IR::LifeTimeInterval *lti = _unhandled[i];
|
|
if (lti->temp() == t) {
|
|
_live.append(lti);
|
|
_unhandled.remove(i);
|
|
break;
|
|
}
|
|
}
|
|
Q_ASSERT(i >= 0); // check that we always found the entry
|
|
|
|
_stackSlotForTemp[t.index] = allocateFreeSlot();
|
|
// qDebug() << "\t - force activating temp" << t.index << "on slot" << _stackSlotForTemp[t.index];
|
|
}
|
|
|
|
virtual void visitPhi(IR::Phi *phi)
|
|
{
|
|
Q_UNUSED(phi);
|
|
#if !defined(QT_NO_DEBUG)
|
|
Q_ASSERT(_stackSlotForTemp.contains(phi->targetTemp->index));
|
|
Q_ASSERT(_slotIsInUse[_stackSlotForTemp[phi->targetTemp->index]]);
|
|
foreach (IR::Expr *e, phi->incoming) {
|
|
if (IR::Temp *t = e->asTemp())
|
|
Q_ASSERT(_stackSlotForTemp.contains(t->index));
|
|
}
|
|
#endif // defined(QT_NO_DEBUG)
|
|
}
|
|
};
|
|
} // anonymous namespace
|
|
|
|
InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator)
|
|
: EvalInstructionSelection(execAllocator, module, jsGenerator)
|
|
, qmlEngine(qmlEngine)
|
|
, _block(0)
|
|
, _codeStart(0)
|
|
, _codeNext(0)
|
|
, _codeEnd(0)
|
|
, _currentStatement(0)
|
|
, compilationUnit(new CompilationUnit)
|
|
{
|
|
setUseTypeInference(false);
|
|
}
|
|
|
|
InstructionSelection::~InstructionSelection()
|
|
{
|
|
}
|
|
|
|
void InstructionSelection::run(int functionIndex)
|
|
{
|
|
IR::Function *function = irModule->functions[functionIndex];
|
|
IR::BasicBlock *block = 0, *nextBlock = 0;
|
|
|
|
QHash<IR::BasicBlock *, QVector<ptrdiff_t> > patches;
|
|
QHash<IR::BasicBlock *, ptrdiff_t> addrs;
|
|
|
|
int codeSize = 4096;
|
|
uchar *codeStart = new uchar[codeSize];
|
|
memset(codeStart, 0, codeSize);
|
|
uchar *codeNext = codeStart;
|
|
uchar *codeEnd = codeStart + codeSize;
|
|
|
|
qSwap(_function, function);
|
|
qSwap(block, _block);
|
|
qSwap(nextBlock, _nextBlock);
|
|
qSwap(patches, _patches);
|
|
qSwap(addrs, _addrs);
|
|
qSwap(codeStart, _codeStart);
|
|
qSwap(codeNext, _codeNext);
|
|
qSwap(codeEnd, _codeEnd);
|
|
|
|
IR::Optimizer opt(_function);
|
|
opt.run(qmlEngine, useTypeInference, /*peelLoops =*/ false);
|
|
if (opt.isInSSA()) {
|
|
static const bool doStackSlotAllocation =
|
|
qEnvironmentVariableIsEmpty("QV4_NO_INTERPRETER_STACK_SLOT_ALLOCATION");
|
|
|
|
if (doStackSlotAllocation) {
|
|
AllocateStackSlots(opt.lifeTimeIntervals()).forFunction(_function);
|
|
} else {
|
|
opt.convertOutOfSSA();
|
|
ConvertTemps().toStackSlots(_function);
|
|
}
|
|
opt.showMeTheCode(_function, "After stack slot allocation");
|
|
} else {
|
|
ConvertTemps().toStackSlots(_function);
|
|
}
|
|
|
|
QSet<IR::Jump *> removableJumps = opt.calculateOptionalJumps();
|
|
qSwap(_removableJumps, removableJumps);
|
|
|
|
IR::Stmt *cs = 0;
|
|
qSwap(_currentStatement, cs);
|
|
|
|
int locals = frameSize();
|
|
Q_ASSERT(locals >= 0);
|
|
|
|
IR::BasicBlock *exceptionHandler = 0;
|
|
|
|
Instruction::Push push;
|
|
push.value = quint32(locals);
|
|
addInstruction(push);
|
|
|
|
currentLine = 0;
|
|
const QVector<IR::BasicBlock *> &basicBlocks = _function->basicBlocks();
|
|
for (int i = 0, ei = basicBlocks.size(); i != ei; ++i) {
|
|
blockNeedsDebugInstruction = irModule->debugMode;
|
|
_block = basicBlocks[i];
|
|
_nextBlock = (i < ei - 1) ? basicBlocks[i + 1] : 0;
|
|
_addrs.insert(_block, _codeNext - _codeStart);
|
|
|
|
if (_block->catchBlock != exceptionHandler) {
|
|
Instruction::SetExceptionHandler set;
|
|
set.offset = 0;
|
|
if (_block->catchBlock) {
|
|
ptrdiff_t loc = addInstruction(set) + (((const char *)&set.offset) - ((const char *)&set));
|
|
_patches[_block->catchBlock].append(loc);
|
|
} else {
|
|
addInstruction(set);
|
|
}
|
|
exceptionHandler = _block->catchBlock;
|
|
}
|
|
|
|
for (IR::Stmt *s : _block->statements()) {
|
|
_currentStatement = s;
|
|
|
|
if (s->location.isValid()) {
|
|
if (s->location.startLine != currentLine) {
|
|
blockNeedsDebugInstruction = false;
|
|
currentLine = s->location.startLine;
|
|
if (irModule->debugMode) {
|
|
Instruction::Debug debug;
|
|
debug.lineNumber = currentLine;
|
|
addInstruction(debug);
|
|
} else {
|
|
Instruction::Line line;
|
|
line.lineNumber = currentLine;
|
|
addInstruction(line);
|
|
}
|
|
}
|
|
}
|
|
|
|
s->accept(this);
|
|
}
|
|
}
|
|
|
|
// TODO: patch stack size (the push instruction)
|
|
patchJumpAddresses();
|
|
|
|
codeRefs.insert(_function, squeezeCode());
|
|
|
|
qSwap(_currentStatement, cs);
|
|
qSwap(_removableJumps, removableJumps);
|
|
qSwap(_function, function);
|
|
qSwap(block, _block);
|
|
qSwap(nextBlock, _nextBlock);
|
|
qSwap(patches, _patches);
|
|
qSwap(addrs, _addrs);
|
|
qSwap(codeStart, _codeStart);
|
|
qSwap(codeNext, _codeNext);
|
|
qSwap(codeEnd, _codeEnd);
|
|
|
|
delete[] codeStart;
|
|
}
|
|
|
|
QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep()
|
|
{
|
|
compilationUnit->codeRefs.resize(irModule->functions.size());
|
|
int i = 0;
|
|
foreach (IR::Function *irFunction, irModule->functions)
|
|
compilationUnit->codeRefs[i++] = codeRefs[irFunction];
|
|
QQmlRefPointer<QV4::CompiledData::CompilationUnit> result;
|
|
result.adopt(compilationUnit.take());
|
|
return result;
|
|
}
|
|
|
|
void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result)
|
|
{
|
|
Instruction::CallValue call;
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.dest = getParam(value);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result)
|
|
{
|
|
if (kind == IR::Member::MemberOfQmlScopeObject) {
|
|
Instruction::CallScopeObjectProperty call;
|
|
call.base = getParam(base);
|
|
call.index = propertyIndex;
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
} else if (kind == IR::Member::MemberOfQmlContextObject) {
|
|
Instruction::CallContextObjectProperty call;
|
|
call.base = getParam(base);
|
|
call.index = propertyIndex;
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
} else {
|
|
Q_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args,
|
|
IR::Expr *result)
|
|
{
|
|
if (useFastLookups) {
|
|
Instruction::CallPropertyLookup call;
|
|
call.base = getParam(base);
|
|
call.lookupIndex = registerGetterLookup(name);
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
} else {
|
|
// call the property on the loaded base
|
|
Instruction::CallProperty call;
|
|
call.base = getParam(base);
|
|
call.name = registerString(name);
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args,
|
|
IR::Expr *result)
|
|
{
|
|
// call the property on the loaded base
|
|
Instruction::CallElement call;
|
|
call.base = getParam(base);
|
|
call.index = getParam(index);
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target)
|
|
{
|
|
// FIXME: do something more useful with this info
|
|
if (target->type & IR::NumberType && !(source->type & IR::NumberType))
|
|
unop(IR::OpUPlus, source, target);
|
|
else
|
|
copyValue(source, target);
|
|
}
|
|
|
|
void InstructionSelection::constructActivationProperty(IR::Name *func,
|
|
IR::ExprList *args,
|
|
IR::Expr *target)
|
|
{
|
|
if (useFastLookups && func->global) {
|
|
Instruction::ConstructGlobalLookup call;
|
|
call.index = registerGlobalGetterLookup(*func->id);
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(target);
|
|
addInstruction(call);
|
|
return;
|
|
}
|
|
Instruction::CreateActivationProperty create;
|
|
create.name = registerString(*func->id);
|
|
prepareCallArgs(args, create.argc);
|
|
create.callData = callDataStart();
|
|
create.result = getResultParam(target);
|
|
addInstruction(create);
|
|
}
|
|
|
|
void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *target)
|
|
{
|
|
if (useFastLookups) {
|
|
Instruction::ConstructPropertyLookup call;
|
|
call.base = getParam(base);
|
|
call.index = registerGetterLookup(name);
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(target);
|
|
addInstruction(call);
|
|
return;
|
|
}
|
|
Instruction::CreateProperty create;
|
|
create.base = getParam(base);
|
|
create.name = registerString(name);
|
|
prepareCallArgs(args, create.argc);
|
|
create.callData = callDataStart();
|
|
create.result = getResultParam(target);
|
|
addInstruction(create);
|
|
}
|
|
|
|
void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *target)
|
|
{
|
|
Instruction::CreateValue create;
|
|
create.func = getParam(value);
|
|
prepareCallArgs(args, create.argc);
|
|
create.callData = callDataStart();
|
|
create.result = getResultParam(target);
|
|
addInstruction(create);
|
|
}
|
|
|
|
void InstructionSelection::loadThisObject(IR::Expr *e)
|
|
{
|
|
Instruction::LoadThis load;
|
|
load.result = getResultParam(e);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::loadQmlContext(IR::Expr *e)
|
|
{
|
|
Instruction::LoadQmlContext load;
|
|
load.result = getResultParam(e);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::loadQmlImportedScripts(IR::Expr *e)
|
|
{
|
|
Instruction::LoadQmlImportedScripts load;
|
|
load.result = getResultParam(e);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *e)
|
|
{
|
|
Instruction::LoadQmlSingleton load;
|
|
load.result = getResultParam(e);
|
|
load.name = registerString(name);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *e)
|
|
{
|
|
Q_ASSERT(sourceConst);
|
|
|
|
Instruction::MoveConst move;
|
|
move.source = convertToValue(sourceConst).asReturnedValue();
|
|
move.result = getResultParam(e);
|
|
addInstruction(move);
|
|
}
|
|
|
|
void InstructionSelection::loadString(const QString &str, IR::Expr *target)
|
|
{
|
|
Instruction::LoadRuntimeString load;
|
|
load.stringId = registerString(str);
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target)
|
|
{
|
|
Instruction::LoadRegExp load;
|
|
load.regExpId = registerRegExp(sourceRegexp);
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target)
|
|
{
|
|
if (useFastLookups && name->global) {
|
|
Instruction::GetGlobalLookup load;
|
|
load.index = registerGlobalGetterLookup(*name->id);
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
}
|
|
Instruction::LoadName load;
|
|
load.name = registerString(*name->id);
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName)
|
|
{
|
|
Instruction::StoreName store;
|
|
store.source = getParam(source);
|
|
store.name = registerString(targetName);
|
|
addInstruction(store);
|
|
}
|
|
|
|
void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target)
|
|
{
|
|
int id = closure->value;
|
|
Instruction::LoadClosure load;
|
|
load.value = id;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target)
|
|
{
|
|
if (useFastLookups) {
|
|
Instruction::GetLookup load;
|
|
load.base = getParam(base);
|
|
load.index = registerGetterLookup(name);
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
}
|
|
Instruction::LoadProperty load;
|
|
load.base = getParam(base);
|
|
load.name = registerString(name);
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase,
|
|
const QString &targetName)
|
|
{
|
|
if (useFastLookups) {
|
|
Instruction::SetLookup store;
|
|
store.base = getParam(targetBase);
|
|
store.index = registerSetterLookup(targetName);
|
|
store.source = getParam(source);
|
|
addInstruction(store);
|
|
return;
|
|
}
|
|
Instruction::StoreProperty store;
|
|
store.base = getParam(targetBase);
|
|
store.name = registerString(targetName);
|
|
store.source = getParam(source);
|
|
addInstruction(store);
|
|
}
|
|
|
|
void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex)
|
|
{
|
|
if (kind == IR::Member::MemberOfQmlScopeObject) {
|
|
Instruction::StoreScopeObjectProperty store;
|
|
store.base = getParam(targetBase);
|
|
store.propertyIndex = propertyIndex;
|
|
store.source = getParam(source);
|
|
addInstruction(store);
|
|
} else if (kind == IR::Member::MemberOfQmlContextObject) {
|
|
Instruction::StoreContextObjectProperty store;
|
|
store.base = getParam(targetBase);
|
|
store.propertyIndex = propertyIndex;
|
|
store.source = getParam(source);
|
|
addInstruction(store);
|
|
} else {
|
|
Q_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex)
|
|
{
|
|
Instruction::StoreQObjectProperty store;
|
|
store.base = getParam(targetBase);
|
|
store.propertyIndex = propertyIndex;
|
|
store.source = getParam(source);
|
|
addInstruction(store);
|
|
}
|
|
|
|
void InstructionSelection::getQmlContextProperty(IR::Expr *source, IR::Member::MemberKind kind,
|
|
QQmlPropertyData *property, int index,
|
|
IR::Expr *target)
|
|
{
|
|
if (property && property->hasAccessors() && property->isFullyResolved()) {
|
|
if (kind == IR::Member::MemberOfQmlScopeObject) {
|
|
if (property->propType == QMetaType::QReal) {
|
|
Instruction::LoadScopeObjectQRealPropertyDirectly load;
|
|
load.base = getParam(source);
|
|
load.accessors = property->accessors;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
} else if (property->isQObject()) {
|
|
Instruction::LoadScopeObjectQObjectPropertyDirectly load;
|
|
load.base = getParam(source);
|
|
load.accessors = property->accessors;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
} else if (property->propType == QMetaType::Int) {
|
|
Instruction::LoadScopeObjectIntPropertyDirectly load;
|
|
load.base = getParam(source);
|
|
load.accessors = property->accessors;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
} else if (property->propType == QMetaType::Bool) {
|
|
Instruction::LoadScopeObjectBoolPropertyDirectly load;
|
|
load.base = getParam(source);
|
|
load.accessors = property->accessors;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
} else if (property->propType == QMetaType::QString) {
|
|
Instruction::LoadScopeObjectQStringPropertyDirectly load;
|
|
load.base = getParam(source);
|
|
load.accessors = property->accessors;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (kind == IR::Member::MemberOfQmlScopeObject) {
|
|
Instruction::LoadScopeObjectProperty load;
|
|
load.base = getParam(source);
|
|
load.propertyIndex = index;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
} else if (kind == IR::Member::MemberOfQmlContextObject) {
|
|
Instruction::LoadContextObjectProperty load;
|
|
load.base = getParam(source);
|
|
load.propertyIndex = index;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
} else if (kind == IR::Member::MemberOfIdObjectsArray) {
|
|
Instruction::LoadIdObject load;
|
|
load.base = getParam(source);
|
|
load.index = index;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
} else {
|
|
Q_ASSERT(false);
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::getQObjectProperty(IR::Expr *base, QQmlPropertyData *property, bool captureRequired, bool isSingletonProperty, int attachedPropertiesId, IR::Expr *target)
|
|
{
|
|
if (property && property->hasAccessors() && property->isFullyResolved()) {
|
|
if (!attachedPropertiesId && !isSingletonProperty) {
|
|
if (property->propType == QMetaType::QReal) {
|
|
Instruction::LoadQRealQObjectPropertyDirectly load;
|
|
load.base = getParam(base);
|
|
load.accessors = property->accessors;
|
|
load.coreIndex = property->coreIndex;
|
|
load.notifyIndex = captureRequired ? property->notifyIndex : -1;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
} else if (property->isQObject()) {
|
|
Instruction::LoadQObjectQObjectPropertyDirectly load;
|
|
load.base = getParam(base);
|
|
load.accessors = property->accessors;
|
|
load.coreIndex = property->coreIndex;
|
|
load.notifyIndex = captureRequired ? property->notifyIndex : -1;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
} else if (property->propType == QMetaType::Int) {
|
|
Instruction::LoadIntQObjectPropertyDirectly load;
|
|
load.base = getParam(base);
|
|
load.accessors = property->accessors;
|
|
load.coreIndex = property->coreIndex;
|
|
load.notifyIndex = captureRequired ? property->notifyIndex : -1;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
} else if (property->propType == QMetaType::Bool) {
|
|
Instruction::LoadBoolQObjectPropertyDirectly load;
|
|
load.base = getParam(base);
|
|
load.accessors = property->accessors;
|
|
load.coreIndex = property->coreIndex;
|
|
load.notifyIndex = captureRequired ? property->notifyIndex : -1;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
} else if (property->propType == QMetaType::QString) {
|
|
Instruction::LoadQStringQObjectPropertyDirectly load;
|
|
load.base = getParam(base);
|
|
load.accessors = property->accessors;
|
|
load.coreIndex = property->coreIndex;
|
|
load.notifyIndex = captureRequired ? property->notifyIndex : -1;
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
const int propertyIndex = property->coreIndex;
|
|
if (attachedPropertiesId != 0) {
|
|
Instruction::LoadAttachedQObjectProperty load;
|
|
load.propertyIndex = propertyIndex;
|
|
load.result = getResultParam(target);
|
|
load.attachedPropertiesId = attachedPropertiesId;
|
|
addInstruction(load);
|
|
} else if (isSingletonProperty) {
|
|
Instruction::LoadSingletonQObjectProperty load;
|
|
load.base = getParam(base);
|
|
load.propertyIndex = propertyIndex;
|
|
load.result = getResultParam(target);
|
|
load.captureRequired = captureRequired;
|
|
addInstruction(load);
|
|
} else {
|
|
Instruction::LoadQObjectProperty load;
|
|
load.base = getParam(base);
|
|
load.propertyIndex = propertyIndex;
|
|
load.result = getResultParam(target);
|
|
load.captureRequired = captureRequired;
|
|
addInstruction(load);
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target)
|
|
{
|
|
if (useFastLookups) {
|
|
Instruction::LoadElementLookup load;
|
|
load.lookup = registerIndexedGetterLookup();
|
|
load.base = getParam(base);
|
|
load.index = getParam(index);
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
return;
|
|
}
|
|
Instruction::LoadElement load;
|
|
load.base = getParam(base);
|
|
load.index = getParam(index);
|
|
load.result = getResultParam(target);
|
|
addInstruction(load);
|
|
}
|
|
|
|
void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase,
|
|
IR::Expr *targetIndex)
|
|
{
|
|
if (useFastLookups) {
|
|
Instruction::StoreElementLookup store;
|
|
store.lookup = registerIndexedSetterLookup();
|
|
store.base = getParam(targetBase);
|
|
store.index = getParam(targetIndex);
|
|
store.source = getParam(source);
|
|
addInstruction(store);
|
|
return;
|
|
}
|
|
Instruction::StoreElement store;
|
|
store.base = getParam(targetBase);
|
|
store.index = getParam(targetIndex);
|
|
store.source = getParam(source);
|
|
addInstruction(store);
|
|
}
|
|
|
|
void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target)
|
|
{
|
|
Instruction::Move move;
|
|
move.source = getParam(source);
|
|
move.result = getResultParam(target);
|
|
if (move.source != move.result)
|
|
addInstruction(move);
|
|
}
|
|
|
|
void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target)
|
|
{
|
|
Instruction::SwapTemps swap;
|
|
swap.left = getParam(source);
|
|
swap.right = getParam(target);
|
|
addInstruction(swap);
|
|
}
|
|
|
|
void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target)
|
|
{
|
|
switch (oper) {
|
|
case IR::OpIfTrue:
|
|
Q_ASSERT(!"unreachable"); break;
|
|
case IR::OpNot: {
|
|
// ### enabling this fails in some cases, where apparently the value is not a bool at runtime
|
|
if (0 && isBoolType(source)) {
|
|
Instruction::UNotBool unot;
|
|
unot.source = getParam(source);
|
|
unot.result = getResultParam(target);
|
|
addInstruction(unot);
|
|
return;
|
|
}
|
|
Instruction::UNot unot;
|
|
unot.source = getParam(source);
|
|
unot.result = getResultParam(target);
|
|
addInstruction(unot);
|
|
return;
|
|
}
|
|
case IR::OpUMinus: {
|
|
Instruction::UMinus uminus;
|
|
uminus.source = getParam(source);
|
|
uminus.result = getResultParam(target);
|
|
addInstruction(uminus);
|
|
return;
|
|
}
|
|
case IR::OpUPlus: {
|
|
if (isNumberType(source)) {
|
|
// use a move
|
|
Instruction::Move move;
|
|
move.source = getParam(source);
|
|
move.result = getResultParam(target);
|
|
if (move.source != move.result)
|
|
addInstruction(move);
|
|
return;
|
|
}
|
|
Instruction::UPlus uplus;
|
|
uplus.source = getParam(source);
|
|
uplus.result = getResultParam(target);
|
|
addInstruction(uplus);
|
|
return;
|
|
}
|
|
case IR::OpCompl: {
|
|
// ### enabling this fails in some cases, where apparently the value is not a int at runtime
|
|
if (0 && isIntegerType(source)) {
|
|
Instruction::UComplInt unot;
|
|
unot.source = getParam(source);
|
|
unot.result = getResultParam(target);
|
|
addInstruction(unot);
|
|
return;
|
|
}
|
|
Instruction::UCompl ucompl;
|
|
ucompl.source = getParam(source);
|
|
ucompl.result = getResultParam(target);
|
|
addInstruction(ucompl);
|
|
return;
|
|
}
|
|
case IR::OpIncrement: {
|
|
Instruction::Increment inc;
|
|
inc.source = getParam(source);
|
|
inc.result = getResultParam(target);
|
|
addInstruction(inc);
|
|
return;
|
|
}
|
|
case IR::OpDecrement: {
|
|
Instruction::Decrement dec;
|
|
dec.source = getParam(source);
|
|
dec.result = getResultParam(target);
|
|
addInstruction(dec);
|
|
return;
|
|
}
|
|
default: break;
|
|
} // switch
|
|
|
|
Q_ASSERT(!"unreachable");
|
|
}
|
|
|
|
void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
|
|
{
|
|
binopHelper(oper, leftSource, rightSource, target);
|
|
}
|
|
|
|
Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
|
|
{
|
|
if (oper == IR::OpAdd) {
|
|
Instruction::Add add;
|
|
add.lhs = getParam(leftSource);
|
|
add.rhs = getParam(rightSource);
|
|
add.result = getResultParam(target);
|
|
addInstruction(add);
|
|
return add.result;
|
|
}
|
|
if (oper == IR::OpSub) {
|
|
Instruction::Sub sub;
|
|
sub.lhs = getParam(leftSource);
|
|
sub.rhs = getParam(rightSource);
|
|
sub.result = getResultParam(target);
|
|
addInstruction(sub);
|
|
return sub.result;
|
|
}
|
|
if (oper == IR::OpMul) {
|
|
Instruction::Mul mul;
|
|
mul.lhs = getParam(leftSource);
|
|
mul.rhs = getParam(rightSource);
|
|
mul.result = getResultParam(target);
|
|
addInstruction(mul);
|
|
return mul.result;
|
|
}
|
|
if (oper == IR::OpBitAnd) {
|
|
if (leftSource->asConst())
|
|
qSwap(leftSource, rightSource);
|
|
if (IR::Const *c = rightSource->asConst()) {
|
|
Instruction::BitAndConst bitAnd;
|
|
bitAnd.lhs = getParam(leftSource);
|
|
bitAnd.rhs = convertToValue(c).Value::toInt32();
|
|
bitAnd.result = getResultParam(target);
|
|
addInstruction(bitAnd);
|
|
return bitAnd.result;
|
|
}
|
|
Instruction::BitAnd bitAnd;
|
|
bitAnd.lhs = getParam(leftSource);
|
|
bitAnd.rhs = getParam(rightSource);
|
|
bitAnd.result = getResultParam(target);
|
|
addInstruction(bitAnd);
|
|
return bitAnd.result;
|
|
}
|
|
if (oper == IR::OpBitOr) {
|
|
if (leftSource->asConst())
|
|
qSwap(leftSource, rightSource);
|
|
if (IR::Const *c = rightSource->asConst()) {
|
|
Instruction::BitOrConst bitOr;
|
|
bitOr.lhs = getParam(leftSource);
|
|
bitOr.rhs = convertToValue(c).Value::toInt32();
|
|
bitOr.result = getResultParam(target);
|
|
addInstruction(bitOr);
|
|
return bitOr.result;
|
|
}
|
|
Instruction::BitOr bitOr;
|
|
bitOr.lhs = getParam(leftSource);
|
|
bitOr.rhs = getParam(rightSource);
|
|
bitOr.result = getResultParam(target);
|
|
addInstruction(bitOr);
|
|
return bitOr.result;
|
|
}
|
|
if (oper == IR::OpBitXor) {
|
|
if (leftSource->asConst())
|
|
qSwap(leftSource, rightSource);
|
|
if (IR::Const *c = rightSource->asConst()) {
|
|
Instruction::BitXorConst bitXor;
|
|
bitXor.lhs = getParam(leftSource);
|
|
bitXor.rhs = convertToValue(c).Value::toInt32();
|
|
bitXor.result = getResultParam(target);
|
|
addInstruction(bitXor);
|
|
return bitXor.result;
|
|
}
|
|
Instruction::BitXor bitXor;
|
|
bitXor.lhs = getParam(leftSource);
|
|
bitXor.rhs = getParam(rightSource);
|
|
bitXor.result = getResultParam(target);
|
|
addInstruction(bitXor);
|
|
return bitXor.result;
|
|
}
|
|
if (oper == IR::OpRShift) {
|
|
if (IR::Const *c = rightSource->asConst()) {
|
|
Instruction::ShrConst shr;
|
|
shr.lhs = getParam(leftSource);
|
|
shr.rhs = convertToValue(c).Value::toInt32() & 0x1f;
|
|
shr.result = getResultParam(target);
|
|
addInstruction(shr);
|
|
return shr.result;
|
|
}
|
|
Instruction::Shr shr;
|
|
shr.lhs = getParam(leftSource);
|
|
shr.rhs = getParam(rightSource);
|
|
shr.result = getResultParam(target);
|
|
addInstruction(shr);
|
|
return shr.result;
|
|
}
|
|
if (oper == IR::OpLShift) {
|
|
if (IR::Const *c = rightSource->asConst()) {
|
|
Instruction::ShlConst shl;
|
|
shl.lhs = getParam(leftSource);
|
|
shl.rhs = convertToValue(c).Value::toInt32() & 0x1f;
|
|
shl.result = getResultParam(target);
|
|
addInstruction(shl);
|
|
return shl.result;
|
|
}
|
|
Instruction::Shl shl;
|
|
shl.lhs = getParam(leftSource);
|
|
shl.rhs = getParam(rightSource);
|
|
shl.result = getResultParam(target);
|
|
addInstruction(shl);
|
|
return shl.result;
|
|
}
|
|
|
|
if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) {
|
|
Instruction::BinopContext binop;
|
|
if (oper == IR::OpInstanceof)
|
|
binop.alu = offsetof(QV4::Runtime, instanceof);
|
|
else if (oper == IR::OpIn)
|
|
binop.alu = offsetof(QV4::Runtime, in);
|
|
else
|
|
binop.alu = offsetof(QV4::Runtime, add);
|
|
binop.lhs = getParam(leftSource);
|
|
binop.rhs = getParam(rightSource);
|
|
binop.result = getResultParam(target);
|
|
Q_ASSERT(binop.alu);
|
|
addInstruction(binop);
|
|
return binop.result;
|
|
} else {
|
|
Instruction::Binop binop;
|
|
binop.alu = aluOpFunction(oper);
|
|
binop.lhs = getParam(leftSource);
|
|
binop.rhs = getParam(rightSource);
|
|
binop.result = getResultParam(target);
|
|
Q_ASSERT(binop.alu);
|
|
addInstruction(binop);
|
|
return binop.result;
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 *args)
|
|
{
|
|
int argLocation = outgoingArgumentTempStart();
|
|
argc = 0;
|
|
if (args)
|
|
*args = argLocation;
|
|
if (e) {
|
|
// We need to move all the temps into the function arg array
|
|
Q_ASSERT(argLocation >= 0);
|
|
while (e) {
|
|
if (IR::Const *c = e->expr->asConst()) {
|
|
Instruction::MoveConst move;
|
|
move.source = convertToValue(c).asReturnedValue();
|
|
move.result = Param::createTemp(argLocation);
|
|
addInstruction(move);
|
|
} else {
|
|
Instruction::Move move;
|
|
move.source = getParam(e->expr);
|
|
move.result = Param::createTemp(argLocation);
|
|
addInstruction(move);
|
|
}
|
|
++argLocation;
|
|
++argc;
|
|
e = e->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::visitJump(IR::Jump *s)
|
|
{
|
|
if (s->target == _nextBlock)
|
|
return;
|
|
if (_removableJumps.contains(s))
|
|
return;
|
|
|
|
if (blockNeedsDebugInstruction) {
|
|
Instruction::Debug debug;
|
|
debug.lineNumber = -int(currentLine);
|
|
addInstruction(debug);
|
|
}
|
|
|
|
Instruction::Jump jump;
|
|
jump.offset = 0;
|
|
ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
|
|
|
|
_patches[s->target].append(loc);
|
|
}
|
|
|
|
void InstructionSelection::visitCJump(IR::CJump *s)
|
|
{
|
|
if (blockNeedsDebugInstruction) {
|
|
Instruction::Debug debug;
|
|
debug.lineNumber = -int(currentLine);
|
|
addInstruction(debug);
|
|
}
|
|
|
|
Param condition;
|
|
if (IR::Temp *t = s->cond->asTemp()) {
|
|
condition = getResultParam(t);
|
|
} else if (IR::Binop *b = s->cond->asBinop()) {
|
|
condition = binopHelper(b->op, b->left, b->right, /*target*/0);
|
|
} else {
|
|
Q_UNIMPLEMENTED();
|
|
}
|
|
|
|
if (s->iftrue == _nextBlock) {
|
|
Instruction::JumpNe jump;
|
|
jump.offset = 0;
|
|
jump.condition = condition;
|
|
ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
|
|
_patches[s->iffalse].append(falseLoc);
|
|
} else {
|
|
Instruction::JumpEq jump;
|
|
jump.offset = 0;
|
|
jump.condition = condition;
|
|
ptrdiff_t trueLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
|
|
_patches[s->iftrue].append(trueLoc);
|
|
|
|
if (s->iffalse != _nextBlock) {
|
|
Instruction::Jump jump;
|
|
jump.offset = 0;
|
|
ptrdiff_t falseLoc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
|
|
_patches[s->iffalse].append(falseLoc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::visitRet(IR::Ret *s)
|
|
{
|
|
if (blockNeedsDebugInstruction) {
|
|
// this is required so stepOut will always be guaranteed to stop in every stack frame
|
|
Instruction::Debug debug;
|
|
debug.lineNumber = -int(currentLine);
|
|
addInstruction(debug);
|
|
}
|
|
|
|
Instruction::Ret ret;
|
|
ret.result = getParam(s->expr);
|
|
addInstruction(ret);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result)
|
|
{
|
|
if (useFastLookups && func->global) {
|
|
Instruction::CallGlobalLookup call;
|
|
call.index = registerGlobalGetterLookup(*func->id);
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
return;
|
|
}
|
|
Instruction::CallActivationProperty call;
|
|
call.name = registerString(*func->id);
|
|
prepareCallArgs(args, call.argc);
|
|
call.callData = callDataStart();
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result)
|
|
{
|
|
if (kind == IR::Member::MemberOfQmlScopeObject) {
|
|
Instruction::CallBuiltinTypeofScopeObjectProperty call;
|
|
call.base = getParam(base);
|
|
call.index = propertyIndex;
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
} else if (kind == IR::Member::MemberOfQmlContextObject) {
|
|
Instruction::CallBuiltinTypeofContextObjectProperty call;
|
|
call.base = getParam(base);
|
|
call.index = propertyIndex;
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
} else {
|
|
Q_UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name,
|
|
IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinTypeofMember call;
|
|
call.base = getParam(base);
|
|
call.member = registerString(name);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index,
|
|
IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinTypeofSubscript call;
|
|
call.base = getParam(base);
|
|
call.index = getParam(index);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinTypeofName call;
|
|
call.name = registerString(name);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinTypeofValue call;
|
|
call.value = getParam(value);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinDeleteMember call;
|
|
call.base = getParam(base);
|
|
call.member = registerString(name);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index,
|
|
IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinDeleteSubscript call;
|
|
call.base = getParam(base);
|
|
call.index = getParam(index);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinDeleteName call;
|
|
call.name = registerString(name);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result)
|
|
{
|
|
Instruction::MoveConst move;
|
|
move.source = QV4::Encode(false);
|
|
move.result = getResultParam(result);
|
|
addInstruction(move);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinThrow(IR::Expr *arg)
|
|
{
|
|
Instruction::CallBuiltinThrow call;
|
|
call.arg = getParam(arg);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinReThrow()
|
|
{
|
|
if (_block->catchBlock) {
|
|
// jump to exception handler
|
|
Instruction::Jump jump;
|
|
jump.offset = 0;
|
|
ptrdiff_t loc = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump));
|
|
|
|
_patches[_block->catchBlock].append(loc);
|
|
} else {
|
|
Instruction::Ret ret;
|
|
int idx = jsUnitGenerator()->registerConstant(QV4::Encode::undefined());
|
|
ret.result = Param::createConstant(idx);
|
|
addInstruction(ret);
|
|
}
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinUnwindException(IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinUnwindException call;
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
|
|
void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName)
|
|
{
|
|
Instruction::CallBuiltinPushCatchScope call;
|
|
call.name = registerString(exceptionName);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinForeachIteratorObject call;
|
|
call.arg = getParam(arg);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinForeachNextPropertyName call;
|
|
call.arg = getParam(arg);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg)
|
|
{
|
|
Instruction::CallBuiltinPushScope call;
|
|
call.arg = getParam(arg);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinPopScope()
|
|
{
|
|
Instruction::CallBuiltinPopScope call;
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
|
|
{
|
|
Instruction::CallBuiltinDeclareVar call;
|
|
call.isDeletable = deletable;
|
|
call.varName = registerString(name);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args)
|
|
{
|
|
Instruction::CallBuiltinDefineArray call;
|
|
prepareCallArgs(args, call.argc, &call.args);
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray)
|
|
{
|
|
int argLocation = outgoingArgumentTempStart();
|
|
|
|
const int classId = registerJSClass(keyValuePairCount, keyValuePairs);
|
|
|
|
// Process key/value pairs first
|
|
IR::ExprList *it = keyValuePairs;
|
|
for (int i = 0; i < keyValuePairCount; ++i, it = it->next) {
|
|
// Skip name
|
|
it = it->next;
|
|
|
|
bool isData = it->expr->asConst()->value;
|
|
it = it->next;
|
|
|
|
if (IR::Const *c = it->expr->asConst()) {
|
|
Instruction::MoveConst move;
|
|
move.source = convertToValue(c).asReturnedValue();
|
|
move.result = Param::createTemp(argLocation);
|
|
addInstruction(move);
|
|
} else {
|
|
Instruction::Move move;
|
|
move.source = getParam(it->expr);
|
|
move.result = Param::createTemp(argLocation);
|
|
addInstruction(move);
|
|
}
|
|
++argLocation;
|
|
|
|
if (!isData) {
|
|
it = it->next;
|
|
|
|
Instruction::Move move;
|
|
move.source = getParam(it->expr);
|
|
move.result = Param::createTemp(argLocation);
|
|
addInstruction(move);
|
|
++argLocation;
|
|
}
|
|
}
|
|
|
|
// Process array values
|
|
uint arrayValueCount = 0;
|
|
it = arrayEntries;
|
|
while (it) {
|
|
IR::Const *index = it->expr->asConst();
|
|
it = it->next;
|
|
|
|
bool isData = it->expr->asConst()->value;
|
|
it = it->next;
|
|
|
|
if (!isData) {
|
|
it = it->next; // getter
|
|
it = it->next; // setter
|
|
continue;
|
|
}
|
|
|
|
++arrayValueCount;
|
|
|
|
Instruction::MoveConst indexMove;
|
|
indexMove.source = convertToValue(index).asReturnedValue();
|
|
indexMove.result = Param::createTemp(argLocation);
|
|
addInstruction(indexMove);
|
|
++argLocation;
|
|
|
|
Instruction::Move move;
|
|
move.source = getParam(it->expr);
|
|
move.result = Param::createTemp(argLocation);
|
|
addInstruction(move);
|
|
++argLocation;
|
|
it = it->next;
|
|
}
|
|
|
|
// Process array getter/setter pairs
|
|
uint arrayGetterSetterCount = 0;
|
|
it = arrayEntries;
|
|
while (it) {
|
|
IR::Const *index = it->expr->asConst();
|
|
it = it->next;
|
|
|
|
bool isData = it->expr->asConst()->value;
|
|
it = it->next;
|
|
|
|
if (isData) {
|
|
it = it->next; // value
|
|
continue;
|
|
}
|
|
|
|
++arrayGetterSetterCount;
|
|
|
|
Instruction::MoveConst indexMove;
|
|
indexMove.source = convertToValue(index).asReturnedValue();
|
|
indexMove.result = Param::createTemp(argLocation);
|
|
addInstruction(indexMove);
|
|
++argLocation;
|
|
|
|
// getter
|
|
Instruction::Move moveGetter;
|
|
moveGetter.source = getParam(it->expr);
|
|
moveGetter.result = Param::createTemp(argLocation);
|
|
addInstruction(moveGetter);
|
|
++argLocation;
|
|
it = it->next;
|
|
|
|
// setter
|
|
Instruction::Move moveSetter;
|
|
moveSetter.source = getParam(it->expr);
|
|
moveSetter.result = Param::createTemp(argLocation);
|
|
addInstruction(moveSetter);
|
|
++argLocation;
|
|
it = it->next;
|
|
}
|
|
|
|
Instruction::CallBuiltinDefineObjectLiteral call;
|
|
call.internalClassId = classId;
|
|
call.arrayValueCount = arrayValueCount;
|
|
call.arrayGetterSetterCountAndFlags = arrayGetterSetterCount | (needSparseArray << 30);
|
|
call.args = outgoingArgumentTempStart();
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result)
|
|
{
|
|
Instruction::CallBuiltinSetupArgumentsObject call;
|
|
call.result = getResultParam(result);
|
|
addInstruction(call);
|
|
}
|
|
|
|
|
|
void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject()
|
|
{
|
|
Instruction::CallBuiltinConvertThisToObject call;
|
|
addInstruction(call);
|
|
}
|
|
|
|
ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr)
|
|
{
|
|
|
|
#ifdef MOTH_THREADED_INTERPRETER
|
|
instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)];
|
|
#else
|
|
instr.common.instructionType = type;
|
|
#endif
|
|
|
|
int instructionSize = Instr::size(type);
|
|
if (_codeEnd - _codeNext < instructionSize) {
|
|
int currSize = _codeEnd - _codeStart;
|
|
uchar *newCode = new uchar[currSize * 2];
|
|
::memset(newCode + currSize, 0, currSize);
|
|
::memcpy(newCode, _codeStart, currSize);
|
|
_codeNext = _codeNext - _codeStart + newCode;
|
|
delete[] _codeStart;
|
|
_codeStart = newCode;
|
|
_codeEnd = _codeStart + currSize * 2;
|
|
}
|
|
|
|
::memcpy(_codeNext, reinterpret_cast<const char *>(&instr), instructionSize);
|
|
ptrdiff_t ptrOffset = _codeNext - _codeStart;
|
|
_codeNext += instructionSize;
|
|
|
|
return ptrOffset;
|
|
}
|
|
|
|
void InstructionSelection::patchJumpAddresses()
|
|
{
|
|
typedef QHash<IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator PatchIt;
|
|
for (PatchIt i = _patches.cbegin(), ei = _patches.cend(); i != ei; ++i) {
|
|
Q_ASSERT(_addrs.contains(i.key()));
|
|
ptrdiff_t target = _addrs.value(i.key());
|
|
|
|
const QVector<ptrdiff_t> &patchList = i.value();
|
|
for (int ii = 0, eii = patchList.count(); ii < eii; ++ii) {
|
|
ptrdiff_t patch = patchList.at(ii);
|
|
|
|
*((ptrdiff_t *)(_codeStart + patch)) = target - patch;
|
|
}
|
|
}
|
|
|
|
_patches.clear();
|
|
_addrs.clear();
|
|
}
|
|
|
|
QByteArray InstructionSelection::squeezeCode() const
|
|
{
|
|
int codeSize = _codeNext - _codeStart;
|
|
QByteArray squeezed;
|
|
squeezed.resize(codeSize);
|
|
::memcpy(squeezed.data(), _codeStart, codeSize);
|
|
return squeezed;
|
|
}
|
|
|
|
Param InstructionSelection::getParam(IR::Expr *e) {
|
|
Q_ASSERT(e);
|
|
|
|
if (IR::Const *c = e->asConst()) {
|
|
int idx = jsUnitGenerator()->registerConstant(convertToValue(c).asReturnedValue());
|
|
return Param::createConstant(idx);
|
|
} else if (IR::Temp *t = e->asTemp()) {
|
|
switch (t->kind) {
|
|
case IR::Temp::StackSlot:
|
|
return Param::createTemp(t->index);
|
|
default:
|
|
Q_UNREACHABLE();
|
|
return Param();
|
|
}
|
|
} else if (IR::ArgLocal *al = e->asArgLocal()) {
|
|
switch (al->kind) {
|
|
case IR::ArgLocal::Formal:
|
|
case IR::ArgLocal::ScopedFormal: return Param::createArgument(al->index, al->scope);
|
|
case IR::ArgLocal::Local: return Param::createLocal(al->index);
|
|
case IR::ArgLocal::ScopedLocal: return Param::createScopedLocal(al->index, al->scope);
|
|
default:
|
|
Q_UNREACHABLE();
|
|
return Param();
|
|
}
|
|
} else {
|
|
Q_UNIMPLEMENTED();
|
|
return Param();
|
|
}
|
|
}
|
|
|
|
|
|
CompilationUnit::~CompilationUnit()
|
|
{
|
|
}
|
|
|
|
void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
|
|
{
|
|
runtimeFunctions.resize(data->functionTableSize);
|
|
runtimeFunctions.fill(0);
|
|
for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
|
|
const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
|
|
|
|
QV4::Function *runtimeFunction = new QV4::Function(engine, this, compiledFunction, &VME::exec);
|
|
runtimeFunction->codeData = reinterpret_cast<const uchar *>(codeRefs.at(i).constData());
|
|
runtimeFunctions[i] = runtimeFunction;
|
|
}
|
|
}
|