qtdeclarative/qv4isel_masm.cpp

372 lines
11 KiB
C++
Raw Normal View History

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)
{
qSwap(_function, function);
enterStandardStackFrame();
int locals = (_function->tempCount - _function->locals.size() + _function->maxNumberOfArguments) * sizeof(Value);
locals = (locals + 15) & ~15;
sub32(TrustedImm32(locals), StackPointerRegister);
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();
foreach (IR::Stmt *s, block->statements) {
s->accept(this);
}
}
pop(ContextRegister);
add32(TrustedImm32(locals), StackPointerRegister);
leaveStandardStackFrame();
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;
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-01 19:39:25 +00:00
JSC::MacroAssembler::Address InstructionSelection::loadTempAddress(RegisterID reg, IR::Temp *t)
{
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();
offset = sizeof(Value) * (-arg - 1)
2012-10-01 19:39:25 +00:00
- sizeof(void*); // size of ebp
reg = StackFrameRegister;
}
return Address(reg, offset);
2012-09-23 08:28:13 +00:00
}
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);
Address tempAddress = loadTempAddress(Gpr0, arg);
FunctionCall fc(this);
fc.addArgumentAsAddress(argumentAddressForCall(i));
fc.addArgumentAsAddress(tempAddress);
fc.call(__qmljs_copy);
}
FunctionCall activationCall(this);
activationCall.addArgumentFromRegister(ContextRegister);
if (result) {
activationCall.addArgumentAsAddress(loadTempAddress(Gpr0, result));
} else {
xor32(Gpr0, Gpr0);
activationCall.addArgumentFromRegister(Gpr0);
}
if (baseName->id) {
move(TrustedImmPtr(identifier(*baseName->id)), Gpr1);
activationCall.addArgumentFromRegister(Gpr1);
activationCall.addArgumentAsAddress(baseAddressForCallArguments());
activationCall.addArgumentByValue(TrustedImm32(argc));
activationCall.call(__qmljs_call_activation_property);
} else {
switch (baseName->builtin) {
case IR::Name::builtin_invalid:
Q_UNREACHABLE();
break;
case IR::Name::builtin_typeof:
activationCall.addArgumentAsAddress(baseAddressForCallArguments());
activationCall.addArgumentByValue(TrustedImm32(argc));
activationCall.call(__qmljs_builtin_typeof);
break;
case IR::Name::builtin_delete:
Q_UNREACHABLE();
break;
case IR::Name::builtin_throw:
activationCall.addArgumentAsAddress(baseAddressForCallArguments());
activationCall.addArgumentByValue(TrustedImm32(argc));
activationCall.call(__qmljs_builtin_throw);
break;
case IR::Name::builtin_rethrow:
activationCall.addArgumentAsAddress(baseAddressForCallArguments());
activationCall.addArgumentByValue(TrustedImm32(argc));
activationCall.call(__qmljs_builtin_rethrow);
return; // we need to return to avoid checking the exceptions
}
}
checkExceptions();
2012-09-23 08:28:13 +00:00
}
void InstructionSelection::callValue(IR::Call *call, IR::Temp *result)
{
}
void InstructionSelection::callProperty(IR::Call *call, IR::Temp *result)
{
}
void InstructionSelection::constructActivationProperty(IR::New *call, IR::Temp *result)
{
}
void InstructionSelection::constructProperty(IR::New *call, IR::Temp *result)
{
}
void InstructionSelection::constructValue(IR::New *call, IR::Temp *result)
{
}
void InstructionSelection::checkExceptions()
{
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)
{
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)
{
if (s->op == IR::OpInvalid) {
if (IR::Name *n = s->target->asName()) {
String *propertyName = identifier(*n->id);
if (IR::Temp *t = s->source->asTemp()) {
FunctionCall fct(this);
fct.addArgumentFromRegister(ContextRegister);
move(TrustedImmPtr(propertyName), Gpr1);
fct.addArgumentFromRegister(Gpr1);
fct.addArgumentAsAddress(loadTempAddress(Gpr2, t));
fct.call(__qmljs_set_activation_property);
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()) {
Address temp = loadTempAddress(Gpr0, t);
2012-10-02 05:21:38 +00:00
FunctionCall fc(this);
fc.addArgumentFromRegister(ContextRegister);
fc.addArgumentAsAddress(temp);
2012-10-01 20:52:05 +00:00
if (*n->id == QStringLiteral("this")) { // ### `this' should be a builtin.
2012-10-02 05:21:38 +00:00
fc.call(__qmljs_get_thisObject);
2012-10-01 20:52:05 +00:00
} else {
String *propertyName = identifier(*n->id);
move(TrustedImmPtr(propertyName), Gpr1);
2012-10-02 05:21:38 +00:00
fc.addArgumentFromRegister(Gpr1);
fc.call(__qmljs_get_activation_property);
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);
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;
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 20:52:05 +00:00
return;
} else if (IR::Closure *clos = s->source->asClosure()) {
FunctionCall fct(this);
fct.addArgumentFromRegister(ContextRegister);
fct.addArgumentAsAddress(loadTempAddress(Gpr0, t));
move(TrustedImmPtr(clos->value), Gpr1);
fct.addArgumentFromRegister(Gpr1);
fct.call(__qmljs_init_closure);
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;
}
}
}
}
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);
{
FunctionCall fct(this);
fct.addArgumentFromRegister(ContextRegister);
fct.addArgumentAsAddress(temp);
fct.call(__qmljs_to_boolean);
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-09-23 08:28:13 +00:00
Q_UNIMPLEMENTED();
assert(!"TODO");
}
void InstructionSelection::visitRet(IR::Ret *s)
{
if (IR::Temp *t = s->expr->asTemp()) {
2012-10-02 05:21:38 +00:00
Address source = loadTempAddress(Gpr0, t);
Address result = Address(ContextRegister, offsetof(Context, result));
FunctionCall fc(this);
fc.addArgumentAsAddress(result);
fc.addArgumentAsAddress(source);
fc.call(__qmljs_copy);
return;
}
2012-09-23 08:28:13 +00:00
Q_UNIMPLEMENTED();
Q_UNUSED(s);
}