Compare commits

...

1 Commits

Author SHA1 Message Date
lizzie c52a6371a4 2026-09-18 06:06:10
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-09-18 19:11:31 +00:00
4 changed files with 49 additions and 48 deletions
@@ -11,8 +11,7 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <fmt/format.h> #include "common/logging.h"
#include <fmt/ostream.h>
#include "dynarmic/mcl/integer_of_size.hpp" #include "dynarmic/mcl/integer_of_size.hpp"
#include "dynarmic/backend/x64/xbyak.h" #include "dynarmic/backend/x64/xbyak.h"
@@ -11,8 +11,7 @@
#include <tuple> #include <tuple>
#include <utility> #include <utility>
#include <fmt/format.h> #include "common/logging.h"
#include <fmt/ostream.h>
#include "dynarmic/mcl/integer_of_size.hpp" #include "dynarmic/mcl/integer_of_size.hpp"
#include "dynarmic/backend/x64/xbyak.h" #include "dynarmic/backend/x64/xbyak.h"
@@ -14,10 +14,6 @@
#define AxxJitState CONCATENATE_TOKENS(Axx, JitState) #define AxxJitState CONCATENATE_TOKENS(Axx, JitState)
#define AxxUserConfig Axx::UserConfig #define AxxUserConfig Axx::UserConfig
namespace {
using Vector = std::array<u64, 2>;
}
std::optional<AxxEmitX64::DoNotFastmemMarker> AxxEmitX64::ShouldFastmem(AxxEmitContext& ctx, IR::Inst* inst) const { std::optional<AxxEmitX64::DoNotFastmemMarker> AxxEmitX64::ShouldFastmem(AxxEmitContext& ctx, IR::Inst* inst) const {
if (!conf.fastmem_pointer || !exception_handler.SupportsFastmem()) { if (!conf.fastmem_pointer || !exception_handler.SupportsFastmem()) {
return std::nullopt; return std::nullopt;
@@ -31,22 +27,19 @@ std::optional<AxxEmitX64::DoNotFastmemMarker> AxxEmitX64::ShouldFastmem(AxxEmitC
} }
FakeCall AxxEmitX64::FastmemCallback(u64 rip_) { FakeCall AxxEmitX64::FastmemCallback(u64 rip_) {
const auto iter = fastmem_patch_info.find(rip_); if (auto const it = fastmem_patch_info.find(rip_); it != fastmem_patch_info.end()) {
if (iter != fastmem_patch_info.end()) {
FakeCall result{ FakeCall result{
.call_rip = iter->second.callback, .call_rip = it->second.callback,
.ret_rip = iter->second.resume_rip, .ret_rip = it->second.resume_rip,
}; };
if (iter->second.recompile) { if (it->second.recompile) {
const auto marker = iter->second.marker; const auto marker = it->second.marker;
do_not_fastmem.insert(marker); do_not_fastmem.insert(marker);
InvalidateBasicBlocks({std::get<0>(marker)}); InvalidateBasicBlocks({std::get<0>(marker)});
} }
return result; return result;
} }
fmt::print("dynarmic: Segfault happened within JITted code at rip = {:016x}\n" UNREACHABLE_MSG("SIGSEGV @ JIT, rip={:#016x}", rip_); //("iter != fastmem_patch_info.end()");
"Segfault wasn't at a fastmem patch location!\n", rip_);
UNREACHABLE(); //("iter != fastmem_patch_info.end()");
} }
template<std::size_t bitsize, auto callback> template<std::size_t bitsize, auto callback>
@@ -83,22 +76,17 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(code, args[1]); const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(code, args[1]);
const int value_idx = bitsize == 128 ? ctx.reg_alloc.ScratchXmm(code).getIdx() : ctx.reg_alloc.ScratchGpr(code).getIdx(); const int value_idx = bitsize == 128 ? ctx.reg_alloc.ScratchXmm(code).getIdx() : ctx.reg_alloc.ScratchGpr(code).getIdx();
const auto wrapped_fn = read_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)]; const auto wrapped_fn = read_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
SharedLabel abort = ctx.GenSharedLabel(), end = ctx.GenSharedLabel(); SharedLabel abort = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
if (fastmem_marker) { if (fastmem_marker) {
// Use fastmem // Use fastmem
bool require_abort_handling = false; bool require_abort_handling = false;
const auto src_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling); const auto src_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling);
const auto location = EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered); const auto location = EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
ctx.deferred_emits.emplace_back([=, this, &ctx] { ctx.deferred_emits.emplace_back([=, this, &ctx] {
code.L(*abort); code.L(*abort);
code.call(wrapped_fn); code.call(wrapped_fn);
fastmem_patch_info.emplace( fastmem_patch_info.emplace(
std::bit_cast<u64>(location), std::bit_cast<u64>(location),
FastmemPatchInfo{ FastmemPatchInfo{
@@ -107,25 +95,23 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) {
*fastmem_marker, *fastmem_marker,
conf.recompile_on_fastmem_failure, conf.recompile_on_fastmem_failure,
}); });
EmitCheckMemoryAbort(ctx, inst, end); EmitCheckMemoryAbort(ctx, inst, end);
code.jmp(*end, code.T_NEAR); code.jmp(*end, code.T_NEAR);
}); });
} else { } else if (conf.page_table) {
// Use page table // Use page table
ASSERT(conf.page_table);
const auto src_ptr = EmitVAddrLookup(code, ctx, bitsize, *abort, vaddr); const auto src_ptr = EmitVAddrLookup(code, ctx, bitsize, *abort, vaddr);
EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered); EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered);
ctx.deferred_emits.emplace_back([=, this, &ctx] { ctx.deferred_emits.emplace_back([=, this, &ctx] {
code.L(*abort); code.L(*abort);
code.call(wrapped_fn); code.call(wrapped_fn);
EmitCheckMemoryAbort(ctx, inst, end); EmitCheckMemoryAbort(ctx, inst, end);
code.jmp(*end, code.T_NEAR); code.jmp(*end, code.T_NEAR);
}); });
} else {
UNREACHABLE();
} }
code.L(*end); code.L(*end);
if constexpr (bitsize == 128) { if constexpr (bitsize == 128) {
ctx.reg_alloc.DefineValue(code, inst, Xbyak::Xmm{value_idx}); ctx.reg_alloc.DefineValue(code, inst, Xbyak::Xmm{value_idx});
} else { } else {
@@ -174,12 +160,10 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
const auto wrapped_fn = write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)]; const auto wrapped_fn = write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
SharedLabel abort = ctx.GenSharedLabel(), end = ctx.GenSharedLabel(); SharedLabel abort = ctx.GenSharedLabel(), end = ctx.GenSharedLabel();
if (fastmem_marker) { if (fastmem_marker) {
// Use fastmem // Use fastmem
bool require_abort_handling = false; bool require_abort_handling = false;
const auto dest_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling); const auto dest_ptr = EmitFastmemVAddr(code, ctx, *abort, vaddr, require_abort_handling);
const auto location = EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered); const auto location = EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered);
ctx.deferred_emits.emplace_back([=, this, &ctx] { ctx.deferred_emits.emplace_back([=, this, &ctx] {
@@ -198,9 +182,8 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
EmitCheckMemoryAbort(ctx, inst, end); EmitCheckMemoryAbort(ctx, inst, end);
code.jmp(*end, code.T_NEAR); code.jmp(*end, code.T_NEAR);
}); });
} else { } else if (conf.page_table) {
// Use page table // Use page table
ASSERT(conf.page_table);
const auto dest_ptr = EmitVAddrLookup(code, ctx, bitsize, *abort, vaddr); const auto dest_ptr = EmitVAddrLookup(code, ctx, bitsize, *abort, vaddr);
EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered); EmitWriteMemoryMov<bitsize>(code, dest_ptr, value_idx, ordered);
@@ -210,6 +193,8 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) {
EmitCheckMemoryAbort(ctx, inst, end); EmitCheckMemoryAbort(ctx, inst, end);
code.jmp(*end, code.T_NEAR); code.jmp(*end, code.T_NEAR);
}); });
} else {
UNREACHABLE();
} }
code.L(*end); code.L(*end);
} }
@@ -249,8 +234,8 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) { if (ordered) {
code.mfence(); code.mfence();
} }
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) { code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, u128& ret) {
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector { ret = conf.global_monitor->ReadAndMark<u128>(conf.processor_id, vaddr, [&]() -> u128 {
return (conf.callbacks->*callback)(vaddr); return (conf.callbacks->*callback)(vaddr);
}); });
}); });
@@ -301,8 +286,8 @@ void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
ctx.reg_alloc.AllocStackSpace(code, 16 + ABI_SHADOW_SPACE); ctx.reg_alloc.AllocStackSpace(code, 16 + ABI_SHADOW_SPACE);
code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]); code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]);
code.movaps(xword[code.ABI_PARAM3], xmm1); code.movaps(xword[code.ABI_PARAM3], xmm1);
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& value) -> u32 { code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, u128& value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<Vector>(conf.processor_id, vaddr, [&](Vector expected) -> bool { return conf.global_monitor->DoExclusiveOperation<u128>(conf.processor_id, vaddr, [&](u128 expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected); return (conf.callbacks->*callback)(vaddr, value, expected);
}) ? 0 : 1; }) ? 0 : 1;
}); });
@@ -135,10 +135,16 @@ template<>
if (unused_top_bits == 0) { if (unused_top_bits == 0) {
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.shr(tmp, int(page_table_const_bits)); code.shr(tmp, int(page_table_const_bits));
if (ctx.conf.page_table_log2_stride > 3) {
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp.cvt64()]);
} else {
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
}
} else if (ctx.conf.silently_mirror_page_table) { } else if (ctx.conf.silently_mirror_page_table) {
if (valid_page_index_bits >= 32) { if (valid_page_index_bits >= 32) {
if (code.HasHostFeature(HostFeature::BMI2)) { if (code.HasHostFeature(HostFeature::BMI2)) {
const Xbyak::Reg64 bit_count = ctx.reg_alloc.ScratchGpr(code); auto const bit_count = ctx.reg_alloc.ScratchGpr(code);
code.mov(bit_count, unused_top_bits); code.mov(bit_count, unused_top_bits);
code.bzhi(tmp, vaddr, bit_count); code.bzhi(tmp, vaddr, bit_count);
code.shr(tmp, int(page_table_const_bits)); code.shr(tmp, int(page_table_const_bits));
@@ -153,20 +159,32 @@ template<>
code.shr(tmp, int(page_table_const_bits)); code.shr(tmp, int(page_table_const_bits));
code.and_(tmp, u32((1 << valid_page_index_bits) - 1)); code.and_(tmp, u32((1 << valid_page_index_bits) - 1));
} }
} else {
ASSERT(valid_page_index_bits < 32);
code.mov(tmp, vaddr);
code.shr(tmp, int(page_table_const_bits));
code.test(tmp, u32(-(1 << valid_page_index_bits)));
code.jnz(abort, code.T_NEAR);
}
if (ctx.conf.page_table_log2_stride > 3) { if (ctx.conf.page_table_log2_stride > 3) {
code.shl(tmp, int(ctx.conf.page_table_log2_stride)); code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp.cvt64()]); code.mov(page, qword[r14 + tmp.cvt64()]);
} else { } else {
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]); code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
} }
} else {
// Common VA sizes: 39 - 12 => 27, 42 - 12 => 30
// Check if bits outside of VA space are non-zero
ASSERT(valid_page_index_bits < 32);
code.mov(tmp, vaddr);
if (ctx.conf.check_halt_on_memory_access) {
auto const tmp2 = ctx.reg_alloc.ScratchGpr(code);
code.mov(tmp2, u64(-(1ull << valid_page_index_bits) << page_table_const_bits));
code.test(tmp, tmp2);
code.jnz(abort, code.T_NEAR);
ctx.reg_alloc.Release(tmp2);
}
code.shr(tmp, int(page_table_const_bits));
if (ctx.conf.page_table_log2_stride > 3) {
code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp.cvt64()]);
} else {
code.mov(page, qword[r14 + tmp.cvt64() * int(1 << ctx.conf.page_table_log2_stride)]);
}
}
// check for marked bit, use as unmapped if marked // check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) { if (ctx.conf.page_table_marked_bit) {
@@ -193,7 +211,7 @@ template<>
return page + vaddr; return page + vaddr;
} }
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.and_(tmp, static_cast<u32>(page_table_const_mask)); code.and_(tmp, u32(page_table_const_mask));
return page + tmp; return page + tmp;
} }