diff options
Diffstat (limited to 'src/v4/moth')
-rw-r--r-- | src/v4/moth/moth.pri | 13 | ||||
-rw-r--r-- | src/v4/moth/qv4instr_moth.cpp | 15 | ||||
-rw-r--r-- | src/v4/moth/qv4instr_moth_p.h | 514 | ||||
-rw-r--r-- | src/v4/moth/qv4isel_moth.cpp | 975 | ||||
-rw-r--r-- | src/v4/moth/qv4isel_moth_p.h | 168 | ||||
-rw-r--r-- | src/v4/moth/qv4vme_moth.cpp | 483 | ||||
-rw-r--r-- | src/v4/moth/qv4vme_moth_p.h | 37 |
7 files changed, 2205 insertions, 0 deletions
diff --git a/src/v4/moth/moth.pri b/src/v4/moth/moth.pri new file mode 100644 index 0000000000..73bd893286 --- /dev/null +++ b/src/v4/moth/moth.pri @@ -0,0 +1,13 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qv4isel_moth_p.h \ + $$PWD/qv4instr_moth_p.h \ + $$PWD/qv4vme_moth_p.h + +SOURCES += \ + $$PWD/qv4isel_moth.cpp \ + $$PWD/qv4instr_moth.cpp \ + $$PWD/qv4vme_moth.cpp + +#DEFINES += DO_TRACE_INSTR diff --git a/src/v4/moth/qv4instr_moth.cpp b/src/v4/moth/qv4instr_moth.cpp new file mode 100644 index 0000000000..a2bad39e00 --- /dev/null +++ b/src/v4/moth/qv4instr_moth.cpp @@ -0,0 +1,15 @@ +#include "qv4instr_moth_p.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +int Instr::size(Type type) +{ +#define MOTH_RETURN_INSTR_SIZE(I, FMT) case I: return InstrMeta<(int)I>::Size; + switch (type) { + FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE) + default: return 0; + } +#undef MOTH_RETURN_INSTR_SIZE +} + diff --git a/src/v4/moth/qv4instr_moth_p.h b/src/v4/moth/qv4instr_moth_p.h new file mode 100644 index 0000000000..741ddba79d --- /dev/null +++ b/src/v4/moth/qv4instr_moth_p.h @@ -0,0 +1,514 @@ +#ifndef QV4INSTR_MOTH_P_H +#define QV4INSTR_MOTH_P_H + +#include <QtCore/qglobal.h> +#include "qv4object.h" + +#define FOR_EACH_MOTH_INSTR(F) \ + F(Ret, ret) \ + F(LoadValue, loadValue) \ + F(LoadClosure, loadClosure) \ + F(MoveTemp, moveTemp) \ + F(LoadName, loadName) \ + F(StoreName, storeName) \ + F(LoadElement, loadElement) \ + F(StoreElement, storeElement) \ + F(LoadProperty, loadProperty) \ + F(StoreProperty, storeProperty) \ + F(Push, push) \ + F(CallValue, callValue) \ + F(CallProperty, callProperty) \ + F(CallElement, callElement) \ + F(CallActivationProperty, callActivationProperty) \ + F(CallBuiltinThrow, callBuiltinThrow) \ + F(CallBuiltinCreateExceptionHandler, callBuiltinCreateExceptionHandler) \ + F(CallBuiltinDeleteExceptionHandler, callBuiltinDeleteExceptionHandler) \ + F(CallBuiltinGetException, callBuiltinGetException) \ + F(CallBuiltinPushScope, callBuiltinPushScope) \ + F(CallBuiltinPopScope, callBuiltinPopScope) \ + F(CallBuiltinForeachIteratorObject, callBuiltinForeachIteratorObject) \ + F(CallBuiltinForeachNextPropertyName, callBuiltinForeachNextPropertyName) \ + F(CallBuiltinDeleteMember, callBuiltinDeleteMember) \ + F(CallBuiltinDeleteSubscript, callBuiltinDeleteSubscript) \ + F(CallBuiltinDeleteName, callBuiltinDeleteName) \ + F(CallBuiltinTypeofMember, callBuiltinTypeofMember) \ + F(CallBuiltinTypeofSubscript, callBuiltinTypeofSubscript) \ + F(CallBuiltinTypeofName, callBuiltinTypeofName) \ + F(CallBuiltinTypeofValue, callBuiltinTypeofValue) \ + F(CallBuiltinPostIncMember, callBuiltinPostIncMember) \ + F(CallBuiltinPostIncSubscript, callBuiltinPostIncSubscript) \ + F(CallBuiltinPostIncName, callBuiltinPostIncName) \ + F(CallBuiltinPostIncValue, callBuiltinPostIncValue) \ + F(CallBuiltinPostDecMember, callBuiltinPostDecMember) \ + F(CallBuiltinPostDecSubscript, callBuiltinPostDecSubscript) \ + F(CallBuiltinPostDecName, callBuiltinPostDecName) \ + F(CallBuiltinPostDecValue, callBuiltinPostDecValue) \ + F(CallBuiltinDeclareVar, callBuiltinDeclareVar) \ + F(CallBuiltinDefineGetterSetter, callBuiltinDefineGetterSetter) \ + F(CallBuiltinDefineProperty, callBuiltinDefineProperty) \ + F(CallBuiltinDefineArrayProperty, callBuiltinDefineArrayProperty) \ + F(CreateValue, createValue) \ + F(CreateProperty, createProperty) \ + F(CreateActivationProperty, createActivationProperty) \ + F(Jump, jump) \ + F(CJump, cjump) \ + F(Unop, unop) \ + F(Binop, binop) \ + F(LoadThis, loadThis) \ + F(InplaceElementOp, inplaceElementOp) \ + F(InplaceMemberOp, inplaceMemberOp) \ + F(InplaceNameOp, inplaceNameOp) + +#if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) +# define MOTH_THREADED_INTERPRETER +#endif + +#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QQmlJS::Moth::Instr) - 1) + +#ifdef MOTH_THREADED_INTERPRETER +# define MOTH_INSTR_HEADER void *code; +#else +# define MOTH_INSTR_HEADER quint8 instructionType; +#endif + +#define MOTH_INSTR_ENUM(I, FMT) I, +#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QQmlJS::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK) + + +namespace QQmlJS { +namespace Moth { + +union Instr +{ + struct Param { + enum { + ValueType = 0, + ArgumentType = 1, + LocalType = 2, + TempType = 3 + }; + VM::Value value; + unsigned type : 2; + unsigned index : 30; + + bool isValue() const { return type == ValueType; } + bool isArgument() const { return type == ArgumentType; } + bool isLocal() const { return type == LocalType; } + bool isTemp() const { return type == TempType; } + + static Param createValue(const VM::Value &v) + { + Param p; + p.type = ValueType; + p.value = v; + return p; + } + + static Param createArgument(unsigned idx) + { + Param p; + p.type = ArgumentType; + p.index = idx; + return p; + } + + static Param createLocal(unsigned idx) + { + Param p; + p.type = LocalType; + p.index = idx; + return p; + } + + static Param createTemp(unsigned idx) + { + Param p; + p.type = TempType; + p.index = idx; + return p; + } + }; + + enum Type { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM) + }; + + struct instr_common { + MOTH_INSTR_HEADER + }; + struct instr_ret { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_loadValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_moveTemp { + MOTH_INSTR_HEADER + Param source; + Param result; + }; + struct instr_loadClosure { + MOTH_INSTR_HEADER + VM::Function *value; + Param result; + }; + struct instr_loadName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_storeName { + MOTH_INSTR_HEADER + VM::String *name; + Param source; + }; + struct instr_loadProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param base; + Param result; + }; + struct instr_storeProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param base; + Param source; + }; + struct instr_loadElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_storeElement { + MOTH_INSTR_HEADER + Param base; + Param index; + Param source; + }; + struct instr_push { + MOTH_INSTR_HEADER + quint32 value; + }; + struct instr_callValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param dest; + Param result; + }; + struct instr_callProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_callElement { + MOTH_INSTR_HEADER + Param base; + Param index; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callActivationProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_callBuiltinThrow { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinCreateExceptionHandler { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_callBuiltinDeleteExceptionHandler { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinGetException { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_callBuiltinPushScope { + MOTH_INSTR_HEADER + Param arg; + }; + struct instr_callBuiltinPopScope { + MOTH_INSTR_HEADER + }; + struct instr_callBuiltinForeachIteratorObject { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinForeachNextPropertyName { + MOTH_INSTR_HEADER + Param arg; + Param result; + }; + struct instr_callBuiltinDeleteMember { + MOTH_INSTR_HEADER + VM::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinDeleteSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinDeleteName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinTypeofMember { + MOTH_INSTR_HEADER + VM::String *member; + Param base; + Param result; + }; + struct instr_callBuiltinTypeofSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinTypeofName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinTypeofValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostIncMember { + MOTH_INSTR_HEADER + Param base; + VM::String *member; + Param result; + }; + struct instr_callBuiltinPostIncSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostIncName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinPostIncValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinPostDecMember { + MOTH_INSTR_HEADER + Param base; + VM::String *member; + Param result; + }; + struct instr_callBuiltinPostDecSubscript { + MOTH_INSTR_HEADER + Param base; + Param index; + Param result; + }; + struct instr_callBuiltinPostDecName { + MOTH_INSTR_HEADER + VM::String *name; + Param result; + }; + struct instr_callBuiltinPostDecValue { + MOTH_INSTR_HEADER + Param value; + Param result; + }; + struct instr_callBuiltinDeclareVar { + MOTH_INSTR_HEADER + VM::String *varName; + bool isDeletable; + }; + struct instr_callBuiltinDefineGetterSetter { + MOTH_INSTR_HEADER + VM::String *name; + Param object; + Param getter; + Param setter; + }; + struct instr_callBuiltinDefineProperty { + MOTH_INSTR_HEADER + VM::String *name; + Param object; + Param value; + }; + struct instr_callBuiltinDefineArrayProperty { + MOTH_INSTR_HEADER + Param object; + Param value; + int index; + }; + struct instr_createValue { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + Param func; + Param result; + }; + struct instr_createProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param base; + Param result; + }; + struct instr_createActivationProperty { + MOTH_INSTR_HEADER + VM::String *name; + quint32 argc; + quint32 args; + Param result; + }; + struct instr_jump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + }; + struct instr_cjump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + Param condition; + }; + struct instr_unop { + MOTH_INSTR_HEADER + VM::Value (*alu)(const VM::Value value, VM::ExecutionContext *ctx); + Param source; + Param result; + }; + struct instr_binop { + MOTH_INSTR_HEADER + VM::Value (*alu)(const VM::Value , const VM::Value, VM::ExecutionContext *); + Param lhs; + Param rhs; + Param result; + }; + struct instr_loadThis { + MOTH_INSTR_HEADER + Param result; + }; + struct instr_inplaceElementOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::Value, VM::Value, VM::ExecutionContext *); + Param base; + Param index; + Param source; + }; + struct instr_inplaceMemberOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::Value, VM::String *, VM::ExecutionContext *); + VM::String *member; + Param base; + Param source; + }; + struct instr_inplaceNameOp { + MOTH_INSTR_HEADER + void (*alu)(VM::Value, VM::String *, VM::ExecutionContext *); + VM::String *name; + Param source; + }; + + instr_common common; + instr_ret ret; + instr_loadValue loadValue; + instr_moveTemp moveTemp; + instr_loadClosure loadClosure; + instr_loadName loadName; + instr_storeName storeName; + instr_loadElement loadElement; + instr_storeElement storeElement; + instr_loadProperty loadProperty; + instr_storeProperty storeProperty; + instr_push push; + instr_callValue callValue; + instr_callProperty callProperty; + instr_callElement callElement; + instr_callActivationProperty callActivationProperty; + instr_callBuiltinThrow callBuiltinThrow; + instr_callBuiltinCreateExceptionHandler callBuiltinCreateExceptionHandler; + instr_callBuiltinDeleteExceptionHandler callBuiltinDeleteExceptionHandler; + instr_callBuiltinGetException callBuiltinGetException; + instr_callBuiltinPushScope callBuiltinPushScope; + instr_callBuiltinPopScope callBuiltinPopScope; + instr_callBuiltinForeachIteratorObject callBuiltinForeachIteratorObject; + instr_callBuiltinForeachNextPropertyName callBuiltinForeachNextPropertyName; + instr_callBuiltinDeleteMember callBuiltinDeleteMember; + instr_callBuiltinDeleteSubscript callBuiltinDeleteSubscript; + instr_callBuiltinDeleteName callBuiltinDeleteName; + instr_callBuiltinTypeofMember callBuiltinTypeofMember; + instr_callBuiltinTypeofSubscript callBuiltinTypeofSubscript; + instr_callBuiltinTypeofName callBuiltinTypeofName; + instr_callBuiltinTypeofValue callBuiltinTypeofValue; + instr_callBuiltinPostIncMember callBuiltinPostIncMember; + instr_callBuiltinPostIncSubscript callBuiltinPostIncSubscript; + instr_callBuiltinPostIncName callBuiltinPostIncName; + instr_callBuiltinPostIncValue callBuiltinPostIncValue; + instr_callBuiltinPostDecMember callBuiltinPostDecMember; + instr_callBuiltinPostDecSubscript callBuiltinPostDecSubscript; + instr_callBuiltinPostDecName callBuiltinPostDecName; + instr_callBuiltinPostDecValue callBuiltinPostDecValue; + instr_callBuiltinDeclareVar callBuiltinDeclareVar; + instr_callBuiltinDefineGetterSetter callBuiltinDefineGetterSetter; + instr_callBuiltinDefineProperty callBuiltinDefineProperty; + instr_callBuiltinDefineArrayProperty callBuiltinDefineArrayProperty; + instr_createValue createValue; + instr_createProperty createProperty; + instr_createActivationProperty createActivationProperty; + instr_jump jump; + instr_cjump cjump; + instr_unop unop; + instr_binop binop; + instr_loadThis loadThis; + instr_inplaceElementOp inplaceElementOp; + instr_inplaceMemberOp inplaceMemberOp; + instr_inplaceNameOp inplaceNameOp; + + static int size(Type type); +}; + +template<int N> +struct InstrMeta { +}; + +#define MOTH_INSTR_META_TEMPLATE(I, FMT) \ + template<> struct InstrMeta<(int)Instr::I> { \ + enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \ + typedef Instr::instr_##FMT DataType; \ + static const DataType &data(const Instr &instr) { return instr.FMT; } \ + static void setData(Instr &instr, const DataType &v) { instr.FMT = v; } \ + }; +FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE); +#undef MOTH_INSTR_META_TEMPLATE + +template<int InstrType> +class InstrData : public InstrMeta<InstrType>::DataType +{ +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4INSTR_MOTH_P_H diff --git a/src/v4/moth/qv4isel_moth.cpp b/src/v4/moth/qv4isel_moth.cpp new file mode 100644 index 0000000000..5522be4c32 --- /dev/null +++ b/src/v4/moth/qv4isel_moth.cpp @@ -0,0 +1,975 @@ +#include "qv4isel_util_p.h" +#include "qv4isel_moth_p.h" +#include "qv4vme_moth_p.h" +#include "qv4functionobject.h" +#include "qv4regexpobject.h" +#include "debugging.h" + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +namespace { + +QTextStream qout(stderr, QIODevice::WriteOnly); + +//#define DEBUG_TEMP_COMPRESSION +#ifdef DEBUG_TEMP_COMPRESSION +# define DBTC(x) x +#else // !DEBUG_TEMP_COMPRESSION +# define DBTC(x) +#endif // DEBUG_TEMP_COMPRESSION +class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor +{ +public: + void run(IR::Function *function) + { + DBTC(qDebug() << "starting on function" << (*function->name) << "with" << function->tempCount << "temps.";) + + _seenTemps.clear(); + _nextFree = 0; + _active.reserve(function->tempCount); + _localCount = function->locals.size(); + + QVector<int> pinned; + foreach (IR::BasicBlock *block, function->basicBlocks) { + if (IR::Stmt *last = block->terminator()) { + const QBitArray &liveOut = last->d->liveOut; + for (int i = 0, ei = liveOut.size(); i < ei; ++i) { + if (liveOut.at(i) && !pinned.contains(i)) { + pinned.append(i); + add(i - _localCount, _nextFree); + } + } + } + } + _pinnedCount = _nextFree; + + int maxUsed = _nextFree; + + foreach (IR::BasicBlock *block, function->basicBlocks) { + DBTC(qDebug("L%d:", block->index)); + + for (int i = 0, ei = block->statements.size(); i < ei; ++i ) { + _currentStatement = block->statements[i]; + if (i == 0) + expireOld(); + + DBTC(_currentStatement->dump(qout);qout<<endl<<flush;) + + if (_currentStatement->d) + _currentStatement->accept(this); + } + maxUsed = std::max(maxUsed, _nextFree); + } + DBTC(qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps.";) + function->tempCount = maxUsed + _localCount; + } + +private: + virtual void visitConst(IR::Const *) {} + virtual void visitString(IR::String *) {} + virtual void visitRegExp(IR::RegExp *) {} + virtual void visitName(IR::Name *) {} + virtual void visitClosure(IR::Closure *) {} + virtual void visitUnop(IR::Unop *e) { e->expr->accept(this); } + virtual void visitBinop(IR::Binop *e) { e->left->accept(this); e->right->accept(this); } + virtual void visitSubscript(IR::Subscript *e) { e->base->accept(this); e->index->accept(this); } + virtual void visitMember(IR::Member *e) { e->base->accept(this); } + virtual void visitExp(IR::Exp *s) { s->expr->accept(this); } + virtual void visitEnter(IR::Enter *) {} + virtual void visitLeave(IR::Leave *) {} + virtual void visitJump(IR::Jump *) {} + virtual void visitCJump(IR::CJump *s) { s->cond->accept(this); } + virtual void visitRet(IR::Ret *s) { s->expr->accept(this); } + + virtual void visitTemp(IR::Temp *e) { + if (_seenTemps.contains(e)) + return; + _seenTemps.insert(e); + + if (e->index < 0) + return; + if (e->index < _localCount) // don't optimise locals yet. + return; + + e->index = remap(e->index - _localCount) + _localCount; + } + + virtual void visitCall(IR::Call *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitNew(IR::New *e) { + e->base->accept(this); + for (IR::ExprList *it = e->args; it; it = it->next) + it->expr->accept(this); + } + + virtual void visitMove(IR::Move *s) { + s->target->accept(this); + s->source->accept(this); + } + + int remap(int tempIndex) { + for (ActiveTemps::const_iterator i = _active.begin(), ei = _active.end(); i < ei; ++i) { + if (i->first == tempIndex) { + DBTC(qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount);) + return i->second; + } + } + + int firstFree = expireOld(); + add(tempIndex, firstFree); + return firstFree; + } + + void add(int tempIndex, int firstFree) { + if (_nextFree <= firstFree) + _nextFree = firstFree + 1; + _active.prepend(qMakePair(tempIndex, firstFree)); + DBTC(qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount);) + } + + int expireOld() { + Q_ASSERT(_currentStatement->d); + + const QBitArray &liveIn = _currentStatement->d->liveIn; + QBitArray inUse(_nextFree); + int i = 0; + while (i < _active.size()) { + const QPair<int, int> &p = _active[i]; + + if (p.second < _pinnedCount) { + inUse.setBit(p.second); + ++i; + continue; + } + + if (liveIn[p.first + _localCount]) { + inUse[p.second] = true; + ++i; + } else { + DBTC(qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount);) + _active.remove(i); + } + } + for (int i = 0, ei = inUse.size(); i < ei; ++i) + if (!inUse[i]) + return i; + return _nextFree; + } + +private: + typedef QVector<QPair<int, int> > ActiveTemps; + ActiveTemps _active; + QSet<IR::Temp *> _seenTemps; + IR::Stmt *_currentStatement; + int _localCount; + int _nextFree; + int _pinnedCount; +}; +#undef DBTC + +typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); +inline ALUFunction 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 VM::__qmljs_bit_and; + case IR::OpBitOr: + return VM::__qmljs_bit_or; + case IR::OpBitXor: + return VM::__qmljs_bit_xor; + case IR::OpAdd: + return VM::__qmljs_add; + case IR::OpSub: + return VM::__qmljs_sub; + case IR::OpMul: + return VM::__qmljs_mul; + case IR::OpDiv: + return VM::__qmljs_div; + case IR::OpMod: + return VM::__qmljs_mod; + case IR::OpLShift: + return VM::__qmljs_shl; + case IR::OpRShift: + return VM::__qmljs_shr; + case IR::OpURShift: + return VM::__qmljs_ushr; + case IR::OpGt: + return VM::__qmljs_gt; + case IR::OpLt: + return VM::__qmljs_lt; + case IR::OpGe: + return VM::__qmljs_ge; + case IR::OpLe: + return VM::__qmljs_le; + case IR::OpEqual: + return VM::__qmljs_eq; + case IR::OpNotEqual: + return VM::__qmljs_ne; + case IR::OpStrictEqual: + return VM::__qmljs_se; + case IR::OpStrictNotEqual: + return VM::__qmljs_sne; + case IR::OpInstanceof: + return VM::__qmljs_instanceof; + case IR::OpIn: + return VM::__qmljs_in; + case IR::OpAnd: + return 0; + case IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; + +} // anonymous namespace + +InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) + : EvalInstructionSelection(engine, module) + , _function(0) + , _vmFunction(0) + , _block(0) + , _codeStart(0) + , _codeNext(0) + , _codeEnd(0) +{ +} + +InstructionSelection::~InstructionSelection() +{ +} + +void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) +{ + IR::BasicBlock *block; + + QHash<IR::BasicBlock *, QVector<ptrdiff_t> > patches; + QHash<IR::BasicBlock *, ptrdiff_t> addrs; + + int codeSize = 4096; + uchar *codeStart = new uchar[codeSize]; + uchar *codeNext = codeStart; + uchar *codeEnd = codeStart + codeSize; + + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + // TODO: FIXME: fix the temp compression with the new temp index layout. +// CompressTemps().run(_function); + + int locals = frameSize(); + assert(locals >= 0); + + Instruction::Push push; + push.value = quint32(locals); + addInstruction(push); + + foreach (_block, _function->basicBlocks) { + _addrs.insert(_block, _codeNext - _codeStart); + + foreach (IR::Stmt *s, _block->statements) + s->accept(this); + } + + patchJumpAddresses(); + + _vmFunction->code = VME::exec; + _vmFunction->codeData = squeezeCode(); + + qSwap(_function, function); + qSwap(_vmFunction, vmFunction); + qSwap(block, _block); + qSwap(patches, _patches); + qSwap(addrs, _addrs); + qSwap(codeStart, _codeStart); + qSwap(codeNext, _codeNext); + qSwap(codeEnd, _codeEnd); + + delete[] codeStart; +} + +void InstructionSelection::callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + Instruction::CallValue call; + prepareCallArgs(args, call.argc, call.args); + call.dest = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallProperty call; + call.base = getParam(base); + call.name = identifier(name); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result) +{ + // call the property on the loaded base + Instruction::CallElement call; + call.base = getParam(base); + call.index = getParam(index); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::constructActivationProperty(IR::Name *func, + IR::ExprList *args, + IR::Temp *result) +{ + Instruction::CreateActivationProperty create; + create.name = identifier(*func->id); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result) +{ + Instruction::CreateProperty create; + create.base = getParam(base); + create.name = identifier(name); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result) +{ + Instruction::CreateValue create; + create.func = getParam(value); + prepareCallArgs(args, create.argc, create.args); + create.result = getResultParam(result); + addInstruction(create); +} + +void InstructionSelection::loadThisObject(IR::Temp *temp) +{ + Instruction::LoadThis load; + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Temp *targetTemp) +{ + assert(sourceConst); + + Instruction::LoadValue load; + load.value = getParam(sourceConst); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadString(const QString &str, IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(VM::Value::fromString(identifier(str))); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue( + VM::Value::fromObject(engine()->newRegExpObject( + *sourceRegexp->value, + sourceRegexp->flags))); + load.result = getResultParam(targetTemp); + addInstruction(load); +} + +void InstructionSelection::getActivationProperty(const QString &name, IR::Temp *temp) +{ + Instruction::LoadName load; + load.name = identifier(name); + load.result = getResultParam(temp); + addInstruction(load); +} + +void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +{ + Instruction::StoreName store; + store.source = getParam(source); + store.name = identifier(targetName); + addInstruction(store); +} + +void InstructionSelection::initClosure(IR::Closure *closure, IR::Temp *target) +{ + VM::Function *vmFunc = vmFunction(closure->value); + assert(vmFunc); + Instruction::LoadClosure load; + load.value = vmFunc; + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::getProperty(IR::Temp *base, const QString &name, IR::Temp *target) +{ + Instruction::LoadProperty load; + load.base = getParam(base); + load.name = identifier(name); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + Instruction::StoreProperty store; + store.base = getParam(targetBase); + store.name = identifier(targetName); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target) +{ + Instruction::LoadElement load; + load.base = getParam(base); + load.index = getParam(index); + load.result = getResultParam(target); + addInstruction(load); +} + +void InstructionSelection::setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex) +{ + Instruction::StoreElement store; + store.base = getParam(targetBase); + store.index = getParam(targetIndex); + store.source = getParam(source); + addInstruction(store); +} + +void InstructionSelection::copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + Instruction::MoveTemp move; + move.source = getParam(sourceTemp); + move.result = getResultParam(targetTemp); + addInstruction(move); +} + +void InstructionSelection::unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp) +{ + VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpIfTrue: assert(!"unreachable"); break; + case IR::OpNot: op = VM::__qmljs_not; break; + case IR::OpUMinus: op = VM::__qmljs_uminus; break; + case IR::OpUPlus: op = VM::__qmljs_uplus; break; + case IR::OpCompl: op = VM::__qmljs_compl; break; + case IR::OpIncrement: op = VM::__qmljs_increment; break; + case IR::OpDecrement: op = VM::__qmljs_decrement; break; + default: assert(!"unreachable"); break; + } // switch + + if (op) { + Instruction::Unop unop; + unop.alu = op; + unop.source = getParam(sourceTemp); + unop.result = getResultParam(targetTemp); + addInstruction(unop); + } else { + qWarning(" UNOP1"); + } +} + +void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) +{ + Instruction::Binop binop; + binop.alu = aluOpFunction(oper); + binop.lhs = getParam(leftSource); + binop.rhs = getParam(rightSource); + binop.result = getResultParam(target); + addInstruction(binop); +} + +void InstructionSelection::inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName) +{ + void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_name; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_name; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_name; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_name; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_name; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_name; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_name; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_name; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_name; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_name; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_name; break; + default: break; + } + + if (op) { + Instruction::InplaceNameOp ieo; + ieo.alu = op; + ieo.name = identifier(targetName); + ieo.source = getParam(sourceExpr); + addInstruction(ieo); + } +} + +void InstructionSelection::inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp) +{ + void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_element; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_element; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_element; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_element; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_element; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_element; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_element; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_element; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_element; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_element; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_element; break; + default: break; + } + + Instruction::InplaceElementOp ieo; + ieo.alu = op; + ieo.base = getParam(targetBaseTemp); + ieo.index = getParam(targetIndexTemp); + ieo.source = getParam(sourceExpr); + addInstruction(ieo); +} + +void InstructionSelection::inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName) +{ + void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0; + switch (oper) { + case IR::OpBitAnd: op = VM::__qmljs_inplace_bit_and_member; break; + case IR::OpBitOr: op = VM::__qmljs_inplace_bit_or_member; break; + case IR::OpBitXor: op = VM::__qmljs_inplace_bit_xor_member; break; + case IR::OpAdd: op = VM::__qmljs_inplace_add_member; break; + case IR::OpSub: op = VM::__qmljs_inplace_sub_member; break; + case IR::OpMul: op = VM::__qmljs_inplace_mul_member; break; + case IR::OpDiv: op = VM::__qmljs_inplace_div_member; break; + case IR::OpMod: op = VM::__qmljs_inplace_mod_member; break; + case IR::OpLShift: op = VM::__qmljs_inplace_shl_member; break; + case IR::OpRShift: op = VM::__qmljs_inplace_shr_member; break; + case IR::OpURShift: op = VM::__qmljs_inplace_ushr_member; break; + default: break; + } + + Instruction::InplaceMemberOp imo; + imo.alu = op; + imo.base = getParam(targetBase); + imo.member = identifier(targetName); + imo.source = getParam(source); + addInstruction(imo); +} + +void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) +{ + bool singleArgIsTemp = false; + if (e && e->next == 0) { + // ok, only 1 argument in the call... + const int idx = e->expr->asTemp()->index; + // We can only pass a reference into the stack, which holds temps that + // are not arguments (idx >= 0) nor locals (idx >= localCound). + singleArgIsTemp = idx >= _function->locals.size(); + } + + if (singleArgIsTemp) { + // We pass single arguments as references to the stack, but only if it's not a local or an argument. + argc = 1; + args = e->expr->asTemp()->index - _function->locals.size(); + } else if (e) { + // We need to move all the temps into the function arg array + int argLocation = outgoingArgumentTempStart(); + assert(argLocation >= 0); + argc = 0; + args = argLocation; + while (e) { + Instruction::MoveTemp move; + move.source = getParam(e->expr); + move.result = Instr::Param::createTemp(argLocation); + addInstruction(move); + ++argLocation; + ++argc; + e = e->next; + } + } else { + argc = 0; + args = 0; + } +} + +void InstructionSelection::visitJump(IR::Jump *s) +{ + 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) +{ + Instr::Param condition; + if (IR::Temp *t = s->cond->asTemp()) { + condition = getResultParam(t); + } else if (IR::Binop *b = s->cond->asBinop()) { + condition = getResultParam(0); + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhs = getParam(b->left); + binop.rhs = getParam(b->right); + binop.result = condition; + addInstruction(binop); + } else { + Q_UNIMPLEMENTED(); + } + + Instruction::CJump 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 (_block->index + 1 != s->iffalse->index) { + 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) +{ + Instruction::Ret ret; + ret.result = getParam(s->expr); + addInstruction(ret); +} + +void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result) +{ + Instruction::CallActivationProperty call; + call.name = identifier(*func->id); + prepareCallArgs(args, call.argc, call.args); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *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::Temp *result) +{ + Instruction::CallBuiltinTypeofName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinTypeofValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinDeleteMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *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::Temp *result) +{ + Instruction::CallBuiltinDeleteName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteValue(IR::Temp *result) +{ + Instruction::LoadValue load; + load.value = Instr::Param::createValue(VM::Value::fromBoolean(false)); + load.result = getResultParam(result); + addInstruction(load); +} + +void InstructionSelection::callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinPostDecValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncMember call; + call.base = getParam(base); + call.member = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncSubscript call; + call.base = getParam(base); + call.index = getParam(index); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementName(const QString &name, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncName call; + call.name = identifier(name); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result) +{ + Instruction::CallBuiltinPostIncValue call; + call.value = getParam(value); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinThrow(IR::Temp *arg) +{ + Instruction::CallBuiltinThrow call; + call.arg = getParam(arg); + addInstruction(call); +} + +void InstructionSelection::callBuiltinCreateExceptionHandler(IR::Temp *result) +{ + Instruction::CallBuiltinCreateExceptionHandler call; + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDeleteExceptionHandler() +{ + Instruction::CallBuiltinDeleteExceptionHandler call; + addInstruction(call); +} + +void InstructionSelection::callBuiltinGetException(IR::Temp *result) +{ + Instruction::CallBuiltinGetException call; + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result) +{ + Instruction::CallBuiltinForeachIteratorObject call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result) +{ + Instruction::CallBuiltinForeachNextPropertyName call; + call.arg = getParam(arg); + call.result = getResultParam(result); + addInstruction(call); +} + +void InstructionSelection::callBuiltinPushWithScope(IR::Temp *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 = identifier(name); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter) +{ + Instruction::CallBuiltinDefineGetterSetter call; + call.object = getParam(object); + call.name = identifier(name); + call.getter = getParam(getter); + call.setter = getParam(setter); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value) +{ + Instruction::CallBuiltinDefineProperty call; + call.object = getParam(object); + call.name = identifier(name); + call.value = getParam(value); + addInstruction(call); +} + +void InstructionSelection::callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value) +{ + Instruction::CallBuiltinDefineArrayProperty call; + call.object = getParam(object); + call.index = index; + call.value = getParam(value); + 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.begin(), ei = _patches.end(); 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(); +} + +uchar *InstructionSelection::squeezeCode() const +{ + int codeSize = _codeNext - _codeStart; + uchar *squeezed = new uchar[codeSize]; + ::memcpy(squeezed, _codeStart, codeSize); + return squeezed; +} + +VM::String *InstructionSelection::identifier(const QString &s) +{ + VM::String *str = engine()->identifier(s); + _vmFunction->identifiers.append(str); + return str; +} diff --git a/src/v4/moth/qv4isel_moth_p.h b/src/v4/moth/qv4isel_moth_p.h new file mode 100644 index 0000000000..0b93ea853f --- /dev/null +++ b/src/v4/moth/qv4isel_moth_p.h @@ -0,0 +1,168 @@ +#ifndef QV4ISEL_MOTH_P_H +#define QV4ISEL_MOTH_P_H + +#include "qv4global.h" +#include "qv4isel_p.h" +#include "qv4ir_p.h" +#include "qv4object.h" +#include "qv4instr_moth_p.h" + +namespace QQmlJS { +namespace Moth { + +class Q_V4_EXPORT InstructionSelection: + public IR::InstructionSelection, + public EvalInstructionSelection +{ +public: + InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module); + ~InstructionSelection(); + + virtual void run(VM::Function *vmFunction, IR::Function *function); + +protected: + virtual void visitJump(IR::Jump *); + virtual void visitCJump(IR::CJump *); + virtual void visitRet(IR::Ret *); + + virtual void callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void callBuiltinTypeofMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinTypeofName(const QString &name, IR::Temp *result); + virtual void callBuiltinTypeofValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinDeleteMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinDeleteName(const QString &name, IR::Temp *result); + virtual void callBuiltinDeleteValue(IR::Temp *result); + virtual void callBuiltinPostDecrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostDecrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostDecrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinPostIncrementMember(IR::Temp *base, const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementSubscript(IR::Temp *base, IR::Temp *index, IR::Temp *result); + virtual void callBuiltinPostIncrementName(const QString &name, IR::Temp *result); + virtual void callBuiltinPostIncrementValue(IR::Temp *value, IR::Temp *result); + virtual void callBuiltinThrow(IR::Temp *arg); + virtual void callBuiltinCreateExceptionHandler(IR::Temp *result); + virtual void callBuiltinDeleteExceptionHandler(); + virtual void callBuiltinGetException(IR::Temp *result); + virtual void callBuiltinForeachIteratorObject(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinForeachNextPropertyname(IR::Temp *arg, IR::Temp *result); + virtual void callBuiltinPushWithScope(IR::Temp *arg); + virtual void callBuiltinPopScope(); + virtual void callBuiltinDeclareVar(bool deletable, const QString &name); + virtual void callBuiltinDefineGetterSetter(IR::Temp *object, const QString &name, IR::Temp *getter, IR::Temp *setter); + virtual void callBuiltinDefineProperty(IR::Temp *object, const QString &name, IR::Temp *value); + virtual void callBuiltinDefineArrayProperty(IR::Temp *object, int index, IR::Temp *value); + virtual void callValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void callProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void callSubscript(IR::Temp *base, IR::Temp *index, IR::ExprList *args, IR::Temp *result); + virtual void constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Temp *result); + virtual void constructProperty(IR::Temp *base, const QString &name, IR::ExprList *args, IR::Temp *result); + virtual void constructValue(IR::Temp *value, IR::ExprList *args, IR::Temp *result); + virtual void loadThisObject(IR::Temp *temp); + virtual void loadConst(IR::Const *sourceConst, IR::Temp *targetTemp); + virtual void loadString(const QString &str, IR::Temp *targetTemp); + virtual void loadRegexp(IR::RegExp *sourceRegexp, IR::Temp *targetTemp); + virtual void getActivationProperty(const QString &name, IR::Temp *temp); + virtual void setActivationProperty(IR::Expr *source, const QString &targetName); + virtual void initClosure(IR::Closure *closure, IR::Temp *target); + virtual void getProperty(IR::Temp *base, const QString &name, IR::Temp *target); + virtual void setProperty(IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + virtual void getElement(IR::Temp *base, IR::Temp *index, IR::Temp *target); + virtual void setElement(IR::Expr *source, IR::Temp *targetBase, IR::Temp *targetIndex); + virtual void copyValue(IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void unop(IR::AluOp oper, IR::Temp *sourceTemp, IR::Temp *targetTemp); + virtual void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target); + virtual void inplaceNameOp(IR::AluOp oper, IR::Expr *sourceExpr, const QString &targetName); + virtual void inplaceElementOp(IR::AluOp oper, IR::Expr *sourceExpr, IR::Temp *targetBaseTemp, IR::Temp *targetIndexTemp); + virtual void inplaceMemberOp(IR::AluOp oper, IR::Expr *source, IR::Temp *targetBase, const QString &targetName); + +private: + struct Instruction { +#define MOTH_INSTR_DATA_TYPEDEF(I, FMT) typedef InstrData<Instr::I> I; + FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF) +#undef MOTH_INSTR_DATA_TYPEDEF + private: + Instruction(); + }; + + Instr::Param getParam(IR::Expr *e) + { + Q_ASSERT(e); + + typedef Instr::Param Param; + if (IR::Const *c = e->asConst()) { + return Param::createValue(convertToValue(c)); + } else if (IR::Temp *t = e->asTemp()) { + const int index = t->index; + if (index < 0) { + return Param::createArgument(-index - 1); + } else { + const int localCount = _function->locals.size(); + if (index < localCount) + return Param::createLocal(index); + else + return Param::createTemp(index - localCount); + } + } else { + Q_UNIMPLEMENTED(); + return Param(); + } + } + + Instr::Param getResultParam(IR::Temp *result) + { + if (result) + return getParam(result); + else + return Instr::Param::createTemp(scratchTempIndex()); + } + + void simpleMove(IR::Move *); + void prepareCallArgs(IR::ExprList *, quint32 &, quint32 &); + + int outgoingArgumentTempStart() const { return _function->tempCount - _function->locals.size(); } + int scratchTempIndex() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } + int frameSize() const { return scratchTempIndex() + 1; } + + template <int Instr> + inline ptrdiff_t addInstruction(const InstrData<Instr> &data); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + void patchJumpAddresses(); + uchar *squeezeCode() const; + + VM::String *identifier(const QString &s); + + IR::Function *_function; + VM::Function *_vmFunction; + IR::BasicBlock *_block; + + QHash<IR::BasicBlock *, QVector<ptrdiff_t> > _patches; + QHash<IR::BasicBlock *, ptrdiff_t> _addrs; + + uchar *_codeStart; + uchar *_codeNext; + uchar *_codeEnd; +}; + +class Q_V4_EXPORT ISelFactory: public EvalISelFactory +{ +public: + virtual ~ISelFactory() {} + virtual EvalInstructionSelection *create(VM::ExecutionEngine *engine, IR::Module *module) + { return new InstructionSelection(engine, module); } +}; + +template<int InstrT> +ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data) +{ + Instr genericInstr; + InstrMeta<InstrT>::setData(genericInstr, data); + return addInstructionHelper(static_cast<Instr::Type>(InstrT), genericInstr); +} + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4ISEL_MOTH_P_H diff --git a/src/v4/moth/qv4vme_moth.cpp b/src/v4/moth/qv4vme_moth.cpp new file mode 100644 index 0000000000..a3ab1de46a --- /dev/null +++ b/src/v4/moth/qv4vme_moth.cpp @@ -0,0 +1,483 @@ +#include "qv4vme_moth_p.h" +#include "qv4instr_moth_p.h" +#include "qmljs_value.h" +#include "debugging.h" + +#include <iostream> + +#include <alloca.h> + +#ifdef DO_TRACE_INSTR +# define TRACE_INSTR(I) fprintf(stderr, "executing a %s\n", #I); +# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); fprintf(stderr, " %s : %s\n", #n, buf); } +#else +# define TRACE_INSTR(I) +# define TRACE(n, str, ...) +#endif // DO_TRACE_INSTR + +using namespace QQmlJS; +using namespace QQmlJS::Moth; + +class FunctionState: public Debugging::FunctionState +{ +public: + FunctionState(QQmlJS::VM::ExecutionContext *context, const uchar **code) + : Debugging::FunctionState(context) + , stack(0) + , stackSize(0) + , code(code) + {} + + virtual VM::Value *temp(unsigned idx) { return stack + idx; } + + void setStack(VM::Value *stack, unsigned stackSize) + { this->stack = stack; this->stackSize = stackSize; } + +private: + VM::Value *stack; + unsigned stackSize; + const uchar **code; +}; + +#define MOTH_BEGIN_INSTR_COMMON(I) { \ + const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ + code += InstrMeta<(int)Instr::I>::Size; \ + Q_UNUSED(instr); \ + TRACE_INSTR(I) + +#ifdef MOTH_THREADED_INTERPRETER + +# define MOTH_BEGIN_INSTR(I) op_##I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + genericInstr = reinterpret_cast<const Instr *>(code); \ + goto *genericInstr->common.code; \ + } + +# define MOTH_END_INSTR(I) } \ + genericInstr = reinterpret_cast<const Instr *>(code); \ + goto *genericInstr->common.code; \ + +#else + +# define MOTH_BEGIN_INSTR(I) \ + case Instr::I: \ + MOTH_BEGIN_INSTR_COMMON(I) + +# define MOTH_NEXT_INSTR(I) { \ + break; \ + } + +# define MOTH_END_INSTR(I) } \ + break; + +#endif + +static inline VM::Value *getValueRef(QQmlJS::VM::ExecutionContext *context, + VM::Value* stack, + const Instr::Param ¶m +#if !defined(QT_NO_DEBUG) + , unsigned stackSize +#endif + ) +{ +#ifdef DO_TRACE_INSTR + if (param.isValue()) { + fprintf(stderr, " value\n"); + } else if (param.isArgument()) { + fprintf(stderr, " argument %d\n", param.index); + } else if (param.isLocal()) { + fprintf(stderr, " local %d\n", param.index); + } else if (param.isTemp()) { + fprintf(stderr, " temp %d\n", param.index); + } else { + Q_ASSERT(!"INVALID"); + } +#endif // DO_TRACE_INSTR + + if (param.isValue()) { + return const_cast<VM::Value *>(¶m.value); + } else if (param.isArgument()) { + const unsigned arg = param.index; + Q_ASSERT(arg >= 0); + Q_ASSERT((unsigned) arg < context->argumentCount); + Q_ASSERT(context->arguments); + return context->arguments + arg; + } else if (param.isLocal()) { + const unsigned index = param.index; + Q_ASSERT(index >= 0); + Q_ASSERT(index < context->variableCount()); + Q_ASSERT(context->locals); + return context->locals + index; + } else if (param.isTemp()) { + Q_ASSERT(param.index < stackSize); + return stack + param.index; + } else { + Q_UNIMPLEMENTED(); + return 0; + } +} + +#if defined(QT_NO_DEBUG) +# define VALUE(param) *getValueRef(context, stack, param) +# define VALUEPTR(param) getValueRef(context, stack, param) +#else +# define VALUE(param) *getValueRef(context, stack, param, stackSize) +# define VALUEPTR(param) getValueRef(context, stack, param, stackSize) +#endif + +VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *code +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable +#endif + ) +{ +#ifdef DO_TRACE_INSTR + qDebug("Starting VME with context=%p and code=%p", context, code); +#endif // DO_TRACE_INSTR + +#ifdef MOTH_THREADED_INTERPRETER + if (storeJumpTable) { +#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, + static void *jumpTable[] = { + FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) + }; +#undef MOTH_INSTR_ADDR + *storeJumpTable = jumpTable; + return VM::Value::undefinedValue(); + } +#endif + + VM::Value *stack = 0; + unsigned stackSize = 0; + FunctionState state(context, &code); + +#ifdef MOTH_THREADED_INTERPRETER + const Instr *genericInstr = reinterpret_cast<const Instr *>(code); + goto *genericInstr->common.code; +#else + for (;;) { + const Instr *genericInstr = reinterpret_cast<const Instr *>(code); + switch (genericInstr->common.instructionType) { +#endif + + MOTH_BEGIN_INSTR(MoveTemp) + VALUE(instr.result) = VALUE(instr.source); + MOTH_END_INSTR(MoveTemp) + + MOTH_BEGIN_INSTR(LoadValue) +// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); + VALUE(instr.result) = VALUE(instr.value); + MOTH_END_INSTR(LoadValue) + + MOTH_BEGIN_INSTR(LoadClosure) + VALUE(instr.result) = __qmljs_init_closure(instr.value, context); + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + VALUE(instr.result) = __qmljs_get_activation_property(context, instr.name); + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(StoreName) + TRACE(inline, "property name = %s", instr.name->toQString().toUtf8().constData()); + __qmljs_set_activation_property(context, instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreName) + + MOTH_BEGIN_INSTR(LoadElement) + VALUE(instr.result) = __qmljs_get_element(context, VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(LoadElement) + + MOTH_BEGIN_INSTR(StoreElement) + __qmljs_set_element(context, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + MOTH_END_INSTR(StoreElement) + + MOTH_BEGIN_INSTR(LoadProperty) + VALUE(instr.result) = __qmljs_get_property(context, VALUE(instr.base), instr.name); + MOTH_END_INSTR(LoadProperty) + + MOTH_BEGIN_INSTR(StoreProperty) + __qmljs_set_property(context, VALUE(instr.base), instr.name, VALUE(instr.source)); + MOTH_END_INSTR(StoreProperty) + + MOTH_BEGIN_INSTR(Push) + TRACE(inline, "stack size: %u", instr.value); + stackSize = instr.value; + stack = static_cast<VM::Value *>(alloca(stackSize * sizeof(VM::Value))); + state.setStack(stack, stackSize); + MOTH_END_INSTR(Push) + + MOTH_BEGIN_INSTR(CallValue) +#ifdef DO_TRACE_INSTR + if (Debugging::Debugger *debugger = context->engine->debugger) { + if (VM::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { + if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { + QString n = debugger->name(o); + std::cerr << "*** Call to \"" << (n.isNull() ? "<no name>" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; + } + } + } +#endif // DO_TRACE_INSTR + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_value(context, VM::Value::undefinedValue(), VALUE(instr.dest), args, instr.argc); + MOTH_END_INSTR(CallValue) + + MOTH_BEGIN_INSTR(CallProperty) + TRACE(property name, "%s", qPrintable(instr.name->toQString())); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_property(context, VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallElement) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_element(context, VALUE(instr.base), VALUE(instr.index), args, instr.argc); + MOTH_END_INSTR(CallProperty) + + MOTH_BEGIN_INSTR(CallActivationProperty) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_call_activation_property(context, instr.name, args, instr.argc); + MOTH_END_INSTR(CallActivationProperty) + + MOTH_BEGIN_INSTR(CallBuiltinThrow) + __qmljs_builtin_throw(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinThrow) + + MOTH_BEGIN_INSTR(CallBuiltinCreateExceptionHandler) + void *buf = __qmljs_create_exception_handler(context); + // The result is the only value we need from the instr to + // continue execution when an exception is caught. + VM::Value *result = getValueRef(context, stack, instr.result +#if !defined(QT_NO_DEBUG) + , stackSize +#endif + ); + int didThrow = setjmp(* static_cast<jmp_buf *>(buf)); + // Two ways to come here: after a create, or after a throw. + if (didThrow) + // At this point, the interpreter state can be anything but + // valid, so first restore the state. + restoreState(context, result, code); + else + // Save the state and any variables we need when catching an + // exception, so we can restore the state at that point. + saveState(context, result, code); + *result = VM::Value::fromInt32(didThrow); + MOTH_END_INSTR(CallBuiltinCreateExceptionHandler) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteExceptionHandler) + __qmljs_delete_exception_handler(context); + MOTH_END_INSTR(CallBuiltinDeleteExceptionHandler) + + MOTH_BEGIN_INSTR(CallBuiltinGetException) + VALUE(instr.result) = __qmljs_get_exception(context); + MOTH_END_INSTR(CallBuiltinGetException) + + MOTH_BEGIN_INSTR(CallBuiltinPushScope) + context = __qmljs_builtin_push_with_scope(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinPushScope) + + MOTH_BEGIN_INSTR(CallBuiltinPopScope) + context = __qmljs_builtin_pop_scope(context); + MOTH_END_INSTR(CallBuiltinPopScope) + + MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) + VALUE(instr.result) = __qmljs_foreach_iterator_object(VALUE(instr.arg), context); + MOTH_END_INSTR(CallBuiltinForeachIteratorObject) + + MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) + VALUE(instr.result) = __qmljs_foreach_next_property_name(VALUE(instr.arg)); + MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) + VALUE(instr.result) = __qmljs_delete_member(context, VALUE(instr.base), instr.member); + MOTH_END_INSTR(CallBuiltinDeleteMember) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) + VALUE(instr.result) = __qmljs_delete_subscript(context, VALUE(instr.base), VALUE(instr.index)); + MOTH_END_INSTR(CallBuiltinDeleteSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinDeleteName) + VALUE(instr.result) = __qmljs_delete_name(context, instr.name); + MOTH_END_INSTR(CallBuiltinDeleteName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) + VALUE(instr.result) = __qmljs_builtin_typeof_member(VALUE(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) + VALUE(instr.result) = __qmljs_builtin_typeof_element(VALUE(instr.base), VALUE(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofName) + VALUE(instr.result) = __qmljs_builtin_typeof_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) + VALUE(instr.result) = __qmljs_builtin_typeof(VALUE(instr.value), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncMember) + VALUE(instr.result) = __qmljs_builtin_post_increment_member(VALUE(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncSubscript) + VALUE(instr.result) = __qmljs_builtin_post_increment_element(VALUE(instr.base), VALUE(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncName) + VALUE(instr.result) = __qmljs_builtin_post_increment_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostIncValue) + VALUE(instr.result) = __qmljs_builtin_post_increment(VALUEPTR(instr.value), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecMember) + VALUE(instr.result) = __qmljs_builtin_post_decrement_member(VALUE(instr.base), instr.member, context); + MOTH_END_INSTR(CallBuiltinTypeofMember) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecSubscript) + VALUE(instr.result) = __qmljs_builtin_post_decrement_element(VALUE(instr.base), VALUE(instr.index), context); + MOTH_END_INSTR(CallBuiltinTypeofSubscript) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecName) + VALUE(instr.result) = __qmljs_builtin_post_decrement_name(instr.name, context); + MOTH_END_INSTR(CallBuiltinTypeofName) + + MOTH_BEGIN_INSTR(CallBuiltinPostDecValue) + VALUE(instr.result) = __qmljs_builtin_post_decrement(VALUEPTR(instr.value), context); + MOTH_END_INSTR(CallBuiltinTypeofValue) + + MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) + __qmljs_builtin_declare_var(context, instr.isDeletable, instr.varName); + MOTH_END_INSTR(CallBuiltinDeclareVar) + + MOTH_BEGIN_INSTR(CallBuiltinDefineGetterSetter) + __qmljs_builtin_define_getter_setter(VALUE(instr.object), instr.name, VALUE(instr.getter), VALUE(instr.setter), context); + MOTH_END_INSTR(CallBuiltinDefineGetterSetter) + + MOTH_BEGIN_INSTR(CallBuiltinDefineProperty) + __qmljs_builtin_define_property(VALUE(instr.object), instr.name, VALUE(instr.value), context); + MOTH_END_INSTR(CallBuiltinDefineProperty) + + MOTH_BEGIN_INSTR(CallBuiltinDefineArrayProperty) + __qmljs_builtin_define_array_property(VALUE(instr.object), instr.index, VALUE(instr.value), context); + MOTH_END_INSTR(CallBuiltinDefineArrayProperty) + + MOTH_BEGIN_INSTR(CreateValue) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_value(context, VALUE(instr.func), args, instr.argc); + MOTH_END_INSTR(CreateValue) + + MOTH_BEGIN_INSTR(CreateProperty) + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_property(context, VALUE(instr.base), instr.name, args, instr.argc); + MOTH_END_INSTR(CreateProperty) + + MOTH_BEGIN_INSTR(CreateActivationProperty) + TRACE(inline, "property name = %s, args = %d, argc = %d", instr.name->toQString().toUtf8().constData(), instr.args, instr.argc); + Q_ASSERT(instr.args + instr.argc < stackSize); + VM::Value *args = stack + instr.args; + VALUE(instr.result) = __qmljs_construct_activation_property(context, instr.name, args, instr.argc); + MOTH_END_INSTR(CreateActivationProperty) + + MOTH_BEGIN_INSTR(Jump) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(CJump) + uint cond = __qmljs_to_boolean(VALUE(instr.condition), context); + TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); + if (cond) + code = ((uchar *)&instr.offset) + instr.offset; + MOTH_END_INSTR(CJump) + + MOTH_BEGIN_INSTR(Unop) + VALUE(instr.result) = instr.alu(VALUE(instr.source), context); + MOTH_END_INSTR(Unop) + + MOTH_BEGIN_INSTR(Binop) + VALUE(instr.result) = instr.alu(VALUE(instr.lhs), VALUE(instr.rhs), context); + MOTH_END_INSTR(Binop) + + MOTH_BEGIN_INSTR(Ret) + VM::Value &result = VALUE(instr.result); +// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); + return result; + MOTH_END_INSTR(Ret) + + MOTH_BEGIN_INSTR(LoadThis) + VALUE(instr.result) = __qmljs_get_thisObject(context); + MOTH_END_INSTR(LoadThis) + + MOTH_BEGIN_INSTR(InplaceElementOp) + instr.alu(VALUE(instr.base), + VALUE(instr.index), + VALUE(instr.source), + context); + MOTH_END_INSTR(InplaceElementOp) + + MOTH_BEGIN_INSTR(InplaceMemberOp) + instr.alu(VALUE(instr.source), + VALUE(instr.base), + instr.member, + context); + MOTH_END_INSTR(InplaceMemberOp) + + MOTH_BEGIN_INSTR(InplaceNameOp) + TRACE(name, "%s", instr.name->toQString().toUtf8().constData()); + instr.alu(VALUE(instr.source), + instr.name, + context); + MOTH_END_INSTR(InplaceNameOp) + +#ifdef MOTH_THREADED_INTERPRETER + // nothing to do +#else + default: + qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); + break; + } + } +#endif + +} + +#ifdef MOTH_THREADED_INTERPRETER +void **VME::instructionJumpTable() +{ + static void **jumpTable = 0; + if (!jumpTable) { + VME dummy; + dummy(0, 0, &jumpTable); + } + return jumpTable; +} +#endif + +VM::Value VME::exec(VM::ExecutionContext *ctxt, const uchar *code) +{ + VME vme; + return vme(ctxt, code); +} + +void VME::restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code) +{ + VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + target = handler.target; + code = handler.code; +} + +void VME::saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code) +{ + VM::ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last(); + handler.target = target; + handler.code = code; +} diff --git a/src/v4/moth/qv4vme_moth_p.h b/src/v4/moth/qv4vme_moth_p.h new file mode 100644 index 0000000000..2fd877f7b9 --- /dev/null +++ b/src/v4/moth/qv4vme_moth_p.h @@ -0,0 +1,37 @@ +#ifndef QV4VME_MOTH_P_H +#define QV4VME_MOTH_P_H + +#include "qmljs_runtime.h" +#include "qv4instr_moth_p.h" + +namespace QQmlJS { +namespace VM { + struct Value; +} + +namespace Moth { + +class VME +{ +public: + static VM::Value exec(VM::ExecutionContext *, const uchar *); + + VM::Value operator()(QQmlJS::VM::ExecutionContext *, const uchar *code +#ifdef MOTH_THREADED_INTERPRETER + , void ***storeJumpTable = 0 +#endif + ); + +#ifdef MOTH_THREADED_INTERPRETER + static void **instructionJumpTable(); +#endif + +private: + static void restoreState(VM::ExecutionContext *context, VM::Value *&target, const uchar *&code); + static void saveState(VM::ExecutionContext *context, VM::Value *target, const uchar *code); +}; + +} // namespace Moth +} // namespace QQmlJS + +#endif // QV4VME_MOTH_P_H |