backend/arm64: Simple implementation of memory read/write
This commit is contained in:
parent
77634509b5
commit
eaf87ec1e4
4 changed files with 112 additions and 34 deletions
|
@ -6,6 +6,7 @@
|
||||||
#include "dynarmic/backend/arm64/a32_address_space.h"
|
#include "dynarmic/backend/arm64/a32_address_space.h"
|
||||||
|
|
||||||
#include "dynarmic/backend/arm64/abi.h"
|
#include "dynarmic/backend/arm64/abi.h"
|
||||||
|
#include "dynarmic/backend/arm64/devirtualize.h"
|
||||||
#include "dynarmic/backend/arm64/emit_arm64.h"
|
#include "dynarmic/backend/arm64/emit_arm64.h"
|
||||||
#include "dynarmic/backend/arm64/stack_layout.h"
|
#include "dynarmic/backend/arm64/stack_layout.h"
|
||||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
|
@ -14,6 +15,28 @@
|
||||||
|
|
||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
|
template<auto mfp, typename T>
|
||||||
|
static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||||
|
using namespace oaknut::util;
|
||||||
|
|
||||||
|
const auto info = Devirtualize<mfp>(this_);
|
||||||
|
|
||||||
|
oaknut::Label l_addr, l_this;
|
||||||
|
|
||||||
|
void* target = code.ptr<void*>();
|
||||||
|
code.LDR(X0, l_this);
|
||||||
|
code.LDR(Xscratch0, l_addr);
|
||||||
|
code.BR(Xscratch0);
|
||||||
|
|
||||||
|
code.align(8);
|
||||||
|
code.l(l_this);
|
||||||
|
code.dx(info.this_ptr);
|
||||||
|
code.l(l_addr);
|
||||||
|
code.dx(info.fn_ptr);
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
||||||
: conf(conf)
|
: conf(conf)
|
||||||
, mem(conf.code_cache_size)
|
, mem(conf.code_cache_size)
|
||||||
|
@ -66,7 +89,6 @@ void A32AddressSpace::ClearCache() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32AddressSpace::EmitPrelude() {
|
void A32AddressSpace::EmitPrelude() {
|
||||||
using namespace oaknut;
|
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
mem.unprotect();
|
mem.unprotect();
|
||||||
|
@ -81,7 +103,14 @@ void A32AddressSpace::EmitPrelude() {
|
||||||
ABI_PopRegisters(code, ABI_CALLEE_SAVE | (1 << 30), sizeof(StackLayout));
|
ABI_PopRegisters(code, ABI_CALLEE_SAVE | (1 << 30), sizeof(StackLayout));
|
||||||
code.RET();
|
code.RET();
|
||||||
|
|
||||||
mem.protect();
|
prelude_info.read_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead8>(code, conf.callbacks);
|
||||||
|
prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||||
|
prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||||
|
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||||
|
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||||
|
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||||
|
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||||
|
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||||
|
|
||||||
prelude_info.end_of_prelude = code.ptr<u32*>();
|
prelude_info.end_of_prelude = code.ptr<u32*>();
|
||||||
}
|
}
|
||||||
|
@ -119,6 +148,30 @@ void A32AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||||
case LinkTarget::ReturnFromRunCode:
|
case LinkTarget::ReturnFromRunCode:
|
||||||
c.B(prelude_info.return_from_run_code);
|
c.B(prelude_info.return_from_run_code);
|
||||||
break;
|
break;
|
||||||
|
case LinkTarget::ReadMemory8:
|
||||||
|
c.BL(prelude_info.read_memory_8);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ReadMemory16:
|
||||||
|
c.BL(prelude_info.read_memory_16);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ReadMemory32:
|
||||||
|
c.BL(prelude_info.read_memory_32);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ReadMemory64:
|
||||||
|
c.BL(prelude_info.read_memory_64);
|
||||||
|
break;
|
||||||
|
case LinkTarget::WriteMemory8:
|
||||||
|
c.BL(prelude_info.write_memory_8);
|
||||||
|
break;
|
||||||
|
case LinkTarget::WriteMemory16:
|
||||||
|
c.BL(prelude_info.write_memory_16);
|
||||||
|
break;
|
||||||
|
case LinkTarget::WriteMemory32:
|
||||||
|
c.BL(prelude_info.write_memory_32);
|
||||||
|
break;
|
||||||
|
case LinkTarget::WriteMemory64:
|
||||||
|
c.BL(prelude_info.write_memory_64);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT_FALSE("Invalid relocation target");
|
ASSERT_FALSE("Invalid relocation target");
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,15 @@ private:
|
||||||
using RunCodeFuncType = HaltReason (*)(CodePtr entry_point, A32JitState* context, volatile u32* halt_reason);
|
using RunCodeFuncType = HaltReason (*)(CodePtr entry_point, A32JitState* context, volatile u32* halt_reason);
|
||||||
RunCodeFuncType run_code;
|
RunCodeFuncType run_code;
|
||||||
void* return_from_run_code;
|
void* return_from_run_code;
|
||||||
|
|
||||||
|
void* read_memory_8;
|
||||||
|
void* read_memory_16;
|
||||||
|
void* read_memory_32;
|
||||||
|
void* read_memory_64;
|
||||||
|
void* write_memory_8;
|
||||||
|
void* write_memory_16;
|
||||||
|
void* write_memory_32;
|
||||||
|
void* write_memory_64;
|
||||||
} prelude_info;
|
} prelude_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,14 @@ using CodePtr = std::byte*;
|
||||||
|
|
||||||
enum class LinkTarget {
|
enum class LinkTarget {
|
||||||
ReturnFromRunCode,
|
ReturnFromRunCode,
|
||||||
|
ReadMemory8,
|
||||||
|
ReadMemory16,
|
||||||
|
ReadMemory32,
|
||||||
|
ReadMemory64,
|
||||||
|
WriteMemory8,
|
||||||
|
WriteMemory16,
|
||||||
|
WriteMemory32,
|
||||||
|
WriteMemory64,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Relocation {
|
struct Relocation {
|
||||||
|
|
|
@ -25,34 +25,38 @@ void EmitIR<IR::Opcode::A32ClearExclusive>(oaknut::CodeGenerator& code, EmitCont
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ReadMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ReadMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReadMemory8);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ReadMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ReadMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReadMemory16);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ReadMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ReadMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReadMemory32);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ReadMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ReadMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReadMemory64);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -89,34 +93,38 @@ void EmitIR<IR::Opcode::A32ExclusiveReadMemory64>(oaknut::CodeGenerator& code, E
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32WriteMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32WriteMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
ctx.reg_alloc.PrepareForCall(nullptr, {}, args[1], args[2]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::WriteMemory8);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32WriteMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32WriteMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
ctx.reg_alloc.PrepareForCall(nullptr, {}, args[1], args[2]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::WriteMemory16);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32WriteMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32WriteMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
ctx.reg_alloc.PrepareForCall(nullptr, {}, args[1], args[2]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::WriteMemory32);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32WriteMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32WriteMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
ctx.reg_alloc.PrepareForCall(nullptr, {}, args[1], args[2]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::WriteMemory64);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
Loading…
Reference in a new issue