#include "qv4isel_p.h" #include "qmljs_runtime.h" #include "qmljs_objects.h" #define TARGET_AMD64 typedef quint64 guint64; typedef qint64 gint64; typedef uchar guint8; typedef uint guint32; typedef void *gpointer; #include "amd64-codegen.h" #include #include #include #ifndef NO_UDIS86 # include #endif using namespace QQmlJS; using namespace QQmlJS::x86_64; using namespace QQmlJS::VM; namespace { QTextStream qout(stdout, QIODevice::WriteOnly); } static inline void amd64_patch (unsigned char* code, gpointer target) { guint8 rex = 0; #ifdef __native_client_codegen__ code = amd64_skip_nops (code); #endif #if defined(__native_client_codegen__) && defined(__native_client__) if (nacl_is_code_address (code)) { /* For tail calls, code is patched after being installed */ /* but not through the normal "patch callsite" method. */ unsigned char buf[kNaClAlignment]; unsigned char *aligned_code = (uintptr_t)code & ~kNaClAlignmentMask; int ret; memcpy (buf, aligned_code, kNaClAlignment); /* Patch a temp buffer of bundle size, */ /* then install to actual location. */ amd64_patch (buf + ((uintptr_t)code - (uintptr_t)aligned_code), target); ret = nacl_dyncode_modify (aligned_code, buf, kNaClAlignment); g_assert (ret == 0); return; } target = nacl_modify_patch_target (target); #endif /* Skip REX */ if ((code [0] >= 0x40) && (code [0] <= 0x4f)) { rex = code [0]; code += 1; } if ((code [0] & 0xf8) == 0xb8) { /* amd64_set_reg_template */ *(guint64*)(code + 1) = (guint64)target; } else if ((code [0] == 0x8b) && rex && x86_modrm_mod (code [1]) == 0 && x86_modrm_rm (code [1]) == 5) { /* mov 0(%rip), %dreg */ *(guint32*)(code + 2) = (guint32)(guint64)target - 7; } else if ((code [0] == 0xff) && (code [1] == 0x15)) { /* call *(%rip) */ *(guint32*)(code + 2) = ((guint32)(guint64)target) - 7; } else if (code [0] == 0xe8) { /* call */ gint64 disp = (guint8*)target - (guint8*)code; assert (amd64_is_imm32 (disp)); x86_patch (code, (unsigned char*)target); } else x86_patch (code, (unsigned char*)target); } InstructionSelection::InstructionSelection(VM::ExecutionEngine *engine, IR::Module *module, uchar *buffer) : _engine(engine) , _module(module) , _function(0) , _block(0) , _buffer(buffer) , _code(buffer) , _codePtr(buffer) { } InstructionSelection::~InstructionSelection() { } void InstructionSelection::operator()(IR::Function *function) { qSwap(_function, function); _code = _codePtr; _code = (uchar *) ((size_t(_code) + 15) & ~15); _function->code = (void (*)(VM::Context *)) _code; _codePtr = _code; int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value); locals = (locals + 15) & ~15; amd64_push_reg(_codePtr, AMD64_RBP); amd64_push_reg(_codePtr, AMD64_R14); amd64_push_reg(_codePtr, AMD64_R15); amd64_mov_reg_reg(_codePtr, AMD64_RBP, AMD64_RSP, 8); amd64_mov_reg_reg(_codePtr, AMD64_R14, AMD64_RDI, 8); amd64_alu_reg_imm(_codePtr, X86_SUB, AMD64_RSP, locals); amd64_mov_reg_membase(_codePtr, AMD64_R15, AMD64_R14, offsetof(Context, locals), 8); foreach (IR::BasicBlock *block, _function->basicBlocks) { _block = block; _addrs[block] = _codePtr; foreach (IR::Stmt *s, block->statements) { s->accept(this); } } QHashIterator > it(_patches); while (it.hasNext()) { it.next(); uchar *target = _addrs[it.key()]; foreach (uchar *instr, it.value()) { amd64_patch(instr, target); } } amd64_alu_reg_imm(_codePtr, X86_ADD, AMD64_RSP, locals); amd64_pop_reg(_codePtr, AMD64_R15); amd64_pop_reg(_codePtr, AMD64_R14); amd64_pop_reg(_codePtr, AMD64_RBP); amd64_ret(_codePtr); #ifndef NO_UDIS86 static bool showCode = !qgetenv("SHOW_CODE").isNull(); if (showCode) { printf("code size: %ld bytes\n", (_codePtr - _code)); ud_t ud_obj; ud_init(&ud_obj); ud_set_input_buffer(&ud_obj, _code, _codePtr - _code); ud_set_mode(&ud_obj, 64); ud_set_syntax(&ud_obj, UD_SYN_ATT); while (ud_disassemble(&ud_obj)) { printf("\t%s\n", ud_insn_asm(&ud_obj)); } } #endif qSwap(_function, _function); } String *InstructionSelection::identifier(const QString &s) { return _engine->identifier(s); } void InstructionSelection::loadTempAddress(int reg, IR::Temp *t) { if (t->index < 0) { const int arg = -t->index - 1; amd64_mov_reg_membase(_codePtr, reg, AMD64_R14, offsetof(Context, arguments), 8); amd64_lea_membase(_codePtr, reg, reg, sizeof(Value) * arg); } else if (t->index < _function->locals.size()) { amd64_lea_membase(_codePtr, reg, AMD64_R15, sizeof(Value) * t->index); } else { amd64_lea_membase(_codePtr, reg, AMD64_RSP, sizeof(Value) * (_function->maxNumberOfArguments + t->index - _function->locals.size())); } } void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result) { IR::Name *baseName = call->base->asName(); assert(baseName != 0); int argc = 0; for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; } int i = 0; for (IR::ExprList *it = call->args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); } amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); if (baseName->id) { amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); amd64_call_code(_codePtr, __qmljs_call_activation_property); } else { switch (baseName->builtin) { case IR::Name::builtin_invalid: Q_UNREACHABLE(); break; case IR::Name::builtin_typeof: amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); amd64_call_code(_codePtr, __qmljs_builtin_typeof); break; case IR::Name::builtin_throw: amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); amd64_call_code(_codePtr, __qmljs_builtin_throw); break; case IR::Name::builtin_rethrow: amd64_lea_membase(_codePtr, AMD64_RDX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_RCX, argc); amd64_call_code(_codePtr, __qmljs_builtin_rethrow); return; // we need to return to avoid checking the exceptions } } checkExceptions(); } void InstructionSelection::callValue(IR::Call *call, IR::Temp *result) { IR::Temp *baseTemp = call->base->asTemp(); assert(baseTemp != 0); int argc = 0; for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; } int i = 0; for (IR::ExprList *it = call->args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); } amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RDX, AMD64_RDX); loadTempAddress(AMD64_RCX, baseTemp); amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_call_value); checkExceptions(); } void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result) { IR::Member *member = call->base->asMember(); assert(member != 0); assert(member->base->asTemp()); int argc = 0; for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; } int i = 0; for (IR::ExprList *it = call->args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); } //__qmljs_call_property(ctx, result, base, name, args, argc); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); loadTempAddress(AMD64_RDX, member->base->asTemp()); amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_call_property); checkExceptions(); } void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result) { IR::Name *baseName = call->base->asName(); assert(baseName != 0); int argc = 0; for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; } int i = 0; for (IR::ExprList *it = call->args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); } amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*baseName->id)); amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); amd64_call_code(_codePtr, __qmljs_construct_activation_property); checkExceptions(); } void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result) { IR::Member *member = call->base->asMember(); assert(member != 0); assert(member->base->asTemp()); int argc = 0; for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; } int i = 0; for (IR::ExprList *it = call->args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); } //__qmljs_call_property(ctx, result, base, name, args, argc); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); loadTempAddress(AMD64_RDX, member->base->asTemp()); amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*member->name)); amd64_lea_membase(_codePtr, AMD64_R8, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R9, argc); amd64_call_code(_codePtr, __qmljs_construct_property); checkExceptions(); } void InstructionSelection::constructValue(IR::New *call, IR::Temp *result) { IR::Temp *baseTemp = call->base->asTemp(); assert(baseTemp != 0); int argc = 0; for (IR::ExprList *it = call->args; it; it = it->next) { ++argc; } int i = 0; for (IR::ExprList *it = call->args; it; it = it->next, ++i) { IR::Temp *arg = it->expr->asTemp(); assert(arg != 0); amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_RSP, sizeof(Value) * i); loadTempAddress(AMD64_RSI, arg); amd64_call_code(_codePtr, __qmljs_copy); } amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); // load the context if (result) loadTempAddress(AMD64_RSI, result); else amd64_alu_reg_reg(_codePtr, X86_XOR, AMD64_RSI, AMD64_RSI); loadTempAddress(AMD64_RDX, baseTemp); amd64_lea_membase(_codePtr, AMD64_RCX, AMD64_RSP, 0); amd64_mov_reg_imm(_codePtr, AMD64_R8, argc); amd64_call_code(_codePtr, __qmljs_construct_value); } void InstructionSelection::checkExceptions() { amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_R14, offsetof(Context, hasUncaughtException), 4); amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 1, 1); _patches[_function->handlersBlock].append(_codePtr); amd64_branch32(_codePtr, X86_CC_E, 0, 1); } void InstructionSelection::visitExp(IR::Exp *s) { if (IR::Call *c = s->expr->asCall()) { if (c->base->asName()) { callActivationProperty(c, 0); return; } else if (c->base->asTemp()) { callValue(c, 0); return; } else if (c->base->asMember()) { callProperty(c, 0); return; } } Q_UNIMPLEMENTED(); assert(!"TODO"); } void InstructionSelection::visitEnter(IR::Enter *) { Q_UNIMPLEMENTED(); assert(!"TODO"); } void InstructionSelection::visitLeave(IR::Leave *) { Q_UNIMPLEMENTED(); assert(!"TODO"); } void InstructionSelection::visitMove(IR::Move *s) { // %rdi, %rsi, %rdx, %rcx, %r8 and %r9 if (s->op == IR::OpInvalid) { if (IR::Name *n = s->target->asName()) { String *propertyName = identifier(*n->id); if (IR::Const *c = s->source->asConst()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); switch (c->type) { case IR::BoolType: amd64_mov_reg_imm(_codePtr, AMD64_RDX, c->value != 0); amd64_call_code(_codePtr, __qmljs_set_activation_property_boolean); break; case IR::NumberType: amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_set_activation_property_number); break; default: Q_UNIMPLEMENTED(); assert(!"TODO"); } } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_set_activation_property_string); } else if (IR::Temp *t = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); loadTempAddress(AMD64_RDX, t); amd64_call_code(_codePtr, __qmljs_set_activation_property); } else if (IR::Name *other = s->source->asName()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*other->id)); amd64_call_code(_codePtr, __qmljs_copy_activation_property); } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); amd64_mov_reg_imm(_codePtr, AMD64_RSI, propertyName); amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); amd64_call_code(_codePtr, __qmljs_set_activation_property_closure); } else { Q_UNIMPLEMENTED(); assert(!"TODO"); } checkExceptions(); return; } else if (IR::Temp *t = s->target->asTemp()) { if (IR::Name *n = s->source->asName()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin. amd64_call_code(_codePtr, __qmljs_get_thisObject); } else { String *propertyName = identifier(*n->id); amd64_mov_reg_imm(_codePtr, AMD64_RDX, propertyName); amd64_call_code(_codePtr, __qmljs_get_activation_property); checkExceptions(); } return; } else if (IR::Const *c = s->source->asConst()) { loadTempAddress(AMD64_RSI, t); switch (c->type) { case IR::NullType: amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NULL_TYPE, 4); break; case IR::UndefinedType: amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, UNDEFINED_TYPE, 4); break; case IR::BoolType: amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, BOOLEAN_TYPE, 4); amd64_mov_membase_imm(_codePtr, AMD64_RSI, offsetof(Value, booleanValue), c->value != 0, 1); break; case IR::NumberType: amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), X86_XMM0); break; default: Q_UNIMPLEMENTED(); assert(!"TODO"); } return; } else if (IR::Temp *t2 = s->source->asTemp()) { loadTempAddress(AMD64_RDI, t); loadTempAddress(AMD64_RSI, t2); amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); amd64_mov_membase_reg(_codePtr, AMD64_RDI, 0, AMD64_RAX, 4); amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, numberValue), 8); amd64_mov_membase_reg(_codePtr, AMD64_RDI, offsetof(Value, numberValue), AMD64_RAX, 8); return; } else if (IR::String *str = s->source->asString()) { loadTempAddress(AMD64_RDI, t); amd64_mov_reg_imm(_codePtr, AMD64_RSI, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_init_string); return; } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); amd64_mov_reg_imm(_codePtr, AMD64_RDX, clos->value); amd64_call_code(_codePtr, __qmljs_init_closure); return; } else if (IR::New *ctor = s->source->asNew()) { if (ctor->base->asName()) { constructActivationProperty(ctor, t); return; } else if (ctor->base->asMember()) { constructProperty(ctor, t); return; } else if (ctor->base->asTemp()) { constructValue(ctor, t); return; } } else if (IR::Member *m = s->source->asMember()) { //__qmljs_get_property(ctx, result, object, name); if (IR::Temp *base = m->base->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); loadTempAddress(AMD64_RDX, base); amd64_mov_reg_imm(_codePtr, AMD64_RCX, identifier(*m->name)); amd64_call_code(_codePtr, __qmljs_get_property); checkExceptions(); return; } assert(!"wip"); return; } else if (IR::Subscript *ss = s->source->asSubscript()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); loadTempAddress(AMD64_RDX, ss->base->asTemp()); loadTempAddress(AMD64_RCX, ss->index->asTemp()); amd64_call_code(_codePtr, __qmljs_get_element); checkExceptions(); return; } else if (IR::Unop *u = s->source->asUnop()) { if (IR::Temp *e = u->expr->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); loadTempAddress(AMD64_RDX, e); void (*op)(Context *, Value *, const Value *) = 0; switch (u->op) { case QQmlJS::IR::OpIfTrue: assert(!"unreachable"); break; case QQmlJS::IR::OpNot: op = __qmljs_not; break; case QQmlJS::IR::OpUMinus: op = __qmljs_uminus; break; case QQmlJS::IR::OpUPlus: op = __qmljs_uplus; break; case QQmlJS::IR::OpCompl: op = __qmljs_compl; break; default: assert(!"unreachable"); break; } // switch amd64_call_code(_codePtr, op); return; } else if (IR::Const *c = u->expr->asConst()) { assert(!"wip"); return; } } else if (IR::Binop *b = s->source->asBinop()) { IR::Temp *l = b->left->asTemp(); IR::Temp *r = b->right->asTemp(); if (l && r) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); loadTempAddress(AMD64_RDX, l); loadTempAddress(AMD64_RCX, r); uchar *label1 = 0, *label2 = 0, *label3 = 0; if (b->op == IR::OpMul || b->op == IR::OpAdd || b->op == IR::OpSub || b->op == IR::OpDiv) { amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RDX, 0, NUMBER_TYPE, 4); label1 = _codePtr; amd64_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_alu_membase_imm_size(_codePtr, X86_CMP, AMD64_RCX, 0, NUMBER_TYPE, 4); label2 = _codePtr; amd64_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_movsd_reg_membase(_codePtr, X86_XMM0, AMD64_RDX, offsetof(Value, numberValue)); amd64_movsd_reg_membase(_codePtr, X86_XMM1, AMD64_RCX, offsetof(Value, numberValue)); switch (b->op) { case IR::OpAdd: amd64_sse_addsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); break; case IR::OpSub: amd64_sse_subsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); break; case IR::OpMul: amd64_sse_mulsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); break; case IR::OpDiv: amd64_sse_divsd_reg_reg(_codePtr, X86_XMM0, X86_XMM1); break; default: Q_UNREACHABLE(); } // switch amd64_mov_membase_imm(_codePtr, AMD64_RSI, 0, NUMBER_TYPE, 4); amd64_movsd_membase_reg(_codePtr, AMD64_RSI, offsetof(Value, numberValue), X86_XMM0); label3 = _codePtr; amd64_jump32(_codePtr, 0); } if (label1 && label2) { amd64_patch(label1, _codePtr); amd64_patch(label2, _codePtr); } void (*op)(Context *, Value *, const Value *, const Value *) = 0; switch (b->op) { case QQmlJS::IR::OpInvalid: case QQmlJS::IR::OpIfTrue: case QQmlJS::IR::OpNot: case QQmlJS::IR::OpUMinus: case QQmlJS::IR::OpUPlus: case QQmlJS::IR::OpCompl: assert(!"unreachable"); break; case QQmlJS::IR::OpBitAnd: op = __qmljs_bit_and; break; case QQmlJS::IR::OpBitOr: op = __qmljs_bit_or; break; case QQmlJS::IR::OpBitXor: op = __qmljs_bit_xor; break; case QQmlJS::IR::OpAdd: op = __qmljs_add; break; case QQmlJS::IR::OpSub: op = __qmljs_sub; break; case QQmlJS::IR::OpMul: op = __qmljs_mul; break; case QQmlJS::IR::OpDiv: op = __qmljs_div; break; case QQmlJS::IR::OpMod: op = __qmljs_mod; break; case QQmlJS::IR::OpLShift: op = __qmljs_shl; break; case QQmlJS::IR::OpRShift: op = __qmljs_shr; break; case QQmlJS::IR::OpURShift: op = __qmljs_ushr; break; case QQmlJS::IR::OpGt: op = __qmljs_gt; break; case QQmlJS::IR::OpLt: op = __qmljs_lt; break; case QQmlJS::IR::OpGe: op = __qmljs_ge; break; case QQmlJS::IR::OpLe: op = __qmljs_le; break; case QQmlJS::IR::OpEqual: op = __qmljs_eq; break; case QQmlJS::IR::OpNotEqual: op = __qmljs_ne; break; case QQmlJS::IR::OpStrictEqual: op = __qmljs_se; break; case QQmlJS::IR::OpStrictNotEqual: op = __qmljs_sne; break; case QQmlJS::IR::OpAnd: case QQmlJS::IR::OpOr: assert(!"unreachable"); break; } amd64_call_code(_codePtr, op); if (label3) amd64_patch(label3, _codePtr); return; } } else if (IR::Call *c = s->source->asCall()) { if (c->base->asName()) { callActivationProperty(c, t); return; } else if (c->base->asMember()) { callProperty(c, t); return; } else if (c->base->asTemp()) { callValue(c, t); return; } } } else if (IR::Member *m = s->target->asMember()) { if (IR::Temp *base = m->base->asTemp()) { if (IR::Const *c = s->source->asConst()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_set_property_number); } else if (IR::String *str = s->source->asString()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RCX, _engine->newString(*str->value)); amd64_call_code(_codePtr, __qmljs_set_property_string); } else if (IR::Temp *t = s->source->asTemp()) { // __qmljs_set_property(ctx, object, name, value); amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); loadTempAddress(AMD64_RCX, t); amd64_call_code(_codePtr, __qmljs_set_property); } else if (IR::Closure *clos = s->source->asClosure()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, base); amd64_mov_reg_imm(_codePtr, AMD64_RDX, identifier(*m->name)); amd64_mov_reg_imm(_codePtr, AMD64_RCX, clos->value); amd64_call_code(_codePtr, __qmljs_set_property_closure); } else { Q_UNIMPLEMENTED(); assert(!"TODO"); } checkExceptions(); return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Temp *t2 = s->source->asTemp()) { loadTempAddress(AMD64_RSI, ss->base->asTemp()); loadTempAddress(AMD64_RDX, ss->index->asTemp()); loadTempAddress(AMD64_RCX, t2); amd64_call_code(_codePtr, __qmljs_set_element); } else if (IR::Const *c = s->source->asConst()) { if (c->type == IR::NumberType) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, ss->base->asTemp()); loadTempAddress(AMD64_RDX, ss->index->asTemp()); amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); amd64_call_code(_codePtr, __qmljs_set_element_number); } else { Q_UNIMPLEMENTED(); assert(!"TODO"); } } else { Q_UNIMPLEMENTED(); assert(!"TODO"); } checkExceptions(); return; } } else { // inplace assignment, e.g. x += 1, ++x, ... if (IR::Temp *t = s->target->asTemp()) { if (IR::Const *c = s->source->asConst()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); amd64_mov_reg_imm(_codePtr, AMD64_RAX, &c->value); amd64_movsd_reg_regp(_codePtr, X86_XMM0, AMD64_RAX); void (*op)(Context *, Value *, double); switch (s->op) { case QQmlJS::IR::OpBitAnd: op = __qmljs_inplace_bit_and; break; case QQmlJS::IR::OpBitOr: op = __qmljs_inplace_bit_or; break; case QQmlJS::IR::OpBitXor: op = __qmljs_inplace_bit_xor; break; case QQmlJS::IR::OpAdd: op = __qmljs_inplace_add; break; case QQmlJS::IR::OpSub: op = __qmljs_inplace_sub; break; case QQmlJS::IR::OpMul: op = __qmljs_inplace_mul; break; case QQmlJS::IR::OpDiv: op = __qmljs_inplace_div; break; case QQmlJS::IR::OpMod: op = __qmljs_inplace_mod; break; case QQmlJS::IR::OpLShift: op = __qmljs_inplace_shl; break; case QQmlJS::IR::OpRShift: op = __qmljs_inplace_shr; break; case QQmlJS::IR::OpURShift: op = __qmljs_inplace_ushr; break; default: Q_UNREACHABLE(); break; } amd64_call_code(_codePtr, op); return; } else if (IR::Temp *t2 = s->source->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); amd64_mov_reg_reg(_codePtr, AMD64_RDX, AMD64_RSI, 8); // loadTempAddress(AMD64_RDX, t); loadTempAddress(AMD64_RCX, t2); void (*op)(Context *, Value *, const Value *, const Value *); switch (s->op) { case QQmlJS::IR::OpBitAnd: op = __qmljs_bit_and; break; case QQmlJS::IR::OpBitOr: op = __qmljs_bit_or; break; case QQmlJS::IR::OpBitXor: op = __qmljs_bit_xor; break; case QQmlJS::IR::OpAdd: op = __qmljs_add; break; case QQmlJS::IR::OpSub: op = __qmljs_sub; break; case QQmlJS::IR::OpMul: op = __qmljs_mul; break; case QQmlJS::IR::OpDiv: op = __qmljs_div; break; case QQmlJS::IR::OpMod: op = __qmljs_mod; break; case QQmlJS::IR::OpLShift: op = __qmljs_shl; break; case QQmlJS::IR::OpRShift: op = __qmljs_shr; break; case QQmlJS::IR::OpURShift: op = __qmljs_ushr; break; default: Q_UNREACHABLE(); break; } amd64_call_code(_codePtr, op); return; } } else if (IR::Name *n = s->target->asName()) { if (IR::Const *c = s->source->asConst()) { assert(!"wip"); return; } else if (IR::Temp *t = s->source->asTemp()) { assert(!"wip"); return; } } else if (IR::Subscript *ss = s->target->asSubscript()) { if (IR::Const *c = s->source->asConst()) { assert(!"wip"); return; } else if (IR::Temp *t = s->source->asTemp()) { assert(!"wip"); return; } } else if (IR::Member *m = s->target->asMember()) { if (IR::Const *c = s->source->asConst()) { assert(!"wip"); return; } else if (IR::Temp *t = s->source->asTemp()) { assert(!"wip"); return; } } } Q_UNIMPLEMENTED(); s->dump(qout, IR::Stmt::MIR); qout << endl; assert(!"TODO"); } void InstructionSelection::visitJump(IR::Jump *s) { if (_block->index + 1 != s->target->index) { _patches[s->target].append(_codePtr); amd64_jump32(_codePtr, 0); } } void InstructionSelection::visitCJump(IR::CJump *s) { if (IR::Temp *t = s->cond->asTemp()) { amd64_mov_reg_reg(_codePtr, AMD64_RDI, AMD64_R14, 8); loadTempAddress(AMD64_RSI, t); amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, 0, 4); amd64_alu_reg_imm(_codePtr, X86_CMP, AMD64_RAX, BOOLEAN_TYPE); uchar *label1 = _codePtr; amd64_branch8(_codePtr, X86_CC_NE, 0, 0); amd64_mov_reg_membase(_codePtr, AMD64_RAX, AMD64_RSI, offsetof(Value, booleanValue), 1); uchar *label2 = _codePtr; amd64_jump8(_codePtr, 0); amd64_patch(label1, _codePtr); amd64_call_code(_codePtr, __qmljs_to_boolean); amd64_patch(label2, _codePtr); amd64_alu_reg_imm_size(_codePtr, X86_CMP, AMD64_RAX, 0, 4); _patches[s->iftrue].append(_codePtr); amd64_branch32(_codePtr, X86_CC_NZ, 0, 1); if (_block->index + 1 != s->iffalse->index) { _patches[s->iffalse].append(_codePtr); amd64_jump32(_codePtr, 0); } return; } Q_UNIMPLEMENTED(); assert(!"TODO"); } void InstructionSelection::visitRet(IR::Ret *s) { if (IR::Temp *t = s->expr->asTemp()) { amd64_lea_membase(_codePtr, AMD64_RDI, AMD64_R14, offsetof(Context, result)); loadTempAddress(AMD64_RSI, t); amd64_call_code(_codePtr, __qmljs_copy); return; } Q_UNIMPLEMENTED(); Q_UNUSED(s); }