unit tests & various fixes

This commit is contained in:
Jeremy Van de woestyne 2020-06-21 19:29:55 +02:00 committed by merry
parent 8146de9fc9
commit 9261f561a0
7 changed files with 267 additions and 12 deletions

View File

@ -196,10 +196,11 @@ struct UserConfig {
/// page boundary.
bool only_detect_misalignment_via_page_table_on_page_boundary = false;
// Fastmem Pointer
// This should point to the beginning of a 4GB address space which is in arranged just like
// what you wish for emulated memory to be. If the host page faults on an address, the JIT
// will fallback to calling the MemoryRead*/MemoryWrite* callbacks.
/// Fastmem Pointer
/// This should point to the beginning of a 2^page_table_address_space_bits bytes
/// address space which is in arranged just like what you wish for emulated memory to
/// be. If the host page faults on an address, the JIT will fallback to calling the
/// MemoryRead*/MemoryWrite* callbacks.
void* fastmem_pointer = nullptr;
/// Determines if instructions that pagefault should cause recompilation of that block
/// with fastmem disabled.

View File

@ -93,7 +93,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
code.EnableWriting();
SCOPE_EXIT { code.DisableWriting(); };
static const std::vector<HostLoc> gpr_order = [this]{
const std::vector<HostLoc> gpr_order = [this]{
std::vector<HostLoc> gprs{any_gpr};
if (conf.page_table) {
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R14));
@ -990,7 +990,7 @@ template<std::size_t bitsize, auto callback>
void A32EmitX64::ReadMemory(A32EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if (!conf.page_table) {
if (!conf.page_table && !conf.fastmem_pointer) {
ctx.reg_alloc.HostCall(inst, {}, args[0]);
Devirtualize<callback>(conf.callbacks).EmitCall(code);
return;
@ -1015,8 +1015,6 @@ void A32EmitX64::ReadMemory(A32EmitContext& ctx, IR::Inst* inst) {
*marker,
}
);
ctx.reg_alloc.DefineValue(inst, value);
} else {
Xbyak::Label abort, end;
@ -1038,7 +1036,7 @@ template<std::size_t bitsize, auto callback>
void A32EmitX64::WriteMemory(A32EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if (!conf.page_table) {
if (!conf.page_table && !conf.fastmem_pointer) {
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
Devirtualize<callback>(conf.callbacks).EmitCall(code);
return;

View File

@ -75,7 +75,7 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) {
code.EnableWriting();
SCOPE_EXIT { code.DisableWriting(); };
static const std::vector<HostLoc> gpr_order = [this]{
const std::vector<HostLoc> gpr_order = [this]{
std::vector<HostLoc> gprs{any_gpr};
if (conf.page_table) {
gprs.erase(std::find(gprs.begin(), gprs.end(), HostLoc::R14));
@ -930,7 +930,7 @@ template<std::size_t bitsize, auto callback>
void A64EmitX64::EmitMemoryRead(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if (!conf.page_table) {
if (!conf.page_table && !conf.fastmem_pointer) {
ctx.reg_alloc.HostCall(inst, {}, args[0]);
Devirtualize<callback>(conf.callbacks).EmitCall(code);
return;
@ -976,7 +976,7 @@ template<std::size_t bitsize, auto callback>
void A64EmitX64::EmitMemoryWrite(A64EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
if (!conf.page_table) {
if (!conf.page_table && !conf.fastmem_pointer) {
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
Devirtualize<callback>(conf.callbacks).EmitCall(code);
return;

View File

@ -485,3 +485,37 @@ TEST_CASE("arm: vclt.f32 with zero", "[arm][A32][.]") {
REQUIRE(jit.ExtRegs()[6] == 0x00000000);
REQUIRE(jit.ExtRegs()[7] == 0x00000000);
}
TEST_CASE("arm: Memory access (fastmem)", "[arm][A32]") {
constexpr size_t address_width = 12;
constexpr size_t memory_size = 1ull << address_width; // 4K
constexpr size_t page_size = 4 * 1024;
constexpr size_t buffer_size = 2 * page_size;
char buffer[buffer_size];
void* buffer_ptr = reinterpret_cast<void*>(buffer);
size_t buffer_size_nconst = buffer_size;
char* backing_memory = reinterpret_cast<char*>(std::align(page_size, memory_size, buffer_ptr, buffer_size_nconst));
A32FastmemTestEnv env{backing_memory};
Dynarmic::A32::UserConfig config{&env};
config.fastmem_pointer = backing_memory;
config.recompile_on_fastmem_failure = false;
config.processor_id = 0;
Dynarmic::A32::Jit jit{config};
memset(backing_memory, 0, memory_size);
memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57);
env.MemoryWrite32(0, 0xE5904000); // LDR R4, [R0]
env.MemoryWrite32(4, 0xE5814000); // STR R4, [R1]
env.MemoryWrite32(8, 0xEAFFFFFE); // B .
jit.Regs()[0] = 0x100;
jit.Regs()[1] = 0x1F0;
jit.Regs()[15] = 0; // PC = 0
jit.SetCpsr(0x000001d0); // User-mode
env.ticks_left = 3;
jit.Run();
REQUIRE(strncmp(backing_memory + 0x100, backing_memory + 0x1F0, 4) == 0);
}

View File

@ -97,3 +97,86 @@ public:
using ArmTestEnv = A32TestEnv<u32, 0xEAFFFFFE>;
using ThumbTestEnv = A32TestEnv<u16, 0xE7FEE7FE>;
class A32FastmemTestEnv final : public Dynarmic::A32::UserCallbacks {
public:
u64 ticks_left = 0;
char* backing_memory = nullptr;
explicit A32FastmemTestEnv(char* addr) : backing_memory(addr) {}
template<typename T>
T read(std::uint32_t vaddr) {
T value;
memcpy(&value, backing_memory + vaddr, sizeof(T));
return value;
}
template<typename T>
void write(std::uint32_t vaddr, const T& value) {
memcpy(backing_memory + vaddr, &value, sizeof(T));
}
std::uint32_t MemoryReadCode(std::uint32_t vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint8_t MemoryRead8(std::uint32_t vaddr) override {
return read<std::uint8_t>(vaddr);
}
std::uint16_t MemoryRead16(std::uint32_t vaddr) override {
return read<std::uint16_t>(vaddr);
}
std::uint32_t MemoryRead32(std::uint32_t vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint64_t MemoryRead64(std::uint32_t vaddr) override {
return read<std::uint64_t>(vaddr);
}
void MemoryWrite8(std::uint32_t vaddr, std::uint8_t value) override {
write(vaddr, value);
}
void MemoryWrite16(std::uint32_t vaddr, std::uint16_t value) override {
write(vaddr, value);
}
void MemoryWrite32(std::uint32_t vaddr, std::uint32_t value) override {
write(vaddr, value);
}
void MemoryWrite64(std::uint32_t vaddr, std::uint64_t value) override {
write(vaddr, value);
}
bool MemoryWriteExclusive8(std::uint32_t vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value);
return true;
}
bool MemoryWriteExclusive16(std::uint32_t vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value);
return true;
}
bool MemoryWriteExclusive32(std::uint32_t vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value);
return true;
}
bool MemoryWriteExclusive64(std::uint32_t vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value);
return true;
}
void InterpreterFallback(std::uint32_t pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:016x}, {})", pc, num_instructions); }
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); }
void ExceptionRaised(std::uint32_t pc, Dynarmic::A32::Exception) override { ASSERT_MSG(false, "ExceptionRaised({:016x})", pc); }
void AddTicks(std::uint64_t ticks) override {
if (ticks > ticks_left) {
ticks_left = 0;
return;
}
ticks_left -= ticks;
}
std::uint64_t GetTicksRemaining() override {
return ticks_left;
}
};

View File

@ -634,3 +634,46 @@ TEST_CASE("A64: Optimization failure when folding ADD", "[a64]") {
REQUIRE(jit.GetPstate() == 0x20000000);
REQUIRE(jit.GetVector(30) == Vector{0xf7f6f5f4, 0});
}
TEST_CASE("A64: Memory access (fastmem)", "[a64]") {
constexpr size_t address_width = 12;
constexpr size_t memory_size = 1ull << address_width; // 4K
constexpr size_t page_size = 4 * 1024;
constexpr size_t buffer_size = 2 * page_size;
char buffer[buffer_size];
void* buffer_ptr = reinterpret_cast<void*>(buffer);
size_t buffer_size_nconst = buffer_size;
char* backing_memory = reinterpret_cast<char*>(std::align(page_size, memory_size, buffer_ptr, buffer_size_nconst));
A64FastmemTestEnv env{backing_memory};
Dynarmic::A64::UserConfig config{&env};
config.fastmem_pointer = backing_memory;
config.page_table_address_space_bits = address_width;
config.recompile_on_fastmem_failure = false;
config.silently_mirror_page_table = true;
config.processor_id = 0;
Dynarmic::A64::Jit jit{config};
memset(backing_memory, 0, memory_size);
memcpy(backing_memory + 0x100, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 57);
env.MemoryWrite32(0, 0xA9401404); // LDP X4, X5, [X0]
env.MemoryWrite32(4, 0xF9400046); // LDR X6, [X2]
env.MemoryWrite32(8, 0xA9001424); // STP X4, X5, [X1]
env.MemoryWrite32(12, 0xF9000066); // STR X6, [X3]
env.MemoryWrite32(16, 0x14000000); // B .
jit.SetRegister(0, 0x100);
jit.SetRegister(1, 0x1F0);
jit.SetRegister(2, 0x10F);
jit.SetRegister(3, 0x1FF);
jit.SetPC(0);
jit.SetSP(memory_size - 1);
jit.SetFpsr(0x03480000);
jit.SetPstate(0x30000000);
env.ticks_left = 5;
jit.Run();
REQUIRE(strncmp(backing_memory + 0x100, backing_memory + 0x1F0, 23) == 0);
}

View File

@ -125,3 +125,99 @@ public:
return 0x10000000000 - ticks_left;
}
};
class A64FastmemTestEnv final : public Dynarmic::A64::UserCallbacks {
public:
u64 ticks_left = 0;
char* backing_memory = nullptr;
explicit A64FastmemTestEnv(char* addr) : backing_memory(addr) {}
template<typename T>
T read(u64 vaddr) {
T value;
memcpy(&value, backing_memory + vaddr, sizeof(T));
return value;
}
template<typename T>
void write(u64 vaddr, const T& value) {
memcpy(backing_memory + vaddr, &value, sizeof(T));
}
std::uint32_t MemoryReadCode(u64 vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint8_t MemoryRead8(u64 vaddr) override {
return read<std::uint8_t>(vaddr);
}
std::uint16_t MemoryRead16(u64 vaddr) override {
return read<std::uint16_t>(vaddr);
}
std::uint32_t MemoryRead32(u64 vaddr) override {
return read<std::uint32_t>(vaddr);
}
std::uint64_t MemoryRead64(u64 vaddr) override {
return read<std::uint64_t>(vaddr);
}
Vector MemoryRead128(u64 vaddr) override {
return read<Vector>(vaddr);
}
void MemoryWrite8(u64 vaddr, std::uint8_t value) override {
write(vaddr, value);
}
void MemoryWrite16(u64 vaddr, std::uint16_t value) override {
write(vaddr, value);
}
void MemoryWrite32(u64 vaddr, std::uint32_t value) override {
write(vaddr, value);
}
void MemoryWrite64(u64 vaddr, std::uint64_t value) override {
write(vaddr, value);
}
void MemoryWrite128(u64 vaddr, Vector value) override {
write(vaddr, value);
}
bool MemoryWriteExclusive8(u64 vaddr, std::uint8_t value, [[maybe_unused]] std::uint8_t expected) override {
MemoryWrite8(vaddr, value);
return true;
}
bool MemoryWriteExclusive16(u64 vaddr, std::uint16_t value, [[maybe_unused]] std::uint16_t expected) override {
MemoryWrite16(vaddr, value);
return true;
}
bool MemoryWriteExclusive32(u64 vaddr, std::uint32_t value, [[maybe_unused]] std::uint32_t expected) override {
MemoryWrite32(vaddr, value);
return true;
}
bool MemoryWriteExclusive64(u64 vaddr, std::uint64_t value, [[maybe_unused]] std::uint64_t expected) override {
MemoryWrite64(vaddr, value);
return true;
}
bool MemoryWriteExclusive128(u64 vaddr, Vector value, [[maybe_unused]] Vector expected) override {
MemoryWrite128(vaddr, value);
return true;
}
void InterpreterFallback(u64 pc, size_t num_instructions) override { ASSERT_MSG(false, "InterpreterFallback({:016x}, {})", pc, num_instructions); }
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); }
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception) override { ASSERT_MSG(false, "ExceptionRaised({:016x})", pc); }
void AddTicks(std::uint64_t ticks) override {
if (ticks > ticks_left) {
ticks_left = 0;
return;
}
ticks_left -= ticks;
}
std::uint64_t GetTicksRemaining() override {
return ticks_left;
}
std::uint64_t GetCNTPCT() override {
return 0x10000000000 - ticks_left;
}
};