Trying out a new style of JIT pattern matching.

This commit is contained in:
Ben Vanik
2014-05-26 20:28:21 -07:00
parent a001714fb0
commit 5a85263e5f
38 changed files with 6403 additions and 5160 deletions

View File

@@ -40,10 +40,10 @@ namespace ivm {
#define DPRINT
#define DFLUSH()
//#define IPRINT if (ics.thread_state->thread_id() == 1) printf
//#define IFLUSH() fflush(stdout)
//#define DPRINT if (ics.thread_state->thread_id() == 1) printf
//#define DFLUSH() fflush(stdout)
#define IPRINT if (ics.thread_state->thread_id() == 1) printf
#define IFLUSH() fflush(stdout)
#define DPRINT if (ics.thread_state->thread_id() == 1) printf
#define DFLUSH() fflush(stdout)
#if XE_CPU_BIGENDIAN
#define VECB16(v,n) (v.b16[n])
@@ -1364,31 +1364,31 @@ int Translate_LOAD_CLOCK(TranslationContext& ctx, Instr* i) {
}
uint32_t IntCode_LOAD_LOCAL_I8(IntCodeState& ics, const IntCode* i) {
ics.rf[i->dest_reg].i8 = *((int8_t*)(ics.locals + ics.rf[i->src1_reg].u64));
ics.rf[i->dest_reg].i8 = *((int8_t*)(ics.locals + ics.rf[i->src1_reg].u32));
return IA_NEXT;
}
uint32_t IntCode_LOAD_LOCAL_I16(IntCodeState& ics, const IntCode* i) {
ics.rf[i->dest_reg].i16 = *((int16_t*)(ics.locals + ics.rf[i->src1_reg].u64));
ics.rf[i->dest_reg].i16 = *((int16_t*)(ics.locals + ics.rf[i->src1_reg].u32));
return IA_NEXT;
}
uint32_t IntCode_LOAD_LOCAL_I32(IntCodeState& ics, const IntCode* i) {
ics.rf[i->dest_reg].i32 = *((int32_t*)(ics.locals + ics.rf[i->src1_reg].u64));
ics.rf[i->dest_reg].i32 = *((int32_t*)(ics.locals + ics.rf[i->src1_reg].u32));
return IA_NEXT;
}
uint32_t IntCode_LOAD_LOCAL_I64(IntCodeState& ics, const IntCode* i) {
ics.rf[i->dest_reg].i64 = *((int64_t*)(ics.locals + ics.rf[i->src1_reg].u64));
ics.rf[i->dest_reg].i64 = *((int64_t*)(ics.locals + ics.rf[i->src1_reg].u32));
return IA_NEXT;
}
uint32_t IntCode_LOAD_LOCAL_F32(IntCodeState& ics, const IntCode* i) {
ics.rf[i->dest_reg].f32 = *((float*)(ics.locals + ics.rf[i->src1_reg].u64));
ics.rf[i->dest_reg].f32 = *((float*)(ics.locals + ics.rf[i->src1_reg].u32));
return IA_NEXT;
}
uint32_t IntCode_LOAD_LOCAL_F64(IntCodeState& ics, const IntCode* i) {
ics.rf[i->dest_reg].f64 = *((double*)(ics.locals + ics.rf[i->src1_reg].u64));
ics.rf[i->dest_reg].f64 = *((double*)(ics.locals + ics.rf[i->src1_reg].u32));
return IA_NEXT;
}
uint32_t IntCode_LOAD_LOCAL_V128(IntCodeState& ics, const IntCode* i) {
ics.rf[i->dest_reg].v128 = *((vec128_t*)(ics.locals + ics.rf[i->src1_reg].u64));
ics.rf[i->dest_reg].v128 = *((vec128_t*)(ics.locals + ics.rf[i->src1_reg].u32));
return IA_NEXT;
}
int Translate_LOAD_LOCAL(TranslationContext& ctx, Instr* i) {
@@ -1405,31 +1405,31 @@ int Translate_LOAD_LOCAL(TranslationContext& ctx, Instr* i) {
}
uint32_t IntCode_STORE_LOCAL_I8(IntCodeState& ics, const IntCode* i) {
*((int8_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].i8;
*((int8_t*)(ics.locals + ics.rf[i->src1_reg].u32)) = ics.rf[i->src2_reg].i8;
return IA_NEXT;
}
uint32_t IntCode_STORE_LOCAL_I16(IntCodeState& ics, const IntCode* i) {
*((int16_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].i16;
*((int16_t*)(ics.locals + ics.rf[i->src1_reg].u32)) = ics.rf[i->src2_reg].i16;
return IA_NEXT;
}
uint32_t IntCode_STORE_LOCAL_I32(IntCodeState& ics, const IntCode* i) {
*((int32_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].i32;
*((int32_t*)(ics.locals + ics.rf[i->src1_reg].u32)) = ics.rf[i->src2_reg].i32;
return IA_NEXT;
}
uint32_t IntCode_STORE_LOCAL_I64(IntCodeState& ics, const IntCode* i) {
*((int64_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].i64;
*((int64_t*)(ics.locals + ics.rf[i->src1_reg].u32)) = ics.rf[i->src2_reg].i64;
return IA_NEXT;
}
uint32_t IntCode_STORE_LOCAL_F32(IntCodeState& ics, const IntCode* i) {
*((float*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].f32;
*((float*)(ics.locals + ics.rf[i->src1_reg].u32)) = ics.rf[i->src2_reg].f32;
return IA_NEXT;
}
uint32_t IntCode_STORE_LOCAL_F64(IntCodeState& ics, const IntCode* i) {
*((double*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].f64;
*((double*)(ics.locals + ics.rf[i->src1_reg].u32)) = ics.rf[i->src2_reg].f64;
return IA_NEXT;
}
uint32_t IntCode_STORE_LOCAL_V128(IntCodeState& ics, const IntCode* i) {
*((vec128_t*)(ics.locals + ics.rf[i->src1_reg].u64)) = ics.rf[i->src2_reg].v128;
*((vec128_t*)(ics.locals + ics.rf[i->src1_reg].u32)) = ics.rf[i->src2_reg].v128;
return IA_NEXT;
}
int Translate_STORE_LOCAL(TranslationContext& ctx, Instr* i) {
@@ -3715,17 +3715,17 @@ int Translate_CNTLZ(TranslationContext& ctx, Instr* i) {
uint32_t IntCode_EXTRACT_INT8_V128(IntCodeState& ics, const IntCode* i) {
const vec128_t& src1 = ics.rf[i->src1_reg].v128;
ics.rf[i->dest_reg].i8 = VECB16(src1,ics.rf[i->src2_reg].i64);
ics.rf[i->dest_reg].i8 = VECB16(src1,ics.rf[i->src2_reg].i8);
return IA_NEXT;
}
uint32_t IntCode_EXTRACT_INT16_V128(IntCodeState& ics, const IntCode* i) {
const vec128_t& src1 = ics.rf[i->src1_reg].v128;
ics.rf[i->dest_reg].i16 = VECS8(src1,ics.rf[i->src2_reg].i64);
ics.rf[i->dest_reg].i16 = VECS8(src1,ics.rf[i->src2_reg].i8);
return IA_NEXT;
}
uint32_t IntCode_EXTRACT_INT32_V128(IntCodeState& ics, const IntCode* i) {
const vec128_t& src1 = ics.rf[i->src1_reg].v128;
ics.rf[i->dest_reg].i32 = VECI4(src1,ics.rf[i->src2_reg].i64);
ics.rf[i->dest_reg].i32 = VECI4(src1,ics.rf[i->src2_reg].i8);
return IA_NEXT;
}
int Translate_EXTRACT(TranslationContext& ctx, Instr* i) {

File diff suppressed because it is too large Load Diff

View File

@@ -1,71 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <alloy/backend/x64/lowering/lowering_table.h>
#include <alloy/backend/x64/x64_emitter.h>
#include <alloy/backend/x64/lowering/lowering_sequences.h>
using namespace alloy;
using namespace alloy::backend::x64;
using namespace alloy::backend::x64::lowering;
LoweringTable::LoweringTable(X64Backend* backend) :
backend_(backend) {
xe_zero_struct(lookup_, sizeof(lookup_));
}
LoweringTable::~LoweringTable() {
for (size_t n = 0; n < XECOUNT(lookup_); n++) {
auto entry = lookup_[n];
while (entry) {
auto next = entry->next;
delete entry;
entry = next;
}
}
}
int LoweringTable::Initialize() {
RegisterSequences(this);
return 0;
}
void LoweringTable::AddSequence(hir::Opcode starting_opcode, sequence_fn_t fn) {
auto existing_entry = lookup_[starting_opcode];
auto new_entry = new sequence_fn_entry_t();
new_entry->fn = fn;
new_entry->next = existing_entry;
lookup_[starting_opcode] = new_entry;
}
int LoweringTable::ProcessBlock(X64Emitter& e, hir::Block* block) {
// Process instructions.
auto instr = block->instr_head;
while (instr) {
bool processed = false;
auto entry = lookup_[instr->opcode->num];
while (entry) {
if ((*entry->fn)(e, instr)) {
processed = true;
break;
}
entry = entry->next;
}
if (!processed) {
// No sequence found!
XELOGE("Unable to process HIR opcode %s", instr->opcode->name);
return 1;
instr = e.Advance(instr);
}
}
return 0;
}

View File

@@ -1,58 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef ALLOY_BACKEND_X64_X64_LOWERING_LOWERING_TABLE_H_
#define ALLOY_BACKEND_X64_X64_LOWERING_LOWERING_TABLE_H_
#include <alloy/core.h>
#include <alloy/hir/hir_builder.h>
namespace alloy {
namespace backend {
namespace x64 {
class X64Backend;
class X64Emitter;
namespace lowering {
class LoweringTable {
public:
LoweringTable(X64Backend* backend);
~LoweringTable();
int Initialize();
int ProcessBlock(X64Emitter& e, hir::Block* block);
public:
typedef bool(*sequence_fn_t)(X64Emitter& e, hir::Instr*& instr);
void AddSequence(hir::Opcode starting_opcode, sequence_fn_t fn);
private:
class sequence_fn_entry_t {
public:
sequence_fn_t fn;
sequence_fn_entry_t* next;
};
// NOTE: this class is shared by multiple threads and is not thread safe.
// Do not modify anything after init.
X64Backend* backend_;
sequence_fn_entry_t* lookup_[hir::__OPCODE_MAX_VALUE];
};
} // namespace lowering
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_LOWERING_LOWERING_TABLE_H_

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'lowering_sequences.cc',
'lowering_sequences.h',
'lowering_table.cc',
'lowering_table.h',
'op_utils.inl',
'tracers.cc',
'tracers.h',
],
}

View File

@@ -12,11 +12,12 @@
'x64_emitter.h',
'x64_function.cc',
'x64_function.h',
'x64_sequence.inl',
'x64_sequences.cc',
'x64_sequences.h',
'x64_thunk_emitter.cc',
'x64_thunk_emitter.h',
],
'includes': [
'lowering/sources.gypi',
'x64_tracers.cc',
'x64_tracers.h',
],
}

View File

@@ -12,26 +12,23 @@
#include <alloy/backend/x64/tracing.h>
#include <alloy/backend/x64/x64_assembler.h>
#include <alloy/backend/x64/x64_code_cache.h>
#include <alloy/backend/x64/x64_sequences.h>
#include <alloy/backend/x64/x64_thunk_emitter.h>
#include <alloy/backend/x64/lowering/lowering_table.h>
#include <alloy/backend/x64/lowering/lowering_sequences.h>
using namespace alloy;
using namespace alloy::backend;
using namespace alloy::backend::x64;
using namespace alloy::backend::x64::lowering;
using namespace alloy::runtime;
X64Backend::X64Backend(Runtime* runtime) :
code_cache_(0), lowering_table_(0),
code_cache_(0),
Backend(runtime) {
}
X64Backend::~X64Backend() {
alloy::tracing::WriteEvent(EventType::Deinit({
}));
delete lowering_table_;
delete code_cache_;
}
@@ -41,6 +38,8 @@ int X64Backend::Initialize() {
return result;
}
RegisterSequences();
machine_info_.register_sets[0] = {
0,
"gpr",
@@ -68,9 +67,6 @@ int X64Backend::Initialize() {
delete thunk_emitter;
delete allocator;
lowering_table_ = new LoweringTable(this);
RegisterSequences(lowering_table_);
alloy::tracing::WriteEvent(EventType::Init({
}));

View File

@@ -20,7 +20,6 @@ namespace backend {
namespace x64 {
class X64CodeCache;
namespace lowering { class LoweringTable; }
#define ALLOY_HAS_X64_BACKEND 1
@@ -38,8 +37,6 @@ public:
HostToGuestThunk host_to_guest_thunk() const { return host_to_guest_thunk_; }
GuestToHostThunk guest_to_host_thunk() const { return guest_to_host_thunk_; }
lowering::LoweringTable* lowering_table() const { return lowering_table_; }
virtual int Initialize();
virtual Assembler* CreateAssembler();
@@ -48,8 +45,6 @@ private:
X64CodeCache* code_cache_;
HostToGuestThunk host_to_guest_thunk_;
GuestToHostThunk guest_to_host_thunk_;
lowering::LoweringTable* lowering_table_;
};

View File

@@ -11,10 +11,14 @@
#include <alloy/backend/x64/x64_backend.h>
#include <alloy/backend/x64/x64_code_cache.h>
#include <alloy/backend/x64/x64_function.h>
#include <alloy/backend/x64/x64_sequences.h>
#include <alloy/backend/x64/x64_thunk_emitter.h>
#include <alloy/backend/x64/lowering/lowering_table.h>
#include <alloy/hir/hir_builder.h>
#include <alloy/runtime/debug_info.h>
#include <alloy/runtime/runtime.h>
#include <alloy/runtime/symbol_info.h>
#include <alloy/runtime/thread_state.h>
using namespace alloy;
using namespace alloy::backend;
@@ -31,6 +35,13 @@ namespace x64 {
static const size_t MAX_CODE_SIZE = 1 * 1024 * 1024;
static const size_t STASH_OFFSET = 32;
// If we are running with tracing on we have to store the EFLAGS in the stack,
// otherwise our calls out to C to print will clear it before DID_CARRY/etc
// can get the value.
#define STORE_EFLAGS 1
} // namespace x64
} // namespace backend
} // namespace alloy
@@ -145,12 +156,9 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
mov(qword[rsp + StackLayout::GUEST_RCX_HOME], rcx);
mov(qword[rsp + StackLayout::GUEST_RET_ADDR], rdx);
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], 0);
// ReloadRDX:
mov(rdx, qword[rcx + 8]); // membase
}
auto lowering_table = backend_->lowering_table();
// Body.
auto block = builder->first_block();
while (block) {
@@ -161,12 +169,17 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
label = label->next;
}
// Add instructions.
// The table will process sequences of instructions to (try to)
// generate optimal code.
current_instr_ = block->instr_head;
if (lowering_table->ProcessBlock(*this, block)) {
return 1;
// Process instructions.
const Instr* instr = block->instr_head;
while (instr) {
const Instr* new_tail = instr;
if (!SelectSequence(*this, instr, &new_tail)) {
// No sequence found!
XEASSERTALWAYS();
XELOGE("Unable to process HIR opcode %s", instr->opcode->name);
break;
}
instr = new_tail;
}
block = block->next;
@@ -191,16 +204,320 @@ int X64Emitter::Emit(HIRBuilder* builder, size_t& out_stack_size) {
return 0;
}
Instr* X64Emitter::Advance(Instr* i) {
auto next = i->next;
current_instr_ = next;
return next;
}
void X64Emitter::MarkSourceOffset(Instr* i) {
void X64Emitter::MarkSourceOffset(const Instr* i) {
auto entry = source_map_arena_.Alloc<SourceMapEntry>();
entry->source_offset = i->src1.offset;
entry->hir_offset = uint32_t(i->block->ordinal << 16) | i->ordinal;
entry->code_offset = getSize();
source_map_count_++;
}
void X64Emitter::DebugBreak() {
// TODO(benvanik): notify debugger.
db(0xCC);
}
void X64Emitter::Trap() {
// TODO(benvanik): notify debugger.
db(0xCC);
}
void X64Emitter::UnimplementedInstr(const hir::Instr* i) {
// TODO(benvanik): notify debugger.
db(0xCC);
XEASSERTALWAYS();
}
uint64_t ResolveFunctionSymbol(void* raw_context, uint64_t symbol_info_ptr) {
// TODO(benvanik): generate this thunk at runtime? or a shim?
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
Function* fn = NULL;
thread_state->runtime()->ResolveFunction(symbol_info->address(), &fn);
XEASSERTNOTNULL(fn);
auto x64_fn = static_cast<X64Function*>(fn);
return reinterpret_cast<uint64_t>(x64_fn->machine_code());
}
void X64Emitter::Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_info) {
auto fn = reinterpret_cast<X64Function*>(symbol_info->function());
// Resolve address to the function to call and store in rax.
// TODO(benvanik): caching/etc. For now this makes debugging easier.
if (fn) {
mov(rax, reinterpret_cast<uint64_t>(fn->machine_code()));
} else {
CallNative(ResolveFunctionSymbol, reinterpret_cast<uint64_t>(symbol_info));
}
// Actually jump/call to rax.
if (instr->flags & CALL_TAIL) {
// Pass the callers return address over.
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
add(rsp, static_cast<uint32_t>(stack_size()));
jmp(rax);
} else {
// Return address is from the previous SET_RETURN_ADDRESS.
mov(rdx, qword[rsp + StackLayout::GUEST_CALL_RET_ADDR]);
call(rax);
}
}
uint64_t ResolveFunctionAddress(void* raw_context, uint64_t target_address) {
// TODO(benvanik): generate this thunk at runtime? or a shim?
auto thread_state = *reinterpret_cast<ThreadState**>(raw_context);
// TODO(benvanik): required?
target_address &= 0xFFFFFFFF;
Function* fn = NULL;
thread_state->runtime()->ResolveFunction(target_address, &fn);
XEASSERTNOTNULL(fn);
auto x64_fn = static_cast<X64Function*>(fn);
return reinterpret_cast<uint64_t>(x64_fn->machine_code());
}
void X64Emitter::CallIndirect(const hir::Instr* instr, const Reg64& reg) {
// Check if return.
if (instr->flags & CALL_POSSIBLE_RETURN) {
cmp(reg.cvt32(), dword[rsp + StackLayout::GUEST_RET_ADDR]);
je("epilog", CodeGenerator::T_NEAR);
}
// Resolve address to the function to call and store in rax.
// TODO(benvanik): caching/etc. For now this makes debugging easier.
if (reg.getIdx() != rdx.getIdx()) {
mov(rdx, reg);
}
CallNative(ResolveFunctionAddress);
// Actually jump/call to rax.
if (instr->flags & CALL_TAIL) {
// Pass the callers return address over.
mov(rdx, qword[rsp + StackLayout::GUEST_RET_ADDR]);
add(rsp, static_cast<uint32_t>(stack_size()));
jmp(rax);
} else {
// Return address is from the previous SET_RETURN_ADDRESS.
mov(rdx, qword[rsp + StackLayout::GUEST_CALL_RET_ADDR]);
call(rax);
}
}
uint64_t UndefinedCallExtern(void* raw_context, uint64_t symbol_info_ptr) {
auto symbol_info = reinterpret_cast<FunctionInfo*>(symbol_info_ptr);
XELOGW("undefined extern call to %.8X %s",
symbol_info->address(),
symbol_info->name());
return 0;
}
void X64Emitter::CallExtern(const hir::Instr* instr, const FunctionInfo* symbol_info) {
XEASSERT(symbol_info->behavior() == FunctionInfo::BEHAVIOR_EXTERN);
if (!symbol_info->extern_handler()) {
CallNative(UndefinedCallExtern, reinterpret_cast<uint64_t>(symbol_info));
} else {
// rcx = context
// rdx = target host function
// r8 = arg0
// r9 = arg1
mov(rdx, reinterpret_cast<uint64_t>(symbol_info->extern_handler()));
mov(r8, reinterpret_cast<uint64_t>(symbol_info->extern_arg0()));
mov(r9, reinterpret_cast<uint64_t>(symbol_info->extern_arg1()));
auto thunk = backend()->guest_to_host_thunk();
mov(rax, reinterpret_cast<uint64_t>(thunk));
call(rax);
ReloadECX();
ReloadEDX();
// rax = host return
}
}
void X64Emitter::CallNative(void* fn) {
mov(rax, reinterpret_cast<uint64_t>(fn));
call(rax);
ReloadECX();
ReloadEDX();
}
void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context)) {
mov(rax, reinterpret_cast<uint64_t>(fn));
call(rax);
ReloadECX();
ReloadEDX();
}
void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0)) {
mov(rax, reinterpret_cast<uint64_t>(fn));
call(rax);
ReloadECX();
ReloadEDX();
}
void X64Emitter::CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0), uint64_t arg0) {
mov(rdx, arg0);
mov(rax, reinterpret_cast<uint64_t>(fn));
call(rax);
ReloadECX();
ReloadEDX();
}
void X64Emitter::SetReturnAddress(uint64_t value) {
mov(qword[rsp + StackLayout::GUEST_CALL_RET_ADDR], value);
}
void X64Emitter::ReloadECX() {
mov(rcx, qword[rsp + StackLayout::GUEST_RCX_HOME]);
}
void X64Emitter::ReloadEDX() {
mov(rdx, qword[rcx + 8]); // membase
}
void X64Emitter::LoadEflags() {
#if STORE_EFLAGS
mov(eax, dword[rsp + STASH_OFFSET]);
push(rax);
popf();
#else
// EFLAGS already present.
#endif // STORE_EFLAGS
}
void X64Emitter::StoreEflags() {
#if STORE_EFLAGS
pushf();
pop(qword[rsp + STASH_OFFSET]);
#else
// EFLAGS should have CA set?
// (so long as we don't fuck with it)
#endif // STORE_EFLAGS
}
bool X64Emitter::ConstantFitsIn32Reg(uint64_t v) {
if ((v & ~0x7FFFFFFF) == 0) {
// Fits under 31 bits, so just load using normal mov.
return true;
} else if ((v & ~0x7FFFFFFF) == ~0x7FFFFFFF) {
// Negative number that fits in 32bits.
return true;
}
return false;
}
void X64Emitter::MovMem64(const RegExp& addr, uint64_t v) {
if ((v & ~0x7FFFFFFF) == 0) {
// Fits under 31 bits, so just load using normal mov.
mov(qword[addr], v);
} else if ((v & ~0x7FFFFFFF) == ~0x7FFFFFFF) {
// Negative number that fits in 32bits.
mov(qword[addr], v);
} else if (!(v >> 32)) {
// All high bits are zero. It'd be nice if we had a way to load a 32bit
// immediate without sign extending!
// TODO(benvanik): this is super common, find a better way.
mov(dword[addr], static_cast<uint32_t>(v));
mov(dword[addr + 4], 0);
} else {
// 64bit number that needs double movs.
mov(dword[addr], static_cast<uint32_t>(v));
mov(dword[addr + 4], static_cast<uint32_t>(v >> 32));
}
}
Address X64Emitter::GetXmmConstPtr(XmmConst id) {
static const vec128_t xmm_consts[] = {
/* XMMZero */ vec128f(0.0f, 0.0f, 0.0f, 0.0f),
/* XMMOne */ vec128f(1.0f, 1.0f, 1.0f, 1.0f),
/* XMMNegativeOne */ vec128f(-1.0f, -1.0f, -1.0f, -1.0f),
/* XMMMaskX16Y16 */ vec128i(0x0000FFFF, 0xFFFF0000, 0x00000000, 0x00000000),
/* XMMFlipX16Y16 */ vec128i(0x00008000, 0x00000000, 0x00000000, 0x00000000),
/* XMMFixX16Y16 */ vec128f(-32768.0f, 0.0f, 0.0f, 0.0f),
/* XMMNormalizeX16Y16 */ vec128f(1.0f / 32767.0f, 1.0f / (32767.0f * 65536.0f), 0.0f, 0.0f),
/* XMM3301 */ vec128f(3.0f, 3.0f, 0.0f, 1.0f),
/* XMMSignMaskPS */ vec128i(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u),
/* XMMSignMaskPD */ vec128i(0x00000000u, 0x80000000u, 0x00000000u, 0x80000000u),
/* XMMByteSwapMask */ vec128i(0x00010203u, 0x04050607u, 0x08090A0Bu, 0x0C0D0E0Fu),
/* XMMPermuteControl15 */ vec128b(15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15),
/* XMMUnpackD3DCOLOR */ vec128i(0xFFFFFF02, 0xFFFFFF01, 0xFFFFFF00, 0xFFFFFF02),
/* XMMOneOver255 */ vec128f(1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f, 1.0f / 255.0f),
/* XMMShiftMaskPS */ vec128i(0x0000001Fu, 0x0000001Fu, 0x0000001Fu, 0x0000001Fu),
/* XMMOneMask */ vec128i(0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu, 0xFFFFFFFFu),
};
// TODO(benvanik): cache base pointer somewhere? stack? It'd be nice to
// prevent this move.
// TODO(benvanik): move to predictable location in PPCContext? could then
// just do rcx relative addression with no rax overwriting.
mov(rax, (uint64_t)&xmm_consts[id]);
return ptr[rax];
}
void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v) {
// http://www.agner.org/optimize/optimizing_assembly.pdf
// 13.4 Generating constants
if (!v.low && !v.high) {
// 0000...
vpxor(dest, dest);
} else if (v.low == ~0ull && v.high == ~0ull) {
// 1111...
vmovaps(dest, GetXmmConstPtr(XMMOneMask));
} else {
// TODO(benvanik): see what other common values are.
// TODO(benvanik): build constant table - 99% are reused.
MovMem64(rsp + STASH_OFFSET, v.low);
MovMem64(rsp + STASH_OFFSET + 8, v.high);
vmovdqa(dest, ptr[rsp + STASH_OFFSET]);
}
}
void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, float v) {
union {
float f;
uint32_t i;
} x = { v };
if (!v) {
// 0
vpxor(dest, dest);
} else if (x.i == ~0UL) {
// 1111...
vmovaps(dest, GetXmmConstPtr(XMMOneMask));
} else {
// TODO(benvanik): see what other common values are.
// TODO(benvanik): build constant table - 99% are reused.
mov(eax, x.i);
vmovd(dest, eax);
}
}
void X64Emitter::LoadConstantXmm(Xbyak::Xmm dest, double v) {
union {
double d;
uint64_t i;
} x = { v };
if (!v) {
// 0
vpxor(dest, dest);
} else if (x.i == ~0ULL) {
// 1111...
vmovaps(dest, GetXmmConstPtr(XMMOneMask));
} else {
// TODO(benvanik): see what other common values are.
// TODO(benvanik): build constant table - 99% are reused.
mov(rax, x.i);
vmovq(dest, rax);
}
}
Address X64Emitter::StashXmm(const Xmm& r) {
auto addr = ptr[rsp + STASH_OFFSET];
vmovups(addr, r);
return addr;
}
Address X64Emitter::StashXmm(const vec128_t& v) {
auto addr = ptr[rsp + STASH_OFFSET];
LoadConstantXmm(xmm0, v);
vmovups(addr, xmm0);
return addr;
}

View File

@@ -19,7 +19,9 @@
XEDECLARECLASS2(alloy, hir, HIRBuilder);
XEDECLARECLASS2(alloy, hir, Instr);
XEDECLARECLASS2(alloy, runtime, DebugInfo);
XEDECLARECLASS2(alloy, runtime, FunctionInfo);
XEDECLARECLASS2(alloy, runtime, Runtime);
XEDECLARECLASS2(alloy, runtime, SymbolInfo);
namespace alloy {
namespace backend {
@@ -33,6 +35,25 @@ enum RegisterFlags {
REG_ABCD = (1 << 1),
};
enum XmmConst {
XMMZero = 0,
XMMOne = 1,
XMMNegativeOne = 2,
XMMMaskX16Y16 = 3,
XMMFlipX16Y16 = 4,
XMMFixX16Y16 = 5,
XMMNormalizeX16Y16 = 6,
XMM3301 = 7,
XMMSignMaskPS = 8,
XMMSignMaskPD = 9,
XMMByteSwapMask = 10,
XMMPermuteControl15 = 11,
XMMUnpackD3DCOLOR = 12,
XMMOneOver255 = 13,
XMMShiftMaskPS = 14,
XMMOneMask = 15,
};
// Unfortunately due to the design of xbyak we have to pass this to the ctor.
class XbyakAllocator : public Xbyak::Allocator {
public:
@@ -54,79 +75,68 @@ public:
void*& out_code_address, size_t& out_code_size);
public:
template<typename V0>
void BeginOp(hir::Value* v0, V0& r0, uint32_t r0_flags) {
SetupReg(v0, r0);
}
template<typename V0, typename V1>
void BeginOp(hir::Value* v0, V0& r0, uint32_t r0_flags,
hir::Value* v1, V1& r1, uint32_t r1_flags) {
SetupReg(v0, r0);
SetupReg(v1, r1);
}
template<typename V0, typename V1, typename V2>
void BeginOp(hir::Value* v0, V0& r0, uint32_t r0_flags,
hir::Value* v1, V1& r1, uint32_t r1_flags,
hir::Value* v2, V2& r2, uint32_t r2_flags) {
SetupReg(v0, r0);
SetupReg(v1, r1);
SetupReg(v2, r2);
}
template<typename V0, typename V1, typename V2, typename V3>
void BeginOp(hir::Value* v0, V0& r0, uint32_t r0_flags,
hir::Value* v1, V1& r1, uint32_t r1_flags,
hir::Value* v2, V2& r2, uint32_t r2_flags,
hir::Value* v3, V3& r3, uint32_t r3_flags) {
SetupReg(v0, r0);
SetupReg(v1, r1);
SetupReg(v2, r2);
SetupReg(v3, r3);
}
template<typename V0>
void EndOp(V0& r0) {
}
template<typename V0, typename V1>
void EndOp(V0& r0, V1& r1) {
}
template<typename V0, typename V1, typename V2>
void EndOp(V0& r0, V1& r1, V2& r2) {
}
template<typename V0, typename V1, typename V2, typename V3>
void EndOp(V0& r0, V1& r1, V2& r2, V3& r3) {
}
// Reserved: rsp
// Scratch: rax/rcx/rdx
// xmm0-1
// xmm0-2 (could be only xmm0 with some trickery)
// Available: rbx, r12-r15 (save to get r8-r11, rbp, rsi, rdi?)
// xmm6-xmm15 (save to get xmm2-xmm5)
// xmm6-xmm15 (save to get xmm3-xmm5)
static const int GPR_COUNT = 5;
static const int XMM_COUNT = 10;
static void SetupReg(hir::Value* v, Xbyak::Reg8& r) {
static void SetupReg(const hir::Value* v, Xbyak::Reg8& r) {
auto idx = gpr_reg_map_[v->reg.index];
r = Xbyak::Reg8(idx);
}
static void SetupReg(hir::Value* v, Xbyak::Reg16& r) {
static void SetupReg(const hir::Value* v, Xbyak::Reg16& r) {
auto idx = gpr_reg_map_[v->reg.index];
r = Xbyak::Reg16(idx);
}
static void SetupReg(hir::Value* v, Xbyak::Reg32& r) {
static void SetupReg(const hir::Value* v, Xbyak::Reg32& r) {
auto idx = gpr_reg_map_[v->reg.index];
r = Xbyak::Reg32(idx);
}
static void SetupReg(hir::Value* v, Xbyak::Reg64& r) {
static void SetupReg(const hir::Value* v, Xbyak::Reg64& r) {
auto idx = gpr_reg_map_[v->reg.index];
r = Xbyak::Reg64(idx);
}
static void SetupReg(hir::Value* v, Xbyak::Xmm& r) {
static void SetupReg(const hir::Value* v, Xbyak::Xmm& r) {
auto idx = xmm_reg_map_[v->reg.index];
r = Xbyak::Xmm(idx);
}
hir::Instr* Advance(hir::Instr* i);
void MarkSourceOffset(const hir::Instr* i);
void MarkSourceOffset(hir::Instr* i);
void DebugBreak();
void Trap();
void UnimplementedInstr(const hir::Instr* i);
void UnimplementedExtern(const hir::Instr* i);
void Call(const hir::Instr* instr, runtime::FunctionInfo* symbol_info);
void CallIndirect(const hir::Instr* instr, const Xbyak::Reg64& reg);
void CallExtern(const hir::Instr* instr, const runtime::FunctionInfo* symbol_info);
void CallNative(void* fn);
void CallNative(uint64_t(*fn)(void* raw_context));
void CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0));
void CallNative(uint64_t(*fn)(void* raw_context, uint64_t arg0), uint64_t arg0);
void SetReturnAddress(uint64_t value);
void ReloadECX();
void ReloadEDX();
// TODO(benvanik): Label for epilog (don't use strings).
void LoadEflags();
void StoreEflags();
// Moves a 64bit immediate into memory.
bool ConstantFitsIn32Reg(uint64_t v);
void MovMem64(const Xbyak::RegExp& addr, uint64_t v);
Xbyak::Address GetXmmConstPtr(XmmConst id);
void LoadConstantXmm(Xbyak::Xmm dest, float v);
void LoadConstantXmm(Xbyak::Xmm dest, double v);
void LoadConstantXmm(Xbyak::Xmm dest, const vec128_t& v);
Xbyak::Address StashXmm(const Xbyak::Xmm& r);
Xbyak::Address StashXmm(const vec128_t& v);
size_t stack_size() const { return stack_size_; }

View File

@@ -0,0 +1,714 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
namespace {
enum KeyType {
KEY_TYPE_X = OPCODE_SIG_TYPE_X,
KEY_TYPE_L = OPCODE_SIG_TYPE_L,
KEY_TYPE_O = OPCODE_SIG_TYPE_O,
KEY_TYPE_S = OPCODE_SIG_TYPE_S,
KEY_TYPE_V_I8 = OPCODE_SIG_TYPE_V + INT8_TYPE,
KEY_TYPE_V_I16 = OPCODE_SIG_TYPE_V + INT16_TYPE,
KEY_TYPE_V_I32 = OPCODE_SIG_TYPE_V + INT32_TYPE,
KEY_TYPE_V_I64 = OPCODE_SIG_TYPE_V + INT64_TYPE,
KEY_TYPE_V_F32 = OPCODE_SIG_TYPE_V + FLOAT32_TYPE,
KEY_TYPE_V_F64 = OPCODE_SIG_TYPE_V + FLOAT64_TYPE,
KEY_TYPE_V_V128 = OPCODE_SIG_TYPE_V + VEC128_TYPE,
};
#pragma pack(push, 1)
union InstrKey {
struct {
uint32_t opcode : 8;
uint32_t dest : 5;
uint32_t src1 : 5;
uint32_t src2 : 5;
uint32_t src3 : 5;
uint32_t reserved : 4;
};
uint32_t value;
operator uint32_t() const {
return value;
}
InstrKey() : value(0) {}
InstrKey(uint32_t v) : value(v) {}
InstrKey(const Instr* i) : value(0) {
opcode = i->opcode->num;
uint32_t sig = i->opcode->signature;
dest = GET_OPCODE_SIG_TYPE_DEST(sig) ? OPCODE_SIG_TYPE_V + i->dest->type : 0;
src1 = GET_OPCODE_SIG_TYPE_SRC1(sig);
if (src1 == OPCODE_SIG_TYPE_V) {
src1 += i->src1.value->type;
}
src2 = GET_OPCODE_SIG_TYPE_SRC2(sig);
if (src2 == OPCODE_SIG_TYPE_V) {
src2 += i->src2.value->type;
}
src3 = GET_OPCODE_SIG_TYPE_SRC3(sig);
if (src3 == OPCODE_SIG_TYPE_V) {
src3 += i->src3.value->type;
}
}
template <Opcode OPCODE,
KeyType DEST = KEY_TYPE_X,
KeyType SRC1 = KEY_TYPE_X,
KeyType SRC2 = KEY_TYPE_X,
KeyType SRC3 = KEY_TYPE_X>
struct Construct {
static const uint32_t value =
(OPCODE) | (DEST << 8) | (SRC1 << 13) | (SRC2 << 18) | (SRC3 << 23);
};
};
#pragma pack(pop)
static_assert(sizeof(InstrKey) <= 4, "Key must be 4 bytes");
template <typename... Ts>
struct CombinedStruct;
template <>
struct CombinedStruct<> {};
template <typename T, typename... Ts>
struct CombinedStruct<T, Ts...> : T, CombinedStruct<Ts...> {};
struct OpBase {};
template <typename T, KeyType KEY_TYPE>
struct Op : OpBase {
static const KeyType key_type = KEY_TYPE;
};
struct VoidOp : Op<VoidOp, KEY_TYPE_X> {
protected:
template <typename T, KeyType KEY_TYPE> friend struct Op;
template <hir::Opcode OPCODE, typename... Ts> friend struct I;
void Load(const Instr::Op& op) {}
};
struct OffsetOp : Op<OffsetOp, KEY_TYPE_O> {
uint64_t value;
protected:
template <typename T, KeyType KEY_TYPE> friend struct Op;
template <hir::Opcode OPCODE, typename... Ts> friend struct I;
void Load(const Instr::Op& op) {
this->value = op.offset;
}
};
struct SymbolOp : Op<SymbolOp, KEY_TYPE_S> {
FunctionInfo* value;
protected:
template <typename T, KeyType KEY_TYPE> friend struct Op;
template <hir::Opcode OPCODE, typename... Ts> friend struct I;
bool Load(const Instr::Op& op) {
this->value = op.symbol_info;
return true;
}
};
struct LabelOp : Op<LabelOp, KEY_TYPE_L> {
hir::Label* value;
protected:
template <typename T, KeyType KEY_TYPE> friend struct Op;
template <hir::Opcode OPCODE, typename... Ts> friend struct I;
void Load(const Instr::Op& op) {
this->value = op.label;
}
};
template <typename T, KeyType KEY_TYPE, typename REG_TYPE, typename CONST_TYPE, int TAG = -1>
struct ValueOp : Op<ValueOp<T, KEY_TYPE, REG_TYPE, CONST_TYPE, TAG>, KEY_TYPE> {
typedef REG_TYPE reg_type;
static const int tag = TAG;
const Value* value;
bool is_constant;
virtual bool ConstantFitsIn32Reg() const { return true; }
const REG_TYPE& reg() const {
XEASSERT(!is_constant);
return reg_;
}
operator const REG_TYPE&() const {
return reg();
}
bool IsEqual(const T& b) const {
if (is_constant && b.is_constant) {
return reinterpret_cast<const T*>(this)->constant() == b.constant();
} else if (!is_constant && !b.is_constant) {
return reg_.getIdx() == b.reg_.getIdx();
} else {
return false;
}
}
bool IsEqual(const Xbyak::Reg& b) const {
if (is_constant) {
return false;
} else if (!is_constant) {
return reg_.getIdx() == b.getIdx();
} else {
return false;
}
}
bool operator== (const T& b) const {
return IsEqual(b);
}
bool operator!= (const T& b) const {
return !IsEqual(b);
}
bool operator== (const Xbyak::Reg& b) const {
return IsEqual(b);
}
bool operator!= (const Xbyak::Reg& b) const {
return !IsEqual(b);
}
void Load(const Instr::Op& op) {
const Value* value = op.value;
this->value = value;
is_constant = value->IsConstant();
if (!is_constant) {
X64Emitter::SetupReg(value, reg_);
}
}
protected:
REG_TYPE reg_;
};
template <int TAG = -1>
struct I8 : ValueOp<I8<TAG>, KEY_TYPE_V_I8, Reg8, int8_t, TAG> {
const int8_t constant() const {
XEASSERT(is_constant);
return value->constant.i8;
}
};
template <int TAG = -1>
struct I16 : ValueOp<I16<TAG>, KEY_TYPE_V_I16, Reg16, int16_t, TAG> {
const int16_t constant() const {
XEASSERT(is_constant);
return value->constant.i16;
}
};
template <int TAG = -1>
struct I32 : ValueOp<I32<TAG>, KEY_TYPE_V_I32, Reg32, int32_t, TAG> {
const int32_t constant() const {
XEASSERT(is_constant);
return value->constant.i32;
}
};
template <int TAG = -1>
struct I64 : ValueOp<I64<TAG>, KEY_TYPE_V_I64, Reg64, int64_t, TAG> {
const int64_t constant() const {
XEASSERT(is_constant);
return value->constant.i64;
}
bool ConstantFitsIn32Reg() const override {
int64_t v = value->constant.i64;
if ((v & ~0x7FFFFFFF) == 0) {
// Fits under 31 bits, so just load using normal mov.
return true;
} else if ((v & ~0x7FFFFFFF) == ~0x7FFFFFFF) {
// Negative number that fits in 32bits.
return true;
}
return false;
}
};
template <int TAG = -1>
struct F32 : ValueOp<F32<TAG>, KEY_TYPE_V_F32, Xmm, float, TAG> {
const float constant() const {
XEASSERT(is_constant);
return value->constant.f32;
}
};
template <int TAG = -1>
struct F64 : ValueOp<F64<TAG>, KEY_TYPE_V_F64, Xmm, double, TAG> {
const double constant() const {
XEASSERT(is_constant);
return value->constant.f64;
}
};
template <int TAG = -1>
struct V128 : ValueOp<V128<TAG>, KEY_TYPE_V_V128, Xmm, vec128_t, TAG> {
const vec128_t& constant() const {
XEASSERT(is_constant);
return value->constant.v128;
}
};
struct TagTable {
struct {
bool valid;
Instr::Op op;
} table[16];
template <typename T, typename std::enable_if<T::key_type == KEY_TYPE_X>::type* = nullptr>
bool CheckTag(const Instr::Op& op) {
return true;
}
template <typename T, typename std::enable_if<T::key_type == KEY_TYPE_L>::type* = nullptr>
bool CheckTag(const Instr::Op& op) {
return true;
}
template <typename T, typename std::enable_if<T::key_type == KEY_TYPE_O>::type* = nullptr>
bool CheckTag(const Instr::Op& op) {
return true;
}
template <typename T, typename std::enable_if<T::key_type == KEY_TYPE_S>::type* = nullptr>
bool CheckTag(const Instr::Op& op) {
return true;
}
template <typename T, typename std::enable_if<T::key_type >= KEY_TYPE_V_I8>::type* = nullptr>
bool CheckTag(const Instr::Op& op) {
const Value* value = op.value;
if (T::tag == -1) {
return true;
}
if (table[T::tag].valid &&
table[T::tag].op.value != value) {
return false;
}
table[T::tag].valid = true;
table[T::tag].op.value = (Value*)value;
return true;
}
};
template <typename DEST, typename... Tf>
struct DestField;
template <typename DEST>
struct DestField<DEST> {
DEST dest;
protected:
bool LoadDest(const Instr* i, TagTable& tag_table) {
Instr::Op op;
op.value = i->dest;
if (tag_table.CheckTag<DEST>(op)) {
dest.Load(op);
return true;
}
return false;
}
};
template <>
struct DestField<VoidOp> {
protected:
bool LoadDest(const Instr* i, TagTable& tag_table) {
return true;
}
};
template <hir::Opcode OPCODE, typename... Ts>
struct I;
template <hir::Opcode OPCODE, typename DEST>
struct I<OPCODE, DEST> : DestField<DEST> {
static const hir::Opcode opcode = OPCODE;
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type>::value;
static const KeyType dest_type = DEST::key_type;
const Instr* instr;
protected:
template <typename... Ti> friend struct SequenceFields;
bool Load(const Instr* i, TagTable& tag_table) {
if (InstrKey(i).value == key &&
LoadDest(i, tag_table)) {
instr = i;
return true;
}
return false;
}
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1>
struct I<OPCODE, DEST, SRC1> : DestField<DEST> {
static const hir::Opcode opcode = OPCODE;
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type>::value;
static const KeyType dest_type = DEST::key_type;
static const KeyType src1_type = SRC1::key_type;
const Instr* instr;
SRC1 src1;
protected:
template <typename... Ti> friend struct SequenceFields;
bool Load(const Instr* i, TagTable& tag_table) {
if (InstrKey(i).value == key &&
LoadDest(i, tag_table) &&
tag_table.CheckTag<SRC1>(i->src1)) {
instr = i;
src1.Load(i->src1);
return true;
}
return false;
}
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2>
struct I<OPCODE, DEST, SRC1, SRC2> : DestField<DEST> {
static const hir::Opcode opcode = OPCODE;
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type, SRC2::key_type>::value;
static const KeyType dest_type = DEST::key_type;
static const KeyType src1_type = SRC1::key_type;
static const KeyType src2_type = SRC2::key_type;
const Instr* instr;
SRC1 src1;
SRC2 src2;
protected:
template <typename... Ti> friend struct SequenceFields;
bool Load(const Instr* i, TagTable& tag_table) {
if (InstrKey(i).value == key &&
LoadDest(i, tag_table) &&
tag_table.CheckTag<SRC1>(i->src1) &&
tag_table.CheckTag<SRC2>(i->src2)) {
instr = i;
src1.Load(i->src1);
src2.Load(i->src2);
return true;
}
return false;
}
};
template <hir::Opcode OPCODE, typename DEST, typename SRC1, typename SRC2, typename SRC3>
struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
static const hir::Opcode opcode = OPCODE;
static const uint32_t key = InstrKey::Construct<OPCODE, DEST::key_type, SRC1::key_type, SRC2::key_type, SRC3::key_type>::value;
static const KeyType dest_type = DEST::key_type;
static const KeyType src1_type = SRC1::key_type;
static const KeyType src2_type = SRC2::key_type;
static const KeyType src3_type = SRC3::key_type;
const Instr* instr;
SRC1 src1;
SRC2 src2;
SRC3 src3;
protected:
template <typename... Ti> friend struct SequenceFields;
bool Load(const Instr* i, TagTable& tag_table) {
if (InstrKey(i).value == key &&
LoadDest(i, tag_table) &&
tag_table.CheckTag<SRC1>(i->src1) &&
tag_table.CheckTag<SRC2>(i->src2) &&
tag_table.CheckTag<SRC3>(i->src3)) {
instr = i;
src1.Load(i->src1);
src2.Load(i->src2);
src3.Load(i->src3);
return true;
}
return false;
}
};
template <typename... Ti>
struct SequenceFields;
template <typename I1>
struct SequenceFields<I1> {
I1 i1;
typedef typename I1 I1Type;
protected:
template <typename SEQ, typename... Ti> friend struct Sequence;
bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
if (i1.Load(i, tag_table)) {
*new_tail = i->next;
return true;
}
return false;
}
};
template <typename I1, typename I2>
struct SequenceFields<I1, I2> : SequenceFields<I1> {
I2 i2;
protected:
template <typename SEQ, typename... Ti> friend struct Sequence;
bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
if (SequenceFields<I1>::Check(i, tag_table, new_tail)) {
auto ni = i->next;
if (ni && i2.Load(ni, tag_table)) {
*new_tail = ni;
return i;
}
}
return false;
}
};
template <typename I1, typename I2, typename I3>
struct SequenceFields<I1, I2, I3> : SequenceFields<I1, I2> {
I3 i3;
protected:
template <typename SEQ, typename... Ti> friend struct Sequence;
bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
if (SequenceFields<I1, I2>::Check(i, tag_table, new_tail)) {
auto ni = i->next;
if (ni && i3.Load(ni, tag_table)) {
*new_tail = ni;
return i;
}
}
return false;
}
};
template <typename I1, typename I2, typename I3, typename I4>
struct SequenceFields<I1, I2, I3, I4> : SequenceFields<I1, I2, I3> {
I4 i4;
protected:
template <typename SEQ, typename... Ti> friend struct Sequence;
bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
if (SequenceFields<I1, I2, I3>::Check(i, tag_table, new_tail)) {
auto ni = i->next;
if (ni && i4.Load(ni, tag_table)) {
*new_tail = ni;
return i;
}
}
return false;
}
};
template <typename I1, typename I2, typename I3, typename I4, typename I5>
struct SequenceFields<I1, I2, I3, I4, I5> : SequenceFields<I1, I2, I3, I4> {
I5 i5;
protected:
template <typename SEQ, typename... Ti> friend struct Sequence;
bool Check(const Instr* i, TagTable& tag_table, const Instr** new_tail) {
if (SequenceFields<I1, I2, I3, I4>::Check(i, tag_table, new_tail)) {
auto ni = i->next;
if (ni && i5.Load(ni, tag_table)) {
*new_tail = ni;
return i;
}
}
return false;
}
};
template <typename SEQ, typename... Ti>
struct Sequence {
struct EmitArgs : SequenceFields<Ti...> {};
static bool Select(X64Emitter& e, const Instr* i, const Instr** new_tail) {
EmitArgs args;
TagTable tag_table;
if (!args.Check(i, tag_table, new_tail)) {
return false;
}
SEQ::Emit(e, args);
return true;
}
};
template <typename T>
const T GetTempReg(X64Emitter& e);
template <>
const Reg8 GetTempReg<Reg8>(X64Emitter& e) {
return e.al;
}
template <>
const Reg16 GetTempReg<Reg16>(X64Emitter& e) {
return e.ax;
}
template <>
const Reg32 GetTempReg<Reg32>(X64Emitter& e) {
return e.eax;
}
template <>
const Reg64 GetTempReg<Reg64>(X64Emitter& e) {
return e.rax;
}
template <typename SEQ, typename T>
struct SingleSequence : public Sequence<SingleSequence<SEQ, T>, T> {
typedef T EmitArgType;
static const uint32_t head_key = T::key;
static void Emit(X64Emitter& e, const EmitArgs& _) {
SEQ::Emit(e, _.i1);
}
template <typename REG_FN>
static void EmitUnaryOp(
X64Emitter& e, const EmitArgType& i,
const REG_FN& reg_fn) {
if (i.src1.is_constant) {
e.mov(i.dest, i.src1.constant());
reg_fn(e, i.dest);
} else {
if (i.dest != i.src1) {
e.mov(i.dest, i.src1);
}
reg_fn(e, i.dest);
}
}
template <typename REG_REG_FN, typename REG_CONST_FN>
static void EmitCommutativeBinaryOp(
X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
if (i.dest == i.src2) {
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src1.constant()));
} else {
auto temp = GetTempReg<decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.dest, temp);
}
} else {
e.mov(i.dest, i.src1.constant());
reg_reg_fn(e, i.dest, i.src2);
}
} else if (i.src2.is_constant) {
if (i.dest == i.src1) {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
} else {
e.mov(i.dest, i.src2.constant());
reg_reg_fn(e, i.dest, i.src1);
}
} else {
if (i.dest == i.src1) {
reg_reg_fn(e, i.dest, i.src2);
} else if (i.dest == i.src2) {
reg_reg_fn(e, i.dest, i.src1);
} else {
e.mov(i.dest, i.src1);
reg_reg_fn(e, i.dest, i.src2);
}
}
}
template <typename REG_REG_FN, typename REG_CONST_FN>
static void EmitAssociativeBinaryOp(
X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
if (i.dest == i.src2) {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2);
e.mov(i.dest, i.src1.constant());
reg_reg_fn(e, i.dest, temp);
} else {
e.mov(i.dest, i.src1.constant());
reg_reg_fn(e, i.dest, i.src2);
}
} else if (i.src2.is_constant) {
if (i.dest == i.src1) {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
} else {
e.mov(i.dest, i.src1);
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, temp);
}
}
} else {
if (i.dest == i.src1) {
reg_reg_fn(e, i.dest, i.src2);
} else if (i.dest == i.src2) {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2);
e.mov(i.dest, i.src1);
reg_reg_fn(e, i.dest, temp);
} else {
e.mov(i.dest, i.src1);
reg_reg_fn(e, i.dest, i.src2);
}
}
}
template <typename REG_REG_FN, typename REG_CONST_FN>
static void EmitCommutativeCompareOp(
X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.src2, static_cast<int32_t>(i.src1.constant()));
} else {
auto temp = GetTempReg<decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.src2, temp);
}
} else if (i.src2.is_constant) {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.src1, static_cast<int32_t>(i.src2.constant()));
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.src1, temp);
}
} else {
reg_reg_fn(e, i.src1, i.src2);
}
}
template <typename REG_REG_FN, typename REG_CONST_FN>
static void EmitAssociativeCompareOp(
X64Emitter& e, const EmitArgType& i,
const REG_REG_FN& reg_reg_fn, const REG_CONST_FN& reg_const_fn) {
if (i.src1.is_constant) {
XEASSERT(!i.src2.is_constant);
if (i.src1.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, i.src2, static_cast<int32_t>(i.src1.constant()), true);
} else {
auto temp = GetTempReg<decltype(i.src1)::reg_type>(e);
e.mov(temp, i.src1.constant());
reg_reg_fn(e, i.dest, i.src2, temp, true);
}
} else if (i.src2.is_constant) {
if (i.src2.ConstantFitsIn32Reg()) {
reg_const_fn(e, i.dest, i.src1, static_cast<int32_t>(i.src2.constant()), false);
} else {
auto temp = GetTempReg<decltype(i.src2)::reg_type>(e);
e.mov(temp, i.src2.constant());
reg_reg_fn(e, i.dest, i.src1, temp, false);
}
} else {
reg_reg_fn(e, i.dest, i.src1, i.src2, false);
}
}
};
static const int ANY = -1;
typedef int tag_t;
static const tag_t TAG0 = 0;
static const tag_t TAG1 = 1;
static const tag_t TAG2 = 2;
static const tag_t TAG3 = 3;
static const tag_t TAG4 = 4;
static const tag_t TAG5 = 5;
static const tag_t TAG6 = 6;
static const tag_t TAG7 = 7;
typedef bool (*SequenceSelectFn)(X64Emitter&, const Instr*, const Instr**);
template <typename T>
void Register() {
sequence_table.insert({ T::head_key, T::Select });
}
template <typename T, typename Tn, typename... Ts>
void Register() {
Register<T>();
Register<Tn, Ts...>();
};
#define EMITTER_OPCODE_TABLE(name, ...) \
void Register_##name() { \
Register<__VA_ARGS__>(); \
}
#define MATCH(...) __VA_ARGS__
#define EMITTER(name, match) struct name : SingleSequence<name, match>
#define SEQUENCE(name, match) struct name : Sequence<name, match>
} // namespace

File diff suppressed because it is too large Load Diff

View File

@@ -2,32 +2,32 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Copyright 2014 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef ALLOY_BACKEND_X64_X64_LOWERING_LOWERING_SEQUENCES_H_
#define ALLOY_BACKEND_X64_X64_LOWERING_LOWERING_SEQUENCES_H_
#ifndef ALLOY_BACKEND_X64_X64_SEQUENCES_H_
#define ALLOY_BACKEND_X64_X64_SEQUENCES_H_
#include <alloy/core.h>
#include <alloy/hir/instr.h>
XEDECLARECLASS2(alloy, hir, Instr);
namespace alloy {
namespace backend {
namespace x64 {
namespace lowering {
class LoweringTable;
void RegisterSequences(LoweringTable* table);
class X64Emitter;
void RegisterSequences();
bool SelectSequence(X64Emitter& e, const hir::Instr* i, const hir::Instr** new_tail);
} // namespace lowering
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_LOWERING_LOWERING_SEQUENCES_H_
#endif // ALLOY_BACKEND_X64_X64_SEQUENCES_H_

View File

@@ -7,7 +7,7 @@
******************************************************************************
*/
#include <alloy/backend/x64/lowering/tracers.h>
#include <alloy/backend/x64/x64_tracers.h>
#include <alloy/backend/x64/x64_emitter.h>
#include <alloy/runtime/runtime.h>
@@ -15,19 +15,14 @@
using namespace alloy;
using namespace alloy::backend::x64;
using namespace alloy::backend::x64::lowering;
using namespace alloy::runtime;
namespace alloy {
namespace backend {
namespace x64 {
namespace lowering {
#define IFLUSH()
#define IPRINT
#define DFLUSH()
#define DPRINT
#define ITRACE 0
#define DTRACE 0
#define TARGET_THREAD 1
@@ -36,6 +31,16 @@ namespace lowering {
#define DFLUSH() fflush(stdout)
#define DPRINT DFLUSH(); if (thread_state->thread_id() == TARGET_THREAD) printf
uint32_t GetTracingMode() {
uint32_t mode = 0;
#if ITRACE
mode |= TRACING_INSTR;
#endif // ITRACE
#if DTRACE
mode |= TRACING_DATA;
#endif // DTRACE
return mode;
}
void TraceString(void* raw_context, const char* str) {
auto thread_state = *((ThreadState**)raw_context);
@@ -190,7 +195,6 @@ void TraceMemoryStoreV128(void* raw_context, uint64_t address, __m128 value) {
}
} // namespace lowering
} // namespace x64
} // namespace backend
} // namespace alloy

View File

@@ -7,8 +7,8 @@
******************************************************************************
*/
#ifndef ALLOY_BACKEND_X64_X64_LOWERING_TRACERS_H_
#define ALLOY_BACKEND_X64_X64_LOWERING_TRACERS_H_
#ifndef ALLOY_BACKEND_X64_X64_TRACERS_H_
#define ALLOY_BACKEND_X64_X64_TRACERS_H_
#include <alloy/core.h>
@@ -33,7 +33,15 @@ namespace alloy {
namespace backend {
namespace x64 {
class X64Emitter;
namespace lowering {
enum TracingMode {
TRACING_INSTR = (1 << 1),
TRACING_DATA = (1 << 2),
};
uint32_t GetTracingMode();
inline bool IsTracingInstr() { return (GetTracingMode() & TRACING_INSTR) != 0; }
inline bool IsTracingData() { return (GetTracingMode() & TRACING_DATA) != 0; }
void TraceString(void* raw_context, const char* str);
@@ -69,10 +77,9 @@ void TraceMemoryStoreF32(void* raw_context, uint64_t address, __m128 value);
void TraceMemoryStoreF64(void* raw_context, uint64_t address, __m128 value);
void TraceMemoryStoreV128(void* raw_context, uint64_t address, __m128 value);
} // namespace lowering
} // namespace x64
} // namespace backend
} // namespace alloy
#endif // ALLOY_BACKEND_X64_X64_LOWERING_TRACERS_H_
#endif // ALLOY_BACKEND_X64_X64_TRACERS_H_