2012-09-23 08:28:13 +00:00
|
|
|
|
|
|
|
#include "qv4isel_masm_p.h"
|
|
|
|
#include "qmljs_runtime.h"
|
|
|
|
#include "qmljs_objects.h"
|
|
|
|
|
|
|
|
#include <assembler/LinkBuffer.h>
|
|
|
|
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
#ifndef NO_UDIS86
|
|
|
|
# include <udis86.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using namespace QQmlJS;
|
|
|
|
using namespace QQmlJS::MASM;
|
|
|
|
using namespace QQmlJS::VM;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
QTextStream qout(stdout, QIODevice::WriteOnly);
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2012-10-01 07:29:20 +00:00
|
|
|
qSwap(_function, function);
|
|
|
|
|
|
|
|
int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value);
|
|
|
|
locals = (locals + 15) & ~15;
|
2012-10-04 20:46:42 +00:00
|
|
|
enterStandardStackFrame(locals);
|
2012-10-01 07:29:20 +00:00
|
|
|
|
|
|
|
push(ContextRegister);
|
|
|
|
loadPtr(addressForArgument(0), ContextRegister);
|
|
|
|
|
|
|
|
foreach (IR::BasicBlock *block, _function->basicBlocks) {
|
|
|
|
_block = block;
|
2012-10-02 04:49:49 +00:00
|
|
|
_addrs[block] = label();
|
2012-10-01 07:29:20 +00:00
|
|
|
foreach (IR::Stmt *s, block->statements) {
|
|
|
|
s->accept(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pop(ContextRegister);
|
|
|
|
|
2012-10-04 20:46:42 +00:00
|
|
|
leaveStandardStackFrame(locals);
|
2012-10-01 07:29:20 +00:00
|
|
|
ret();
|
|
|
|
|
2012-10-02 04:49:49 +00:00
|
|
|
QHashIterator<IR::BasicBlock *, QVector<Jump> > it(_patches);
|
|
|
|
while (it.hasNext()) {
|
|
|
|
it.next();
|
|
|
|
IR::BasicBlock *block = it.key();
|
|
|
|
Label target = _addrs.value(block);
|
|
|
|
assert(target.isSet());
|
|
|
|
foreach (Jump jump, it.value())
|
|
|
|
jump.linkTo(target, this);
|
|
|
|
}
|
|
|
|
|
2012-09-23 08:28:13 +00:00
|
|
|
JSC::JSGlobalData dummy;
|
2012-10-01 07:29:20 +00:00
|
|
|
JSC::LinkBuffer linkBuffer(dummy, this, 0);
|
|
|
|
foreach (CallToLink ctl, _callsToLink)
|
|
|
|
linkBuffer.link(ctl.call, ctl.externalFunction);
|
|
|
|
_function->codeRef = linkBuffer.finalizeCodeWithDisassembly("operator()(IR::Function*)");
|
|
|
|
_function->code = (void (*)(VM::Context *, const uchar *)) _function->codeRef.code().executableAddress();
|
|
|
|
|
|
|
|
qSwap(_function, function);
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
String *InstructionSelection::identifier(const QString &s)
|
|
|
|
{
|
|
|
|
return _engine->identifier(s);
|
|
|
|
}
|
|
|
|
|
2012-10-07 19:43:29 +00:00
|
|
|
InstructionSelection::Pointer InstructionSelection::loadTempAddress(RegisterID reg, IR::Temp *t)
|
2012-10-01 19:39:25 +00:00
|
|
|
{
|
|
|
|
int32_t offset = 0;
|
|
|
|
if (t->index < 0) {
|
|
|
|
const int arg = -t->index - 1;
|
|
|
|
loadPtr(Address(ContextRegister, offsetof(Context, arguments)), reg);
|
|
|
|
offset = arg * sizeof(Value);
|
|
|
|
} else if (t->index < _function->locals.size()) {
|
|
|
|
loadPtr(Address(ContextRegister, offsetof(Context, locals)), reg);
|
|
|
|
offset = t->index * sizeof(Value);
|
|
|
|
} else {
|
|
|
|
const int arg = _function->maxNumberOfArguments + t->index - _function->locals.size();
|
2012-10-02 06:52:38 +00:00
|
|
|
offset = sizeof(Value) * (-arg - 1)
|
2012-10-01 19:39:25 +00:00
|
|
|
- sizeof(void*); // size of ebp
|
|
|
|
reg = StackFrameRegister;
|
|
|
|
}
|
2012-10-07 19:43:29 +00:00
|
|
|
return Pointer(reg, offset);
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::callActivationProperty(IR::Call *call, IR::Temp *result)
|
|
|
|
{
|
2012-10-02 06:55:18 +00:00
|
|
|
IR::Name *baseName = call->base->asName();
|
|
|
|
assert(baseName != 0);
|
|
|
|
|
2012-10-04 21:23:50 +00:00
|
|
|
if (baseName->id)
|
2012-10-07 19:43:29 +00:00
|
|
|
callRuntimeMethod(__qmljs_call_activation_property, result, call->base, call->args);
|
2012-10-04 21:23:50 +00:00
|
|
|
else {
|
2012-10-02 06:55:18 +00:00
|
|
|
switch (baseName->builtin) {
|
|
|
|
case IR::Name::builtin_invalid:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
break;
|
|
|
|
case IR::Name::builtin_typeof:
|
2012-10-07 19:43:29 +00:00
|
|
|
callRuntimeMethod(__qmljs_builtin_typeof, result, call->args);
|
2012-10-02 06:55:18 +00:00
|
|
|
break;
|
|
|
|
case IR::Name::builtin_delete:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
break;
|
|
|
|
case IR::Name::builtin_throw:
|
2012-10-07 19:43:29 +00:00
|
|
|
callRuntimeMethod(__qmljs_builtin_throw, result, call->args);
|
2012-10-02 06:55:18 +00:00
|
|
|
break;
|
|
|
|
case IR::Name::builtin_rethrow:
|
2012-10-07 19:43:29 +00:00
|
|
|
callRuntimeMethod(__qmljs_builtin_rethrow, result, call->args);
|
2012-10-02 06:55:18 +00:00
|
|
|
return; // we need to return to avoid checking the exceptions
|
|
|
|
}
|
|
|
|
}
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InstructionSelection::callValue(IR::Call *call, IR::Temp *result)
|
|
|
|
{
|
2012-10-07 19:57:10 +00:00
|
|
|
IR::Temp *baseTemp = call->base->asTemp();
|
|
|
|
assert(baseTemp != 0);
|
|
|
|
|
|
|
|
int argc = prepareVariableArguments(call->args);
|
|
|
|
IR::Temp* thisObject = 0;
|
|
|
|
generateFunctionCall(__qmljs_call_value, ContextRegister, result, baseTemp, thisObject, baseAddressForCallArguments(), TrustedImm32(argc));
|
|
|
|
checkExceptions();
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result)
|
|
|
|
{
|
2012-10-07 19:57:10 +00:00
|
|
|
IR::Member *member = call->base->asMember();
|
|
|
|
assert(member != 0);
|
|
|
|
assert(member->base->asTemp() != 0);
|
|
|
|
|
|
|
|
int argc = prepareVariableArguments(call->args);
|
|
|
|
IR::Temp* thisObject = 0;
|
|
|
|
generateFunctionCall(__qmljs_call_property, ContextRegister, result, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc));
|
|
|
|
checkExceptions();
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result)
|
|
|
|
{
|
2012-10-04 21:23:50 +00:00
|
|
|
IR::Name *baseName = call->base->asName();
|
|
|
|
assert(baseName != 0);
|
|
|
|
|
2012-10-07 19:43:29 +00:00
|
|
|
callRuntimeMethod(__qmljs_construct_activation_property, result, call->base, call->args);
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result)
|
|
|
|
{
|
2012-10-07 20:02:06 +00:00
|
|
|
IR::Member *member = call->base->asMember();
|
|
|
|
assert(member != 0);
|
|
|
|
assert(member->base->asTemp() != 0);
|
|
|
|
|
|
|
|
int argc = prepareVariableArguments(call->args);
|
|
|
|
IR::Temp* thisObject = 0;
|
|
|
|
generateFunctionCall(__qmljs_construct_property, ContextRegister, result, member->base->asTemp(), identifier(*member->name), baseAddressForCallArguments(), TrustedImm32(argc));
|
|
|
|
checkExceptions();
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::constructValue(IR::New *call, IR::Temp *result)
|
|
|
|
{
|
2012-10-07 20:02:06 +00:00
|
|
|
IR::Temp *baseTemp = call->base->asTemp();
|
|
|
|
assert(baseTemp != 0);
|
|
|
|
|
|
|
|
int argc = prepareVariableArguments(call->args);
|
|
|
|
generateFunctionCall(__qmljs_construct_value, ContextRegister, result, baseTemp, baseAddressForCallArguments(), TrustedImm32(argc));
|
|
|
|
checkExceptions();
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::checkExceptions()
|
|
|
|
{
|
2012-10-02 06:55:18 +00:00
|
|
|
Address addr(ContextRegister, offsetof(Context, hasUncaughtException));
|
|
|
|
Jump jmp = branch8(Equal, addr, TrustedImm32(1));
|
|
|
|
_patches[_function->handlersBlock].append(jmp);
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::visitExp(IR::Exp *s)
|
|
|
|
{
|
2012-10-07 20:02:41 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2012-09-23 08:28:13 +00:00
|
|
|
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)
|
|
|
|
{
|
2012-10-01 07:29:20 +00:00
|
|
|
if (s->op == IR::OpInvalid) {
|
2012-10-02 06:55:18 +00:00
|
|
|
if (IR::Name *n = s->target->asName()) {
|
|
|
|
String *propertyName = identifier(*n->id);
|
|
|
|
|
|
|
|
if (IR::Temp *t = s->source->asTemp()) {
|
2012-10-05 09:35:46 +00:00
|
|
|
generateFunctionCall(__qmljs_set_activation_property, ContextRegister, propertyName, t);
|
2012-10-02 06:55:18 +00:00
|
|
|
checkExceptions();
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
} else if (IR::Temp *t = s->target->asTemp()) {
|
2012-10-01 20:52:05 +00:00
|
|
|
if (IR::Name *n = s->source->asName()) {
|
|
|
|
if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin.
|
2012-10-05 09:35:46 +00:00
|
|
|
generateFunctionCall(__qmljs_get_thisObject, ContextRegister, t);
|
2012-10-01 20:52:05 +00:00
|
|
|
} else {
|
|
|
|
String *propertyName = identifier(*n->id);
|
2012-10-05 09:35:46 +00:00
|
|
|
generateFunctionCall(__qmljs_get_activation_property, ContextRegister, t, propertyName);
|
2012-10-01 20:52:05 +00:00
|
|
|
checkExceptions();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else if (IR::Const *c = s->source->asConst()) {
|
2012-10-01 19:39:25 +00:00
|
|
|
Address dest = loadTempAddress(Gpr0, t);
|
2012-10-01 07:29:20 +00:00
|
|
|
switch (c->type) {
|
2012-10-01 20:35:53 +00:00
|
|
|
case IR::NullType:
|
|
|
|
storeValue<Value::Null_Type>(TrustedImm32(0), dest);
|
|
|
|
break;
|
|
|
|
case IR::UndefinedType:
|
|
|
|
storeValue<Value::Undefined_Type>(TrustedImm32(0), dest);
|
|
|
|
break;
|
|
|
|
case IR::BoolType:
|
|
|
|
storeValue<Value::Boolean_Type>(TrustedImm32(c->value != 0), dest);
|
|
|
|
break;
|
2012-10-01 07:29:20 +00:00
|
|
|
case IR::NumberType:
|
|
|
|
// ### Taking address of pointer inside IR.
|
|
|
|
loadDouble(&c->value, FPGpr0);
|
|
|
|
storeDouble(FPGpr0, dest);
|
|
|
|
break;
|
2012-10-01 20:35:53 +00:00
|
|
|
default:
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
assert(!"TODO");
|
2012-10-01 07:29:20 +00:00
|
|
|
}
|
2012-10-01 20:52:05 +00:00
|
|
|
return;
|
2012-10-04 19:40:17 +00:00
|
|
|
} else if (IR::Temp *t2 = s->source->asTemp()) {
|
2012-10-05 09:48:05 +00:00
|
|
|
generateFunctionCall(__qmljs_copy, t, t2);
|
2012-10-04 19:48:38 +00:00
|
|
|
return;
|
2012-10-04 20:08:22 +00:00
|
|
|
} else if (IR::String *str = s->source->asString()) {
|
2012-10-05 09:48:05 +00:00
|
|
|
generateFunctionCall(__qmljs_init_string, t, _engine->newString(*str->value));
|
2012-10-07 20:14:01 +00:00
|
|
|
return;
|
2012-10-02 06:55:18 +00:00
|
|
|
} else if (IR::Closure *clos = s->source->asClosure()) {
|
2012-10-07 17:45:40 +00:00
|
|
|
generateFunctionCall(__qmljs_init_closure, ContextRegister, t, TrustedImmPtr(clos->value));
|
2012-10-02 06:55:18 +00:00
|
|
|
return;
|
2012-10-04 20:08:22 +00:00
|
|
|
} else if (IR::New *ctor = s->source->asNew()) {
|
2012-10-04 21:23:50 +00:00
|
|
|
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;
|
|
|
|
}
|
2012-10-04 20:08:22 +00:00
|
|
|
} else if (IR::Member *m = s->source->asMember()) {
|
2012-10-07 20:10:27 +00:00
|
|
|
//__qmljs_get_property(ctx, result, object, name);
|
|
|
|
if (IR::Temp *base = m->base->asTemp()) {
|
|
|
|
generateFunctionCall(__qmljs_get_property, ContextRegister, t, base, identifier(*m->name));
|
|
|
|
checkExceptions();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assert(!"wip");
|
|
|
|
return;
|
2012-10-04 20:08:22 +00:00
|
|
|
} else if (IR::Subscript *ss = s->source->asSubscript()) {
|
2012-10-07 20:10:27 +00:00
|
|
|
generateFunctionCall(__qmljs_get_element, ContextRegister, t, ss->base->asTemp(), ss->index->asTemp());
|
|
|
|
checkExceptions();
|
|
|
|
return;
|
2012-10-04 20:08:22 +00:00
|
|
|
} else if (IR::Unop *u = s->source->asUnop()) {
|
2012-10-04 20:50:01 +00:00
|
|
|
if (IR::Temp *e = u->expr->asTemp()) {
|
|
|
|
void (*op)(Context *, Value *, const Value *) = 0;
|
|
|
|
switch (u->op) {
|
|
|
|
case IR::OpIfTrue: assert(!"unreachable"); break;
|
|
|
|
case IR::OpNot: op = __qmljs_not; break;
|
|
|
|
case IR::OpUMinus: op = __qmljs_uminus; break;
|
|
|
|
case IR::OpUPlus: op = __qmljs_uplus; break;
|
|
|
|
case IR::OpCompl: op = __qmljs_compl; break;
|
|
|
|
default: assert(!"unreachable"); break;
|
|
|
|
} // switch
|
2012-10-05 09:48:05 +00:00
|
|
|
generateFunctionCall(op, ContextRegister, t, e);
|
2012-10-04 20:50:01 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-10-04 20:08:22 +00:00
|
|
|
} else if (IR::Binop *b = s->source->asBinop()) {
|
2012-10-04 20:46:42 +00:00
|
|
|
IR::Temp *l = b->left->asTemp();
|
|
|
|
IR::Temp *r = b->right->asTemp();
|
|
|
|
if (l && r) {
|
|
|
|
void (*op)(Context *, Value *, const Value *, const Value *) = 0;
|
|
|
|
|
|
|
|
switch ((IR::AluOp) b->op) {
|
|
|
|
case IR::OpInvalid:
|
|
|
|
case IR::OpIfTrue:
|
|
|
|
case IR::OpNot:
|
|
|
|
case IR::OpUMinus:
|
|
|
|
case IR::OpUPlus:
|
|
|
|
case IR::OpCompl:
|
|
|
|
assert(!"unreachable");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IR::OpBitAnd: op = __qmljs_bit_and; break;
|
|
|
|
case IR::OpBitOr: op = __qmljs_bit_or; break;
|
|
|
|
case IR::OpBitXor: op = __qmljs_bit_xor; break;
|
|
|
|
case IR::OpAdd: op = __qmljs_add; break;
|
|
|
|
case IR::OpSub: op = __qmljs_sub; break;
|
|
|
|
case IR::OpMul: op = __qmljs_mul; break;
|
|
|
|
case IR::OpDiv: op = __qmljs_div; break;
|
|
|
|
case IR::OpMod: op = __qmljs_mod; break;
|
|
|
|
case IR::OpLShift: op = __qmljs_shl; break;
|
|
|
|
case IR::OpRShift: op = __qmljs_shr; break;
|
|
|
|
case IR::OpURShift: op = __qmljs_ushr; break;
|
|
|
|
case IR::OpGt: op = __qmljs_gt; break;
|
|
|
|
case IR::OpLt: op = __qmljs_lt; break;
|
|
|
|
case IR::OpGe: op = __qmljs_ge; break;
|
|
|
|
case IR::OpLe: op = __qmljs_le; break;
|
|
|
|
case IR::OpEqual: op = __qmljs_eq; break;
|
|
|
|
case IR::OpNotEqual: op = __qmljs_ne; break;
|
|
|
|
case IR::OpStrictEqual: op = __qmljs_se; break;
|
|
|
|
case IR::OpStrictNotEqual: op = __qmljs_sne; break;
|
|
|
|
case IR::OpInstanceof: op = __qmljs_instanceof; break;
|
|
|
|
case IR::OpIn: op = __qmljs_in; break;
|
|
|
|
|
|
|
|
case IR::OpAnd:
|
|
|
|
case IR::OpOr:
|
|
|
|
assert(!"unreachable");
|
|
|
|
break;
|
|
|
|
}
|
2012-10-05 09:48:05 +00:00
|
|
|
generateFunctionCall(op, ContextRegister, t, l, r);
|
2012-10-04 20:46:42 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-10-02 06:55:18 +00:00
|
|
|
} 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;
|
|
|
|
}
|
2012-10-01 07:29:20 +00:00
|
|
|
}
|
2012-10-04 19:48:38 +00:00
|
|
|
} else if (IR::Member *m = s->target->asMember()) {
|
2012-10-07 20:10:27 +00:00
|
|
|
if (IR::Temp *base = m->base->asTemp()) {
|
|
|
|
if (IR::Temp *t = s->source->asTemp()) {
|
|
|
|
generateFunctionCall(__qmljs_set_property, ContextRegister, base, identifier(*m->name), t);
|
|
|
|
checkExceptions();
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
}
|
|
|
|
}
|
2012-10-04 19:48:38 +00:00
|
|
|
} else if (IR::Subscript *ss = s->target->asSubscript()) {
|
2012-10-07 20:10:27 +00:00
|
|
|
if (IR::Temp *t2 = s->source->asTemp()) {
|
2012-10-08 19:58:24 +00:00
|
|
|
generateFunctionCall(__qmljs_set_element, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t2);
|
2012-10-07 20:10:27 +00:00
|
|
|
checkExceptions();
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
}
|
2012-10-01 07:29:20 +00:00
|
|
|
}
|
2012-10-04 19:48:38 +00:00
|
|
|
} else {
|
2012-10-05 06:09:12 +00:00
|
|
|
// inplace assignment, e.g. x += 1, ++x, ...
|
|
|
|
if (IR::Temp *t = s->target->asTemp()) {
|
|
|
|
if (IR::Temp *t2 = s->source->asTemp()) {
|
|
|
|
void (*op)(Context *, Value *, const Value *, const Value *) = 0;
|
|
|
|
switch (s->op) {
|
|
|
|
case IR::OpBitAnd: op = __qmljs_bit_and; break;
|
|
|
|
case IR::OpBitOr: op = __qmljs_bit_or; break;
|
|
|
|
case IR::OpBitXor: op = __qmljs_bit_xor; break;
|
|
|
|
case IR::OpAdd: op = __qmljs_add; break;
|
|
|
|
case IR::OpSub: op = __qmljs_sub; break;
|
|
|
|
case IR::OpMul: op = __qmljs_mul; break;
|
|
|
|
case IR::OpDiv: op = __qmljs_div; break;
|
|
|
|
case IR::OpMod: op = __qmljs_mod; break;
|
|
|
|
case IR::OpLShift: op = __qmljs_shl; break;
|
|
|
|
case IR::OpRShift: op = __qmljs_shr; break;
|
|
|
|
case IR::OpURShift: op = __qmljs_ushr; break;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-10-05 09:48:05 +00:00
|
|
|
generateFunctionCall(op, ContextRegister, t, t, t2);
|
2012-10-05 06:09:12 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (IR::Name *n = s->target->asName()) {
|
|
|
|
if (IR::Temp *t = s->source->asTemp()) {
|
|
|
|
void (*op)(Context *, String *, Value *) = 0;
|
|
|
|
switch (s->op) {
|
|
|
|
case IR::OpBitAnd: op = __qmljs_inplace_bit_and_name; break;
|
|
|
|
case IR::OpBitOr: op = __qmljs_inplace_bit_or_name; break;
|
|
|
|
case IR::OpBitXor: op = __qmljs_inplace_bit_xor_name; break;
|
|
|
|
case IR::OpAdd: op = __qmljs_inplace_add_name; break;
|
|
|
|
case IR::OpSub: op = __qmljs_inplace_sub_name; break;
|
|
|
|
case IR::OpMul: op = __qmljs_inplace_mul_name; break;
|
|
|
|
case IR::OpDiv: op = __qmljs_inplace_div_name; break;
|
|
|
|
case IR::OpMod: op = __qmljs_inplace_mod_name; break;
|
|
|
|
case IR::OpLShift: op = __qmljs_inplace_shl_name; break;
|
|
|
|
case IR::OpRShift: op = __qmljs_inplace_shr_name; break;
|
|
|
|
case IR::OpURShift: op = __qmljs_inplace_ushr_name; break;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
2012-10-05 09:35:46 +00:00
|
|
|
generateFunctionCall(op, ContextRegister, identifier(*n->id), t);
|
2012-10-05 06:09:12 +00:00
|
|
|
checkExceptions();
|
|
|
|
return;
|
|
|
|
}
|
2012-10-07 20:10:27 +00:00
|
|
|
} else if (IR::Subscript *ss = s->target->asSubscript()) {
|
|
|
|
if (IR::Temp *t = s->source->asTemp()) {
|
|
|
|
void (*op)(Context *, Value *, Value *, Value *) = 0;
|
|
|
|
switch (s->op) {
|
|
|
|
case IR::OpBitAnd: op = __qmljs_inplace_bit_and_element; break;
|
|
|
|
case IR::OpBitOr: op = __qmljs_inplace_bit_or_element; break;
|
|
|
|
case IR::OpBitXor: op = __qmljs_inplace_bit_xor_element; break;
|
|
|
|
case IR::OpAdd: op = __qmljs_inplace_add_element; break;
|
|
|
|
case IR::OpSub: op = __qmljs_inplace_sub_element; break;
|
|
|
|
case IR::OpMul: op = __qmljs_inplace_mul_element; break;
|
|
|
|
case IR::OpDiv: op = __qmljs_inplace_div_element; break;
|
|
|
|
case IR::OpMod: op = __qmljs_inplace_mod_element; break;
|
|
|
|
case IR::OpLShift: op = __qmljs_inplace_shl_element; break;
|
|
|
|
case IR::OpRShift: op = __qmljs_inplace_shr_element; break;
|
|
|
|
case IR::OpURShift: op = __qmljs_inplace_ushr_element; break;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
generateFunctionCall(op, ContextRegister, ss->base->asTemp(), ss->index->asTemp(), t);
|
|
|
|
checkExceptions();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (IR::Member *m = s->target->asMember()) {
|
|
|
|
if (IR::Temp *t = s->source->asTemp()) {
|
|
|
|
void (*op)(Context *, Value *, String *, Value *) = 0;
|
|
|
|
switch (s->op) {
|
|
|
|
case IR::OpBitAnd: op = __qmljs_inplace_bit_and_member; break;
|
|
|
|
case IR::OpBitOr: op = __qmljs_inplace_bit_or_member; break;
|
|
|
|
case IR::OpBitXor: op = __qmljs_inplace_bit_xor_member; break;
|
|
|
|
case IR::OpAdd: op = __qmljs_inplace_add_member; break;
|
|
|
|
case IR::OpSub: op = __qmljs_inplace_sub_member; break;
|
|
|
|
case IR::OpMul: op = __qmljs_inplace_mul_member; break;
|
|
|
|
case IR::OpDiv: op = __qmljs_inplace_div_member; break;
|
|
|
|
case IR::OpMod: op = __qmljs_inplace_mod_member; break;
|
|
|
|
case IR::OpLShift: op = __qmljs_inplace_shl_member; break;
|
|
|
|
case IR::OpRShift: op = __qmljs_inplace_shr_member; break;
|
|
|
|
case IR::OpURShift: op = __qmljs_inplace_ushr_member; break;
|
|
|
|
default:
|
|
|
|
Q_UNREACHABLE();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
generateFunctionCall(op, ContextRegister, m->base->asTemp(), identifier(*m->name), t);
|
|
|
|
checkExceptions();
|
|
|
|
return;
|
|
|
|
}
|
2012-10-05 06:09:12 +00:00
|
|
|
}
|
2012-10-01 07:29:20 +00:00
|
|
|
}
|
2012-09-23 08:28:13 +00:00
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
s->dump(qout, IR::Stmt::MIR);
|
|
|
|
qout << endl;
|
|
|
|
assert(!"TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::visitJump(IR::Jump *s)
|
|
|
|
{
|
2012-10-04 17:24:22 +00:00
|
|
|
jumpToBlock(s->target);
|
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::jumpToBlock(IR::BasicBlock *target)
|
|
|
|
{
|
|
|
|
if (_block->index + 1 != target->index)
|
|
|
|
_patches[target].append(jump());
|
2012-09-23 08:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::visitCJump(IR::CJump *s)
|
|
|
|
{
|
2012-10-04 17:24:22 +00:00
|
|
|
if (IR::Temp *t = s->cond->asTemp()) {
|
|
|
|
Address temp = loadTempAddress(Gpr1, t);
|
|
|
|
Address tag = temp;
|
|
|
|
tag.offset += offsetof(VM::ValueData, tag);
|
|
|
|
Jump booleanConversion = branch32(NotEqual, tag, TrustedImm32(VM::Value::Boolean_Type));
|
|
|
|
|
|
|
|
Address data = temp;
|
|
|
|
data.offset += offsetof(VM::ValueData, b);
|
|
|
|
load32(data, Gpr1);
|
|
|
|
Jump testBoolean = jump();
|
|
|
|
|
|
|
|
booleanConversion.link(this);
|
|
|
|
{
|
2012-10-05 09:48:05 +00:00
|
|
|
generateFunctionCall(__qmljs_to_boolean, ContextRegister, t);
|
2012-10-04 17:24:22 +00:00
|
|
|
move(ReturnValueRegister, Gpr1);
|
|
|
|
}
|
|
|
|
|
|
|
|
testBoolean.link(this);
|
|
|
|
move(TrustedImm32(1), Gpr0);
|
|
|
|
Jump target = branch32(Equal, Gpr1, Gpr0);
|
|
|
|
_patches[s->iftrue].append(target);
|
|
|
|
|
|
|
|
jumpToBlock(s->iffalse);
|
|
|
|
return;
|
2012-10-04 20:02:43 +00:00
|
|
|
} else if (IR::Binop *b = s->cond->asBinop()) {
|
|
|
|
IR::Temp *l = b->left->asTemp();
|
|
|
|
IR::Temp *r = b->right->asTemp();
|
|
|
|
if (l && r) {
|
|
|
|
bool (*op)(Context *, const Value *, const Value *);
|
|
|
|
switch (b->op) {
|
|
|
|
default: Q_UNREACHABLE(); assert(!"todo"); break;
|
|
|
|
case IR::OpGt: op = __qmljs_cmp_gt; break;
|
|
|
|
case IR::OpLt: op = __qmljs_cmp_lt; break;
|
|
|
|
case IR::OpGe: op = __qmljs_cmp_ge; break;
|
|
|
|
case IR::OpLe: op = __qmljs_cmp_le; break;
|
|
|
|
case IR::OpEqual: op = __qmljs_cmp_eq; break;
|
|
|
|
case IR::OpNotEqual: op = __qmljs_cmp_ne; break;
|
|
|
|
case IR::OpStrictEqual: op = __qmljs_cmp_se; break;
|
|
|
|
case IR::OpStrictNotEqual: op = __qmljs_cmp_sne; break;
|
|
|
|
case IR::OpInstanceof: op = __qmljs_cmp_instanceof; break;
|
|
|
|
case IR::OpIn: op = __qmljs_cmp_in; break;
|
|
|
|
} // switch
|
|
|
|
|
2012-10-05 09:48:05 +00:00
|
|
|
generateFunctionCall(op, ContextRegister, l, r);
|
2012-10-04 20:02:43 +00:00
|
|
|
move(ReturnValueRegister, Gpr0);
|
|
|
|
|
|
|
|
move(TrustedImm32(1), Gpr1);
|
|
|
|
Jump target = branch32(Equal, Gpr0, Gpr1);
|
|
|
|
_patches[s->iftrue].append(target);
|
|
|
|
|
|
|
|
jumpToBlock(s->iffalse);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
assert(!"wip");
|
|
|
|
}
|
|
|
|
Q_UNIMPLEMENTED();
|
2012-10-04 17:24:22 +00:00
|
|
|
}
|
2012-09-23 08:28:13 +00:00
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
assert(!"TODO");
|
|
|
|
}
|
|
|
|
|
|
|
|
void InstructionSelection::visitRet(IR::Ret *s)
|
|
|
|
{
|
2012-10-01 07:29:20 +00:00
|
|
|
if (IR::Temp *t = s->expr->asTemp()) {
|
2012-10-07 19:43:29 +00:00
|
|
|
generateFunctionCall(__qmljs_copy, Pointer(ContextRegister, offsetof(Context, result)), t);
|
2012-10-01 07:29:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-09-23 08:28:13 +00:00
|
|
|
Q_UNIMPLEMENTED();
|
|
|
|
Q_UNUSED(s);
|
|
|
|
}
|
|
|
|
|
2012-10-07 19:43:29 +00:00
|
|
|
int InstructionSelection::prepareVariableArguments(IR::ExprList* args)
|
2012-10-04 21:23:50 +00:00
|
|
|
{
|
|
|
|
int argc = 0;
|
|
|
|
for (IR::ExprList *it = args; it; it = it->next) {
|
|
|
|
++argc;
|
|
|
|
}
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
for (IR::ExprList *it = args; it; it = it->next, ++i) {
|
|
|
|
IR::Temp *arg = it->expr->asTemp();
|
|
|
|
assert(arg != 0);
|
2012-10-07 19:43:29 +00:00
|
|
|
generateFunctionCall(__qmljs_copy, argumentAddressForCall(i), arg);
|
2012-10-04 21:23:50 +00:00
|
|
|
}
|
|
|
|
|
2012-10-05 05:55:05 +00:00
|
|
|
return argc;
|
2012-10-04 21:23:50 +00:00
|
|
|
}
|
|
|
|
|
2012-10-07 19:43:29 +00:00
|
|
|
void InstructionSelection::callRuntimeMethod(ActivationMethod method, IR::Temp *result, IR::Expr *base, IR::ExprList *args)
|
2012-10-04 21:23:50 +00:00
|
|
|
{
|
|
|
|
IR::Name *baseName = base->asName();
|
|
|
|
assert(baseName != 0);
|
|
|
|
|
2012-10-05 05:55:05 +00:00
|
|
|
int argc = prepareVariableArguments(args);
|
2012-10-07 19:43:29 +00:00
|
|
|
generateFunctionCall(method, ContextRegister, result, identifier(*baseName->id), baseAddressForCallArguments(), TrustedImm32(argc));
|
|
|
|
checkExceptions();
|
2012-10-04 21:23:50 +00:00
|
|
|
}
|
|
|
|
|
2012-10-07 19:43:29 +00:00
|
|
|
void InstructionSelection::callRuntimeMethod(BuiltinMethod method, IR::Temp *result, IR::ExprList *args)
|
2012-10-04 21:23:50 +00:00
|
|
|
{
|
2012-10-05 05:55:05 +00:00
|
|
|
int argc = prepareVariableArguments(args);
|
2012-10-07 19:43:29 +00:00
|
|
|
generateFunctionCall(method, ContextRegister, result, baseAddressForCallArguments(), TrustedImm32(argc));
|
|
|
|
checkExceptions();
|
2012-10-04 21:23:50 +00:00
|
|
|
}
|
2012-10-07 19:43:29 +00:00
|
|
|
|