#include "qv4isel_util_p.h" #include "qv4isel_moth_p.h" #include "qv4vme_moth_p.h" #include "debugging.h" using namespace QQmlJS; using namespace QQmlJS::Moth; namespace { QTextStream qout(stderr, QIODevice::WriteOnly); static unsigned toValueOrTemp(IR::Expr *e, Instr::ValueOrTemp &vot) { if (IR::Const *c = e->asConst()) { vot.value = convertToValue(c); return 0; } else if (IR::Temp *t = e->asTemp()) { vot.tempIndex = t->index; return 1; } else { Q_UNREACHABLE(); } } #undef DEBUG_TEMP_COMPRESSION class CompressTemps: public IR::StmtVisitor, IR::ExprVisitor { public: void run(IR::Function *function) { #ifdef DEBUG_TEMP_COMPRESSION qDebug() << "starting on function" << (*function->name) << "with" << function->tempCount << "temps."; #endif // DEBUG_TEMP_COMPRESSION _seenTemps.clear(); _nextFree = 0; _active.reserve(function->tempCount); _localCount = function->locals.size(); QVector 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) { #ifdef DEBUG_TEMP_COMPRESSION qDebug("L%d:", block->index); #endif // DEBUG_TEMP_COMPRESSION for (int i = 0, ei = block->statements.size(); i < ei; ++i ) { _currentStatement = block->statements[i]; if (i == 0) expireOld(); #ifdef DEBUG_TEMP_COMPRESSION _currentStatement->dump(qout);qout<d) _currentStatement->accept(this); } maxUsed = std::max(maxUsed, _nextFree); } #ifdef DEBUG_TEMP_COMPRESSION qDebug() << "function" << (*function->name) << "uses" << maxUsed << "temps."; #endif // DEBUG_TEMP_COMPRESSION 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) { #ifdef DEBUG_TEMP_COMPRESSION qDebug() << " lookup" << (tempIndex + _localCount) << "->" << (i->second + _localCount); #endif // DEBUG_TEMP_COMPRESSION 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)); #ifdef DEBUG_TEMP_COMPRESSION qDebug() << " add" << (tempIndex + _localCount) << "->" << (firstFree+ _localCount); #endif // DEBUG_TEMP_COMPRESSION } int expireOld() { Q_ASSERT(_currentStatement->d); const QBitArray &liveIn = _currentStatement->d->liveIn; QBitArray inUse(_nextFree); int i = 0; while (i < _active.size()) { const QPair &p = _active[i]; if (p.second < _pinnedCount) { inUse.setBit(p.second); ++i; continue; } if (liveIn[p.first + _localCount]) { inUse[p.second] = true; ++i; } else { #ifdef DEBUG_TEMP_COMPRESSION qDebug() << " remove" << (p.first + _localCount) << "->" << (p.second + _localCount); #endif // DEBUG_TEMP_COMPRESSION _active.remove(i); } } for (int i = 0, ei = inUse.size(); i < ei; ++i) if (!inUse[i]) return i; return _nextFree; } private: typedef QVector > ActiveTemps; ActiveTemps _active; QSet _seenTemps; IR::Stmt *_currentStatement; int _localCount; int _nextFree; int _pinnedCount; }; } // anonymous namespace InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module) : EvalInstructionSelection(engine, module) , _function(0) , _block(0) , _code(0) , _ccode(0) { } InstructionSelection::~InstructionSelection() { } void InstructionSelection::run(VM::Function *vmFunction, IR::Function *function) { qSwap(_function, function); IR::BasicBlock *block; QHash > patches; QHash addrs; // FIXME: make the size dynamic. This requires changing the patching. uchar *code = new uchar[getpagesize() * 4000]; uchar *ccode = code; qSwap(block, _block); qSwap(patches, _patches); qSwap(addrs, _addrs); qSwap(code, _code); qSwap(ccode, _ccode); 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, _ccode - _code); foreach (IR::Stmt *s, _block->statements) s->accept(this); } for (QHash >::ConstIterator iter = _patches.begin(); iter != _patches.end(); ++iter) { Q_ASSERT(_addrs.contains(iter.key())); ptrdiff_t target = _addrs.value(iter.key()); const QVector &patchList = iter.value(); for (int ii = 0; ii < patchList.count(); ++ii) { ptrdiff_t patch = patchList.at(ii); *((ptrdiff_t *)(_code + patch)) = target - patch; } } qSwap(_function, function); _patches.clear(); _addrs.clear(); vmFunction->code = VME::exec; vmFunction->codeData = _code; qSwap(block, _block); qSwap(patches, _patches); qSwap(addrs, _addrs); qSwap(code, _code); qSwap(ccode, _ccode); } void InstructionSelection::callActivationProperty(IR::Call *c, int targetTempIndex) { IR::Name *baseName = c->base->asName(); Q_ASSERT(baseName); switch (baseName->builtin) { case IR::Name::builtin_invalid: { const int scratchIndex = scratchTempIndex(); Instruction::LoadName load; load.name = engine()->newString(*baseName->id); load.targetTempIndex = scratchIndex; addInstruction(load); Instruction::CallValue call; prepareCallArgs(c->args, call.argc, call.args); call.destIndex = scratchIndex; call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_typeof: { if (IR::Member *m = c->args->expr->asMember()) { Instruction::CallBuiltinTypeofMember call; call.base = m->base->asTemp()->index; call.member = engine()->identifier(*m->name); call.targetTempIndex = targetTempIndex; addInstruction(call); } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { Instruction::CallBuiltinTypeofSubscript call; call.base = ss->base->asTemp()->index; call.index = ss->index->asTemp()->index; call.targetTempIndex = targetTempIndex; addInstruction(call); } else if (IR::Name *n = c->args->expr->asName()) { Instruction::CallBuiltinTypeofName call; call.name = engine()->identifier(*n->id); call.targetTempIndex = targetTempIndex; addInstruction(call); } else if (IR::Temp *arg = c->args->expr->asTemp()){ assert(arg != 0); Instruction::CallBuiltinTypeofValue call; call.tempIndex = arg->index; call.targetTempIndex = targetTempIndex; addInstruction(call); } else { assert(false); } } break; case IR::Name::builtin_delete: { if (IR::Member *m = c->args->expr->asMember()) { Instruction::CallBuiltinDeleteMember call; call.base = m->base->asTemp()->index; call.member = engine()->newString(*m->name); call.targetTempIndex = targetTempIndex; addInstruction(call); } else if (IR::Subscript *ss = c->args->expr->asSubscript()) { Instruction::CallBuiltinDeleteSubscript call; call.base = ss->base->asTemp()->index; call.index = ss->index->asTemp()->index; call.targetTempIndex = targetTempIndex; addInstruction(call); } else if (IR::Name *n = c->args->expr->asName()) { Instruction::CallBuiltinDeleteName call; call.name = engine()->newString(*n->id); call.targetTempIndex = targetTempIndex; addInstruction(call); } else { Instruction::CallBuiltinDeleteValue call; call.tempIndex = c->args->expr->asTemp()->index; call.targetTempIndex = targetTempIndex; addInstruction(call); } } break; case IR::Name::builtin_throw: { IR::Temp *arg = c->args->expr->asTemp(); assert(arg != 0); Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_throw; prepareCallArgs(c->args, call.argc, call.args); call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_rethrow: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_rethrow; call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_create_exception_handler: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_create_exception_handler; call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_delete_exception_handler: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_delete_exception_handler; call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_get_exception: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_get_exception; call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_foreach_iterator_object: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_foreach_iterator_object; prepareCallArgs(c->args, call.argc, call.args); call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_foreach_next_property_name: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_foreach_next_property_name; prepareCallArgs(c->args, call.argc, call.args); call.targetTempIndex = targetTempIndex; addInstruction(call); } break; case IR::Name::builtin_push_with: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_push_with; prepareCallArgs(c->args, call.argc, call.args); assert(call.argc == 1); addInstruction(call); } break; case IR::Name::builtin_pop_with: { Instruction::CallBuiltin call; call.builtin = Instruction::CallBuiltin::builtin_pop_with; addInstruction(call); } break; case IR::Name::builtin_declare_vars: if (c->args) { IR::Const *deletable = c->args->expr->asConst(); assert(deletable->type == IR::BoolType); const bool isDeletable = deletable->value != 0; for (IR::ExprList *it = c->args->next; it; it = it->next) { Instruction::CallBuiltinDeclareVar call; call.isDeletable = isDeletable; call.varName = engine()->newString(*it->expr->asName()->id); addInstruction(call); } } break; default: Q_UNIMPLEMENTED(); c->dump(qout); qout << endl; } } void InstructionSelection::callValue(IR::Call *c, int targetTempIndex) { IR::Temp *t = c->base->asTemp(); Q_ASSERT(t); Instruction::CallValue call; prepareCallArgs(c->args, call.argc, call.args); call.destIndex = t->index; call.targetTempIndex = targetTempIndex; addInstruction(call); } void InstructionSelection::callProperty(IR::Call *c, int targetTempIndex) { IR::Member *m = c->base->asMember(); Q_ASSERT(m); // call the property on the loaded base Instruction::CallProperty call; call.baseTemp = m->base->asTemp()->index; call.name = engine()->newString(*m->name); prepareCallArgs(c->args, call.argc, call.args); call.targetTempIndex = targetTempIndex; addInstruction(call); } void InstructionSelection::construct(IR::New *ctor, int targetTempIndex) { if (IR::Name *baseName = ctor->base->asName()) { Instruction::CreateActivationProperty create; create.name = engine()->newString(*baseName->id); prepareCallArgs(ctor->args, create.argc, create.args); create.targetTempIndex = targetTempIndex; addInstruction(create); } else if (IR::Member *member = ctor->base->asMember()) { IR::Temp *base = member->base->asTemp(); assert(base != 0); Instruction::CreateProperty create; create.base = base->index; create.name = engine()->newString(*member->name); prepareCallArgs(ctor->args, create.argc, create.args); create.targetTempIndex = targetTempIndex; addInstruction(create); } else if (IR::Temp *baseTemp = ctor->base->asTemp()) { Instruction::CreateValue create; create.func = baseTemp->index; prepareCallArgs(ctor->args, create.argc, create.args); create.targetTempIndex = targetTempIndex; addInstruction(create); } else { qWarning(" NEW"); } } void InstructionSelection::prepareCallArgs(IR::ExprList *e, quint32 &argc, quint32 &args) { argc = 0; args = 0; bool singleArgIsTemp = false; if (e && e->next == 0) { // ok, only 1 argument in the cal... const int idx = e->expr->asTemp()->index; if (idx >= 0) { // not an argument to this function... // so if it's not a local, we're in: 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; } else if (e) { // We need to move all the temps into the function arg array int argLocation = outgoingArgumentTempStart(); assert(argLocation >= 0); args = argLocation; while (e) { Instruction::MoveTemp move; move.fromTempIndex = e->expr->asTemp()->index; move.toTempIndex = argLocation; addInstruction(move); ++argLocation; ++argc; e = e->next; } } } void InstructionSelection::visitExp(IR::Exp *s) { if (IR::Call *c = s->expr->asCall()) { // These are calls where the result is ignored. const int targetTempIndex = scratchTempIndex(); if (c->base->asName()) { callActivationProperty(c, targetTempIndex); } else if (c->base->asTemp()) { callValue(c, targetTempIndex); } else if (c->base->asMember()) { callProperty(c, targetTempIndex); } else { Q_UNREACHABLE(); } } else { Q_UNREACHABLE(); } } void InstructionSelection::visitEnter(IR::Enter *) { qWarning("%s", __PRETTY_FUNCTION__); Q_UNREACHABLE(); } void InstructionSelection::visitLeave(IR::Leave *) { qWarning("%s", __PRETTY_FUNCTION__); Q_UNREACHABLE(); } typedef VM::Value (*ALUFunction)(const VM::Value, const VM::Value, VM::ExecutionContext*); static 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; } }; void InstructionSelection::visitMove(IR::Move *s) { if (IR::Temp *t = s->target->asTemp()) { const int targetTempIndex = t->index; // Check what kind of load it is, and generate the instruction for that. // The store to the temp (the target) is done afterwards. if (IR::Name *n = s->source->asName()) { Q_UNUSED(n); if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. Instruction::LoadThis load; load.targetTempIndex = targetTempIndex; addInstruction(load); } else { Instruction::LoadName load; load.name = engine()->newString(*n->id); load.targetTempIndex = targetTempIndex; addInstruction(load); } } else if (s->source->asTemp() || s->source->asConst()) { if (s->op == IR::OpInvalid) { if (IR::Temp *t2 = s->source->asTemp()) { Instruction::MoveTemp move; move.fromTempIndex = t2->index; move.toTempIndex = targetTempIndex; addInstruction(move); } else { IR::Const *c = s->source->asConst(); assert(c); Instruction::LoadValue load; load.targetTempIndex = targetTempIndex; load.value = convertToValue(c); addInstruction(load); } } else { Instruction::Binop binop; binop.alu = aluOpFunction(s->op); binop.lhsIsTemp = toValueOrTemp(t, binop.lhs); binop.rhsIsTemp = toValueOrTemp(s->source, binop.rhs); binop.targetTempIndex = targetTempIndex; addInstruction(binop); } } else if (IR::String *str = s->source->asString()) { Instruction::LoadValue load; load.value = VM::Value::fromString(engine()->newString(*str->value)); load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::Closure *clos = s->source->asClosure()) { VM::Function *vmFunc = vmFunction(clos->value); assert(vmFunc); Instruction::LoadClosure load; load.value = vmFunc; load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::New *ctor = s->source->asNew()) { construct(ctor, targetTempIndex); } else if (IR::Member *m = s->source->asMember()) { if (IR::Temp *base = m->base->asTemp()) { Instruction::LoadProperty load; load.baseTemp = base->index; load.name = engine()->newString(*m->name); load.targetTempIndex = targetTempIndex; addInstruction(load); } else { qWarning(" MEMBER"); } } else if (IR::Subscript *ss = s->source->asSubscript()) { Instruction::LoadElement load; load.base = ss->base->asTemp()->index; load.index = ss->index->asTemp()->index; load.targetTempIndex = targetTempIndex; addInstruction(load); } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { VM::Value (*op)(const VM::Value value, VM::ExecutionContext *ctx) = 0; switch (u->op) { 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.e = e->index; unop.targetTempIndex = targetTempIndex; addInstruction(unop); } else { qWarning(" UNOP1"); } } else { qWarning(" UNOP2"); s->dump(qout, IR::Stmt::MIR); qout << endl; } } else if (IR::Binop *b = s->source->asBinop()) { Instruction::Binop binop; binop.alu = aluOpFunction(b->op); binop.lhsIsTemp = toValueOrTemp(b->left, binop.lhs); binop.rhsIsTemp = toValueOrTemp(b->right, binop.rhs); binop.targetTempIndex = targetTempIndex; addInstruction(binop); } else if (IR::Call *c = s->source->asCall()) { if (c->base->asName()) { callActivationProperty(c, targetTempIndex); } else if (c->base->asMember()) { callProperty(c, targetTempIndex); } else if (c->base->asTemp()) { callValue(c, targetTempIndex); } else { Q_UNREACHABLE(); } } return; } else if (IR::Name *n = s->target->asName()) { if (s->source->asTemp() || s->source->asConst()) { void (*op)(VM::Value value, VM::String *name, VM::ExecutionContext *ctx) = 0; switch (s->op) { 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.targetName = engine()->newString(*n->id); ieo.sourceIsTemp = toValueOrTemp(s->source, ieo.source); addInstruction(ieo); return; } else if (s->op == IR::OpInvalid) { Instruction::StoreName store; store.sourceIsTemp = toValueOrTemp(s->source, store.source); store.name = engine()->newString(*n->id); addInstruction(store); return; } } qWarning("NAME"); } else if (IR::Subscript *ss = s->target->asSubscript()) { if (s->source->asTemp() || s->source->asConst()) { void (*op)(VM::Value base, VM::Value index, VM::Value value, VM::ExecutionContext *ctx) = 0; switch (s->op) { 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; } if (op) { Instruction::InplaceElementOp ieo; ieo.alu = op; ieo.targetBase = ss->base->asTemp()->index; ieo.targetIndex = ss->index->asTemp()->index; ieo.sourceIsTemp = toValueOrTemp(s->source, ieo.source); addInstruction(ieo); return; } else if (s->op == IR::OpInvalid) { Instruction::StoreElement store; store.base = ss->base->asTemp()->index; store.index = ss->index->asTemp()->index; store.sourceIsTemp = toValueOrTemp(s->source, store.source); addInstruction(store); return; } } qWarning("SUBSCRIPT"); } else if (IR::Member *m = s->target->asMember()) { if (s->source->asTemp() || s->source->asConst()) { void (*op)(VM::Value value, VM::Value base, VM::String *name, VM::ExecutionContext *ctx) = 0; switch (s->op) { 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; } if (op) { Instruction::InplaceMemberOp imo; imo.alu = op; imo.targetBase = m->base->asTemp()->index; imo.targetMember = engine()->newString(*m->name); imo.sourceIsTemp = toValueOrTemp(s->source, imo.source); addInstruction(imo); return; } else if (s->op == IR::OpInvalid) { Instruction::StoreProperty store; store.baseTemp = m->base->asTemp()->index; store.name = engine()->newString(*m->name); store.sourceIsTemp = toValueOrTemp(s->source, store.source); addInstruction(store); return; } } qWarning("MEMBER"); } Q_UNIMPLEMENTED(); s->dump(qout, IR::Stmt::MIR); qout << endl; Q_UNREACHABLE(); } 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) { int tempIndex; if (IR::Temp *t = s->cond->asTemp()) { tempIndex = t->index; } else if (IR::Binop *b = s->cond->asBinop()) { tempIndex = scratchTempIndex(); Instruction::Binop binop; binop.alu = aluOpFunction(b->op); binop.lhsIsTemp = toValueOrTemp(b->left, binop.lhs); binop.rhsIsTemp = toValueOrTemp(b->right, binop.rhs); binop.targetTempIndex = tempIndex; addInstruction(binop); } else { Q_UNREACHABLE(); } Instruction::CJump jump; jump.offset = 0; jump.tempIndex = tempIndex; 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); } ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) { #ifdef MOTH_THREADED_INTERPRETER instr.common.code = VME::instructionJumpTable()[static_cast(type)]; #else instr.common.instructionType = type; #endif ptrdiff_t ptrOffset = _ccode - _code; int size = Instr::size(type); ::memcpy(_ccode, reinterpret_cast(&instr), size); _ccode += size; return ptrOffset; }