Fix distant ARM64 memory fallback branches (#362)
Use an inverse local conditional branch followed by an unconditional branch for unbound or distant targets. Keep the original one-instruction form for bound near targets and preserve registers and NZCV. Adapt Eden 57c538a73d.
This commit is contained in:
parent
97e12e0498
commit
7b7dc386c8
|
|
@ -402,6 +402,7 @@ if(ARCHITECTURE_arm64)
|
|||
dynarmic/backend/arm64/emit_arm64_data_processing.cpp
|
||||
dynarmic/backend/arm64/emit_arm64_floating_point.cpp
|
||||
dynarmic/backend/arm64/emit_arm64_memory.cpp
|
||||
dynarmic/backend/arm64/emit_arm64_far_branch.h
|
||||
dynarmic/backend/arm64/emit_arm64_memory.h
|
||||
dynarmic/backend/arm64/emit_arm64_packed.cpp
|
||||
dynarmic/backend/arm64/emit_arm64_saturation.cpp
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "dynarmic/backend/arm64/a32_jitstate.h"
|
||||
#include "dynarmic/backend/arm64/abi.h"
|
||||
#include "dynarmic/backend/arm64/emit_arm64.h"
|
||||
#include "dynarmic/backend/arm64/emit_arm64_far_branch.h"
|
||||
#include "dynarmic/backend/arm64/emit_context.h"
|
||||
#include "dynarmic/backend/arm64/fpsr_manager.h"
|
||||
#include "dynarmic/backend/arm64/reg_alloc.h"
|
||||
|
|
@ -172,7 +173,7 @@ void EmitA32CheckMemoryAbort(oaknut::CodeGenerator& code, EmitContext& ctx, IR::
|
|||
|
||||
code.LDAR(Xscratch0, Xhalt);
|
||||
code.TST(Xscratch0, static_cast<u32>(HaltReason::MemoryAbort));
|
||||
code.B(EQ, end);
|
||||
EmitFarConditionalBranch(code, EQ, end);
|
||||
EmitSetUpperLocationDescriptor(code, ctx, current_location, ctx.block.Location());
|
||||
code.MOV(Wscratch0, current_location.PC());
|
||||
code.STR(Wscratch0, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * 15);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include "dynarmic/backend/arm64/a64_jitstate.h"
|
||||
#include "dynarmic/backend/arm64/abi.h"
|
||||
#include "dynarmic/backend/arm64/emit_arm64.h"
|
||||
#include "dynarmic/backend/arm64/emit_arm64_far_branch.h"
|
||||
#include "dynarmic/backend/arm64/emit_context.h"
|
||||
#include "dynarmic/backend/arm64/fpsr_manager.h"
|
||||
#include "dynarmic/backend/arm64/reg_alloc.h"
|
||||
|
|
@ -155,7 +156,7 @@ void EmitA64CheckMemoryAbort(oaknut::CodeGenerator& code, EmitContext& ctx, IR::
|
|||
|
||||
code.LDAR(Xscratch0, Xhalt);
|
||||
code.TST(Xscratch0, static_cast<u32>(HaltReason::MemoryAbort));
|
||||
code.B(EQ, end);
|
||||
EmitFarConditionalBranch(code, EQ, end);
|
||||
code.MOV(Xscratch0, current_location.PC());
|
||||
code.STR(Xscratch0, Xstate, offsetof(A64JitState, pc));
|
||||
EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
namespace Dynarmic::Backend::Arm64 {
|
||||
|
||||
// Deferred memory fallbacks can lie outside B.cond/CBZ/CBNZ's +/-1 MiB range.
|
||||
// Keep the conditional leg local and use B for the distant target. AddressSpace
|
||||
// limits the entire arm64 code cache to 128 MiB, within B's reach.
|
||||
// Bound targets within signed imm19 range retain the original single instruction.
|
||||
// Unbound fallbacks require the long form because Oaknut cannot expand a fixup.
|
||||
inline bool IsNearConditionalTarget(const oaknut::CodeGenerator& code,
|
||||
const oaknut::Label& target) {
|
||||
if (!target.is_bound()) {
|
||||
return false;
|
||||
}
|
||||
const auto distance = target.offset() - code.offset();
|
||||
return distance >= -(1 << 20) && distance <= (1 << 20) - 4;
|
||||
}
|
||||
|
||||
inline void EmitFarConditionalBranch(oaknut::CodeGenerator& code, oaknut::Cond cond,
|
||||
oaknut::Label& target) {
|
||||
if (IsNearConditionalTarget(code, target)) {
|
||||
code.B(cond, target);
|
||||
return;
|
||||
}
|
||||
oaknut::Label continue_execution;
|
||||
code.B(oaknut::invert(cond), continue_execution);
|
||||
code.B(target);
|
||||
code.l(continue_execution);
|
||||
}
|
||||
|
||||
template<typename Reg>
|
||||
void EmitFarZeroBranch(oaknut::CodeGenerator& code, Reg reg, oaknut::Label& target) {
|
||||
if (IsNearConditionalTarget(code, target)) {
|
||||
code.CBZ(reg, target);
|
||||
return;
|
||||
}
|
||||
oaknut::Label continue_execution;
|
||||
code.CBNZ(reg, continue_execution);
|
||||
code.B(target);
|
||||
code.l(continue_execution);
|
||||
}
|
||||
|
||||
template<typename Reg>
|
||||
void EmitFarNonZeroBranch(oaknut::CodeGenerator& code, Reg reg, oaknut::Label& target) {
|
||||
if (IsNearConditionalTarget(code, target)) {
|
||||
code.CBNZ(reg, target);
|
||||
return;
|
||||
}
|
||||
oaknut::Label continue_execution;
|
||||
code.CBZ(reg, continue_execution);
|
||||
code.B(target);
|
||||
code.l(continue_execution);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::Arm64
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "dynarmic/backend/arm64/abi.h"
|
||||
#include "dynarmic/backend/arm64/emit_arm64.h"
|
||||
#include "dynarmic/backend/arm64/emit_arm64_far_branch.h"
|
||||
#include "dynarmic/backend/arm64/emit_context.h"
|
||||
#include "dynarmic/backend/arm64/fastmem.h"
|
||||
#include "dynarmic/backend/arm64/fpsr_manager.h"
|
||||
|
|
@ -237,12 +238,12 @@ void EmitDetectMisalignedVAddr(oaknut::CodeGenerator& code, EmitContext& ctx, oa
|
|||
}();
|
||||
|
||||
code.TST(Xaddr, align_mask);
|
||||
code.B(NE, *fallback);
|
||||
EmitFarConditionalBranch(code, NE, *fallback);
|
||||
} else {
|
||||
// If (addr & page_mask) > page_size - byte_size, use fallback.
|
||||
code.AND(Xscratch0, Xaddr, page_mask);
|
||||
code.CMP(Xscratch0, page_size - bitsize / 8);
|
||||
code.B(HI, *fallback);
|
||||
EmitFarConditionalBranch(code, HI, *fallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -262,7 +263,7 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
|||
} else {
|
||||
code.LSR(Xscratch0, Xaddr, page_bits);
|
||||
code.TST(Xscratch0, u64(~u64(0)) << valid_page_index_bits);
|
||||
code.B(NE, *fallback);
|
||||
EmitFarConditionalBranch(code, NE, *fallback);
|
||||
}
|
||||
|
||||
code.LDR(Xscratch0, Xpagetable, Xscratch0, LSL, 3);
|
||||
|
|
@ -272,7 +273,7 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
|
|||
code.AND(Xscratch0, Xscratch0, mask);
|
||||
}
|
||||
|
||||
code.CBZ(Xscratch0, *fallback);
|
||||
EmitFarZeroBranch(code, Xscratch0, *fallback);
|
||||
|
||||
if (ctx.conf.absolute_offset_page_table) {
|
||||
return std::make_pair(Xscratch0, Xaddr);
|
||||
|
|
@ -517,7 +518,7 @@ std::pair<oaknut::XReg, oaknut::XReg> FastmemEmitVAddrLookup(oaknut::CodeGenerat
|
|||
}
|
||||
|
||||
code.LSR(Xscratch0, Xaddr, ctx.conf.fastmem_address_space_bits);
|
||||
code.CBNZ(Xscratch0, *fallback);
|
||||
EmitFarNonZeroBranch(code, Xscratch0, *fallback);
|
||||
return std::make_pair(Xfastmem, Xaddr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
<ClInclude Include="dynarmic\backend\arm64\address_space.h" />
|
||||
<ClInclude Include="dynarmic\backend\arm64\devirtualize.h" />
|
||||
<ClInclude Include="dynarmic\backend\arm64\emit_arm64.h" />
|
||||
<ClInclude Include="dynarmic\backend\arm64\emit_arm64_far_branch.h" />
|
||||
<ClInclude Include="dynarmic\backend\arm64\emit_arm64_memory.h" />
|
||||
<ClInclude Include="dynarmic\backend\arm64\emit_context.h" />
|
||||
<ClInclude Include="dynarmic\backend\arm64\fastmem.h" />
|
||||
|
|
|
|||
|
|
@ -219,6 +219,9 @@
|
|||
<ClInclude Include="dynarmic\backend\arm64\emit_arm64.h">
|
||||
<Filter>Header Files\dynamic\backend\arm64</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dynarmic\backend\arm64\emit_arm64_far_branch.h">
|
||||
<Filter>Header Files\dynamic\backend\arm64</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dynarmic\backend\arm64\emit_arm64_memory.h">
|
||||
<Filter>Header Files\dynamic\backend\arm64</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
Loading…
Reference in New Issue