2008-06-25 20:53:48 +00:00
|
|
|
/* Copyright (c) 2008, Avian Contributors
|
|
|
|
|
|
|
|
Permission to use, copy, modify, and/or distribute this software
|
|
|
|
for any purpose with or without fee is hereby granted, provided
|
|
|
|
that the above copyright notice and this permission notice appear
|
|
|
|
in all copies.
|
|
|
|
|
|
|
|
There is NO WARRANTY for this software. See license.txt for
|
|
|
|
details. */
|
|
|
|
|
2008-02-08 23:18:57 +00:00
|
|
|
#include "assembler.h"
|
2008-02-09 20:11:37 +00:00
|
|
|
#include "vector.h"
|
2008-02-08 23:18:57 +00:00
|
|
|
|
|
|
|
using namespace vm;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
enum {
|
2008-02-08 23:18:57 +00:00
|
|
|
rax = 0,
|
|
|
|
rcx = 1,
|
|
|
|
rdx = 2,
|
|
|
|
rbx = 3,
|
|
|
|
rsp = 4,
|
|
|
|
rbp = 5,
|
|
|
|
rsi = 6,
|
|
|
|
rdi = 7,
|
|
|
|
r8 = 8,
|
|
|
|
r9 = 9,
|
|
|
|
r10 = 10,
|
|
|
|
r11 = 11,
|
|
|
|
r12 = 12,
|
|
|
|
r13 = 13,
|
|
|
|
r14 = 14,
|
|
|
|
r15 = 15,
|
|
|
|
};
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
inline bool
|
|
|
|
isInt8(intptr_t v)
|
|
|
|
{
|
|
|
|
return v == static_cast<int8_t>(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
isInt32(intptr_t v)
|
|
|
|
{
|
|
|
|
return v == static_cast<int32_t>(v);
|
|
|
|
}
|
|
|
|
|
|
|
|
class Task;
|
|
|
|
|
|
|
|
class Context {
|
|
|
|
public:
|
|
|
|
Context(System* s, Allocator* a, Zone* zone):
|
2008-03-13 23:43:11 +00:00
|
|
|
s(s), zone(zone), client(0), code(s, a, 1024), tasks(0), result(0)
|
2008-02-12 00:20:32 +00:00
|
|
|
{ }
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
Zone* zone;
|
2008-03-13 23:43:11 +00:00
|
|
|
Assembler::Client* client;
|
2008-02-12 00:20:32 +00:00
|
|
|
Vector code;
|
|
|
|
Task* tasks;
|
|
|
|
uint8_t* result;
|
|
|
|
};
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
typedef void (*OperationType)(Context*);
|
|
|
|
|
|
|
|
typedef void (*UnaryOperationType)(Context*, unsigned, Assembler::Operand*);
|
|
|
|
|
|
|
|
typedef void (*BinaryOperationType)
|
|
|
|
(Context*, unsigned, Assembler::Operand*, unsigned, Assembler::Operand*);
|
|
|
|
|
|
|
|
typedef void (*TernaryOperationType)
|
|
|
|
(Context*, unsigned, Assembler::Operand*, unsigned, Assembler::Operand*,
|
|
|
|
unsigned, Assembler::Operand*);
|
|
|
|
|
|
|
|
class ArchitectureContext {
|
|
|
|
public:
|
|
|
|
ArchitectureContext(System* s): s(s) { }
|
|
|
|
|
|
|
|
System* s;
|
|
|
|
OperationType operations[OperationCount];
|
|
|
|
UnaryOperationType unaryOperations[UnaryOperationCount
|
|
|
|
* OperandTypeCount];
|
|
|
|
BinaryOperationType binaryOperations[BinaryOperationCount
|
|
|
|
* OperandTypeCount
|
|
|
|
* OperandTypeCount];
|
|
|
|
TernaryOperationType ternaryOperations[TernaryOperationCount
|
|
|
|
* OperandTypeCount
|
|
|
|
* OperandTypeCount
|
|
|
|
* OperandTypeCount];
|
|
|
|
};
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
inline void NO_RETURN
|
|
|
|
abort(Context* c)
|
|
|
|
{
|
|
|
|
abort(c->s);
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
inline void NO_RETURN
|
|
|
|
abort(ArchitectureContext* c)
|
|
|
|
{
|
|
|
|
abort(c->s);
|
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
#ifndef NDEBUG
|
|
|
|
inline void
|
|
|
|
assert(Context* c, bool v)
|
|
|
|
{
|
|
|
|
assert(c->s, v);
|
|
|
|
}
|
2008-08-19 23:38:37 +00:00
|
|
|
|
|
|
|
inline void
|
|
|
|
assert(ArchitectureContext* c, bool v)
|
|
|
|
{
|
|
|
|
assert(c->s, v);
|
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
#endif // not NDEBUG
|
|
|
|
|
|
|
|
inline void
|
|
|
|
expect(Context* c, bool v)
|
|
|
|
{
|
|
|
|
expect(c->s, v);
|
|
|
|
}
|
|
|
|
|
2008-04-28 22:08:31 +00:00
|
|
|
ResolvedPromise*
|
|
|
|
resolved(Context* c, int64_t value)
|
|
|
|
{
|
|
|
|
return new (c->zone->allocate(sizeof(ResolvedPromise)))
|
|
|
|
ResolvedPromise(value);
|
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
class CodePromise: public Promise {
|
|
|
|
public:
|
|
|
|
CodePromise(Context* c, unsigned offset): c(c), offset(offset) { }
|
|
|
|
|
|
|
|
virtual int64_t value() {
|
|
|
|
if (resolved()) {
|
|
|
|
return reinterpret_cast<intptr_t>(c->result + offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
abort(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool resolved() {
|
|
|
|
return c->result != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Context* c;
|
|
|
|
unsigned offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
CodePromise*
|
|
|
|
codePromise(Context* c, unsigned offset)
|
|
|
|
{
|
|
|
|
return new (c->zone->allocate(sizeof(CodePromise))) CodePromise(c, offset);
|
|
|
|
}
|
|
|
|
|
2008-02-08 23:18:57 +00:00
|
|
|
class Task {
|
|
|
|
public:
|
2008-02-09 20:11:37 +00:00
|
|
|
Task(Task* next): next(next) { }
|
|
|
|
|
|
|
|
virtual ~Task() { }
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
virtual void run(Context* c) = 0;
|
2008-02-09 20:11:37 +00:00
|
|
|
|
|
|
|
Task* next;
|
2008-02-08 23:18:57 +00:00
|
|
|
};
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
class OffsetTask: public Task {
|
|
|
|
public:
|
2008-02-17 20:57:40 +00:00
|
|
|
OffsetTask(Task* next, Promise* promise, unsigned instructionOffset,
|
2008-02-12 00:20:32 +00:00
|
|
|
unsigned instructionSize):
|
|
|
|
Task(next),
|
|
|
|
promise(promise),
|
|
|
|
instructionOffset(instructionOffset),
|
|
|
|
instructionSize(instructionSize)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual void run(Context* c) {
|
|
|
|
uint8_t* instruction = c->result + instructionOffset;
|
|
|
|
intptr_t v = reinterpret_cast<uint8_t*>(promise->value())
|
|
|
|
- instruction - instructionSize;
|
|
|
|
|
|
|
|
expect(c, isInt32(v));
|
|
|
|
|
|
|
|
int32_t v4 = v;
|
|
|
|
memcpy(instruction + instructionSize - 4, &v4, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
Promise* promise;
|
2008-02-17 20:57:40 +00:00
|
|
|
unsigned instructionOffset;
|
2008-02-12 00:20:32 +00:00
|
|
|
unsigned instructionSize;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
appendOffsetTask(Context* c, Promise* promise, int instructionOffset,
|
|
|
|
unsigned instructionSize)
|
|
|
|
{
|
|
|
|
c->tasks = new (c->zone->allocate(sizeof(OffsetTask))) OffsetTask
|
|
|
|
(c->tasks, promise, instructionOffset, instructionSize);
|
|
|
|
}
|
|
|
|
|
2008-02-17 20:57:40 +00:00
|
|
|
class ImmediateTask: public Task {
|
|
|
|
public:
|
|
|
|
ImmediateTask(Task* next, Promise* promise, unsigned offset):
|
|
|
|
Task(next),
|
|
|
|
promise(promise),
|
|
|
|
offset(offset)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
virtual void run(Context* c) {
|
|
|
|
intptr_t v = promise->value();
|
|
|
|
memcpy(c->result + offset, &v, BytesPerWord);
|
|
|
|
}
|
|
|
|
|
|
|
|
Promise* promise;
|
|
|
|
unsigned offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
appendImmediateTask(Context* c, Promise* promise, unsigned offset)
|
|
|
|
{
|
|
|
|
c->tasks = new (c->zone->allocate(sizeof(ImmediateTask))) ImmediateTask
|
|
|
|
(c->tasks, promise, offset);
|
|
|
|
}
|
|
|
|
|
2008-02-12 00:20:32 +00:00
|
|
|
void
|
|
|
|
encode(Context* c, uint8_t* instruction, unsigned length, int a, int b,
|
|
|
|
int32_t displacement, int index, unsigned scale)
|
|
|
|
{
|
|
|
|
c->code.append(instruction, length);
|
|
|
|
|
|
|
|
uint8_t width;
|
|
|
|
if (displacement == 0 and b != rbp) {
|
|
|
|
width = 0;
|
|
|
|
} else if (isInt8(displacement)) {
|
|
|
|
width = 0x40;
|
|
|
|
} else {
|
|
|
|
width = 0x80;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index == -1) {
|
|
|
|
c->code.append(width | (a << 3) | b);
|
|
|
|
if (b == rsp) {
|
|
|
|
c->code.append(0x24);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert(c, b != rsp);
|
|
|
|
c->code.append(width | (a << 3) | 4);
|
|
|
|
c->code.append((log(scale) << 6) | (index << 3) | b);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (displacement == 0 and b != rbp) {
|
|
|
|
// do nothing
|
|
|
|
} else if (isInt8(displacement)) {
|
|
|
|
c->code.append(displacement);
|
|
|
|
} else {
|
|
|
|
c->code.append4(displacement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-13 19:48:20 +00:00
|
|
|
rex(Context* c, uint8_t mask, int r)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
|
|
|
if (BytesPerWord == 8) {
|
2008-04-13 19:48:20 +00:00
|
|
|
c->code.append(mask | ((r & 8) >> 3));
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-13 19:48:20 +00:00
|
|
|
rex(Context* c)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
2008-04-13 19:48:20 +00:00
|
|
|
rex(c, 0x48, rax);
|
|
|
|
}
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2008-04-13 19:48:20 +00:00
|
|
|
void
|
|
|
|
encode(Context* c, uint8_t instruction, int a, Assembler::Memory* b, bool rex)
|
|
|
|
{
|
2008-02-12 00:20:32 +00:00
|
|
|
if (rex) {
|
|
|
|
::rex(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
encode(c, &instruction, 1, a, b->base, b->offset, b->index, b->scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
encode2(Context* c, uint16_t instruction, int a, Assembler::Memory* b,
|
|
|
|
bool rex)
|
|
|
|
{
|
|
|
|
if (rex) {
|
|
|
|
::rex(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t i[2] = { instruction >> 8, instruction & 0xff };
|
|
|
|
encode(c, i, 2, a, b->base, b->offset, b->index, b->scale);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
return_(Context* c)
|
|
|
|
{
|
|
|
|
c->code.append(0xc3);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
unconditional(Context* c, unsigned jump, Assembler::Constant* a)
|
|
|
|
{
|
|
|
|
appendOffsetTask(c, a->value, c->code.length(), 5);
|
|
|
|
|
|
|
|
c->code.append(jump);
|
|
|
|
c->code.append4(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
conditional(Context* c, unsigned condition, Assembler::Constant* a)
|
|
|
|
{
|
|
|
|
appendOffsetTask(c, a->value, c->code.length(), 6);
|
|
|
|
|
|
|
|
c->code.append(0x0f);
|
|
|
|
c->code.append(condition);
|
|
|
|
c->code.append4(0);
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
inline unsigned
|
|
|
|
index(UnaryOperation operation, OperandType operand)
|
2008-06-02 13:49:09 +00:00
|
|
|
{
|
2008-08-19 23:38:37 +00:00
|
|
|
return operation + (UnaryOperationCount * operand);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
inline unsigned
|
|
|
|
index(BinaryOperation operation,
|
|
|
|
OperandType operand1,
|
|
|
|
OperandType operand2)
|
2008-02-17 22:29:04 +00:00
|
|
|
{
|
2008-08-19 23:38:37 +00:00
|
|
|
return operation
|
|
|
|
+ (BinaryOperationCount * operand1)
|
|
|
|
+ (BinaryOperationCount * OperandTypeCount * operand2);
|
2008-02-17 22:29:04 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
inline unsigned
|
|
|
|
index(TernaryOperation operation,
|
|
|
|
OperandType operand1,
|
|
|
|
OperandType operand2,
|
|
|
|
OperandType operand3)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
2008-08-19 23:38:37 +00:00
|
|
|
return operation
|
|
|
|
+ (TernaryOperationCount * operand1)
|
|
|
|
+ (TernaryOperationCount * OperandTypeCount * operand2)
|
|
|
|
+ (TernaryOperationCount * OperandTypeCount * OperandTypeCount * operand3);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2008-09-05 15:00:38 +00:00
|
|
|
void
|
|
|
|
pushR(Context* c, unsigned size, Assembler::Register* a)
|
|
|
|
{
|
|
|
|
if (BytesPerWord == 4 and size == 8) {
|
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
|
|
|
|
pushR(c, 4, &ah);
|
|
|
|
pushR(c, 4, a);
|
|
|
|
} else {
|
|
|
|
c->code.append(0x50 | a->low);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
moveRR(Context* c, unsigned aSize, Assembler::Register* a,
|
|
|
|
unsigned bSize, Assembler::Register* b)
|
|
|
|
{
|
|
|
|
if (BytesPerWord == 4 and aSize == 8 and bSize == 8) {
|
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
Assembler::Register bh(b->high);
|
|
|
|
|
|
|
|
moveRR(c, 4, a, 4, b);
|
|
|
|
moveRR(c, 4, &ah, 4, &bh);
|
|
|
|
} else {
|
|
|
|
switch (aSize) {
|
|
|
|
case 1:
|
|
|
|
if (BytesPerWord == 4 and a->low > rbx) {
|
|
|
|
assert(c, b->low <= rbx);
|
|
|
|
|
|
|
|
moveRR(c, BytesPerWord, a, BytesPerWord, b);
|
|
|
|
moveRR(c, 1, b, BytesPerWord, b);
|
|
|
|
} else {
|
|
|
|
rex(c);
|
|
|
|
c->code.append(0x0f);
|
|
|
|
c->code.append(0xbe);
|
|
|
|
c->code.append(0xc0 | (b->low << 3) | a->low);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
rex(c);
|
|
|
|
c->code.append(0x0f);
|
|
|
|
c->code.append(0xbf);
|
|
|
|
c->code.append(0xc0 | (b->low << 3) | a->low);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
case 4:
|
|
|
|
if (aSize == 4 and bSize == 8) {
|
|
|
|
if (BytesPerWord == 8) {
|
|
|
|
rex(c);
|
|
|
|
c->code.append(0x63);
|
|
|
|
c->code.append(0xc0 | (b->low << 3) | a->low);
|
|
|
|
} else {
|
|
|
|
if (a->low == rax and b->low == rax and b->high == rdx) {
|
|
|
|
c->code.append(0x99); // cdq
|
|
|
|
} else {
|
|
|
|
assert(c, b->low == rax and b->high == rdx);
|
|
|
|
|
|
|
|
moveRR(c, 4, a, 4, b);
|
|
|
|
moveRR(c, 4, b, 8, b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (a->low != b->low) {
|
|
|
|
rex(c);
|
|
|
|
c->code.append(0x89);
|
|
|
|
c->code.append(0xc0 | (a->low << 3) | b->low);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
popR(Context* c, unsigned size, Assembler::Register* a)
|
|
|
|
{
|
|
|
|
if (BytesPerWord == 4 and size == 8) {
|
|
|
|
Assembler::Register ah(a->high);
|
|
|
|
|
|
|
|
popR(c, 4, a);
|
|
|
|
popR(c, 4, &ah);
|
|
|
|
} else {
|
|
|
|
c->code.append(0x58 | a->low);
|
|
|
|
if (BytesPerWord == 8 and size == 4) {
|
|
|
|
moveRR(c, 4, a, 8, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-10 22:37:21 +00:00
|
|
|
void
|
2008-08-19 23:38:37 +00:00
|
|
|
populateTables(ArchitectureContext*)
|
2008-03-10 22:37:21 +00:00
|
|
|
{
|
2008-08-19 23:38:37 +00:00
|
|
|
// todo
|
2008-03-10 22:37:21 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
// const int Constant = ConstantOperand;
|
|
|
|
// const int Address = AddressOperand;
|
|
|
|
// const int Register = RegisterOperand;
|
|
|
|
// const int Memory = MemoryOperand;
|
2008-02-17 22:29:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
// #define CAST1(x) reinterpret_cast<UnaryOperationType>(x)
|
|
|
|
// #define CAST2(x) reinterpret_cast<BinaryOperationType>(x)
|
|
|
|
// #define CAST3(x) reinterpret_cast<TernaryOperationType>(x)
|
2008-02-17 22:29:04 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
class MyArchitecture: public Assembler::Architecture {
|
|
|
|
public:
|
|
|
|
MyArchitecture(System* system): c(system), referenceCount(0) {
|
|
|
|
populateTables(&c);
|
|
|
|
}
|
2008-06-02 13:49:09 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned registerCount() {
|
|
|
|
return 8;//BytesPerWord == 4 ? 8 : 16;
|
|
|
|
}
|
2008-06-02 13:49:09 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual int stack() {
|
|
|
|
return rsp;
|
2008-05-31 22:14:27 +00:00
|
|
|
}
|
2008-03-13 20:50:56 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual int thread() {
|
|
|
|
return rbx;
|
|
|
|
}
|
2008-04-20 19:35:36 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual int returnLow() {
|
|
|
|
return rax;
|
|
|
|
}
|
2008-04-20 19:35:36 +00:00
|
|
|
|
2008-08-23 18:04:36 +00:00
|
|
|
virtual bool reserved(int register_) {
|
|
|
|
switch (register_) {
|
|
|
|
case rbp:
|
|
|
|
case rsp:
|
|
|
|
case rbx:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual int returnHigh() {
|
|
|
|
return (BytesPerWord == 4 ? rdx : NoRegister);
|
|
|
|
}
|
2008-03-13 23:43:11 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned argumentRegisterCount() {
|
|
|
|
return (BytesPerWord == 4 ? 0 : 6);
|
|
|
|
}
|
2008-03-13 23:43:11 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual int argumentRegister(unsigned index) {
|
|
|
|
assert(&c, BytesPerWord == 8);
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
switch (index) {
|
|
|
|
case 0:
|
|
|
|
return rdi;
|
|
|
|
case 1:
|
|
|
|
return rsi;
|
|
|
|
case 2:
|
|
|
|
return rdx;
|
|
|
|
case 3:
|
|
|
|
return rcx;
|
|
|
|
case 4:
|
|
|
|
return r8;
|
|
|
|
case 5:
|
|
|
|
return r9;
|
|
|
|
default:
|
|
|
|
abort(&c);
|
|
|
|
}
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void updateCall(void* returnAddress, void* newTarget) {
|
|
|
|
uint8_t* instruction = static_cast<uint8_t*>(returnAddress) - 5;
|
|
|
|
assert(&c, *instruction == 0xE8);
|
|
|
|
assert(&c, reinterpret_cast<uintptr_t>(instruction + 1) % 4 == 0);
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
int32_t v = static_cast<uint8_t*>(newTarget)
|
|
|
|
- static_cast<uint8_t*>(returnAddress);
|
|
|
|
memcpy(instruction + 1, &v, 4);
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned alignFrameSize(unsigned sizeInWords) {
|
|
|
|
const unsigned alignment = 16 / BytesPerWord;
|
|
|
|
return (ceiling(sizeInWords + 2, alignment) * alignment);
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void* frameIp(void* stack) {
|
|
|
|
return *static_cast<void**>(stack);
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned frameHeaderSize() {
|
|
|
|
return 2;
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned frameFooterSize() {
|
|
|
|
return 0;
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void nextFrame(void** stack, void** base) {
|
|
|
|
*stack = static_cast<void**>(*base) + 1;
|
|
|
|
*base = *static_cast<void**>(*base);
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void* popReturnAddress(void* stack) {
|
|
|
|
return static_cast<void**>(stack) + 1;
|
|
|
|
}
|
2008-03-15 20:24:04 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void plan
|
|
|
|
(UnaryOperation,
|
|
|
|
unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask,
|
|
|
|
bool* thunk)
|
|
|
|
{
|
|
|
|
*aTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
*aRegisterMask = ~static_cast<uint64_t>(0);
|
|
|
|
*thunk = false;
|
|
|
|
}
|
2008-03-13 23:43:11 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void plan
|
|
|
|
(BinaryOperation op,
|
|
|
|
unsigned aSize, uint8_t* aTypeMask, uint64_t* aRegisterMask,
|
|
|
|
unsigned bSize, uint8_t* bTypeMask, uint64_t* bRegisterMask,
|
|
|
|
bool* thunk)
|
|
|
|
{
|
|
|
|
*aTypeMask = ~0;
|
|
|
|
*aRegisterMask = ~static_cast<uint64_t>(0);
|
2008-04-29 22:11:17 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
*bTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
*bRegisterMask = ~static_cast<uint64_t>(0);
|
2008-03-13 23:43:11 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
*thunk = false;
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
switch (op) {
|
|
|
|
case Compare:
|
|
|
|
if (BytesPerWord == 8 and aSize != 8) {
|
|
|
|
*aTypeMask = ~(1 << MemoryOperand);
|
|
|
|
*bTypeMask = ~(1 << MemoryOperand);
|
2008-04-29 22:11:17 +00:00
|
|
|
} else {
|
2008-08-19 23:38:37 +00:00
|
|
|
*bTypeMask = ~(1 << ConstantOperand);
|
2008-04-29 22:11:17 +00:00
|
|
|
}
|
2008-08-19 23:38:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Move:
|
2008-04-29 23:16:53 +00:00
|
|
|
if (BytesPerWord == 4) {
|
2008-08-19 23:38:37 +00:00
|
|
|
if (aSize == 4 and bSize == 8) {
|
|
|
|
const uint32_t mask = ~((1 << rax) | (1 << rdx));
|
|
|
|
*aRegisterMask = (static_cast<uint64_t>(mask) << 32) | mask;
|
|
|
|
*bRegisterMask = (static_cast<uint64_t>(1) << (rdx + 32))
|
|
|
|
| (static_cast<uint64_t>(1) << rax);
|
|
|
|
} else if (aSize == 1) {
|
|
|
|
const uint32_t mask
|
|
|
|
= (1 << rax) | (1 << rcx) | (1 << rdx) | (1 << rbx);
|
|
|
|
*aRegisterMask = (static_cast<uint64_t>(mask) << 32) | mask;
|
|
|
|
*bRegisterMask = (static_cast<uint64_t>(mask) << 32) | mask;
|
|
|
|
}
|
2008-04-29 23:16:53 +00:00
|
|
|
}
|
2008-08-19 23:38:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2008-03-13 23:43:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void plan
|
|
|
|
(TernaryOperation,
|
|
|
|
unsigned, uint8_t* aTypeMask, uint64_t* aRegisterMask,
|
|
|
|
unsigned, uint8_t* bTypeMask, uint64_t* bRegisterMask,
|
|
|
|
unsigned, uint8_t* cTypeMask, uint64_t* cRegisterMask,
|
|
|
|
bool* thunk)
|
|
|
|
{
|
|
|
|
*aTypeMask = ~0;
|
|
|
|
*aRegisterMask = ~static_cast<uint64_t>(0);
|
2008-03-15 23:54:20 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
*bTypeMask = ~0;
|
|
|
|
*bRegisterMask = ~static_cast<uint64_t>(0);
|
2008-03-15 23:54:20 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
*cTypeMask = (1 << RegisterOperand) | (1 << MemoryOperand);
|
|
|
|
*cRegisterMask = ~static_cast<uint64_t>(0);
|
2008-02-12 15:21:51 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
*thunk = false;
|
2008-02-12 15:21:51 +00:00
|
|
|
}
|
2008-03-10 22:37:21 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void acquire() {
|
|
|
|
++ referenceCount;
|
2008-03-10 22:37:21 +00:00
|
|
|
}
|
2008-02-12 15:21:51 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void release() {
|
|
|
|
if (-- referenceCount == 0) {
|
|
|
|
c.s->free(this);
|
2008-02-17 22:29:04 +00:00
|
|
|
}
|
2008-02-12 15:21:51 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
ArchitectureContext c;
|
|
|
|
unsigned referenceCount;
|
|
|
|
};
|
2008-03-09 21:27:51 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
class MyAssembler: public Assembler {
|
|
|
|
public:
|
2008-08-23 18:04:36 +00:00
|
|
|
MyAssembler(System* s, Allocator* a, Zone* zone, MyArchitecture* arch):
|
|
|
|
c(s, a, zone), arch_(arch)
|
2008-08-19 23:38:37 +00:00
|
|
|
{ }
|
2008-03-09 21:27:51 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void setClient(Client* client) {
|
|
|
|
assert(&c, c.client == 0);
|
|
|
|
c.client = client;
|
2008-03-09 21:27:51 +00:00
|
|
|
}
|
|
|
|
|
2008-08-23 18:04:36 +00:00
|
|
|
virtual Architecture* arch() {
|
|
|
|
return arch_;
|
|
|
|
}
|
|
|
|
|
2008-09-05 15:00:38 +00:00
|
|
|
virtual void saveFrame(unsigned stackOffset, unsigned baseOffset) {
|
|
|
|
Register stack(rsp);
|
|
|
|
Memory stackDst(rbx, stackOffset);
|
|
|
|
apply(Move, BytesPerWord, RegisterOperand, &stack,
|
|
|
|
BytesPerWord, MemoryOperand, &stackDst);
|
|
|
|
|
|
|
|
Register base(rbp);
|
|
|
|
Memory baseDst(rbx, baseOffset);
|
|
|
|
apply(Move, BytesPerWord, RegisterOperand, &base,
|
|
|
|
BytesPerWord, MemoryOperand, &baseDst);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void pushFrame(unsigned argumentCount, ...) {
|
|
|
|
struct {
|
|
|
|
unsigned size;
|
|
|
|
OperandType type;
|
|
|
|
Operand* operand;
|
|
|
|
} arguments[argumentCount];
|
|
|
|
va_list a; va_start(a, argumentCount);
|
|
|
|
unsigned footprint = 0;
|
|
|
|
for (unsigned i = 0; i < argumentCount; ++i) {
|
|
|
|
arguments[i].size = va_arg(a, unsigned);
|
|
|
|
arguments[i].type = static_cast<OperandType>(va_arg(a, int));
|
|
|
|
arguments[i].operand = va_arg(a, Operand*);
|
|
|
|
footprint += ceiling(arguments[i].size, BytesPerWord);
|
|
|
|
}
|
|
|
|
va_end(a);
|
|
|
|
|
|
|
|
allocateFrame(footprint);
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < argumentCount; ++i) {
|
|
|
|
Memory dst(rsp, footprint);
|
|
|
|
apply(Move,
|
|
|
|
arguments[i].size, arguments[i].type, arguments[i].operand,
|
|
|
|
pad(arguments[i].size), MemoryOperand, &dst);
|
|
|
|
footprint -= ceiling(arguments[i].size, BytesPerWord);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void allocateFrame(unsigned footprint) {
|
|
|
|
Register base(rbp);
|
|
|
|
pushR(&c, BytesPerWord, &base);
|
|
|
|
|
|
|
|
Register stack(rsp);
|
|
|
|
apply(Move, BytesPerWord, RegisterOperand, &stack,
|
|
|
|
BytesPerWord, RegisterOperand, &base);
|
|
|
|
|
|
|
|
Constant footprintConstant
|
|
|
|
(resolved(&c, arch_->alignFrameSize(footprint) * BytesPerWord));
|
|
|
|
apply(Subtract, BytesPerWord, ConstantOperand, &footprintConstant,
|
|
|
|
BytesPerWord, RegisterOperand, &stack,
|
|
|
|
BytesPerWord, RegisterOperand, &stack);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void popFrame() {
|
|
|
|
Register base(rbp);
|
|
|
|
Register stack(rsp);
|
|
|
|
apply(Move, BytesPerWord, RegisterOperand, &base,
|
|
|
|
BytesPerWord, RegisterOperand, &stack);
|
|
|
|
|
|
|
|
popR(&c, BytesPerWord, &base);
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void apply(Operation op) {
|
2008-08-23 18:04:36 +00:00
|
|
|
arch_->c.operations[op](&c);
|
2008-04-28 22:08:31 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void apply(UnaryOperation op,
|
|
|
|
unsigned aSize, OperandType aType, Operand* aOperand)
|
|
|
|
{
|
2008-08-23 18:04:36 +00:00
|
|
|
arch_->c.unaryOperations[index(op, aType)](&c, aSize, aOperand);
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void apply(BinaryOperation op,
|
|
|
|
unsigned aSize, OperandType aType, Operand* aOperand,
|
|
|
|
unsigned bSize, OperandType bType, Operand* bOperand)
|
|
|
|
{
|
2008-08-23 18:04:36 +00:00
|
|
|
arch_->c.binaryOperations[index(op, aType, bType)]
|
2008-08-19 23:38:37 +00:00
|
|
|
(&c, aSize, aOperand, bSize, bOperand);
|
|
|
|
}
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void apply(TernaryOperation op,
|
|
|
|
unsigned aSize, OperandType aType, Operand* aOperand,
|
|
|
|
unsigned bSize, OperandType bType, Operand* bOperand,
|
|
|
|
unsigned cSize, OperandType cType, Operand* cOperand)
|
|
|
|
{
|
2008-08-23 18:04:36 +00:00
|
|
|
arch_->c.ternaryOperations[index(op, aType, bType, cType)]
|
2008-08-19 23:38:37 +00:00
|
|
|
(&c, aSize, aOperand, bSize, bOperand, cSize, cOperand);
|
2008-04-28 22:08:31 +00:00
|
|
|
}
|
2008-04-26 20:56:03 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void writeTo(uint8_t* dst) {
|
|
|
|
c.result = dst;
|
|
|
|
memcpy(dst, c.code.data, c.code.length());
|
|
|
|
|
|
|
|
for (Task* t = c.tasks; t; t = t->next) {
|
|
|
|
t->run(&c);
|
|
|
|
}
|
|
|
|
}
|
2008-02-17 20:57:40 +00:00
|
|
|
|
2008-08-30 20:12:27 +00:00
|
|
|
virtual Offset* offset() {
|
|
|
|
// todo
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void endBlock() {
|
|
|
|
// todo
|
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual unsigned length() {
|
|
|
|
return c.code.length();
|
2008-02-17 20:57:40 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
virtual void dispose() {
|
|
|
|
c.code.dispose();
|
|
|
|
}
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
Context c;
|
2008-08-23 18:04:36 +00:00
|
|
|
MyArchitecture* arch_;
|
2008-08-19 23:38:37 +00:00
|
|
|
};
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
} // namespace
|
2008-04-28 22:08:31 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
namespace vm {
|
2008-02-12 00:20:32 +00:00
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
Assembler::Architecture*
|
|
|
|
makeArchitecture(System* system)
|
|
|
|
{
|
|
|
|
return new (allocate(system, sizeof(MyArchitecture))) MyArchitecture(system);
|
2008-02-12 00:20:32 +00:00
|
|
|
}
|
|
|
|
|
2008-08-19 23:38:37 +00:00
|
|
|
Assembler*
|
|
|
|
makeAssembler(System* system, Allocator* allocator, Zone* zone,
|
|
|
|
Assembler::Architecture* architecture)
|
2008-02-12 00:20:32 +00:00
|
|
|
{
|
2008-08-19 23:38:37 +00:00
|
|
|
return new (zone->allocate(sizeof(MyAssembler)))
|
|
|
|
MyAssembler(system, allocator, zone,
|
2008-08-23 18:04:36 +00:00
|
|
|
static_cast<MyArchitecture*>(architecture));
|
2008-08-19 23:38:37 +00:00
|
|
|
}
|
2008-05-23 00:16:44 +00:00
|
|
|
|
2008-02-08 23:18:57 +00:00
|
|
|
|
|
|
|
} // namespace vm
|