diff options
-rw-r--r-- | main.cpp | 10 | ||||
-rw-r--r-- | moth/qv4instr_moth_p.h | 84 | ||||
-rw-r--r-- | moth/qv4isel_moth.cpp | 282 | ||||
-rw-r--r-- | moth/qv4isel_moth_p.h | 13 | ||||
-rw-r--r-- | moth/qv4vme_moth.cpp | 100 | ||||
-rw-r--r-- | moth/qv4vme_moth_p.h | 2 | ||||
-rw-r--r-- | qmljs_objects.cpp | 4 | ||||
-rw-r--r-- | qv4ir_p.h | 8 | ||||
-rw-r--r-- | qv4isel_x86_64.cpp | 2 |
9 files changed, 468 insertions, 37 deletions
@@ -213,6 +213,9 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS x86_64::InstructionSelection isel(vm, &module, code); foreach (IR::Function *function, module.functions) isel(function); + + if (! protect(code, codeSize)) + Q_UNREACHABLE(); } } @@ -220,11 +223,6 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS return; } - if (!useMoth) { - if (! protect(code, codeSize)) - Q_UNREACHABLE(); - } - VM::Context *ctx = vm->rootContext; ctx->varCount = globalCode->locals.size(); @@ -240,7 +238,7 @@ void evaluate(QQmlJS::VM::ExecutionEngine *vm, const QString &fileName, const QS Moth::VME vme; vme(ctx, code); } else { - globalCode->code(ctx); + globalCode->code(ctx, globalCode->codeData); } if (ctx->hasUncaughtException) { diff --git a/moth/qv4instr_moth_p.h b/moth/qv4instr_moth_p.h index e582bb4ab6..d9f2c11cdd 100644 --- a/moth/qv4instr_moth_p.h +++ b/moth/qv4instr_moth_p.h @@ -2,10 +2,26 @@ #define QV4INSTR_MOTH_P_H #include <QtCore/qglobal.h> +#include "qmljs_objects.h" #define FOR_EACH_MOTH_INSTR(F) \ - F(Nop, common) \ - F(Done, common) \ + F(Ret, ret) \ + F(LoadUndefined, common) \ + F(LoadNull, common) \ + F(LoadFalse, common) \ + F(LoadTrue, common) \ + F(LoadNumber, loadNumber) \ + F(LoadString, loadString) \ + F(LoadClosure, loadClosure) \ + F(StoreTemp, storeTemp) \ + F(LoadTemp, loadTemp) \ + F(MoveTemp, moveTemp) \ + F(LoadName, loadName) \ + F(Push, push) \ + F(Call, call) \ + F(Jump, jump) \ + F(CJump, jump) \ + F(Binop, binop) \ #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -35,8 +51,72 @@ union Instr struct instr_common { MOTH_INSTR_HEADER }; + struct instr_ret { + MOTH_INSTR_HEADER + int tempIndex; + }; + struct instr_storeTemp { + MOTH_INSTR_HEADER + int tempIndex; + }; + struct instr_loadTemp { + MOTH_INSTR_HEADER + int tempIndex; + }; + struct instr_moveTemp { + MOTH_INSTR_HEADER + int fromTempIndex; + int toTempIndex; + }; + struct instr_loadNumber { + MOTH_INSTR_HEADER + double value; + }; + struct instr_loadString { + MOTH_INSTR_HEADER + VM::String *value; + }; + struct instr_loadClosure { + MOTH_INSTR_HEADER + IR::Function *value; + }; + struct instr_loadName { + MOTH_INSTR_HEADER + VM::String *value; + }; + struct instr_push { + MOTH_INSTR_HEADER + quint32 value; + }; + struct instr_call { + MOTH_INSTR_HEADER + quint32 argc; + quint32 args; + }; + struct instr_jump { + MOTH_INSTR_HEADER + ptrdiff_t offset; + }; + struct instr_binop { + MOTH_INSTR_HEADER + int lhsTempIndex; + int rhsTempIndex; + void (*alu)(VM::Context *, VM::Value *, const VM::Value *, const VM::Value *); + }; instr_common common; + instr_ret ret; + instr_storeTemp storeTemp; + instr_loadTemp loadTemp; + instr_moveTemp moveTemp; + instr_loadNumber loadNumber; + instr_loadString loadString; + instr_loadClosure loadClosure; + instr_loadName loadName; + instr_push push; + instr_call call; + instr_jump jump; + instr_binop binop; static int size(Type type); }; diff --git a/moth/qv4isel_moth.cpp b/moth/qv4isel_moth.cpp index 628e2f0a91..95db21620d 100644 --- a/moth/qv4isel_moth.cpp +++ b/moth/qv4isel_moth.cpp @@ -6,7 +6,7 @@ using namespace QQmlJS::Moth; InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *code) -: _code(code), _ccode(code) +: _engine(engine), _code(code), _ccode(code) { } @@ -18,52 +18,305 @@ void InstructionSelection::operator()(IR::Function *function) { qSwap(_function, function); + _function->code = VME::exec; + _function->codeData = _ccode; + + int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; + assert(locals >= 0); + + Instruction::Push push; + push.value = quint32(locals); + addInstruction(push); + foreach (_block, _function->basicBlocks) { + _addrs.insert(_block, _ccode - _code); + foreach (IR::Stmt *s, _block->statements) s->accept(this); } - addInstruction(Instruction::Done()); + for (QHash<IR::BasicBlock *, QVector<ptrdiff_t> >::ConstIterator iter = _patches.begin(); + iter != _patches.end(); ++iter) { + + Q_ASSERT(_addrs.contains(iter.key())); + ptrdiff_t target = _addrs.value(iter.key()); + + const QVector<ptrdiff_t> &patchList = iter.value(); + for (int ii = 0; ii < patchList.count(); ++ii) { + ptrdiff_t patch = patchList.at(ii); + + *((ptrdiff_t *)(_code + patch)) = target; + } + } qSwap(_function, function); } -void InstructionSelection::visitExp(IR::Exp *) +void InstructionSelection::visitExp(IR::Exp *s) { - qWarning("%s", __PRETTY_FUNCTION__); + if (IR::Call *c = s->expr->asCall()) { + if (IR::Name *n = c->base->asName()) { + if (n->builtin == IR::Name::builtin_invalid) { + Instruction::LoadName load; + load.value = _engine->newString(*n->id); + addInstruction(load); + } else { + Q_UNIMPLEMENTED(); + } + } else if (IR::Member *m = c->base->asMember()) { + Q_UNIMPLEMENTED(); + } else if (IR::Temp *t = c->base->asTemp()) { + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + } else { + Q_UNREACHABLE(); + } + + call(c->args); + } else { + Q_UNREACHABLE(); + } +} + +void InstructionSelection::call(IR::ExprList *e) +{ + Instruction::Call call; + call.argc = 0; + call.args = 0; + + int locals = _function->tempCount - _function->locals.size() + _function->maxNumberOfArguments; + + if (e && e->next == 0 && e->expr->asTemp()->index >= 0 && e->expr->asTemp()->index < locals) { + // We pass single arguments as references to the stack + call.argc = 1; + call.args = e->expr->asTemp()->index; + } else if (e) { + // We need to move all the temps into the function arg array + int argLocation = _function->tempCount - _function->locals.size(); + assert(argLocation >= 0); + call.args = argLocation; + while (e) { + Instruction::MoveTemp move; + move.fromTempIndex = e->expr->asTemp()->index; + move.toTempIndex = argLocation; + addInstruction(move); + ++argLocation; + ++call.argc; + e = e->next; + } + } + + addInstruction(call); } void InstructionSelection::visitEnter(IR::Enter *) { qWarning("%s", __PRETTY_FUNCTION__); + Q_UNREACHABLE(); } void InstructionSelection::visitLeave(IR::Leave *) { qWarning("%s", __PRETTY_FUNCTION__); + Q_UNREACHABLE(); } -void InstructionSelection::visitMove(IR::Move *) +void InstructionSelection::visitMove(IR::Move *s) { - qWarning("%s", __PRETTY_FUNCTION__); + if (s->op == IR::OpInvalid) + simpleMove(s); + else + qWarning("UNKNOWN MOVE"); } -void InstructionSelection::visitJump(IR::Jump *) +typedef void (*ALUFunction)(VM::Context*, VM::Value*, const VM::Value*, const VM::Value *); +ALUFunction aluOpFunction(IR::AluOp op) { - qWarning("%s", __PRETTY_FUNCTION__); + 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 0; + case IR::OpBitOr: + return 0; + case IR::OpBitXor: + return 0; + 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 0; + case IR::OpIn: + return 0; + case IR::OpAnd: + return 0; + case IR::OpOr: + return 0; + default: + assert(!"Unknown AluOp"); + return 0; + } +}; + +// A move that doesn't involve an inplace operation +void InstructionSelection::simpleMove(IR::Move *s) +{ + if (IR::Name *n = s->target->asName()) { + qWarning("NAME"); + } else if (IR::Temp *t = s->target->asTemp()) { + + if (IR::Name *n = s->source->asName()) { + qWarning(" NAME"); + } else if (IR::Const *c = s->source->asConst()) { + switch (c->type) { + case IR::UndefinedType: + addInstruction(Instruction::LoadUndefined()); + break; + case IR::NullType: + addInstruction(Instruction::LoadNull()); + break; + case IR::BoolType: + if (c->value) addInstruction(Instruction::LoadTrue()); + else addInstruction(Instruction::LoadFalse()); + break; + case IR::NumberType: { + Instruction::LoadNumber load; + load.value = c->value; + addInstruction(load); + } break; + default: + Q_UNREACHABLE(); + break; + } + } else if (IR::Temp *t2 = s->source->asTemp()) { + Instruction::LoadTemp load; + load.tempIndex = t2->index; + addInstruction(load); + } else if (IR::String *str = s->source->asString()) { + Instruction::LoadString load; + load.value = _engine->newString(*str->value); + addInstruction(load); + } else if (IR::Closure *clos = s->source->asClosure()) { + Instruction::LoadClosure load; + load.value = clos->value; + addInstruction(load); + } else if (IR::New *ctor = s->source->asNew()) { + qWarning(" NEW"); + } else if (IR::Member *m = s->source->asMember()) { + qWarning(" MEMBER"); + } else if (IR::Subscript *ss = s->source->asSubscript()) { + qWarning(" SUBSCRIPT"); + } else if (IR::Unop *u = s->source->asUnop()) { + qWarning(" UNOP"); + } else if (IR::Binop *b = s->source->asBinop()) { + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhsTempIndex = b->left->index; + binop.rhsTempIndex = b->right->index; + addInstruction(binop); + } else if (IR::Call *c = s->source->asCall()) { + qWarning(" CALL"); + } + + Instruction::StoreTemp st; + st.tempIndex = t->index; + addInstruction(st); + + } else if (IR::Member *m = s->target->asMember()) { + qWarning("MEMBER"); + } else if (IR::Subscript *ss = s->target->asSubscript()) { + qWarning("SUBSCRIPT"); + } else { + Q_UNREACHABLE(); + } } -void InstructionSelection::visitCJump(IR::CJump *) +void InstructionSelection::visitJump(IR::Jump *s) { - qWarning("%s", __PRETTY_FUNCTION__); + 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::visitRet(IR::Ret *) +void InstructionSelection::visitCJump(IR::CJump *s) { - qWarning("%s", __PRETTY_FUNCTION__); + if (IR::Temp *t = s->cond->asTemp()) { + Instruction::LoadTemp load; + load.tempIndex = t->index; + addInstruction(load); + } else if (IR::Binop *b = s->cond->asBinop()) { + Instruction::Binop binop; + binop.alu = aluOpFunction(b->op); + binop.lhsTempIndex = b->left->index; + binop.rhsTempIndex = b->right->index; + addInstruction(binop); + } else { + Q_UNREACHABLE(); + } + + Instruction::CJump jump; + jump.offset = 0; + ptrdiff_t tl = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iftrue].append(tl); + + if (_block->index + 1 != s->iffalse->index) { + Instruction::Jump jump; + jump.offset = 0; + ptrdiff_t fl = addInstruction(jump) + (((const char *)&jump.offset) - ((const char *)&jump)); + _patches[s->iffalse].append(fl); + } +} + +void InstructionSelection::visitRet(IR::Ret *s) +{ + Instruction::Ret ret; + ret.tempIndex = s->expr->index; + addInstruction(ret); } -int InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) +ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) { #ifdef MOTH_THREADED_INTERPRETER instr.common.code = VME::instructionJumpTable()[static_cast<int>(type)]; @@ -71,8 +324,7 @@ int InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) instr.common.instructionType = type; #endif - // XXX - int is wrong size for both of these - int ptrOffset = (int)(_ccode - _code); + ptrdiff_t ptrOffset = _ccode - _code; int size = Instr::size(type); ::memcpy(_ccode, reinterpret_cast<const char *>(&instr), size); diff --git a/moth/qv4isel_moth_p.h b/moth/qv4isel_moth_p.h index d8e56cac70..009d0b3a36 100644 --- a/moth/qv4isel_moth_p.h +++ b/moth/qv4isel_moth_p.h @@ -34,19 +34,26 @@ private: Instruction(); }; + void simpleMove(IR::Move *); + void call(IR::ExprList *); + template <int Instr> - inline int addInstruction(const InstrData<Instr> &data); - int addInstructionHelper(Instr::Type type, Instr &instr); + inline ptrdiff_t addInstruction(const InstrData<Instr> &data); + ptrdiff_t addInstructionHelper(Instr::Type type, Instr &instr); + VM::ExecutionEngine *_engine; IR::Function *_function; IR::BasicBlock *_block; + QHash<IR::BasicBlock *, QVector<ptrdiff_t> > _patches; + QHash<IR::BasicBlock *, ptrdiff_t> _addrs; + uchar *_code; uchar *_ccode; }; template<int InstrT> -int InstructionSelection::addInstruction(const InstrData<InstrT> &data) +ptrdiff_t InstructionSelection::addInstruction(const InstrData<InstrT> &data) { Instr genericInstr; InstrMeta<InstrT>::setData(genericInstr, data); diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index c14a61cb9c..98ea922e7e 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -38,7 +38,21 @@ using namespace QQmlJS::Moth; #endif -void VME::operator()(QQmlJS::VM::Context *, const uchar *code +static inline VM::Value *tempValue(QQmlJS::VM::Context *context, QVector<VM::Value> &stack, int index) +{ + if (index < 0) { + const int arg = -index - 1; + return context->arguments + arg; + } else if (index < stack.count()) { + return stack.data() + index; + } else { + return context->locals + index - stack.count(); + } +} + +#define TEMP(index) *tempValue(context, stack, index) + +void VME::operator()(QQmlJS::VM::Context *context, const uchar *basecode #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable #endif @@ -57,6 +71,11 @@ void VME::operator()(QQmlJS::VM::Context *, const uchar *code } #endif + const uchar *code = basecode; + + QVector<VM::Value> stack; + VM::Value tempRegister; + #ifdef MOTH_THREADED_INTERPRETER const Instr *genericInstr = reinterpret_cast<const Instr *>(code); goto *genericInstr->common.code; @@ -66,13 +85,76 @@ void VME::operator()(QQmlJS::VM::Context *, const uchar *code switch (genericInstr->common.instructionType) { #endif - MOTH_BEGIN_INSTR(Nop) - qWarning("NOP"); - MOTH_END_INSTR(Nop) + MOTH_BEGIN_INSTR(StoreTemp) + TEMP(instr.tempIndex) = tempRegister; + MOTH_END_INSTR(StoreTemp) + + MOTH_BEGIN_INSTR(LoadTemp) + tempRegister = TEMP(instr.tempIndex); + MOTH_END_INSTR(LoadTemp) + + MOTH_BEGIN_INSTR(MoveTemp) + TEMP(instr.toTempIndex) = TEMP(instr.fromTempIndex); + MOTH_END_INSTR(MoveTemp) + + MOTH_BEGIN_INSTR(LoadUndefined) + tempRegister = VM::Value::undefinedValue(); + MOTH_END_INSTR(LoadUndefined) + + MOTH_BEGIN_INSTR(LoadNull) + tempRegister = VM::Value::nullValue(); + MOTH_END_INSTR(LoadNull) + + MOTH_BEGIN_INSTR(LoadTrue) + tempRegister = VM::Value::fromBoolean(true); + MOTH_END_INSTR(LoadTrue) + + MOTH_BEGIN_INSTR(LoadFalse) + tempRegister = VM::Value::fromBoolean(false); + MOTH_END_INSTR(LoadFalse) - MOTH_BEGIN_INSTR(Done) + MOTH_BEGIN_INSTR(LoadNumber) + tempRegister = VM::Value::fromNumber(instr.value); + MOTH_END_INSTR(LoadNumber) + + MOTH_BEGIN_INSTR(LoadString) + tempRegister = VM::Value::fromString(instr.value); + MOTH_END_INSTR(LoadString) + + MOTH_BEGIN_INSTR(LoadClosure) + __qmljs_init_closure(context, &tempRegister, instr.value); + MOTH_END_INSTR(LoadClosure) + + MOTH_BEGIN_INSTR(LoadName) + __qmljs_get_activation_property(context, &tempRegister, instr.value); + MOTH_END_INSTR(LoadName) + + MOTH_BEGIN_INSTR(Push) + stack.resize(instr.value); + MOTH_END_INSTR(Push) + + MOTH_BEGIN_INSTR(Call) + VM::Value *args = stack.data() + instr.args; + __qmljs_call_value(context, &tempRegister, /*thisObject=*/0, &tempRegister, args, instr.argc); + MOTH_END_INSTR(Call) + + MOTH_BEGIN_INSTR(Jump) + code = basecode + instr.offset; + MOTH_END_INSTR(Jump) + + MOTH_BEGIN_INSTR(CJump) + if (__qmljs_to_boolean(context, &tempRegister)) + code = basecode + instr.offset; + MOTH_END_INSTR(CJump) + + MOTH_BEGIN_INSTR(Binop) + instr.alu(context, &tempRegister, &TEMP(instr.lhsTempIndex), &TEMP(instr.rhsTempIndex)); + MOTH_END_INSTR(Binop) + + MOTH_BEGIN_INSTR(Ret) + context->result = TEMP(instr.tempIndex); return; - MOTH_END_INSTR(Done) + MOTH_END_INSTR(Ret) #ifdef MOTH_THREADED_INTERPRETER // nothing to do @@ -98,3 +180,9 @@ void **VME::instructionJumpTable() } #endif +void VME::exec(VM::Context *ctxt, const uchar *code) +{ + VME vme; + vme(ctxt, code); +} + diff --git a/moth/qv4vme_moth_p.h b/moth/qv4vme_moth_p.h index 5055407ca2..db1507d84d 100644 --- a/moth/qv4vme_moth_p.h +++ b/moth/qv4vme_moth_p.h @@ -10,6 +10,8 @@ namespace Moth { class VME { public: + static void exec(VM::Context *, const uchar *); + void operator()(QQmlJS::VM::Context *, const uchar *code #ifdef MOTH_THREADED_INTERPRETER , void ***storeJumpTable = 0 diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 94df898277..9746d8d358 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -185,7 +185,7 @@ ScriptFunction::~ScriptFunction() void ScriptFunction::call(VM::Context *ctx) { - function->code(ctx); + function->code(ctx, function->codeData); } void ScriptFunction::construct(VM::Context *ctx) @@ -195,7 +195,7 @@ void ScriptFunction::construct(VM::Context *ctx) if (proto.isObject()) obj->prototype = proto.objectValue; __qmljs_init_object(&ctx->thisObject, obj); - function->code(ctx); + function->code(ctx, function->codeData); } Value *ActivationObject::getPropertyDescriptor(Context *ctx, String *name, PropertyAttributes *attributes) @@ -505,7 +505,7 @@ struct Jump: Stmt { }; struct CJump: Stmt { - Expr *cond; + Expr *cond; // Temp, Binop BasicBlock *iftrue; BasicBlock *iffalse; @@ -558,7 +558,10 @@ struct Function { QList<const QString *> formals; QList<const QString *> locals; IR::BasicBlock *handlersBlock; - void (*code)(VM::Context *); + + void (*code)(VM::Context *, const uchar *); + const uchar *codeData; + bool hasDirectEval: 1; bool hasNestedFunctions: 1; @@ -571,6 +574,7 @@ struct Function { , maxNumberOfArguments(0) , handlersBlock(0) , code(0) + , codeData(0) , hasDirectEval(false) , hasNestedFunctions(false) { this->name = newString(name); } diff --git a/qv4isel_x86_64.cpp b/qv4isel_x86_64.cpp index 2a18ba962e..8846c29004 100644 --- a/qv4isel_x86_64.cpp +++ b/qv4isel_x86_64.cpp @@ -108,7 +108,7 @@ void InstructionSelection::operator()(IR::Function *function) _code = _codePtr; _code = (uchar *) ((size_t(_code) + 15) & ~15); - _function->code = (void (*)(VM::Context *)) _code; + _function->code = (void (*)(VM::Context *, const uchar *)) _code; _codePtr = _code; _patches.clear(); _addrs.clear(); |