consolidate x64 register layout information

added support for a second temporary, fixes several emitting issues
This commit is contained in:
Anthony Pesch 2016-02-04 01:25:53 -08:00
parent d16aac170a
commit 4b42923567
7 changed files with 408 additions and 424 deletions

View File

@ -18,6 +18,7 @@ namespace backend {
struct Register {
const char *name;
int value_types;
const void *data;
};
enum BlockFlags {

View File

@ -16,22 +16,38 @@ namespace jit {
namespace backend {
namespace interpreter {
const Register int_registers[NUM_INT_REGISTERS] = {
{"ia", ir::VALUE_INT_MASK}, {"ib", ir::VALUE_INT_MASK},
{"ic", ir::VALUE_INT_MASK}, {"id", ir::VALUE_INT_MASK},
{"ie", ir::VALUE_INT_MASK}, {"if", ir::VALUE_INT_MASK},
{"ig", ir::VALUE_INT_MASK}, {"ih", ir::VALUE_INT_MASK},
{"ii", ir::VALUE_INT_MASK}, {"ij", ir::VALUE_INT_MASK},
{"ik", ir::VALUE_INT_MASK}, {"il", ir::VALUE_INT_MASK},
{"im", ir::VALUE_INT_MASK}, {"in", ir::VALUE_INT_MASK},
{"io", ir::VALUE_INT_MASK}, {"ip", ir::VALUE_INT_MASK},
{"fa", ir::VALUE_FLOAT_MASK}, {"fb", ir::VALUE_FLOAT_MASK},
{"fc", ir::VALUE_FLOAT_MASK}, {"fd", ir::VALUE_FLOAT_MASK},
{"fe", ir::VALUE_FLOAT_MASK}, {"ff", ir::VALUE_FLOAT_MASK},
{"fg", ir::VALUE_FLOAT_MASK}, {"fh", ir::VALUE_FLOAT_MASK},
{"fi", ir::VALUE_FLOAT_MASK}, {"fj", ir::VALUE_FLOAT_MASK},
{"fk", ir::VALUE_FLOAT_MASK}, {"fl", ir::VALUE_FLOAT_MASK},
{"fm", ir::VALUE_FLOAT_MASK}, {"fn", ir::VALUE_FLOAT_MASK},
{"fo", ir::VALUE_FLOAT_MASK}, {"fp", ir::VALUE_FLOAT_MASK}};
{"ia", ir::VALUE_INT_MASK, nullptr},
{"ib", ir::VALUE_INT_MASK, nullptr},
{"ic", ir::VALUE_INT_MASK, nullptr},
{"id", ir::VALUE_INT_MASK, nullptr},
{"ie", ir::VALUE_INT_MASK, nullptr},
{"if", ir::VALUE_INT_MASK, nullptr},
{"ig", ir::VALUE_INT_MASK, nullptr},
{"ih", ir::VALUE_INT_MASK, nullptr},
{"ii", ir::VALUE_INT_MASK, nullptr},
{"ij", ir::VALUE_INT_MASK, nullptr},
{"ik", ir::VALUE_INT_MASK, nullptr},
{"il", ir::VALUE_INT_MASK, nullptr},
{"im", ir::VALUE_INT_MASK, nullptr},
{"in", ir::VALUE_INT_MASK, nullptr},
{"io", ir::VALUE_INT_MASK, nullptr},
{"ip", ir::VALUE_INT_MASK, nullptr},
{"fa", ir::VALUE_FLOAT_MASK, nullptr},
{"fb", ir::VALUE_FLOAT_MASK, nullptr},
{"fc", ir::VALUE_FLOAT_MASK, nullptr},
{"fd", ir::VALUE_FLOAT_MASK, nullptr},
{"fe", ir::VALUE_FLOAT_MASK, nullptr},
{"ff", ir::VALUE_FLOAT_MASK, nullptr},
{"fg", ir::VALUE_FLOAT_MASK, nullptr},
{"fh", ir::VALUE_FLOAT_MASK, nullptr},
{"fi", ir::VALUE_FLOAT_MASK, nullptr},
{"fj", ir::VALUE_FLOAT_MASK, nullptr},
{"fk", ir::VALUE_FLOAT_MASK, nullptr},
{"fl", ir::VALUE_FLOAT_MASK, nullptr},
{"fm", ir::VALUE_FLOAT_MASK, nullptr},
{"fn", ir::VALUE_FLOAT_MASK, nullptr},
{"fo", ir::VALUE_FLOAT_MASK, nullptr},
{"fp", ir::VALUE_FLOAT_MASK, nullptr}};
const int int_num_registers = sizeof(int_registers) / sizeof(Register);

View File

@ -755,7 +755,7 @@ INT_CALLBACK(UMUL) {
using U1 = typename std::make_unsigned<A1>::type;
U0 lhs = static_cast<U0>(LOAD_ARG0());
U1 rhs = static_cast<U1>(LOAD_ARG1());
STORE_RESULT((A0)(lhs * rhs));
STORE_RESULT(static_cast<A0>(lhs * rhs));
}
REGISTER_INT_CALLBACK(UMUL, UMUL, I8, I8, I8);
REGISTER_INT_CALLBACK(UMUL, UMUL, I16, I16, I16);

View File

@ -37,25 +37,87 @@ namespace dvm {
namespace jit {
namespace backend {
namespace x64 {
// x64 register layout
// %rax %eax %ax %al <-- temporary
// %rcx %ecx %cx %cl <-- argument
// %rdx %edx %dx %dl <-- argument
// %rbx %ebx %bx %bl <-- available, callee saved
// %rsp %esp %sp %spl <-- reserved
// %rbp %ebp %bp %bpl <-- available, callee saved
// %rsi %esi %si %sil <-- argument
// %rdi %edi %di %dil <-- argument
// %r8 %r8d %r8w %r8b <-- argument
// %r9 %r9d %r9w %r9b <-- argument
// %r10 %r10d %r10w %r10b <-- available, not callee saved
// %r11 %r11d %r11w %r11b <-- available, not callee saved
// %r12 %r12d %r12w %r12b <-- available, callee saved
// %r13 %r13d %r13w %r13b <-- available, callee saved
// %r14 %r14d %r14w %r14b <-- available, callee saved
// %r15 %r15d %r15w %r15b <-- available, callee saved
// msvc calling convention uses rcx, rdx, r8 and r9 for arguments
// amd64 calling convention uses rdi, rsi, rdx, rcx, r8 and r9 for arguments
// both use the same xmm registers for floating point arguments
// our largest function call uses only 3 arguments, leaving rdi, rsi and r9
// available on msvc and rcx, r8 and r9 available on amd64
// rax is used as a scratch register, while rdi/r8, r9 and xmm1 are used for
// storing
// a constant in case the constant propagation pass didn't eliminate it
// rsi is left unused on msvc and rcx is left unused on amd64
const Register x64_registers[] = {
{"rbx", ir::VALUE_INT_MASK}, {"rbp", ir::VALUE_INT_MASK},
{"r12", ir::VALUE_INT_MASK}, {"r13", ir::VALUE_INT_MASK},
{"r14", ir::VALUE_INT_MASK}, {"r15", ir::VALUE_INT_MASK},
{"xmm6", ir::VALUE_FLOAT_MASK}, {"xmm7", ir::VALUE_FLOAT_MASK},
{"xmm8", ir::VALUE_FLOAT_MASK}, {"xmm9", ir::VALUE_FLOAT_MASK},
{"xmm10", ir::VALUE_FLOAT_MASK}, {"xmm11", ir::VALUE_FLOAT_MASK}};
{"rbx", ir::VALUE_INT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::rbx)},
{"rbp", ir::VALUE_INT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::rbp)},
{"r12", ir::VALUE_INT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::r12)},
{"r13", ir::VALUE_INT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::r13)},
{"r14", ir::VALUE_INT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::r14)},
{"r15", ir::VALUE_INT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::r15)},
{"xmm6", ir::VALUE_FLOAT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::xmm6)},
{"xmm7", ir::VALUE_FLOAT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::xmm7)},
{"xmm8", ir::VALUE_FLOAT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::xmm8)},
{"xmm9", ir::VALUE_FLOAT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::xmm9)},
{"xmm10", ir::VALUE_FLOAT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::xmm10)},
{"xmm11", ir::VALUE_FLOAT_MASK,
reinterpret_cast<const void *>(&Xbyak::util::xmm11)}};
const int x64_num_registers = sizeof(x64_registers) / sizeof(Register);
#if PLATFORM_WINDOWS
const int x64_arg0_idx = Xbyak::Operand::RCX;
const int x64_arg1_idx = Xbyak::Operand::RDX;
const int x64_arg2_idx = Xbyak::Operand::R8;
const int x64_tmp0_idx = Xbyak::Operand::RDI;
const int x64_tmp1_idx = Xbyak::Operand::R9;
#else
const int x64_arg0_idx = Xbyak::Operand::RDI;
const int x64_arg1_idx = Xbyak::Operand::RSI;
const int x64_arg2_idx = Xbyak::Operand::RDX;
const int x64_tmp0_idx = Xbyak::Operand::R8;
const int x64_tmp1_idx = Xbyak::Operand::R9;
#endif
}
}
}
}
// this will break down if running two instances of the x64 backend, but it's
// extremely useful when profiling to group JITd blocks of code with an actual
// symbol name
static const size_t x64_codegen_size = 1024 * 1024 * 8;
static uint8_t x64_codegen[x64_codegen_size];
}
}
}
}
X64Backend::X64Backend(Memory &memory)
: Backend(memory), emitter_(x64_codegen, x64_codegen_size) {}
@ -114,23 +176,21 @@ bool X64Backend::HandleException(BlockPointer block, int *block_flags,
ex.thread_state.rsp -= STACK_SHADOW_SPACE + 24 + 8;
dvm::store(
reinterpret_cast<uint8_t *>(ex.thread_state.rsp + STACK_SHADOW_SPACE),
ex.thread_state.r[Xbyak::Operand::INT_ARG2]);
ex.thread_state.r[x64_arg2_idx]);
dvm::store(
reinterpret_cast<uint8_t *>(ex.thread_state.rsp + STACK_SHADOW_SPACE + 8),
ex.thread_state.r[Xbyak::Operand::INT_ARG1]);
ex.thread_state.r[x64_arg1_idx]);
dvm::store(reinterpret_cast<uint8_t *>(ex.thread_state.rsp +
STACK_SHADOW_SPACE + 16),
ex.thread_state.r[Xbyak::Operand::INT_ARG0]);
ex.thread_state.r[x64_arg0_idx]);
dvm::store(reinterpret_cast<uint8_t *>(ex.thread_state.rsp +
STACK_SHADOW_SPACE + 24),
ex.thread_state.rip + mov.length);
if (mov.is_load) {
// prep argument registers (memory object, guest_addr) for read function
ex.thread_state.r[Xbyak::Operand::INT_ARG0] =
reinterpret_cast<uint64_t>(&memory_);
ex.thread_state.r[Xbyak::Operand::INT_ARG1] =
static_cast<uint64_t>(guest_addr);
ex.thread_state.r[x64_arg0_idx] = reinterpret_cast<uint64_t>(&memory_);
ex.thread_state.r[x64_arg1_idx] = static_cast<uint64_t>(guest_addr);
// prep function call address for thunk
switch (mov.operand_size) {
@ -206,12 +266,9 @@ bool X64Backend::HandleException(BlockPointer block, int *block_flags,
} else {
// prep argument registers (memory object, guest_addr, value) for write
// function
ex.thread_state.r[Xbyak::Operand::INT_ARG0] =
reinterpret_cast<uint64_t>(&memory_);
ex.thread_state.r[Xbyak::Operand::INT_ARG1] =
static_cast<uint64_t>(guest_addr);
ex.thread_state.r[Xbyak::Operand::INT_ARG2] =
*(&ex.thread_state.r[mov.reg]);
ex.thread_state.r[x64_arg0_idx] = reinterpret_cast<uint64_t>(&memory_);
ex.thread_state.r[x64_arg1_idx] = static_cast<uint64_t>(guest_addr);
ex.thread_state.r[x64_arg2_idx] = *(&ex.thread_state.r[mov.reg]);
// prep function call address for thunk
switch (mov.operand_size) {

View File

@ -11,6 +11,11 @@ namespace x64 {
extern const Register x64_registers[];
extern const int x64_num_registers;
extern const int x64_arg0_idx;
extern const int x64_arg1_idx;
extern const int x64_arg2_idx;
extern const int x64_tmp0_idx;
extern const int x64_tmp1_idx;
class X64Backend : public Backend {
public:

File diff suppressed because it is too large Load Diff

View File

@ -11,16 +11,6 @@ namespace jit {
namespace backend {
namespace x64 {
#if PLATFORM_WINDOWS
#define INT_ARG0 RCX
#define INT_ARG1 RDX
#define INT_ARG2 R8
#else
#define INT_ARG0 RDI
#define INT_ARG1 RSI
#define INT_ARG2 RDX
#endif
enum {
#if PLATFORM_WINDOWS
STACK_SHADOW_SPACE = 32,
@ -48,11 +38,9 @@ class X64Emitter : public Xbyak::CodeGenerator {
hw::Memory &memory, void *guest_ctx, int block_flags);
// helpers for the emitter callbacks
const Xbyak::Operand &GetOperand(const ir::Value *v, int size = -1);
const Xbyak::Reg &GetRegister(const ir::Value *v);
const Xbyak::Xmm &GetXMMRegister(const ir::Value *v);
const Xbyak::Operand &CopyOperand(const ir::Value *v,
const Xbyak::Operand &to);
const Xbyak::Reg GetRegister(const ir::Value *v);
const Xbyak::Xmm GetXMMRegister(const ir::Value *v);
void CopyOperand(const ir::Value *v, const Xbyak::Reg &to);
Xbyak::Label *AllocLabel();
@ -72,6 +60,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
Xbyak::Label *epilog_label_;
int modified_marker_;
int *modified_;
int num_temps_;
};
}
}