Pure dynamic MMIO access. Prep for more complex GPU memory management.

This commit is contained in:
Ben Vanik
2014-06-01 23:36:18 -07:00
parent 3a8065b7b1
commit 0e3854555d
19 changed files with 335 additions and 586 deletions

View File

@@ -61,7 +61,6 @@ int IVMAssembler::Assemble(
fn->set_debug_info(debug_info);
TranslationContext ctx;
ctx.access_callbacks = backend_->runtime()->access_callbacks();
ctx.register_count = 0;
ctx.intcode_count = 0;
ctx.intcode_arena = &intcode_arena_;

View File

@@ -120,7 +120,6 @@ int IVMFunction::CallImpl(ThreadState* thread_state, uint64_t return_address) {
ics.membase = memory->membase();
ics.did_carry = 0;
ics.did_saturate = 0;
ics.access_callbacks = thread_state->runtime()->access_callbacks();
ics.thread_state = thread_state;
ics.return_address = return_address;
ics.call_return_address = 0;

View File

@@ -196,213 +196,6 @@ int DispatchToC(TranslationContext& ctx, Instr* i, IntCodeFn fn) {
return 0;
}
uint32_t IntCode_LOAD_REGISTER_I8(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = (RegisterAccessCallbacks*)
(i->src2_reg | ((uint64_t)i->src3_reg << 32));
ics.rf[i->dest_reg].i8 = (int8_t)cbs->read(cbs->context, address);
return IA_NEXT;
}
uint32_t IntCode_LOAD_REGISTER_I16(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = (RegisterAccessCallbacks*)
(i->src2_reg | ((uint64_t)i->src3_reg << 32));
ics.rf[i->dest_reg].i16 = XESWAP16((int16_t)cbs->read(cbs->context, address));
return IA_NEXT;
}
uint32_t IntCode_LOAD_REGISTER_I32(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = (RegisterAccessCallbacks*)
(i->src2_reg | ((uint64_t)i->src3_reg << 32));
ics.rf[i->dest_reg].i32 = XESWAP32((int32_t)cbs->read(cbs->context, address));
return IA_NEXT;
}
uint32_t IntCode_LOAD_REGISTER_I64(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = (RegisterAccessCallbacks*)
(i->src2_reg | ((uint64_t)i->src3_reg << 32));
ics.rf[i->dest_reg].i64 = XESWAP64((int64_t)cbs->read(cbs->context, address));
return IA_NEXT;
}
int DispatchRegisterRead(
TranslationContext& ctx, Instr* i, RegisterAccessCallbacks* cbs) {
static IntCodeFn fns[] = {
IntCode_LOAD_REGISTER_I8,
IntCode_LOAD_REGISTER_I16,
IntCode_LOAD_REGISTER_I32,
IntCode_LOAD_REGISTER_I64,
IntCode_INVALID_TYPE,
IntCode_INVALID_TYPE,
IntCode_INVALID_TYPE,
};
IntCodeFn fn = fns[i->dest->type];
XEASSERT(fn != IntCode_INVALID_TYPE);
uint32_t dest_reg = AllocDynamicRegister(ctx, i->dest);
uint32_t src1_reg = AllocOpRegister(ctx, OPCODE_SIG_TYPE_V, &i->src1);
ctx.intcode_count++;
IntCode* ic = ctx.intcode_arena->Alloc<IntCode>();
ic->intcode_fn = fn;
ic->flags = i->flags;
ic->debug_flags = 0;
ic->dest_reg = dest_reg;
ic->src1_reg = src1_reg;
ic->src2_reg = (uint32_t)((uint64_t)cbs);
ic->src3_reg = (uint32_t)(((uint64_t)cbs) >> 32);
return 0;
}
uint32_t IntCode_LOAD_REGISTER_I8_DYNAMIC(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = ics.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
ics.rf[i->dest_reg].i8 = (int8_t)cbs->read(cbs->context, address);
return IA_NEXT;
}
cbs = cbs->next;
}
return IA_NEXT;
}
uint32_t IntCode_LOAD_REGISTER_I16_DYNAMIC(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = ics.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
ics.rf[i->dest_reg].i16 = XESWAP16((int16_t)cbs->read(cbs->context, address));
return IA_NEXT;
}
cbs = cbs->next;
}
return IA_NEXT;
}
uint32_t IntCode_LOAD_REGISTER_I32_DYNAMIC(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = ics.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
ics.rf[i->dest_reg].i32 = XESWAP32((int32_t)cbs->read(cbs->context, address));
return IA_NEXT;
}
cbs = cbs->next;
}
return IA_NEXT;
}
uint32_t IntCode_LOAD_REGISTER_I64_DYNAMIC(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = ics.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
ics.rf[i->dest_reg].i64 = XESWAP64((int64_t)cbs->read(cbs->context, address));
return IA_NEXT;
}
cbs = cbs->next;
}
return IA_NEXT;
}
uint32_t IntCode_STORE_REGISTER_I8(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = (RegisterAccessCallbacks*)
(i->src3_reg | ((uint64_t)i->dest_reg << 32));
cbs->write(cbs->context, address, ics.rf[i->src2_reg].i8);
return IA_NEXT;
}
uint32_t IntCode_STORE_REGISTER_I16(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = (RegisterAccessCallbacks*)
(i->src3_reg | ((uint64_t)i->dest_reg << 32));
cbs->write(cbs->context, address, XESWAP16(ics.rf[i->src2_reg].i16));
return IA_NEXT;
}
uint32_t IntCode_STORE_REGISTER_I32(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = (RegisterAccessCallbacks*)
(i->src3_reg | ((uint64_t)i->dest_reg << 32));
cbs->write(cbs->context, address, XESWAP32(ics.rf[i->src2_reg].i32));
return IA_NEXT;
}
uint32_t IntCode_STORE_REGISTER_I64(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = (RegisterAccessCallbacks*)
(i->src3_reg | ((uint64_t)i->dest_reg << 32));
cbs->write(cbs->context, address, XESWAP64(ics.rf[i->src2_reg].i64));
return IA_NEXT;
}
int DispatchRegisterWrite(
TranslationContext& ctx, Instr* i, RegisterAccessCallbacks* cbs) {
static IntCodeFn fns[] = {
IntCode_STORE_REGISTER_I8,
IntCode_STORE_REGISTER_I16,
IntCode_STORE_REGISTER_I32,
IntCode_STORE_REGISTER_I64,
IntCode_INVALID_TYPE,
IntCode_INVALID_TYPE,
IntCode_INVALID_TYPE,
};
IntCodeFn fn = fns[i->src2.value->type];
XEASSERT(fn != IntCode_INVALID_TYPE);
uint32_t src1_reg = AllocOpRegister(ctx, OPCODE_SIG_TYPE_V, &i->src1);
uint32_t src2_reg = AllocOpRegister(ctx, OPCODE_SIG_TYPE_V, &i->src2);
ctx.intcode_count++;
IntCode* ic = ctx.intcode_arena->Alloc<IntCode>();
ic->intcode_fn = fn;
ic->flags = i->flags;
ic->debug_flags = 0;
ic->dest_reg = (uint32_t)(((uint64_t)cbs) >> 32);
ic->src1_reg = src1_reg;
ic->src2_reg = src2_reg;
ic->src3_reg = (uint32_t)((uint64_t)cbs);
return 0;
}
uint32_t IntCode_STORE_REGISTER_I8_DYNAMIC(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = ics.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
cbs->write(cbs->context, address, ics.rf[i->src2_reg].i8);
return IA_NEXT;
}
cbs = cbs->next;
}
return IA_NEXT;
}
uint32_t IntCode_STORE_REGISTER_I16_DYNAMIC(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = ics.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
cbs->write(cbs->context, address, XESWAP16(ics.rf[i->src2_reg].i16));
return IA_NEXT;
}
cbs = cbs->next;
}
return IA_NEXT;
}
uint32_t IntCode_STORE_REGISTER_I32_DYNAMIC(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = ics.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
cbs->write(cbs->context, address, XESWAP32(ics.rf[i->src2_reg].i32));
return IA_NEXT;
}
cbs = cbs->next;
}
return IA_NEXT;
}
uint32_t IntCode_STORE_REGISTER_I64_DYNAMIC(IntCodeState& ics, const IntCode* i) {
uint64_t address = ics.rf[i->src1_reg].u32;
RegisterAccessCallbacks* cbs = ics.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
cbs->write(cbs->context, address, XESWAP64(ics.rf[i->src2_reg].i64));
return IA_NEXT;
}
cbs = cbs->next;
}
return IA_NEXT;
}
uint32_t IntCode_INVALID(IntCodeState& ics, const IntCode* i) {
XEASSERTALWAYS();
return IA_NEXT;
@@ -1549,7 +1342,8 @@ int Translate_STORE_CONTEXT(TranslationContext& ctx, Instr* i) {
uint32_t IntCode_LOAD_I8(IntCodeState& ics, const IntCode* i) {
uint32_t address = ics.rf[i->src1_reg].u32;
if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) {
return IntCode_LOAD_REGISTER_I8_DYNAMIC(ics, i);
ics.rf[i->dest_reg].i8 = ics.thread_state->memory()->LoadI8(address);
return IA_NEXT;
}
DPRINT("%d (%X) = load.i8 %.8X\n",
*((int8_t*)(ics.membase + address)),
@@ -1562,7 +1356,9 @@ uint32_t IntCode_LOAD_I8(IntCodeState& ics, const IntCode* i) {
uint32_t IntCode_LOAD_I16(IntCodeState& ics, const IntCode* i) {
uint32_t address = ics.rf[i->src1_reg].u32;
if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) {
return IntCode_LOAD_REGISTER_I16_DYNAMIC(ics, i);
ics.rf[i->dest_reg].i16 =
XESWAP16(ics.thread_state->memory()->LoadI16(address));
return IA_NEXT;
}
DPRINT("%d (%X) = load.i16 %.8X\n",
*((int16_t*)(ics.membase + address)),
@@ -1575,7 +1371,9 @@ uint32_t IntCode_LOAD_I16(IntCodeState& ics, const IntCode* i) {
uint32_t IntCode_LOAD_I32(IntCodeState& ics, const IntCode* i) {
uint32_t address = ics.rf[i->src1_reg].u32;
if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) {
return IntCode_LOAD_REGISTER_I32_DYNAMIC(ics, i);
ics.rf[i->dest_reg].i32 =
XESWAP32(ics.thread_state->memory()->LoadI32(address));
return IA_NEXT;
}
DFLUSH();
DPRINT("%d (%X) = load.i32 %.8X\n",
@@ -1589,7 +1387,9 @@ uint32_t IntCode_LOAD_I32(IntCodeState& ics, const IntCode* i) {
uint32_t IntCode_LOAD_I64(IntCodeState& ics, const IntCode* i) {
uint32_t address = ics.rf[i->src1_reg].u32;
if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) {
return IntCode_LOAD_REGISTER_I64(ics, i);
ics.rf[i->dest_reg].i64 =
XESWAP64(ics.thread_state->memory()->LoadI64(address));
return IA_NEXT;
}
DPRINT("%lld (%llX) = load.i64 %.8X\n",
*((int64_t*)(ics.membase + address)),
@@ -1642,26 +1442,14 @@ int Translate_LOAD(TranslationContext& ctx, Instr* i) {
IntCode_LOAD_F64,
IntCode_LOAD_V128,
};
if (i->src1.value->IsConstant()) {
// Constant address - check register access callbacks.
// NOTE: we still will likely want to check on access in debug mode, as
// constant propagation may not have happened.
uint64_t address = i->src1.value->AsUint64();
RegisterAccessCallbacks* cbs = ctx.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
return DispatchRegisterRead(ctx, i, cbs);
}
cbs = cbs->next;
}
}
return DispatchToC(ctx, i, fns[i->dest->type]);
}
uint32_t IntCode_STORE_I8(IntCodeState& ics, const IntCode* i) {
uint32_t address = ics.rf[i->src1_reg].u32;
if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) {
return IntCode_STORE_REGISTER_I8_DYNAMIC(ics, i);
ics.thread_state->memory()->StoreI8(address, ics.rf[i->src2_reg].i8);
return IA_NEXT;
}
DPRINT("store.i8 %.8X = %d (%X)\n",
address, ics.rf[i->src2_reg].i8, ics.rf[i->src2_reg].u8);
@@ -1672,7 +1460,9 @@ uint32_t IntCode_STORE_I8(IntCodeState& ics, const IntCode* i) {
uint32_t IntCode_STORE_I16(IntCodeState& ics, const IntCode* i) {
uint32_t address = ics.rf[i->src1_reg].u32;
if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) {
return IntCode_STORE_REGISTER_I16_DYNAMIC(ics, i);
ics.thread_state->memory()->StoreI16(address,
XESWAP16(ics.rf[i->src2_reg].i16));
return IA_NEXT;
}
DPRINT("store.i16 %.8X = %d (%X)\n",
address, ics.rf[i->src2_reg].i16, ics.rf[i->src2_reg].u16);
@@ -1683,7 +1473,9 @@ uint32_t IntCode_STORE_I16(IntCodeState& ics, const IntCode* i) {
uint32_t IntCode_STORE_I32(IntCodeState& ics, const IntCode* i) {
uint32_t address = ics.rf[i->src1_reg].u32;
if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) {
return IntCode_STORE_REGISTER_I32_DYNAMIC(ics, i);
ics.thread_state->memory()->StoreI32(address,
XESWAP32(ics.rf[i->src2_reg].i32));
return IA_NEXT;
}
DPRINT("store.i32 %.8X = %d (%X)\n",
address, ics.rf[i->src2_reg].i32, ics.rf[i->src2_reg].u32);
@@ -1694,7 +1486,9 @@ uint32_t IntCode_STORE_I32(IntCodeState& ics, const IntCode* i) {
uint32_t IntCode_STORE_I64(IntCodeState& ics, const IntCode* i) {
uint32_t address = ics.rf[i->src1_reg].u32;
if (DYNAMIC_REGISTER_ACCESS_CHECK(address)) {
return IntCode_STORE_REGISTER_I64_DYNAMIC(ics, i);
ics.thread_state->memory()->StoreI64(address,
XESWAP64(ics.rf[i->src2_reg].i64));
return IA_NEXT;
}
DPRINT("store.i64 %.8X = %lld (%llX)\n",
address, ics.rf[i->src2_reg].i64, ics.rf[i->src2_reg].u64);
@@ -1738,19 +1532,6 @@ int Translate_STORE(TranslationContext& ctx, Instr* i) {
IntCode_STORE_F64,
IntCode_STORE_V128,
};
if (i->src1.value->IsConstant()) {
// Constant address - check register access callbacks.
// NOTE: we still will likely want to check on access in debug mode, as
// constant propagation may not have happened.
uint64_t address = i->src1.value->AsUint64();
RegisterAccessCallbacks* cbs = ctx.access_callbacks;
while (cbs) {
if (cbs->handles(cbs->context, address)) {
return DispatchRegisterWrite(ctx, i, cbs);
}
cbs = cbs->next;
}
}
return DispatchToC(ctx, i, fns[i->src2.value->type]);
}

View File

@@ -14,7 +14,6 @@
#include <alloy/hir/instr.h>
#include <alloy/hir/opcodes.h>
#include <alloy/runtime/register_access.h>
namespace alloy { namespace runtime { class ThreadState; } }
@@ -46,7 +45,6 @@ typedef struct {
uint8_t* membase;
int8_t did_carry;
int8_t did_saturate;
runtime::RegisterAccessCallbacks* access_callbacks;
runtime::ThreadState* thread_state;
uint64_t return_address;
uint64_t call_return_address;
@@ -97,8 +95,6 @@ typedef struct SourceMapEntry_s {
typedef struct {
runtime::RegisterAccessCallbacks* access_callbacks;
uint32_t register_count;
size_t intcode_count;
Arena* intcode_arena;

View File

@@ -1456,42 +1456,6 @@ EMITTER_OPCODE_TABLE(
// ============================================================================
// Note: most *should* be aligned, but needs to be checked!
template <typename T>
bool CheckLoadAccessCallback(X64Emitter& e, const T& i) {
// If this is a constant address load, check to see if it's in a
// register range. We'll also probably want a dynamic check for
// unverified stores. So far, most games use constants.
if (!i.src1.is_constant) {
return false;
}
uint64_t address = i.src1.constant() & 0xFFFFFFFF;
auto cbs = e.runtime()->access_callbacks();
while (cbs) {
if (cbs->handles(cbs->context, address)) {
e.mov(e.rcx, reinterpret_cast<uint64_t>(cbs->context));
e.mov(e.rdx, address);
e.CallNative(cbs->read);
if (T::dest_type == KEY_TYPE_V_I8) {
// No swap required.
e.mov(i.dest, e.al);
} else if (T::dest_type == KEY_TYPE_V_I16) {
e.ror(e.ax, 8);
e.mov(i.dest, e.ax);
} else if (T::dest_type == KEY_TYPE_V_I32) {
e.bswap(e.eax);
e.mov(i.dest, e.eax);
} else if (T::dest_type == KEY_TYPE_V_I64) {
e.bswap(e.rax);
e.mov(i.dest, e.rax);
} else {
XEASSERTALWAYS();
}
return true;
}
cbs = cbs->next;
}
return false;
}
template <typename T>
RegExp ComputeMemoryAddress(X64Emitter& e, const T& guest) {
if (guest.is_constant) {
// TODO(benvanik): figure out how to do this without a temp.
@@ -1506,128 +1470,12 @@ RegExp ComputeMemoryAddress(X64Emitter& e, const T& guest) {
return e.rdx + e.rax;
}
}
uint64_t DynamicRegisterLoad(void* raw_context, uint32_t address) {
auto thread_state = *((ThreadState**)raw_context);
auto cbs = thread_state->runtime()->access_callbacks();
while (cbs) {
if (cbs->handles(cbs->context, address)) {
return cbs->read(cbs->context, address);
}
cbs = cbs->next;
}
return 0;
}
void DynamicRegisterStore(void* raw_context, uint32_t address, uint64_t value) {
auto thread_state = *((ThreadState**)raw_context);
auto cbs = thread_state->runtime()->access_callbacks();
while (cbs) {
if (cbs->handles(cbs->context, address)) {
cbs->write(cbs->context, address, value);
return;
}
cbs = cbs->next;
}
}
template <typename DEST_REG>
void EmitLoadCheck(X64Emitter& e, const I64<>& addr_value, DEST_REG& dest) {
// rax = reserved
// if (address >> 24 == 0x7F) call register load handler;
auto addr = ComputeMemoryAddress(e, addr_value);
e.lea(e.r8d, e.ptr[addr]);
e.shr(e.r8d, 24);
e.cmp(e.r8b, 0x7F);
e.inLocalLabel();
Xbyak::Label normal_addr;
Xbyak::Label skip_load;
e.jne(normal_addr);
e.lea(e.rdx, e.ptr[addr]);
e.CallNative(DynamicRegisterLoad);
if (DEST_REG::key_type == KEY_TYPE_V_I32) {
e.bswap(e.eax);
e.mov(dest, e.eax);
}
e.jmp(skip_load);
e.L(normal_addr);
if (DEST_REG::key_type == KEY_TYPE_V_I32) {
e.mov(dest, e.dword[addr]);
}
if (IsTracingData()) {
e.mov(e.r8, dest);
e.lea(e.rdx, e.ptr[addr]);
if (DEST_REG::key_type == KEY_TYPE_V_I32) {
e.CallNative(TraceMemoryLoadI32);
} else if (DEST_REG::key_type == KEY_TYPE_V_I64) {
e.CallNative(TraceMemoryLoadI64);
}
}
e.L(skip_load);
e.outLocalLabel();
}
template <typename SRC_REG>
void EmitStoreCheck(X64Emitter& e, const I64<>& addr_value, SRC_REG& src) {
// rax = reserved
// if (address >> 24 == 0x7F) call register store handler;
auto addr = ComputeMemoryAddress(e, addr_value);
e.lea(e.r8d, e.ptr[addr]);
e.shr(e.r8d, 24);
e.cmp(e.r8b, 0x7F);
e.inLocalLabel();
Xbyak::Label normal_addr;
Xbyak::Label skip_load;
e.jne(normal_addr);
e.lea(e.rdx, e.ptr[addr]);
if (SRC_REG::key_type == KEY_TYPE_V_I32) {
if (src.is_constant) {
e.mov(e.r8d, XESWAP32(static_cast<uint32_t>(src.constant())));
} else {
e.mov(e.r8d, src);
e.bswap(e.r8d);
}
} else if (SRC_REG::key_type == KEY_TYPE_V_I64) {
if (src.is_constant) {
e.mov(e.r8, XESWAP64(static_cast<uint64_t>(src.constant())));
} else {
e.mov(e.r8, src);
e.bswap(e.r8);
}
}
e.CallNative(DynamicRegisterStore);
e.jmp(skip_load);
e.L(normal_addr);
if (SRC_REG::key_type == KEY_TYPE_V_I32) {
if (src.is_constant) {
e.mov(e.dword[addr], src.constant());
} else {
e.mov(e.dword[addr], src);
}
} else if (SRC_REG::key_type == KEY_TYPE_V_I64) {
if (src.is_constant) {
e.MovMem64(addr, src.constant());
} else {
e.mov(e.qword[addr], src);
}
}
if (IsTracingData()) {
e.mov(e.r8, e.qword[addr]);
e.lea(e.rdx, e.ptr[addr]);
if (SRC_REG::key_type == KEY_TYPE_V_I32) {
e.CallNative(TraceMemoryStoreI32);
} else if (SRC_REG::key_type == KEY_TYPE_V_I64) {
e.CallNative(TraceMemoryStoreI64);
}
}
e.L(skip_load);
e.outLocalLabel();
}
EMITTER(LOAD_I8, MATCH(I<OPCODE_LOAD, I8<>, I64<>>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
if (CheckLoadAccessCallback(e, i)) {
return;
}
auto addr = ComputeMemoryAddress(e, i.src1);
e.mov(i.dest, e.byte[addr]);
if (IsTracingData()) {
e.mov(e.r8, i.dest);
e.mov(e.r8b, i.dest);
e.lea(e.rdx, e.ptr[addr]);
e.CallNative(TraceMemoryLoadI8);
}
@@ -1635,13 +1483,10 @@ EMITTER(LOAD_I8, MATCH(I<OPCODE_LOAD, I8<>, I64<>>)) {
};
EMITTER(LOAD_I16, MATCH(I<OPCODE_LOAD, I16<>, I64<>>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
if (CheckLoadAccessCallback(e, i)) {
return;
}
auto addr = ComputeMemoryAddress(e, i.src1);
e.mov(i.dest, e.word[addr]);
if (IsTracingData()) {
e.mov(e.r8, i.dest);
e.mov(e.r8w, i.dest);
e.lea(e.rdx, e.ptr[addr]);
e.CallNative(TraceMemoryLoadI16);
}
@@ -1649,17 +1494,17 @@ EMITTER(LOAD_I16, MATCH(I<OPCODE_LOAD, I16<>, I64<>>)) {
};
EMITTER(LOAD_I32, MATCH(I<OPCODE_LOAD, I32<>, I64<>>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
if (CheckLoadAccessCallback(e, i)) {
return;
auto addr = ComputeMemoryAddress(e, i.src1);
e.mov(i.dest, e.dword[addr]);
if (IsTracingData()) {
e.mov(e.r8d, i.dest);
e.lea(e.rdx, e.ptr[addr]);
e.CallNative(TraceMemoryLoadI32);
}
EmitLoadCheck(e, i.src1, i.dest);
}
};
EMITTER(LOAD_I64, MATCH(I<OPCODE_LOAD, I64<>, I64<>>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
if (CheckLoadAccessCallback(e, i)) {
return;
}
auto addr = ComputeMemoryAddress(e, i.src1);
e.mov(i.dest, e.qword[addr]);
if (IsTracingData()) {
@@ -1718,51 +1563,8 @@ EMITTER_OPCODE_TABLE(
// OPCODE_STORE
// ============================================================================
// Note: most *should* be aligned, but needs to be checked!
template <typename T>
bool CheckStoreAccessCallback(X64Emitter& e, const T& i) {
// If this is a constant address store, check to see if it's in a
// register range. We'll also probably want a dynamic check for
// unverified stores. So far, most games use constants.
if (!i.src1.is_constant) {
return false;
}
uint64_t address = i.src1.constant() & 0xFFFFFFFF;
auto cbs = e.runtime()->access_callbacks();
while (cbs) {
if (cbs->handles(cbs->context, address)) {
e.mov(e.rcx, reinterpret_cast<uint64_t>(cbs->context));
e.mov(e.rdx, address);
if (i.src2.is_constant) {
e.mov(e.r8, i.src2.constant());
} else {
if (T::src2_type == KEY_TYPE_V_I8) {
// No swap required.
e.movzx(e.r8, i.src2.reg().cvt8());
} else if (T::src2_type == KEY_TYPE_V_I16) {
e.movzx(e.r8, i.src2.reg().cvt16());
e.ror(e.r8w, 8);
} else if (T::src2_type == KEY_TYPE_V_I32) {
e.mov(e.r8d, i.src2.reg().cvt32());
e.bswap(e.r8d);
} else if (T::src2_type == KEY_TYPE_V_I64) {
e.mov(e.r8, i.src2);
e.bswap(e.r8);
} else {
XEASSERTALWAYS();
}
}
e.CallNative(cbs->write);
return true;
}
cbs = cbs->next;
}
return false;
}
EMITTER(STORE_I8, MATCH(I<OPCODE_STORE, VoidOp, I64<>, I8<>>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
if (CheckStoreAccessCallback(e, i)) {
return;
}
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.src2.is_constant) {
e.mov(e.byte[addr], i.src2.constant());
@@ -1770,7 +1572,7 @@ EMITTER(STORE_I8, MATCH(I<OPCODE_STORE, VoidOp, I64<>, I8<>>)) {
e.mov(e.byte[addr], i.src2);
}
if (IsTracingData()) {
e.mov(e.r8, e.byte[addr]);
e.mov(e.r8b, e.byte[addr]);
e.lea(e.rdx, e.ptr[addr]);
e.CallNative(TraceMemoryStoreI8);
}
@@ -1778,9 +1580,6 @@ EMITTER(STORE_I8, MATCH(I<OPCODE_STORE, VoidOp, I64<>, I8<>>)) {
};
EMITTER(STORE_I16, MATCH(I<OPCODE_STORE, VoidOp, I64<>, I16<>>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
if (CheckStoreAccessCallback(e, i)) {
return;
}
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.src2.is_constant) {
e.mov(e.word[addr], i.src2.constant());
@@ -1788,7 +1587,7 @@ EMITTER(STORE_I16, MATCH(I<OPCODE_STORE, VoidOp, I64<>, I16<>>)) {
e.mov(e.word[addr], i.src2);
}
if (IsTracingData()) {
e.mov(e.r8, e.word[addr]);
e.mov(e.r8w, e.word[addr]);
e.lea(e.rdx, e.ptr[addr]);
e.CallNative(TraceMemoryStoreI16);
}
@@ -1796,18 +1595,32 @@ EMITTER(STORE_I16, MATCH(I<OPCODE_STORE, VoidOp, I64<>, I16<>>)) {
};
EMITTER(STORE_I32, MATCH(I<OPCODE_STORE, VoidOp, I64<>, I32<>>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
if (CheckStoreAccessCallback(e, i)) {
return;
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.src2.is_constant) {
e.mov(e.dword[addr], i.src2.constant());
} else {
e.mov(e.dword[addr], i.src2);
}
if (IsTracingData()) {
e.mov(e.r8d, e.dword[addr]);
e.lea(e.rdx, e.ptr[addr]);
e.CallNative(TraceMemoryStoreI32);
}
EmitStoreCheck(e, i.src1, i.src2);
}
};
EMITTER(STORE_I64, MATCH(I<OPCODE_STORE, VoidOp, I64<>, I64<>>)) {
static void Emit(X64Emitter& e, const EmitArgType& i) {
if (CheckStoreAccessCallback(e, i)) {
return;
auto addr = ComputeMemoryAddress(e, i.src1);
if (i.src2.is_constant) {
e.MovMem64(addr, i.src2.constant());
} else {
e.mov(e.qword[addr], i.src2);
}
if (IsTracingData()) {
e.mov(e.r8, e.qword[addr]);
e.lea(e.rdx, e.ptr[addr]);
e.CallNative(TraceMemoryStoreI64);
}
EmitStoreCheck(e, i.src1, i.src2);
}
};
EMITTER(STORE_F32, MATCH(I<OPCODE_STORE, VoidOp, I64<>, F32<>>)) {

View File

@@ -43,6 +43,15 @@ public:
uint64_t SearchAligned(uint64_t start, uint64_t end,
const uint32_t* values, size_t value_count);
virtual uint8_t LoadI8(uint64_t address) = 0;
virtual uint16_t LoadI16(uint64_t address) = 0;
virtual uint32_t LoadI32(uint64_t address) = 0;
virtual uint64_t LoadI64(uint64_t address) = 0;
virtual void StoreI8(uint64_t address, uint8_t value) = 0;
virtual void StoreI16(uint64_t address, uint16_t value) = 0;
virtual void StoreI32(uint64_t address, uint32_t value) = 0;
virtual void StoreI64(uint64_t address, uint64_t value) = 0;
virtual uint64_t HeapAlloc(
uint64_t base_address, size_t size, uint32_t flags,
uint32_t alignment = 0x20) = 0;

View File

@@ -1,38 +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_RUNTIME_REGISTER_ACCESS_H_
#define ALLOY_RUNTIME_REGISTER_ACCESS_H_
#include <alloy/core.h>
namespace alloy {
namespace runtime {
typedef bool (*RegisterHandlesCallback)(void* context, uint64_t addr);
typedef uint64_t (*RegisterReadCallback)(void* context, uint64_t addr);
typedef void (*RegisterWriteCallback)(void* context, uint64_t addr,
uint64_t value);
typedef struct RegisterAccessCallbacks_s {
void* context;
RegisterHandlesCallback handles;
RegisterReadCallback read;
RegisterWriteCallback write;
RegisterAccessCallbacks_s* next;
} RegisterAccessCallbacks;
} // namespace runtime
} // namespace alloy
#endif // ALLOY_RUNTIME_REGISTER_ACCESS_H_

View File

@@ -25,8 +25,7 @@ DEFINE_string(runtime_backend, "any",
Runtime::Runtime(Memory* memory) :
memory_(memory), debugger_(0), backend_(0), frontend_(0),
access_callbacks_(0) {
memory_(memory), debugger_(0), backend_(0), frontend_(0) {
tracing::Initialize();
modules_lock_ = AllocMutex(10000);
}
@@ -41,14 +40,6 @@ Runtime::~Runtime() {
UnlockMutex(modules_lock_);
FreeMutex(modules_lock_);
RegisterAccessCallbacks* cbs = access_callbacks_;
while (cbs) {
RegisterAccessCallbacks* next = cbs->next;
delete cbs;
cbs = next;
}
access_callbacks_ = NULL;
delete frontend_;
delete backend_;
delete debugger_;
@@ -281,11 +272,3 @@ int Runtime::DemandFunction(
return 0;
}
void Runtime::AddRegisterAccessCallbacks(
const RegisterAccessCallbacks& callbacks) {
RegisterAccessCallbacks* cbs = new RegisterAccessCallbacks();
xe_copy_struct(cbs, &callbacks, sizeof(callbacks));
cbs->next = access_callbacks_;
access_callbacks_ = cbs;
}

View File

@@ -17,7 +17,6 @@
#include <alloy/runtime/debugger.h>
#include <alloy/runtime/entry_table.h>
#include <alloy/runtime/module.h>
#include <alloy/runtime/register_access.h>
#include <alloy/runtime/symbol_info.h>
#include <alloy/runtime/thread_state.h>
@@ -38,9 +37,6 @@ public:
Debugger* debugger() const { return debugger_; }
frontend::Frontend* frontend() const { return frontend_; }
backend::Backend* backend() const { return backend_; }
RegisterAccessCallbacks* access_callbacks() const {
return access_callbacks_;
}
int Initialize(frontend::Frontend* frontend, backend::Backend* backend = 0);
@@ -55,9 +51,6 @@ public:
FunctionInfo** out_symbol_info);
int ResolveFunction(uint64_t address, Function** out_function);
void AddRegisterAccessCallbacks(
const RegisterAccessCallbacks& callbacks);
//uint32_t CreateCallback(void (*callback)(void* data), void* data);
private:
@@ -74,8 +67,6 @@ protected:
EntryTable entry_table_;
Mutex* modules_lock_;
ModuleList modules_;
RegisterAccessCallbacks* access_callbacks_;
};

View File

@@ -15,7 +15,6 @@
'module.h',
'raw_module.cc',
'raw_module.h',
'register_access.h',
'runtime.cc',
'runtime.h',
'symbol_info.cc',