CPU/Recompiler: Tidy up type names

And reduce global namespace pollution.
This commit is contained in:
Stenzek 2024-11-21 16:42:33 +10:00
parent 6cb5aa9ab4
commit 7e23a23536
No known key found for this signature in database
17 changed files with 861 additions and 959 deletions

View File

@ -129,8 +129,6 @@ add_library(core
set(RECOMPILER_SRCS
cpu_recompiler.cpp
cpu_recompiler.h
cpu_recompiler_thunks.h
cpu_recompiler_types.h
)
target_precompile_headers(core PRIVATE "pch.h")

View File

@ -106,8 +106,6 @@
<ClInclude Include="cpu_recompiler_x64.h">
<ExcludedFromBuild Condition="'$(Platform)'!='x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="cpu_recompiler_thunks.h" />
<ClInclude Include="cpu_recompiler_types.h" />
<ClInclude Include="digital_controller.h" />
<ClInclude Include="fullscreen_ui.h" />
<ClInclude Include="game_database.h" />

View File

@ -90,9 +90,7 @@
<ClInclude Include="gpu_sw.h" />
<ClInclude Include="gpu_hw_shadergen.h" />
<ClInclude Include="bios.h" />
<ClInclude Include="cpu_recompiler_types.h" />
<ClInclude Include="cpu_code_cache.h" />
<ClInclude Include="cpu_recompiler_thunks.h" />
<ClInclude Include="sio.h" />
<ClInclude Include="controller.h" />
<ClInclude Include="analog_controller.h" />

View File

@ -6,7 +6,6 @@
#include "cpu_core.h"
#include "cpu_core_private.h"
#include "cpu_disasm.h"
#include "cpu_recompiler_types.h"
#include "host.h"
#include "settings.h"
#include "system.h"
@ -1564,7 +1563,7 @@ bool CPU::CodeCache::CompileBlock(Block* block)
#ifdef ENABLE_RECOMPILER
if (g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler)
host_code = Recompiler::g_compiler->CompileBlock(block, &host_code_size, &host_far_code_size);
host_code = g_compiler->CompileBlock(block, &host_code_size, &host_far_code_size);
#endif
block->host_code = host_code;

View File

@ -7,7 +7,6 @@
#include "cpu_core_private.h"
#include "cpu_disasm.h"
#include "cpu_pgxp.h"
#include "cpu_recompiler_thunks.h"
#include "gte.h"
#include "host.h"
#include "pcdrv.h"
@ -2628,13 +2627,13 @@ template void CPU::CodeCache::InterpretUncachedBlock<PGXPMode::Disabled>();
template void CPU::CodeCache::InterpretUncachedBlock<PGXPMode::Memory>();
template void CPU::CodeCache::InterpretUncachedBlock<PGXPMode::CPU>();
bool CPU::Recompiler::Thunks::InterpretInstruction()
bool CPU::RecompilerThunks::InterpretInstruction()
{
ExecuteInstruction<PGXPMode::Disabled, false>();
return g_state.exception_raised;
}
bool CPU::Recompiler::Thunks::InterpretInstructionPGXP()
bool CPU::RecompilerThunks::InterpretInstructionPGXP()
{
ExecuteInstruction<PGXPMode::Memory, false>();
return g_state.exception_raised;
@ -3477,7 +3476,7 @@ bool CPU::WriteMemoryWord(VirtualMemoryAddress addr, u32 value)
return true;
}
u64 CPU::Recompiler::Thunks::ReadMemoryByte(u32 address)
u64 CPU::RecompilerThunks::ReadMemoryByte(u32 address)
{
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::Byte)(address);
if (g_state.bus_error) [[unlikely]]
@ -3490,7 +3489,7 @@ u64 CPU::Recompiler::Thunks::ReadMemoryByte(u32 address)
return ZeroExtend64(value);
}
u64 CPU::Recompiler::Thunks::ReadMemoryHalfWord(u32 address)
u64 CPU::RecompilerThunks::ReadMemoryHalfWord(u32 address)
{
if (!Common::IsAlignedPow2(address, 2)) [[unlikely]]
{
@ -3509,7 +3508,7 @@ u64 CPU::Recompiler::Thunks::ReadMemoryHalfWord(u32 address)
return ZeroExtend64(value);
}
u64 CPU::Recompiler::Thunks::ReadMemoryWord(u32 address)
u64 CPU::RecompilerThunks::ReadMemoryWord(u32 address)
{
if (!Common::IsAlignedPow2(address, 4)) [[unlikely]]
{
@ -3528,7 +3527,7 @@ u64 CPU::Recompiler::Thunks::ReadMemoryWord(u32 address)
return ZeroExtend64(value);
}
u32 CPU::Recompiler::Thunks::WriteMemoryByte(u32 address, u32 value)
u32 CPU::RecompilerThunks::WriteMemoryByte(u32 address, u32 value)
{
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Byte, address, value);
@ -3542,7 +3541,7 @@ u32 CPU::Recompiler::Thunks::WriteMemoryByte(u32 address, u32 value)
return 0;
}
u32 CPU::Recompiler::Thunks::WriteMemoryHalfWord(u32 address, u32 value)
u32 CPU::RecompilerThunks::WriteMemoryHalfWord(u32 address, u32 value)
{
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::HalfWord, address, value);
@ -3562,7 +3561,7 @@ u32 CPU::Recompiler::Thunks::WriteMemoryHalfWord(u32 address, u32 value)
return 0;
}
u32 CPU::Recompiler::Thunks::WriteMemoryWord(u32 address, u32 value)
u32 CPU::RecompilerThunks::WriteMemoryWord(u32 address, u32 value)
{
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Word, address, value);
@ -3582,40 +3581,40 @@ u32 CPU::Recompiler::Thunks::WriteMemoryWord(u32 address, u32 value)
return 0;
}
u32 CPU::Recompiler::Thunks::UncheckedReadMemoryByte(u32 address)
u32 CPU::RecompilerThunks::UncheckedReadMemoryByte(u32 address)
{
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::Byte)(address);
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::Byte, address, value);
return value;
}
u32 CPU::Recompiler::Thunks::UncheckedReadMemoryHalfWord(u32 address)
u32 CPU::RecompilerThunks::UncheckedReadMemoryHalfWord(u32 address)
{
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::HalfWord)(address);
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::HalfWord, address, value);
return value;
}
u32 CPU::Recompiler::Thunks::UncheckedReadMemoryWord(u32 address)
u32 CPU::RecompilerThunks::UncheckedReadMemoryWord(u32 address)
{
const u32 value = GetMemoryReadHandler(address, MemoryAccessSize::Word)(address);
MEMORY_BREAKPOINT(MemoryAccessType::Read, MemoryAccessSize::Word, address, value);
return value;
}
void CPU::Recompiler::Thunks::UncheckedWriteMemoryByte(u32 address, u32 value)
void CPU::RecompilerThunks::UncheckedWriteMemoryByte(u32 address, u32 value)
{
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Byte, address, value);
GetMemoryWriteHandler(address, MemoryAccessSize::Byte)(address, value);
}
void CPU::Recompiler::Thunks::UncheckedWriteMemoryHalfWord(u32 address, u32 value)
void CPU::RecompilerThunks::UncheckedWriteMemoryHalfWord(u32 address, u32 value)
{
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::HalfWord, address, value);
GetMemoryWriteHandler(address, MemoryAccessSize::HalfWord)(address, value);
}
void CPU::Recompiler::Thunks::UncheckedWriteMemoryWord(u32 address, u32 value)
void CPU::RecompilerThunks::UncheckedWriteMemoryWord(u32 address, u32 value)
{
MEMORY_BREAKPOINT(MemoryAccessType::Write, MemoryAccessSize::Word, address, value);
GetMemoryWriteHandler(address, MemoryAccessSize::Word)(address, value);

View File

@ -132,4 +132,36 @@ ALWAYS_INLINE static void StallUntilGTEComplete()
void HandleA0Syscall();
void HandleB0Syscall();
#ifdef ENABLE_RECOMPILER
namespace RecompilerThunks {
//////////////////////////////////////////////////////////////////////////
// Trampolines for calling back from the JIT
// Needed because we can't cast member functions to void*...
// TODO: Abuse carry flag or something else for exception
//////////////////////////////////////////////////////////////////////////
bool InterpretInstruction();
bool InterpretInstructionPGXP();
// Memory access functions for the JIT - MSB is set on exception.
u64 ReadMemoryByte(u32 address);
u64 ReadMemoryHalfWord(u32 address);
u64 ReadMemoryWord(u32 address);
u32 WriteMemoryByte(u32 address, u32 value);
u32 WriteMemoryHalfWord(u32 address, u32 value);
u32 WriteMemoryWord(u32 address, u32 value);
// Unchecked memory access variants. No alignment or bus exceptions.
u32 UncheckedReadMemoryByte(u32 address);
u32 UncheckedReadMemoryHalfWord(u32 address);
u32 UncheckedReadMemoryWord(u32 address);
void UncheckedWriteMemoryByte(u32 address, u32 value);
void UncheckedWriteMemoryHalfWord(u32 address, u32 value);
void UncheckedWriteMemoryWord(u32 address, u32 value);
} // namespace RecompilerThunks
#endif
} // namespace CPU

View File

@ -4,7 +4,6 @@
#pragma once
#include "cpu_code_cache_private.h"
#include "cpu_recompiler_types.h"
#include "cpu_types.h"
#include <array>
@ -13,36 +12,72 @@
#include <utility>
#include <vector>
namespace CPU::Recompiler {
namespace CPU {
// TODO: Get rid of the virtuals... somehow.
class Recompiler
{
public:
// Global options
static constexpr bool EMULATE_LOAD_DELAYS = true;
static constexpr bool SWAP_BRANCH_DELAY_SLOTS = true;
// Arch-specific options
#if defined(CPU_ARCH_X64)
// A reasonable "maximum" number of bytes per instruction.
static constexpr u32 MAX_NEAR_HOST_BYTES_PER_INSTRUCTION = 64;
static constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
// Number of host registers.
static constexpr u32 NUM_HOST_REGS = 16;
static constexpr bool HAS_MEMORY_OPERANDS = true;
#elif defined(CPU_ARCH_ARM32)
// A reasonable "maximum" number of bytes per instruction.
static constexpr u32 MAX_NEAR_HOST_BYTES_PER_INSTRUCTION = 64;
static constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
// Number of host registers.
static constexpr u32 NUM_HOST_REGS = 16;
static constexpr bool HAS_MEMORY_OPERANDS = false;
#elif defined(CPU_ARCH_ARM64)
// Number of host registers.
static constexpr u32 NUM_HOST_REGS = 32;
static constexpr bool HAS_MEMORY_OPERANDS = false;
// A reasonable "maximum" number of bytes per instruction.
static constexpr u32 MAX_NEAR_HOST_BYTES_PER_INSTRUCTION = 64;
static constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
#elif defined(CPU_ARCH_RISCV64)
// Number of host registers.
static constexpr u32 NUM_HOST_REGS = 32;
static constexpr bool HAS_MEMORY_OPERANDS = false;
// A reasonable "maximum" number of bytes per instruction.
static constexpr u32 MAX_NEAR_HOST_BYTES_PER_INSTRUCTION = 64;
static constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
#endif
// TODO: Get rid of the virtuals... somehow.
class Recompiler
{
public:
Recompiler();
virtual ~Recompiler();
const void* CompileBlock(CodeCache::Block* block, u32* host_code_size, u32* host_far_code_size);
static void BackpatchLoadStore(void* exception_pc, const CodeCache::LoadstoreBackpatchInfo& info);
static u32 CompileLoadStoreThunk(void* thunk_code, u32 thunk_space, void* code_address, u32 code_size,
TickCount cycles_to_add, TickCount cycles_to_remove, u32 gpr_bitmask,
u8 address_register, u8 data_register, MemoryAccessSize size, bool is_signed,
bool is_load);
protected:
enum FlushFlags : u32
{
@ -533,11 +568,5 @@ protected:
static const std::array<const void*, 3> s_pgxp_mem_store_functions;
};
void BackpatchLoadStore(void* exception_pc, const CodeCache::LoadstoreBackpatchInfo& info);
u32 CompileLoadStoreThunk(void* thunk_code, u32 thunk_space, void* code_address, u32 code_size, TickCount cycles_to_add,
TickCount cycles_to_remove, u32 gpr_bitmask, u8 address_register, u8 data_register,
MemoryAccessSize size, bool is_signed, bool is_load);
extern Recompiler* g_compiler;
} // namespace CPU::Recompiler
} // namespace CPU

View File

@ -4,8 +4,6 @@
#include "cpu_recompiler_arm32.h"
#include "cpu_core_private.h"
#include "cpu_pgxp.h"
#include "cpu_recompiler_thunks.h"
#include "cpu_recompiler_types.h"
#include "gte.h"
#include "settings.h"
#include "timing_event.h"
@ -20,6 +18,9 @@
#ifdef CPU_ARCH_ARM32
#include "vixl/aarch32/constants-aarch32.h"
#include "vixl/aarch32/instructions-aarch32.h"
#ifdef ENABLE_HOST_DISASSEMBLY
#include "vixl/aarch32/disasm-aarch32.h"
#include <iostream>
@ -30,43 +31,64 @@ LOG_CHANNEL(Recompiler);
#define PTR(x) vixl::aarch32::MemOperand(RSTATE, (((u8*)(x)) - ((u8*)&g_state)))
#define RMEMBASE vixl::aarch32::r3
namespace CPU::Recompiler {
static constexpr u32 FUNCTION_CALLEE_SAVED_SPACE_RESERVE = 80; // 8 registers
static constexpr u32 FUNCTION_CALLER_SAVED_SPACE_RESERVE = 144; // 18 registers -> 224 bytes
static constexpr u32 FUNCTION_STACK_SIZE = FUNCTION_CALLEE_SAVED_SPACE_RESERVE + FUNCTION_CALLER_SAVED_SPACE_RESERVE;
using namespace vixl::aarch32;
#define RRET vixl::aarch32::r0
#define RRETHI vixl::aarch32::r1
#define RARG1 vixl::aarch32::r0
#define RARG2 vixl::aarch32::r1
#define RARG3 vixl::aarch32::r2
#define RSCRATCH vixl::aarch32::r12
#define RSTATE vixl::aarch32::r4
constexpr u32 FUNCTION_CALLEE_SAVED_SPACE_RESERVE = 80; // 8 registers
constexpr u32 FUNCTION_CALLER_SAVED_SPACE_RESERVE = 144; // 18 registers -> 224 bytes
constexpr u32 FUNCTION_STACK_SIZE = FUNCTION_CALLEE_SAVED_SPACE_RESERVE + FUNCTION_CALLER_SAVED_SPACE_RESERVE;
static bool armIsCallerSavedRegister(u32 id);
static s32 armGetPCDisplacement(const void* current, const void* target);
static bool armIsPCDisplacementInImmediateRange(s32 displacement);
static void armMoveAddressToReg(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr);
static void armEmitMov(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& rd, u32 imm);
static void armEmitJmp(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_inline);
static void armEmitCall(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_inline);
static void armEmitCondBranch(vixl::aarch32::Assembler* armAsm, vixl::aarch32::Condition cond, const void* ptr);
static void armEmitFarLoad(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr);
static void armEmitFarStore(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr,
const vixl::aarch32::Register& tempreg = RSCRATCH);
static u8* armGetJumpTrampoline(const void* target);
static constexpr u32 TRAMPOLINE_AREA_SIZE = 4 * 1024;
static std::unordered_map<const void*, u32> s_trampoline_targets;
static u8* s_trampoline_start_ptr = nullptr;
static u32 s_trampoline_used = 0;
namespace CPU {
using namespace vixl::aarch32;
static ARM32Recompiler s_instance;
Recompiler* g_compiler = &s_instance;
} // namespace CPU::Recompiler
} // namespace CPU
bool CPU::Recompiler::armIsCallerSavedRegister(u32 id)
bool armIsCallerSavedRegister(u32 id)
{
return ((id >= 0 && id <= 3) || // r0-r3
(id == 12 || id == 14)); // sp, pc
}
s32 CPU::Recompiler::armGetPCDisplacement(const void* current, const void* target)
s32 armGetPCDisplacement(const void* current, const void* target)
{
Assert(Common::IsAlignedPow2(reinterpret_cast<size_t>(current), 4));
Assert(Common::IsAlignedPow2(reinterpret_cast<size_t>(target), 4));
return static_cast<s32>((reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(current)));
}
bool CPU::Recompiler::armIsPCDisplacementInImmediateRange(s32 displacement)
bool armIsPCDisplacementInImmediateRange(s32 displacement)
{
return (displacement >= -33554432 && displacement <= 33554428);
}
void CPU::Recompiler::armEmitMov(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& rd, u32 imm)
void armEmitMov(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& rd, u32 imm)
{
if (vixl::IsUintN(16, imm))
{
@ -78,13 +100,12 @@ void CPU::Recompiler::armEmitMov(vixl::aarch32::Assembler* armAsm, const vixl::a
armAsm->movt(al, rd, imm >> 16);
}
void CPU::Recompiler::armMoveAddressToReg(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg,
const void* addr)
void armMoveAddressToReg(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr)
{
armEmitMov(armAsm, reg, static_cast<u32>(reinterpret_cast<uintptr_t>(addr)));
}
void CPU::Recompiler::armEmitJmp(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_inline)
void armEmitJmp(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_inline)
{
const void* cur = armAsm->GetCursorAddress<const void*>();
s32 displacement = armGetPCDisplacement(cur, ptr);
@ -110,7 +131,7 @@ void CPU::Recompiler::armEmitJmp(vixl::aarch32::Assembler* armAsm, const void* p
}
}
void CPU::Recompiler::armEmitCall(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_inline)
void armEmitCall(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_inline)
{
const void* cur = armAsm->GetCursorAddress<const void*>();
s32 displacement = armGetPCDisplacement(cur, ptr);
@ -136,8 +157,7 @@ void CPU::Recompiler::armEmitCall(vixl::aarch32::Assembler* armAsm, const void*
}
}
void CPU::Recompiler::armEmitCondBranch(vixl::aarch32::Assembler* armAsm, vixl::aarch32::Condition cond,
const void* ptr)
void armEmitCondBranch(vixl::aarch32::Assembler* armAsm, vixl::aarch32::Condition cond, const void* ptr)
{
const s32 displacement = armGetPCDisplacement(armAsm->GetCursorAddress<const void*>(), ptr);
if (!armIsPCDisplacementInImmediateRange(displacement))
@ -152,15 +172,14 @@ void CPU::Recompiler::armEmitCondBranch(vixl::aarch32::Assembler* armAsm, vixl::
}
}
void CPU::Recompiler::armEmitFarLoad(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg,
const void* addr)
void armEmitFarLoad(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr)
{
armMoveAddressToReg(armAsm, reg, addr);
armAsm->ldr(reg, MemOperand(reg));
}
void CPU::Recompiler::armEmitFarStore(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg,
const void* addr, const vixl::aarch32::Register& tempreg)
void armEmitFarStore(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr,
const vixl::aarch32::Register& tempreg)
{
armMoveAddressToReg(armAsm, tempreg, addr);
armAsm->str(reg, MemOperand(tempreg));
@ -319,19 +338,19 @@ u32 CPU::CodeCache::EmitASMFunctions(void* code, u32 code_size)
return static_cast<u32>(armAsm->GetCursorOffset()) /* + TRAMPOLINE_AREA_SIZE*/;
}
CPU::Recompiler::ARM32Recompiler::ARM32Recompiler() : m_emitter(A32), m_far_emitter(A32)
CPU::ARM32Recompiler::ARM32Recompiler() : m_emitter(A32), m_far_emitter(A32)
{
}
CPU::Recompiler::ARM32Recompiler::~ARM32Recompiler() = default;
CPU::ARM32Recompiler::~ARM32Recompiler() = default;
const void* CPU::Recompiler::ARM32Recompiler::GetCurrentCodePointer()
const void* CPU::ARM32Recompiler::GetCurrentCodePointer()
{
return armAsm->GetCursorAddress<const void*>();
}
void CPU::Recompiler::ARM32Recompiler::Reset(CodeCache::Block* block, u8* code_buffer, u32 code_buffer_space,
u8* far_code_buffer, u32 far_code_space)
void CPU::ARM32Recompiler::Reset(CodeCache::Block* block, u8* code_buffer, u32 code_buffer_space, u8* far_code_buffer,
u32 far_code_space)
{
Recompiler::Reset(block, code_buffer, code_buffer_space, far_code_buffer, far_code_space);
@ -369,7 +388,7 @@ void CPU::Recompiler::ARM32Recompiler::Reset(CodeCache::Block* block, u8* code_b
}
}
void CPU::Recompiler::ARM32Recompiler::SwitchToFarCode(bool emit_jump, vixl::aarch32::ConditionType cond)
void CPU::ARM32Recompiler::SwitchToFarCode(bool emit_jump, vixl::aarch32::ConditionType cond)
{
DebugAssert(armAsm == &m_emitter);
if (emit_jump)
@ -395,7 +414,7 @@ void CPU::Recompiler::ARM32Recompiler::SwitchToFarCode(bool emit_jump, vixl::aar
armAsm = &m_far_emitter;
}
void CPU::Recompiler::ARM32Recompiler::SwitchToFarCodeIfBitSet(const vixl::aarch32::Register& reg, u32 bit)
void CPU::ARM32Recompiler::SwitchToFarCodeIfBitSet(const vixl::aarch32::Register& reg, u32 bit)
{
armAsm->tst(reg, 1u << bit);
@ -416,8 +435,7 @@ void CPU::Recompiler::ARM32Recompiler::SwitchToFarCodeIfBitSet(const vixl::aarch
armAsm = &m_far_emitter;
}
void CPU::Recompiler::ARM32Recompiler::SwitchToFarCodeIfRegZeroOrNonZero(const vixl::aarch32::Register& reg,
bool nonzero)
void CPU::ARM32Recompiler::SwitchToFarCodeIfRegZeroOrNonZero(const vixl::aarch32::Register& reg, bool nonzero)
{
armAsm->cmp(reg, 0);
@ -438,7 +456,7 @@ void CPU::Recompiler::ARM32Recompiler::SwitchToFarCodeIfRegZeroOrNonZero(const v
armAsm = &m_far_emitter;
}
void CPU::Recompiler::ARM32Recompiler::SwitchToNearCode(bool emit_jump, vixl::aarch32::ConditionType cond)
void CPU::ARM32Recompiler::SwitchToNearCode(bool emit_jump, vixl::aarch32::ConditionType cond)
{
DebugAssert(armAsm == &m_far_emitter);
if (emit_jump)
@ -464,17 +482,17 @@ void CPU::Recompiler::ARM32Recompiler::SwitchToNearCode(bool emit_jump, vixl::aa
armAsm = &m_emitter;
}
void CPU::Recompiler::ARM32Recompiler::EmitMov(const vixl::aarch32::Register& dst, u32 val)
void CPU::ARM32Recompiler::EmitMov(const vixl::aarch32::Register& dst, u32 val)
{
armEmitMov(armAsm, dst, val);
}
void CPU::Recompiler::ARM32Recompiler::EmitCall(const void* ptr, bool force_inline /*= false*/)
void CPU::ARM32Recompiler::EmitCall(const void* ptr, bool force_inline /*= false*/)
{
armEmitCall(armAsm, ptr, force_inline);
}
vixl::aarch32::Operand CPU::Recompiler::ARM32Recompiler::armCheckAddSubConstant(s32 val)
vixl::aarch32::Operand CPU::ARM32Recompiler::armCheckAddSubConstant(s32 val)
{
if (ImmediateA32::IsImmediateA32(static_cast<u32>(val)))
return vixl::aarch32::Operand(static_cast<int32_t>(val));
@ -483,27 +501,27 @@ vixl::aarch32::Operand CPU::Recompiler::ARM32Recompiler::armCheckAddSubConstant(
return vixl::aarch32::Operand(RSCRATCH);
}
vixl::aarch32::Operand CPU::Recompiler::ARM32Recompiler::armCheckAddSubConstant(u32 val)
vixl::aarch32::Operand CPU::ARM32Recompiler::armCheckAddSubConstant(u32 val)
{
return armCheckAddSubConstant(static_cast<s32>(val));
}
vixl::aarch32::Operand CPU::Recompiler::ARM32Recompiler::armCheckCompareConstant(s32 val)
vixl::aarch32::Operand CPU::ARM32Recompiler::armCheckCompareConstant(s32 val)
{
return armCheckAddSubConstant(val);
}
vixl::aarch32::Operand CPU::Recompiler::ARM32Recompiler::armCheckLogicalConstant(u32 val)
vixl::aarch32::Operand CPU::ARM32Recompiler::armCheckLogicalConstant(u32 val)
{
return armCheckAddSubConstant(val);
}
void CPU::Recompiler::ARM32Recompiler::BeginBlock()
void CPU::ARM32Recompiler::BeginBlock()
{
Recompiler::BeginBlock();
}
void CPU::Recompiler::ARM32Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, const u8* shadow_ptr, u32 size)
void CPU::ARM32Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, const u8* shadow_ptr, u32 size)
{
// store it first to reduce code size, because we can offset
armMoveAddressToReg(armAsm, RARG1, ram_ptr);
@ -579,7 +597,7 @@ bool foo(const void* a, const void* b)
armAsm->bind(&block_unchanged);
}
void CPU::Recompiler::ARM32Recompiler::GenerateICacheCheckAndUpdate()
void CPU::ARM32Recompiler::GenerateICacheCheckAndUpdate()
{
if (!m_block->HasFlag(CodeCache::BlockFlags::IsUsingICache))
{
@ -635,7 +653,7 @@ void CPU::Recompiler::ARM32Recompiler::GenerateICacheCheckAndUpdate()
}
}
void CPU::Recompiler::ARM32Recompiler::GenerateCall(const void* func, s32 arg1reg /*= -1*/, s32 arg2reg /*= -1*/,
void CPU::ARM32Recompiler::GenerateCall(const void* func, s32 arg1reg /*= -1*/, s32 arg2reg /*= -1*/,
s32 arg3reg /*= -1*/)
{
if (arg1reg >= 0 && arg1reg != static_cast<s32>(RARG1.GetCode()))
@ -647,7 +665,7 @@ void CPU::Recompiler::ARM32Recompiler::GenerateCall(const void* func, s32 arg1re
EmitCall(func);
}
void CPU::Recompiler::ARM32Recompiler::EndBlock(const std::optional<u32>& newpc, bool do_event_test)
void CPU::ARM32Recompiler::EndBlock(const std::optional<u32>& newpc, bool do_event_test)
{
if (newpc.has_value())
{
@ -664,7 +682,7 @@ void CPU::Recompiler::ARM32Recompiler::EndBlock(const std::optional<u32>& newpc,
EndAndLinkBlock(newpc, do_event_test, false);
}
void CPU::Recompiler::ARM32Recompiler::EndBlockWithException(Exception excode)
void CPU::ARM32Recompiler::EndBlockWithException(Exception excode)
{
// flush regs, but not pc, it's going to get overwritten
// flush cycles because of the GTE instruction stuff...
@ -682,8 +700,7 @@ void CPU::Recompiler::ARM32Recompiler::EndBlockWithException(Exception excode)
EndAndLinkBlock(std::nullopt, true, false);
}
void CPU::Recompiler::ARM32Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test,
bool force_run_events)
void CPU::ARM32Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, bool force_run_events)
{
// event test
// pc should've been flushed
@ -740,7 +757,7 @@ void CPU::Recompiler::ARM32Recompiler::EndAndLinkBlock(const std::optional<u32>&
}
}
const void* CPU::Recompiler::ARM32Recompiler::EndCompile(u32* code_size, u32* far_code_size)
const void* CPU::ARM32Recompiler::EndCompile(u32* code_size, u32* far_code_size)
{
#ifdef VIXL_DEBUG
m_emitter_check.reset();
@ -757,7 +774,7 @@ const void* CPU::Recompiler::ARM32Recompiler::EndCompile(u32* code_size, u32* fa
return code;
}
const char* CPU::Recompiler::ARM32Recompiler::GetHostRegName(u32 reg) const
const char* CPU::ARM32Recompiler::GetHostRegName(u32 reg) const
{
static constexpr std::array<const char*, 32> reg64_names = {
{"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
@ -765,80 +782,80 @@ const char* CPU::Recompiler::ARM32Recompiler::GetHostRegName(u32 reg) const
return (reg < reg64_names.size()) ? reg64_names[reg] : "UNKNOWN";
}
void CPU::Recompiler::ARM32Recompiler::LoadHostRegWithConstant(u32 reg, u32 val)
void CPU::ARM32Recompiler::LoadHostRegWithConstant(u32 reg, u32 val)
{
EmitMov(Register(reg), val);
}
void CPU::Recompiler::ARM32Recompiler::LoadHostRegFromCPUPointer(u32 reg, const void* ptr)
void CPU::ARM32Recompiler::LoadHostRegFromCPUPointer(u32 reg, const void* ptr)
{
armAsm->ldr(Register(reg), PTR(ptr));
}
void CPU::Recompiler::ARM32Recompiler::StoreHostRegToCPUPointer(u32 reg, const void* ptr)
void CPU::ARM32Recompiler::StoreHostRegToCPUPointer(u32 reg, const void* ptr)
{
armAsm->str(Register(reg), PTR(ptr));
}
void CPU::Recompiler::ARM32Recompiler::StoreConstantToCPUPointer(u32 val, const void* ptr)
void CPU::ARM32Recompiler::StoreConstantToCPUPointer(u32 val, const void* ptr)
{
EmitMov(RSCRATCH, val);
armAsm->str(RSCRATCH, PTR(ptr));
}
void CPU::Recompiler::ARM32Recompiler::CopyHostReg(u32 dst, u32 src)
void CPU::ARM32Recompiler::CopyHostReg(u32 dst, u32 src)
{
if (src != dst)
armAsm->mov(Register(dst), Register(src));
}
void CPU::Recompiler::ARM32Recompiler::AssertRegOrConstS(CompileFlags cf) const
void CPU::ARM32Recompiler::AssertRegOrConstS(CompileFlags cf) const
{
DebugAssert(cf.valid_host_s || cf.const_s);
}
void CPU::Recompiler::ARM32Recompiler::AssertRegOrConstT(CompileFlags cf) const
void CPU::ARM32Recompiler::AssertRegOrConstT(CompileFlags cf) const
{
DebugAssert(cf.valid_host_t || cf.const_t);
}
vixl::aarch32::MemOperand CPU::Recompiler::ARM32Recompiler::MipsPtr(Reg r) const
vixl::aarch32::MemOperand CPU::ARM32Recompiler::MipsPtr(Reg r) const
{
DebugAssert(r < Reg::count);
return PTR(&g_state.regs.r[static_cast<u32>(r)]);
}
vixl::aarch32::Register CPU::Recompiler::ARM32Recompiler::CFGetRegD(CompileFlags cf) const
vixl::aarch32::Register CPU::ARM32Recompiler::CFGetRegD(CompileFlags cf) const
{
DebugAssert(cf.valid_host_d);
return Register(cf.host_d);
}
vixl::aarch32::Register CPU::Recompiler::ARM32Recompiler::CFGetRegS(CompileFlags cf) const
vixl::aarch32::Register CPU::ARM32Recompiler::CFGetRegS(CompileFlags cf) const
{
DebugAssert(cf.valid_host_s);
return Register(cf.host_s);
}
vixl::aarch32::Register CPU::Recompiler::ARM32Recompiler::CFGetRegT(CompileFlags cf) const
vixl::aarch32::Register CPU::ARM32Recompiler::CFGetRegT(CompileFlags cf) const
{
DebugAssert(cf.valid_host_t);
return Register(cf.host_t);
}
vixl::aarch32::Register CPU::Recompiler::ARM32Recompiler::CFGetRegLO(CompileFlags cf) const
vixl::aarch32::Register CPU::ARM32Recompiler::CFGetRegLO(CompileFlags cf) const
{
DebugAssert(cf.valid_host_lo);
return Register(cf.host_lo);
}
vixl::aarch32::Register CPU::Recompiler::ARM32Recompiler::CFGetRegHI(CompileFlags cf) const
vixl::aarch32::Register CPU::ARM32Recompiler::CFGetRegHI(CompileFlags cf) const
{
DebugAssert(cf.valid_host_hi);
return Register(cf.host_hi);
}
vixl::aarch32::Register CPU::Recompiler::ARM32Recompiler::GetMembaseReg()
vixl::aarch32::Register CPU::ARM32Recompiler::GetMembaseReg()
{
const u32 code = RMEMBASE.GetCode();
if (!IsHostRegAllocated(code))
@ -852,7 +869,7 @@ vixl::aarch32::Register CPU::Recompiler::ARM32Recompiler::GetMembaseReg()
return RMEMBASE;
}
void CPU::Recompiler::ARM32Recompiler::MoveSToReg(const vixl::aarch32::Register& dst, CompileFlags cf)
void CPU::ARM32Recompiler::MoveSToReg(const vixl::aarch32::Register& dst, CompileFlags cf)
{
if (cf.valid_host_s)
{
@ -871,7 +888,7 @@ void CPU::Recompiler::ARM32Recompiler::MoveSToReg(const vixl::aarch32::Register&
}
}
void CPU::Recompiler::ARM32Recompiler::MoveTToReg(const vixl::aarch32::Register& dst, CompileFlags cf)
void CPU::ARM32Recompiler::MoveTToReg(const vixl::aarch32::Register& dst, CompileFlags cf)
{
if (cf.valid_host_t)
{
@ -890,7 +907,7 @@ void CPU::Recompiler::ARM32Recompiler::MoveTToReg(const vixl::aarch32::Register&
}
}
void CPU::Recompiler::ARM32Recompiler::MoveMIPSRegToReg(const vixl::aarch32::Register& dst, Reg reg)
void CPU::ARM32Recompiler::MoveMIPSRegToReg(const vixl::aarch32::Register& dst, Reg reg)
{
DebugAssert(reg < Reg::count);
if (const std::optional<u32> hreg = CheckHostReg(0, Recompiler::HR_TYPE_CPU_REG, reg))
@ -901,8 +918,7 @@ void CPU::Recompiler::ARM32Recompiler::MoveMIPSRegToReg(const vixl::aarch32::Reg
armAsm->ldr(dst, MipsPtr(reg));
}
void CPU::Recompiler::ARM32Recompiler::GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val,
Reg arg2reg /* = Reg::count */,
void CPU::ARM32Recompiler::GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val, Reg arg2reg /* = Reg::count */,
Reg arg3reg /* = Reg::count */)
{
DebugAssert(g_settings.gpu_pgxp_enable);
@ -918,7 +934,7 @@ void CPU::Recompiler::ARM32Recompiler::GeneratePGXPCallWithMIPSRegs(const void*
EmitCall(func);
}
void CPU::Recompiler::ARM32Recompiler::Flush(u32 flags)
void CPU::ARM32Recompiler::Flush(u32 flags)
{
Recompiler::Flush(flags);
@ -1010,13 +1026,13 @@ void CPU::Recompiler::ARM32Recompiler::Flush(u32 flags)
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_Fallback()
void CPU::ARM32Recompiler::Compile_Fallback()
{
WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", iinfo->pc, inst->bits);
Flush(FLUSH_FOR_INTERPRETER);
EmitCall(reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::InterpretInstruction));
EmitCall(reinterpret_cast<const void*>(&CPU::RecompilerThunks::InterpretInstruction));
// TODO: make me less garbage
// TODO: this is wrong, it flushes the load delay on the same cycle when we return.
@ -1035,7 +1051,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_Fallback()
m_load_delay_dirty = EMULATE_LOAD_DELAYS;
}
void CPU::Recompiler::ARM32Recompiler::CheckBranchTarget(const vixl::aarch32::Register& pcreg)
void CPU::ARM32Recompiler::CheckBranchTarget(const vixl::aarch32::Register& pcreg)
{
if (!g_settings.cpu_recompiler_memory_exceptions)
return;
@ -1050,7 +1066,7 @@ void CPU::Recompiler::ARM32Recompiler::CheckBranchTarget(const vixl::aarch32::Re
SwitchToNearCode(false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_jr(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_jr(CompileFlags cf)
{
const Register pcreg = CFGetRegS(cf);
CheckBranchTarget(pcreg);
@ -1061,7 +1077,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_jr(CompileFlags cf)
EndBlock(std::nullopt, true);
}
void CPU::Recompiler::ARM32Recompiler::Compile_jalr(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_jalr(CompileFlags cf)
{
const Register pcreg = CFGetRegS(cf);
if (MipsD() != Reg::zero)
@ -1074,7 +1090,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_jalr(CompileFlags cf)
EndBlock(std::nullopt, true);
}
void CPU::Recompiler::ARM32Recompiler::Compile_bxx(CompileFlags cf, BranchCondition cond)
void CPU::ARM32Recompiler::Compile_bxx(CompileFlags cf, BranchCondition cond)
{
AssertRegOrConstS(cf);
@ -1148,7 +1164,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_bxx(CompileFlags cf, BranchCondit
EndBlock(taken_pc, true);
}
void CPU::Recompiler::ARM32Recompiler::Compile_addi(CompileFlags cf, bool overflow)
void CPU::ARM32Recompiler::Compile_addi(CompileFlags cf, bool overflow)
{
const Register rs = CFGetRegS(cf);
const Register rt = CFGetRegT(cf);
@ -1170,27 +1186,27 @@ void CPU::Recompiler::ARM32Recompiler::Compile_addi(CompileFlags cf, bool overfl
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_addi(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_addi(CompileFlags cf)
{
Compile_addi(cf, g_settings.cpu_recompiler_memory_exceptions);
}
void CPU::Recompiler::ARM32Recompiler::Compile_addiu(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_addiu(CompileFlags cf)
{
Compile_addi(cf, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_slti(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_slti(CompileFlags cf)
{
Compile_slti(cf, true);
}
void CPU::Recompiler::ARM32Recompiler::Compile_sltiu(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_sltiu(CompileFlags cf)
{
Compile_slti(cf, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_slti(CompileFlags cf, bool sign)
void CPU::ARM32Recompiler::Compile_slti(CompileFlags cf, bool sign)
{
const Register rs = CFGetRegS(cf);
const Register rt = CFGetRegT(cf);
@ -1199,7 +1215,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_slti(CompileFlags cf, bool sign)
armAsm->mov(sign ? lt : lo, rt, 1);
}
void CPU::Recompiler::ARM32Recompiler::Compile_andi(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_andi(CompileFlags cf)
{
const Register rt = CFGetRegT(cf);
if (const u32 imm = inst->i.imm_zext32(); imm != 0)
@ -1208,7 +1224,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_andi(CompileFlags cf)
EmitMov(rt, 0);
}
void CPU::Recompiler::ARM32Recompiler::Compile_ori(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_ori(CompileFlags cf)
{
const Register rt = CFGetRegT(cf);
const Register rs = CFGetRegS(cf);
@ -1218,7 +1234,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_ori(CompileFlags cf)
armAsm->mov(rt, rs);
}
void CPU::Recompiler::ARM32Recompiler::Compile_xori(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_xori(CompileFlags cf)
{
const Register rt = CFGetRegT(cf);
const Register rs = CFGetRegS(cf);
@ -1228,10 +1244,9 @@ void CPU::Recompiler::ARM32Recompiler::Compile_xori(CompileFlags cf)
armAsm->mov(rt, rs);
}
void CPU::Recompiler::ARM32Recompiler::Compile_shift(CompileFlags cf,
void CPU::ARM32Recompiler::Compile_shift(CompileFlags cf,
void (vixl::aarch32::Assembler::*op)(vixl::aarch32::Register,
vixl::aarch32::Register,
const Operand&))
vixl::aarch32::Register, const Operand&))
{
const Register rd = CFGetRegD(cf);
const Register rt = CFGetRegT(cf);
@ -1241,24 +1256,25 @@ void CPU::Recompiler::ARM32Recompiler::Compile_shift(CompileFlags cf,
armAsm->mov(rd, rt);
}
void CPU::Recompiler::ARM32Recompiler::Compile_sll(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_sll(CompileFlags cf)
{
Compile_shift(cf, &Assembler::lsl);
}
void CPU::Recompiler::ARM32Recompiler::Compile_srl(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_srl(CompileFlags cf)
{
Compile_shift(cf, &Assembler::lsr);
}
void CPU::Recompiler::ARM32Recompiler::Compile_sra(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_sra(CompileFlags cf)
{
Compile_shift(cf, &Assembler::asr);
}
void CPU::Recompiler::ARM32Recompiler::Compile_variable_shift(
CompileFlags cf,
void (vixl::aarch32::Assembler::*op)(vixl::aarch32::Register, vixl::aarch32::Register, const Operand&))
void CPU::ARM32Recompiler::Compile_variable_shift(CompileFlags cf,
void (vixl::aarch32::Assembler::*op)(vixl::aarch32::Register,
vixl::aarch32::Register,
const Operand&))
{
const Register rd = CFGetRegD(cf);
@ -1283,22 +1299,22 @@ void CPU::Recompiler::ARM32Recompiler::Compile_variable_shift(
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_sllv(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_sllv(CompileFlags cf)
{
Compile_variable_shift(cf, &Assembler::lsl);
}
void CPU::Recompiler::ARM32Recompiler::Compile_srlv(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_srlv(CompileFlags cf)
{
Compile_variable_shift(cf, &Assembler::lsr);
}
void CPU::Recompiler::ARM32Recompiler::Compile_srav(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_srav(CompileFlags cf)
{
Compile_variable_shift(cf, &Assembler::asr);
}
void CPU::Recompiler::ARM32Recompiler::Compile_mult(CompileFlags cf, bool sign)
void CPU::ARM32Recompiler::Compile_mult(CompileFlags cf, bool sign)
{
const Register rs = cf.valid_host_s ? CFGetRegS(cf) : RARG1;
if (!cf.valid_host_s)
@ -1315,17 +1331,17 @@ void CPU::Recompiler::ARM32Recompiler::Compile_mult(CompileFlags cf, bool sign)
(sign) ? armAsm->smull(lo, hi, rs, rt) : armAsm->umull(lo, hi, rs, rt);
}
void CPU::Recompiler::ARM32Recompiler::Compile_mult(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_mult(CompileFlags cf)
{
Compile_mult(cf, true);
}
void CPU::Recompiler::ARM32Recompiler::Compile_multu(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_multu(CompileFlags cf)
{
Compile_mult(cf, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_div(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_div(CompileFlags cf)
{
const Register rs = cf.valid_host_s ? CFGetRegS(cf) : RARG1;
if (!cf.valid_host_s)
@ -1371,7 +1387,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_div(CompileFlags cf)
armAsm->bind(&done);
}
void CPU::Recompiler::ARM32Recompiler::Compile_divu(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_divu(CompileFlags cf)
{
const Register rs = cf.valid_host_s ? CFGetRegS(cf) : RARG1;
if (!cf.valid_host_s)
@ -1402,7 +1418,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_divu(CompileFlags cf)
armAsm->bind(&done);
}
void CPU::Recompiler::ARM32Recompiler::TestOverflow(const vixl::aarch32::Register& result)
void CPU::ARM32Recompiler::TestOverflow(const vixl::aarch32::Register& result)
{
SwitchToFarCode(true, vs);
@ -1418,10 +1434,9 @@ void CPU::Recompiler::ARM32Recompiler::TestOverflow(const vixl::aarch32::Registe
SwitchToNearCode(false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_dst_op(CompileFlags cf,
void CPU::ARM32Recompiler::Compile_dst_op(CompileFlags cf,
void (vixl::aarch32::Assembler::*op)(vixl::aarch32::Register,
vixl::aarch32::Register,
const Operand&),
vixl::aarch32::Register, const Operand&),
bool commutative, bool logical, bool overflow)
{
AssertRegOrConstS(cf);
@ -1470,7 +1485,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_dst_op(CompileFlags cf,
TestOverflow(rd);
}
void CPU::Recompiler::ARM32Recompiler::Compile_add(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_add(CompileFlags cf)
{
if (g_settings.cpu_recompiler_memory_exceptions)
Compile_dst_op(cf, &Assembler::adds, true, false, true);
@ -1478,12 +1493,12 @@ void CPU::Recompiler::ARM32Recompiler::Compile_add(CompileFlags cf)
Compile_dst_op(cf, &Assembler::add, true, false, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_addu(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_addu(CompileFlags cf)
{
Compile_dst_op(cf, &Assembler::add, true, false, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_sub(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_sub(CompileFlags cf)
{
if (g_settings.cpu_recompiler_memory_exceptions)
Compile_dst_op(cf, &Assembler::subs, false, false, true);
@ -1491,12 +1506,12 @@ void CPU::Recompiler::ARM32Recompiler::Compile_sub(CompileFlags cf)
Compile_dst_op(cf, &Assembler::sub, false, false, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_subu(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_subu(CompileFlags cf)
{
Compile_dst_op(cf, &Assembler::sub, false, false, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_and(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_and(CompileFlags cf)
{
AssertRegOrConstS(cf);
AssertRegOrConstT(cf);
@ -1517,7 +1532,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_and(CompileFlags cf)
Compile_dst_op(cf, &Assembler::and_, true, true, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_or(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_or(CompileFlags cf)
{
AssertRegOrConstS(cf);
AssertRegOrConstT(cf);
@ -1533,7 +1548,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_or(CompileFlags cf)
Compile_dst_op(cf, &Assembler::orr, true, true, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_xor(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_xor(CompileFlags cf)
{
AssertRegOrConstS(cf);
AssertRegOrConstT(cf);
@ -1555,23 +1570,23 @@ void CPU::Recompiler::ARM32Recompiler::Compile_xor(CompileFlags cf)
Compile_dst_op(cf, &Assembler::eor, true, true, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_nor(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_nor(CompileFlags cf)
{
Compile_or(cf);
armAsm->mvn(CFGetRegD(cf), CFGetRegD(cf));
}
void CPU::Recompiler::ARM32Recompiler::Compile_slt(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_slt(CompileFlags cf)
{
Compile_slt(cf, true);
}
void CPU::Recompiler::ARM32Recompiler::Compile_sltu(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_sltu(CompileFlags cf)
{
Compile_slt(cf, false);
}
void CPU::Recompiler::ARM32Recompiler::Compile_slt(CompileFlags cf, bool sign)
void CPU::ARM32Recompiler::Compile_slt(CompileFlags cf, bool sign)
{
AssertRegOrConstS(cf);
AssertRegOrConstT(cf);
@ -1597,8 +1612,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_slt(CompileFlags cf, bool sign)
}
vixl::aarch32::Register
CPU::Recompiler::ARM32Recompiler::ComputeLoadStoreAddressArg(CompileFlags cf,
const std::optional<VirtualMemoryAddress>& address,
CPU::ARM32Recompiler::ComputeLoadStoreAddressArg(CompileFlags cf, const std::optional<VirtualMemoryAddress>& address,
const std::optional<const vixl::aarch32::Register>& reg)
{
const u32 imm = inst->i.imm_sext32();
@ -1639,9 +1653,9 @@ CPU::Recompiler::ARM32Recompiler::ComputeLoadStoreAddressArg(CompileFlags cf,
}
template<typename RegAllocFn>
vixl::aarch32::Register
CPU::Recompiler::ARM32Recompiler::GenerateLoad(const vixl::aarch32::Register& addr_reg, MemoryAccessSize size,
bool sign, bool use_fastmem, const RegAllocFn& dst_reg_alloc)
vixl::aarch32::Register CPU::ARM32Recompiler::GenerateLoad(const vixl::aarch32::Register& addr_reg,
MemoryAccessSize size, bool sign, bool use_fastmem,
const RegAllocFn& dst_reg_alloc)
{
if (use_fastmem)
{
@ -1683,20 +1697,20 @@ CPU::Recompiler::ARM32Recompiler::GenerateLoad(const vixl::aarch32::Register& ad
{
case MemoryAccessSize::Byte:
{
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::ReadMemoryByte) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryByte));
EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryByte) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryByte));
}
break;
case MemoryAccessSize::HalfWord:
{
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::ReadMemoryHalfWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryHalfWord));
EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryHalfWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryHalfWord));
}
break;
case MemoryAccessSize::Word:
{
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::ReadMemoryWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryWord));
EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryWord));
}
break;
}
@ -1751,7 +1765,7 @@ CPU::Recompiler::ARM32Recompiler::GenerateLoad(const vixl::aarch32::Register& ad
return dst_reg;
}
void CPU::Recompiler::ARM32Recompiler::GenerateStore(const vixl::aarch32::Register& addr_reg,
void CPU::ARM32Recompiler::GenerateStore(const vixl::aarch32::Register& addr_reg,
const vixl::aarch32::Register& value_reg, MemoryAccessSize size,
bool use_fastmem)
{
@ -1793,20 +1807,20 @@ void CPU::Recompiler::ARM32Recompiler::GenerateStore(const vixl::aarch32::Regist
{
case MemoryAccessSize::Byte:
{
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::WriteMemoryByte) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryByte));
EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryByte) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryByte));
}
break;
case MemoryAccessSize::HalfWord:
{
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::WriteMemoryHalfWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryHalfWord));
EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryHalfWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryHalfWord));
}
break;
case MemoryAccessSize::Word:
{
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::WriteMemoryWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryWord));
EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryWord));
}
break;
}
@ -1837,7 +1851,7 @@ void CPU::Recompiler::ARM32Recompiler::GenerateStore(const vixl::aarch32::Regist
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_lxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::ARM32Recompiler::Compile_lxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
const std::optional<Register> addr_reg = g_settings.gpu_pgxp_enable ?
@ -1865,7 +1879,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_lxx(CompileFlags cf, MemoryAccess
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_lwx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::ARM32Recompiler::Compile_lwx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
DebugAssert(size == MemoryAccessSize::Word && !sign);
@ -1959,7 +1973,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_lwx(CompileFlags cf, MemoryAccess
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_lwc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::ARM32Recompiler::Compile_lwc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
const u32 index = static_cast<u32>(inst->r.rt.GetValue());
@ -2045,7 +2059,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_lwc2(CompileFlags cf, MemoryAcces
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_sxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::ARM32Recompiler::Compile_sxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
AssertRegOrConstS(cf);
@ -2073,7 +2087,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_sxx(CompileFlags cf, MemoryAccess
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_swx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::ARM32Recompiler::Compile_swx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
DebugAssert(size == MemoryAccessSize::Word && !sign);
@ -2147,7 +2161,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_swx(CompileFlags cf, MemoryAccess
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_swc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::ARM32Recompiler::Compile_swc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
const u32 index = static_cast<u32>(inst->r.rt.GetValue());
@ -2203,7 +2217,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_swc2(CompileFlags cf, MemoryAcces
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_mtc0(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_mtc0(CompileFlags cf)
{
// TODO: we need better constant setting here.. which will need backprop
AssertRegOrConstT(cf);
@ -2281,7 +2295,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_mtc0(CompileFlags cf)
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_rfe(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_rfe(CompileFlags cf)
{
// shift mode bits right two, preserving upper bits
armAsm->ldr(RARG1, PTR(&g_state.cop0_regs.sr.bits));
@ -2293,7 +2307,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_rfe(CompileFlags cf)
TestInterrupts(RARG1);
}
void CPU::Recompiler::ARM32Recompiler::TestInterrupts(const vixl::aarch32::Register& sr)
void CPU::ARM32Recompiler::TestInterrupts(const vixl::aarch32::Register& sr)
{
// if Iec == 0 then goto no_interrupt
Label no_interrupt;
@ -2344,7 +2358,7 @@ void CPU::Recompiler::ARM32Recompiler::TestInterrupts(const vixl::aarch32::Regis
armAsm->bind(&no_interrupt);
}
void CPU::Recompiler::ARM32Recompiler::Compile_mfc2(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_mfc2(CompileFlags cf)
{
const u32 index = inst->cop.Cop2Index();
const Reg rt = inst->r.rt;
@ -2385,7 +2399,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_mfc2(CompileFlags cf)
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_mtc2(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_mtc2(CompileFlags cf)
{
const u32 index = inst->cop.Cop2Index();
const auto [ptr, action] = GetGTERegisterPointer(index, true);
@ -2447,7 +2461,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_mtc2(CompileFlags cf)
}
}
void CPU::Recompiler::ARM32Recompiler::Compile_cop2(CompileFlags cf)
void CPU::ARM32Recompiler::Compile_cop2(CompileFlags cf)
{
TickCount func_ticks;
GTE::InstructionImpl func = GTE::GetInstructionImpl(inst->bits, &func_ticks);
@ -2514,24 +2528,24 @@ u32 CPU::Recompiler::CompileLoadStoreThunk(void* thunk_code, u32 thunk_space, vo
case MemoryAccessSize::Byte:
{
armEmitCall(armAsm,
is_load ? reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryByte) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryByte),
is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryByte) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryByte),
false);
}
break;
case MemoryAccessSize::HalfWord:
{
armEmitCall(armAsm,
is_load ? reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryHalfWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryHalfWord),
is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryHalfWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryHalfWord),
false);
}
break;
case MemoryAccessSize::Word:
{
armEmitCall(armAsm,
is_load ? reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryWord),
is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryWord),
false);
}
break;

View File

@ -12,7 +12,7 @@
#include "vixl/aarch32/assembler-aarch32.h"
#include "vixl/aarch32/operands-aarch32.h"
namespace CPU::Recompiler {
namespace CPU {
class ARM32Recompiler final : public Recompiler
{
@ -165,6 +165,6 @@ private:
#endif
};
} // namespace CPU::Recompiler
} // namespace CPU
#endif // CPU_ARCH_ARM32

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
#include "vixl/aarch64/assembler-aarch64.h"
namespace CPU::Recompiler {
namespace CPU {
class ARM64Recompiler final : public Recompiler
{
@ -166,6 +166,6 @@ private:
#endif
};
} // namespace CPU::Recompiler
} // namespace CPU
#endif // CPU_ARCH_ARM64

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,9 @@
#ifdef CPU_ARCH_RISCV64
namespace CPU::Recompiler {
#include "biscuit/assembler.hpp"
namespace CPU {
class RISCV64Recompiler final : public Recompiler
{
@ -171,6 +173,6 @@ private:
biscuit::Assembler* rvAsm;
};
} // namespace CPU::Recompiler
} // namespace CPU
#endif // CPU_ARCH_RISCV64

View File

@ -1,35 +0,0 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#pragma once
#include "cpu_code_cache.h"
#include "cpu_types.h"
namespace CPU::Recompiler::Thunks {
//////////////////////////////////////////////////////////////////////////
// Trampolines for calling back from the JIT
// Needed because we can't cast member functions to void*...
// TODO: Abuse carry flag or something else for exception
//////////////////////////////////////////////////////////////////////////
bool InterpretInstruction();
bool InterpretInstructionPGXP();
// Memory access functions for the JIT - MSB is set on exception.
u64 ReadMemoryByte(u32 address);
u64 ReadMemoryHalfWord(u32 address);
u64 ReadMemoryWord(u32 address);
u32 WriteMemoryByte(u32 address, u32 value);
u32 WriteMemoryHalfWord(u32 address, u32 value);
u32 WriteMemoryWord(u32 address, u32 value);
// Unchecked memory access variants. No alignment or bus exceptions.
u32 UncheckedReadMemoryByte(u32 address);
u32 UncheckedReadMemoryHalfWord(u32 address);
u32 UncheckedReadMemoryWord(u32 address);
void UncheckedWriteMemoryByte(u32 address, u32 value);
void UncheckedWriteMemoryHalfWord(u32 address, u32 value);
void UncheckedWriteMemoryWord(u32 address, u32 value);
} // namespace CPU::Recompiler::Thunks

View File

@ -1,182 +0,0 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
// Shared code between recompiler backends.
#pragma once
#include "cpu_types.h"
#include <utility>
#if defined(CPU_ARCH_X64)
// We need to include windows.h before xbyak does..
#ifdef _WIN32
#include "common/windows_headers.h"
#endif
#define XBYAK_NO_OP_NAMES 1
#include "xbyak.h"
namespace CPU::Recompiler {
// A reasonable "maximum" number of bytes per instruction.
constexpr u32 MAX_NEAR_HOST_BYTES_PER_INSTRUCTION = 64;
constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
// ABI selection
#if defined(_WIN32)
#define ABI_WIN64 1
#define RWRET Xbyak::Reg32(Xbyak::Operand::EAX)
#define RWARG1 Xbyak::Reg32(Xbyak::Operand::RCX)
#define RWARG2 Xbyak::Reg32(Xbyak::Operand::RDX)
#define RWARG3 Xbyak::Reg32(Xbyak::Operand::R8D)
#define RWARG4 Xbyak::Reg32(Xbyak::Operand::R9D)
#define RXRET Xbyak::Reg64(Xbyak::Operand::RAX)
#define RXARG1 Xbyak::Reg64(Xbyak::Operand::RCX)
#define RXARG2 Xbyak::Reg64(Xbyak::Operand::RDX)
#define RXARG3 Xbyak::Reg64(Xbyak::Operand::R8)
#define RXARG4 Xbyak::Reg64(Xbyak::Operand::R9)
static constexpr u32 FUNCTION_CALL_SHADOW_SPACE = 32;
#elif defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__) || defined(__FreeBSD__)
#define ABI_SYSV 1
#define RWRET Xbyak::Reg32(Xbyak::Operand::EAX)
#define RWARG1 Xbyak::Reg32(Xbyak::Operand::EDI)
#define RWARG2 Xbyak::Reg32(Xbyak::Operand::ESI)
#define RWARG3 Xbyak::Reg32(Xbyak::Operand::EDX)
#define RWARG4 Xbyak::Reg32(Xbyak::Operand::ECX)
#define RXRET Xbyak::Reg64(Xbyak::Operand::RAX)
#define RXARG1 Xbyak::Reg64(Xbyak::Operand::RDI)
#define RXARG2 Xbyak::Reg64(Xbyak::Operand::RSI)
#define RXARG3 Xbyak::Reg64(Xbyak::Operand::RDX)
#define RXARG4 Xbyak::Reg64(Xbyak::Operand::RCX)
static constexpr u32 FUNCTION_CALL_SHADOW_SPACE = 0;
#else
#error Unknown ABI.
#endif
bool IsCallerSavedRegister(u32 id);
} // namespace CPU::Recompiler
#elif defined(CPU_ARCH_ARM32)
#include "vixl/aarch32/assembler-aarch32.h"
#include "vixl/aarch32/constants-aarch32.h"
#include "vixl/aarch32/instructions-aarch32.h"
namespace CPU::Recompiler {
// A reasonable "maximum" number of bytes per instruction.
constexpr u32 MAX_NEAR_HOST_BYTES_PER_INSTRUCTION = 64;
constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
#define RRET vixl::aarch32::r0
#define RRETHI vixl::aarch32::r1
#define RARG1 vixl::aarch32::r0
#define RARG2 vixl::aarch32::r1
#define RARG3 vixl::aarch32::r2
#define RSCRATCH vixl::aarch32::r12
#define RSTATE vixl::aarch32::r4
bool armIsCallerSavedRegister(u32 id);
s32 armGetPCDisplacement(const void* current, const void* target);
bool armIsPCDisplacementInImmediateRange(s32 displacement);
void armMoveAddressToReg(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr);
void armEmitMov(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& rd, u32 imm);
void armEmitJmp(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_inline);
void armEmitCall(vixl::aarch32::Assembler* armAsm, const void* ptr, bool force_inline);
void armEmitCondBranch(vixl::aarch32::Assembler* armAsm, vixl::aarch32::Condition cond, const void* ptr);
void armEmitFarLoad(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr);
void armEmitFarStore(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr,
const vixl::aarch32::Register& tempreg = RSCRATCH);
u8* armGetJumpTrampoline(const void* target);
} // namespace CPU::Recompiler
#elif defined(CPU_ARCH_ARM64)
#include "vixl/aarch64/assembler-aarch64.h"
#include "vixl/aarch64/constants-aarch64.h"
namespace CPU::Recompiler {
// A reasonable "maximum" number of bytes per instruction.
constexpr u32 MAX_NEAR_HOST_BYTES_PER_INSTRUCTION = 64;
constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
#define RWRET vixl::aarch64::w0
#define RXRET vixl::aarch64::x0
#define RWARG1 vixl::aarch64::w0
#define RXARG1 vixl::aarch64::x0
#define RWARG2 vixl::aarch64::w1
#define RXARG2 vixl::aarch64::x1
#define RWARG3 vixl::aarch64::w2
#define RXARG3 vixl::aarch64::x2
#define RWSCRATCH vixl::aarch64::w16
#define RXSCRATCH vixl::aarch64::x16
#define RSTATE vixl::aarch64::x19
#define RMEMBASE vixl::aarch64::x20
bool armIsCallerSavedRegister(u32 id);
s64 armGetPCDisplacement(const void* current, const void* target);
bool armIsInAdrpRange(vixl::aarch64::Assembler* armAsm, const void* addr);
void armMoveAddressToReg(vixl::aarch64::Assembler* armAsm, const vixl::aarch64::Register& reg, const void* addr);
void armEmitMov(vixl::aarch64::Assembler* armAsm, const vixl::aarch64::Register& rd, u64 imm);
void armEmitJmp(vixl::aarch64::Assembler* armAsm, const void* ptr, bool force_inline);
void armEmitCall(vixl::aarch64::Assembler* armAsm, const void* ptr, bool force_inline);
void armEmitCondBranch(vixl::aarch64::Assembler* armAsm, vixl::aarch64::Condition cond, const void* ptr);
void armEmitFarLoad(vixl::aarch64::Assembler* armAsm, const vixl::aarch64::Register& reg, const void* addr,
bool sign_extend_word = false);
void armEmitFarStore(vixl::aarch64::Assembler* armAsm, const vixl::aarch64::Register& reg, const void* addr,
const vixl::aarch64::Register& tempreg = RXSCRATCH);
u8* armGetJumpTrampoline(const void* target);
} // namespace CPU::Recompiler
#elif defined(CPU_ARCH_RISCV64)
#include "biscuit/assembler.hpp"
namespace CPU::Recompiler {
// A reasonable "maximum" number of bytes per instruction.
constexpr u32 MAX_NEAR_HOST_BYTES_PER_INSTRUCTION = 64;
constexpr u32 MAX_FAR_HOST_BYTES_PER_INSTRUCTION = 128;
#define RRET biscuit::a0
#define RARG1 biscuit::a0
#define RARG2 biscuit::a1
#define RARG3 biscuit::a2
#define RSCRATCH biscuit::t6
#define RSTATE biscuit::s10
#define RMEMBASE biscuit::s11
bool rvIsCallerSavedRegister(u32 id);
bool rvIsValidSExtITypeImm(u32 imm);
std::pair<s32, s32> rvGetAddressImmediates(const void* cur, const void* target);
void rvMoveAddressToReg(biscuit::Assembler* armAsm, const biscuit::GPR& reg, const void* addr);
void rvEmitMov(biscuit::Assembler* rvAsm, const biscuit::GPR& rd, u32 imm);
void rvEmitMov64(biscuit::Assembler* rvAsm, const biscuit::GPR& rd, const biscuit::GPR& scratch, u64 imm);
u32 rvEmitJmp(biscuit::Assembler* rvAsm, const void* ptr, const biscuit::GPR& link_reg = biscuit::zero);
u32 rvEmitCall(biscuit::Assembler* rvAsm, const void* ptr);
void rvEmitFarLoad(biscuit::Assembler* rvAsm, const biscuit::GPR& reg, const void* addr, bool sign_extend_word = false);
void rvEmitFarStore(biscuit::Assembler* rvAsm, const biscuit::GPR& reg, const void* addr,
const biscuit::GPR& tempreg = RSCRATCH);
void rvEmitSExtB(biscuit::Assembler* rvAsm, const biscuit::GPR& rd, const biscuit::GPR& rs); // -> word
void rvEmitUExtB(biscuit::Assembler* rvAsm, const biscuit::GPR& rd, const biscuit::GPR& rs); // -> word
void rvEmitSExtH(biscuit::Assembler* rvAsm, const biscuit::GPR& rd, const biscuit::GPR& rs); // -> word
void rvEmitUExtH(biscuit::Assembler* rvAsm, const biscuit::GPR& rd, const biscuit::GPR& rs); // -> word
void rvEmitDSExtW(biscuit::Assembler* rvAsm, const biscuit::GPR& rd, const biscuit::GPR& rs); // -> doubleword
void rvEmitDUExtW(biscuit::Assembler* rvAsm, const biscuit::GPR& rd, const biscuit::GPR& rs); // -> doubleword
} // namespace CPU::Recompiler
#endif

View File

@ -5,8 +5,6 @@
#include "cpu_code_cache_private.h"
#include "cpu_core_private.h"
#include "cpu_pgxp.h"
#include "cpu_recompiler_thunks.h"
#include "cpu_recompiler_types.h"
#include "gte.h"
#include "settings.h"
#include "timing_event.h"
@ -37,25 +35,58 @@ LOG_CHANNEL(Recompiler);
// PGXP TODO: LWL etc, MFC0
// PGXP TODO: Spyro 1 level gates have issues.
namespace CPU::Recompiler {
using namespace Xbyak;
static constexpr u32 BACKPATCH_JMP_SIZE = 5;
static bool IsCallerSavedRegister(u32 id);
// ABI selection
#if defined(_WIN32)
#define RWRET Xbyak::Reg32(Xbyak::Operand::EAX)
#define RWARG1 Xbyak::Reg32(Xbyak::Operand::RCX)
#define RWARG2 Xbyak::Reg32(Xbyak::Operand::RDX)
#define RWARG3 Xbyak::Reg32(Xbyak::Operand::R8D)
#define RWARG4 Xbyak::Reg32(Xbyak::Operand::R9D)
#define RXRET Xbyak::Reg64(Xbyak::Operand::RAX)
#define RXARG1 Xbyak::Reg64(Xbyak::Operand::RCX)
#define RXARG2 Xbyak::Reg64(Xbyak::Operand::RDX)
#define RXARG3 Xbyak::Reg64(Xbyak::Operand::R8)
#define RXARG4 Xbyak::Reg64(Xbyak::Operand::R9)
// on win32, we need to reserve an additional 32 bytes shadow space when calling out to C
#ifdef _WIN32
static constexpr u32 STACK_SHADOW_SIZE = 32;
#else
#elif defined(__linux__) || defined(__ANDROID__) || defined(__APPLE__) || defined(__FreeBSD__)
#define RWRET Xbyak::Reg32(Xbyak::Operand::EAX)
#define RWARG1 Xbyak::Reg32(Xbyak::Operand::EDI)
#define RWARG2 Xbyak::Reg32(Xbyak::Operand::ESI)
#define RWARG3 Xbyak::Reg32(Xbyak::Operand::EDX)
#define RWARG4 Xbyak::Reg32(Xbyak::Operand::ECX)
#define RXRET Xbyak::Reg64(Xbyak::Operand::RAX)
#define RXARG1 Xbyak::Reg64(Xbyak::Operand::RDI)
#define RXARG2 Xbyak::Reg64(Xbyak::Operand::RSI)
#define RXARG3 Xbyak::Reg64(Xbyak::Operand::RDX)
#define RXARG4 Xbyak::Reg64(Xbyak::Operand::RCX)
static constexpr u32 STACK_SHADOW_SIZE = 0;
#else
#error Unknown ABI.
#endif
namespace CPU {
using namespace Xbyak;
static X64Recompiler s_instance;
Recompiler* g_compiler = &s_instance;
} // namespace CPU::Recompiler
} // namespace CPU
bool CPU::Recompiler::IsCallerSavedRegister(u32 id)
bool IsCallerSavedRegister(u32 id)
{
#ifdef _WIN32
// The x64 ABI considers the registers RAX, RCX, RDX, R8, R9, R10, R11, and XMM0-XMM5 volatile.
@ -330,12 +361,12 @@ u32 CPU::CodeCache::GetHostInstructionCount(const void* start, u32 size)
#endif // ENABLE_HOST_DISASSEMBLY
CPU::Recompiler::X64Recompiler::X64Recompiler() = default;
CPU::X64Recompiler::X64Recompiler() = default;
CPU::Recompiler::X64Recompiler::~X64Recompiler() = default;
CPU::X64Recompiler::~X64Recompiler() = default;
void CPU::Recompiler::X64Recompiler::Reset(CodeCache::Block* block, u8* code_buffer, u32 code_buffer_space,
u8* far_code_buffer, u32 far_code_space)
void CPU::X64Recompiler::Reset(CodeCache::Block* block, u8* code_buffer, u32 code_buffer_space, u8* far_code_buffer,
u32 far_code_space)
{
Recompiler::Reset(block, code_buffer, code_buffer_space, far_code_buffer, far_code_space);
@ -366,7 +397,7 @@ void CPU::Recompiler::X64Recompiler::Reset(CodeCache::Block* block, u8* code_buf
}
}
void CPU::Recompiler::X64Recompiler::SwitchToFarCode(bool emit_jump, void (Xbyak::CodeGenerator::*jump_op)(const void*))
void CPU::X64Recompiler::SwitchToFarCode(bool emit_jump, void (Xbyak::CodeGenerator::*jump_op)(const void*))
{
DebugAssert(cg == m_emitter.get());
if (emit_jump)
@ -377,8 +408,7 @@ void CPU::Recompiler::X64Recompiler::SwitchToFarCode(bool emit_jump, void (Xbyak
cg = m_far_emitter.get();
}
void CPU::Recompiler::X64Recompiler::SwitchToNearCode(bool emit_jump,
void (Xbyak::CodeGenerator::*jump_op)(const void*))
void CPU::X64Recompiler::SwitchToNearCode(bool emit_jump, void (Xbyak::CodeGenerator::*jump_op)(const void*))
{
DebugAssert(cg == m_far_emitter.get());
if (emit_jump)
@ -389,7 +419,7 @@ void CPU::Recompiler::X64Recompiler::SwitchToNearCode(bool emit_jump,
cg = m_emitter.get();
}
void CPU::Recompiler::X64Recompiler::BeginBlock()
void CPU::X64Recompiler::BeginBlock()
{
Recompiler::BeginBlock();
@ -408,7 +438,7 @@ void CPU::Recompiler::X64Recompiler::BeginBlock()
#endif
}
void CPU::Recompiler::X64Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, const u8* shadow_ptr, u32 size)
void CPU::X64Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr, const u8* shadow_ptr, u32 size)
{
// store it first to reduce code size, because we can offset
cg->mov(RXARG1, static_cast<size_t>(reinterpret_cast<uintptr_t>(ram_ptr)));
@ -459,7 +489,7 @@ void CPU::Recompiler::X64Recompiler::GenerateBlockProtectCheck(const u8* ram_ptr
DebugAssert(size == 0);
}
void CPU::Recompiler::X64Recompiler::GenerateICacheCheckAndUpdate()
void CPU::X64Recompiler::GenerateICacheCheckAndUpdate()
{
if (!m_block->HasFlag(CodeCache::BlockFlags::IsUsingICache))
{
@ -500,7 +530,7 @@ void CPU::Recompiler::X64Recompiler::GenerateICacheCheckAndUpdate()
}
}
void CPU::Recompiler::X64Recompiler::GenerateCall(const void* func, s32 arg1reg /*= -1*/, s32 arg2reg /*= -1*/,
void CPU::X64Recompiler::GenerateCall(const void* func, s32 arg1reg /*= -1*/, s32 arg2reg /*= -1*/,
s32 arg3reg /*= -1*/)
{
if (arg1reg >= 0 && arg1reg != static_cast<s32>(RXARG1.getIdx()))
@ -512,7 +542,7 @@ void CPU::Recompiler::X64Recompiler::GenerateCall(const void* func, s32 arg1reg
cg->call(func);
}
void CPU::Recompiler::X64Recompiler::EndBlock(const std::optional<u32>& newpc, bool do_event_test)
void CPU::X64Recompiler::EndBlock(const std::optional<u32>& newpc, bool do_event_test)
{
if (newpc.has_value())
{
@ -526,7 +556,7 @@ void CPU::Recompiler::X64Recompiler::EndBlock(const std::optional<u32>& newpc, b
EndAndLinkBlock(newpc, do_event_test, false);
}
void CPU::Recompiler::X64Recompiler::EndBlockWithException(Exception excode)
void CPU::X64Recompiler::EndBlockWithException(Exception excode)
{
// flush regs, but not pc, it's going to get overwritten
// flush cycles because of the GTE instruction stuff...
@ -544,8 +574,7 @@ void CPU::Recompiler::X64Recompiler::EndBlockWithException(Exception excode)
EndAndLinkBlock(std::nullopt, true, false);
}
void CPU::Recompiler::X64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test,
bool force_run_events)
void CPU::X64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, bool force_run_events)
{
// event test
// pc should've been flushed
@ -614,7 +643,7 @@ void CPU::Recompiler::X64Recompiler::EndAndLinkBlock(const std::optional<u32>& n
}
}
const void* CPU::Recompiler::X64Recompiler::EndCompile(u32* code_size, u32* far_code_size)
const void* CPU::X64Recompiler::EndCompile(u32* code_size, u32* far_code_size)
{
const void* code = m_emitter->getCode();
*code_size = static_cast<u32>(m_emitter->getSize());
@ -625,81 +654,81 @@ const void* CPU::Recompiler::X64Recompiler::EndCompile(u32* code_size, u32* far_
return code;
}
const void* CPU::Recompiler::X64Recompiler::GetCurrentCodePointer()
const void* CPU::X64Recompiler::GetCurrentCodePointer()
{
return cg->getCurr();
}
const char* CPU::Recompiler::X64Recompiler::GetHostRegName(u32 reg) const
const char* CPU::X64Recompiler::GetHostRegName(u32 reg) const
{
static constexpr std::array<const char*, 16> reg64_names = {
{"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}};
return (reg < reg64_names.size()) ? reg64_names[reg] : "UNKNOWN";
}
void CPU::Recompiler::X64Recompiler::LoadHostRegWithConstant(u32 reg, u32 val)
void CPU::X64Recompiler::LoadHostRegWithConstant(u32 reg, u32 val)
{
cg->mov(Reg32(reg), val);
}
void CPU::Recompiler::X64Recompiler::LoadHostRegFromCPUPointer(u32 reg, const void* ptr)
void CPU::X64Recompiler::LoadHostRegFromCPUPointer(u32 reg, const void* ptr)
{
cg->mov(Reg32(reg), cg->dword[PTR(ptr)]);
}
void CPU::Recompiler::X64Recompiler::StoreHostRegToCPUPointer(u32 reg, const void* ptr)
void CPU::X64Recompiler::StoreHostRegToCPUPointer(u32 reg, const void* ptr)
{
cg->mov(cg->dword[PTR(ptr)], Reg32(reg));
}
void CPU::Recompiler::X64Recompiler::StoreConstantToCPUPointer(u32 val, const void* ptr)
void CPU::X64Recompiler::StoreConstantToCPUPointer(u32 val, const void* ptr)
{
cg->mov(cg->dword[PTR(ptr)], val);
}
void CPU::Recompiler::X64Recompiler::CopyHostReg(u32 dst, u32 src)
void CPU::X64Recompiler::CopyHostReg(u32 dst, u32 src)
{
if (src != dst)
cg->mov(Reg32(dst), Reg32(src));
}
Xbyak::Address CPU::Recompiler::X64Recompiler::MipsPtr(Reg r) const
Xbyak::Address CPU::X64Recompiler::MipsPtr(Reg r) const
{
DebugAssert(r < Reg::count);
return cg->dword[PTR(&g_state.regs.r[static_cast<u32>(r)])];
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::CFGetRegD(CompileFlags cf) const
Xbyak::Reg32 CPU::X64Recompiler::CFGetRegD(CompileFlags cf) const
{
DebugAssert(cf.valid_host_d);
return Reg32(cf.host_d);
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::CFGetRegS(CompileFlags cf) const
Xbyak::Reg32 CPU::X64Recompiler::CFGetRegS(CompileFlags cf) const
{
DebugAssert(cf.valid_host_s);
return Reg32(cf.host_s);
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::CFGetRegT(CompileFlags cf) const
Xbyak::Reg32 CPU::X64Recompiler::CFGetRegT(CompileFlags cf) const
{
DebugAssert(cf.valid_host_t);
return Reg32(cf.host_t);
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::CFGetRegLO(CompileFlags cf) const
Xbyak::Reg32 CPU::X64Recompiler::CFGetRegLO(CompileFlags cf) const
{
DebugAssert(cf.valid_host_lo);
return Reg32(cf.host_lo);
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::CFGetRegHI(CompileFlags cf) const
Xbyak::Reg32 CPU::X64Recompiler::CFGetRegHI(CompileFlags cf) const
{
DebugAssert(cf.valid_host_hi);
return Reg32(cf.host_hi);
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::MoveSToD(CompileFlags cf)
Xbyak::Reg32 CPU::X64Recompiler::MoveSToD(CompileFlags cf)
{
DebugAssert(cf.valid_host_d);
DebugAssert(!cf.valid_host_t || cf.host_t != cf.host_d);
@ -710,7 +739,7 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::MoveSToD(CompileFlags cf)
return rd;
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::MoveSToT(CompileFlags cf)
Xbyak::Reg32 CPU::X64Recompiler::MoveSToT(CompileFlags cf)
{
DebugAssert(cf.valid_host_t);
@ -736,7 +765,7 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::MoveSToT(CompileFlags cf)
return rt;
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::MoveTToD(CompileFlags cf)
Xbyak::Reg32 CPU::X64Recompiler::MoveTToD(CompileFlags cf)
{
DebugAssert(cf.valid_host_d);
DebugAssert(!cf.valid_host_s || cf.host_s != cf.host_d);
@ -746,7 +775,7 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::MoveTToD(CompileFlags cf)
return rd;
}
void CPU::Recompiler::X64Recompiler::MoveSToReg(const Xbyak::Reg32& dst, CompileFlags cf)
void CPU::X64Recompiler::MoveSToReg(const Xbyak::Reg32& dst, CompileFlags cf)
{
if (cf.valid_host_s)
{
@ -767,7 +796,7 @@ void CPU::Recompiler::X64Recompiler::MoveSToReg(const Xbyak::Reg32& dst, Compile
}
}
void CPU::Recompiler::X64Recompiler::MoveTToReg(const Xbyak::Reg32& dst, CompileFlags cf)
void CPU::X64Recompiler::MoveTToReg(const Xbyak::Reg32& dst, CompileFlags cf)
{
if (cf.valid_host_t)
{
@ -788,7 +817,7 @@ void CPU::Recompiler::X64Recompiler::MoveTToReg(const Xbyak::Reg32& dst, Compile
}
}
void CPU::Recompiler::X64Recompiler::MoveMIPSRegToReg(const Xbyak::Reg32& dst, Reg reg)
void CPU::X64Recompiler::MoveMIPSRegToReg(const Xbyak::Reg32& dst, Reg reg)
{
DebugAssert(reg < Reg::count);
if (const std::optional<u32> hreg = CheckHostReg(0, Recompiler::HR_TYPE_CPU_REG, reg))
@ -799,8 +828,7 @@ void CPU::Recompiler::X64Recompiler::MoveMIPSRegToReg(const Xbyak::Reg32& dst, R
cg->mov(dst, MipsPtr(reg));
}
void CPU::Recompiler::X64Recompiler::GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val,
Reg arg2reg /* = Reg::count */,
void CPU::X64Recompiler::GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val, Reg arg2reg /* = Reg::count */,
Reg arg3reg /* = Reg::count */)
{
DebugAssert(g_settings.gpu_pgxp_enable);
@ -816,7 +844,7 @@ void CPU::Recompiler::X64Recompiler::GeneratePGXPCallWithMIPSRegs(const void* fu
cg->call(func);
}
void CPU::Recompiler::X64Recompiler::Flush(u32 flags)
void CPU::X64Recompiler::Flush(u32 flags)
{
Recompiler::Flush(flags);
@ -899,13 +927,13 @@ void CPU::Recompiler::X64Recompiler::Flush(u32 flags)
}
}
void CPU::Recompiler::X64Recompiler::Compile_Fallback()
void CPU::X64Recompiler::Compile_Fallback()
{
WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", iinfo->pc, inst->bits);
Flush(FLUSH_FOR_INTERPRETER);
cg->call(&CPU::Recompiler::Thunks::InterpretInstruction);
cg->call(&CPU::RecompilerThunks::InterpretInstruction);
// TODO: make me less garbage
// TODO: this is wrong, it flushes the load delay on the same cycle when we return.
@ -923,7 +951,7 @@ void CPU::Recompiler::X64Recompiler::Compile_Fallback()
m_load_delay_dirty = EMULATE_LOAD_DELAYS;
}
void CPU::Recompiler::X64Recompiler::CheckBranchTarget(const Xbyak::Reg32& pcreg)
void CPU::X64Recompiler::CheckBranchTarget(const Xbyak::Reg32& pcreg)
{
if (!g_settings.cpu_recompiler_memory_exceptions)
return;
@ -938,7 +966,7 @@ void CPU::Recompiler::X64Recompiler::CheckBranchTarget(const Xbyak::Reg32& pcreg
SwitchToNearCode(false);
}
void CPU::Recompiler::X64Recompiler::Compile_jr(CompileFlags cf)
void CPU::X64Recompiler::Compile_jr(CompileFlags cf)
{
if (!cf.valid_host_s)
cg->mov(RWARG1, MipsPtr(cf.MipsS()));
@ -952,7 +980,7 @@ void CPU::Recompiler::X64Recompiler::Compile_jr(CompileFlags cf)
EndBlock(std::nullopt, true);
}
void CPU::Recompiler::X64Recompiler::Compile_jalr(CompileFlags cf)
void CPU::X64Recompiler::Compile_jalr(CompileFlags cf)
{
if (!cf.valid_host_s)
cg->mov(RWARG1, MipsPtr(cf.MipsS()));
@ -969,7 +997,7 @@ void CPU::Recompiler::X64Recompiler::Compile_jalr(CompileFlags cf)
EndBlock(std::nullopt, true);
}
void CPU::Recompiler::X64Recompiler::Compile_bxx(CompileFlags cf, BranchCondition cond)
void CPU::X64Recompiler::Compile_bxx(CompileFlags cf, BranchCondition cond)
{
const u32 taken_pc = GetConditionalBranchTarget(cf);
@ -1045,7 +1073,7 @@ void CPU::Recompiler::X64Recompiler::Compile_bxx(CompileFlags cf, BranchConditio
EndBlock(taken_pc, true);
}
void CPU::Recompiler::X64Recompiler::Compile_addi(CompileFlags cf)
void CPU::X64Recompiler::Compile_addi(CompileFlags cf)
{
const Reg32 rt = MoveSToT(cf);
if (const u32 imm = inst->i.imm_sext32(); imm != 0)
@ -1059,24 +1087,24 @@ void CPU::Recompiler::X64Recompiler::Compile_addi(CompileFlags cf)
}
}
void CPU::Recompiler::X64Recompiler::Compile_addiu(CompileFlags cf)
void CPU::X64Recompiler::Compile_addiu(CompileFlags cf)
{
const Reg32 rt = MoveSToT(cf);
if (const u32 imm = inst->i.imm_sext32(); imm != 0)
cg->add(rt, imm);
}
void CPU::Recompiler::X64Recompiler::Compile_slti(CompileFlags cf)
void CPU::X64Recompiler::Compile_slti(CompileFlags cf)
{
Compile_slti(cf, true);
}
void CPU::Recompiler::X64Recompiler::Compile_sltiu(CompileFlags cf)
void CPU::X64Recompiler::Compile_sltiu(CompileFlags cf)
{
Compile_slti(cf, false);
}
void CPU::Recompiler::X64Recompiler::Compile_slti(CompileFlags cf, bool sign)
void CPU::X64Recompiler::Compile_slti(CompileFlags cf, bool sign)
{
const Reg32 rt = cf.valid_host_t ? CFGetRegT(cf) : RWARG1;
@ -1098,7 +1126,7 @@ void CPU::Recompiler::X64Recompiler::Compile_slti(CompileFlags cf, bool sign)
cg->mov(MipsPtr(cf.MipsT()), rt);
}
void CPU::Recompiler::X64Recompiler::Compile_andi(CompileFlags cf)
void CPU::X64Recompiler::Compile_andi(CompileFlags cf)
{
if (const u32 imm = inst->i.imm_zext32(); imm != 0)
{
@ -1112,43 +1140,44 @@ void CPU::Recompiler::X64Recompiler::Compile_andi(CompileFlags cf)
}
}
void CPU::Recompiler::X64Recompiler::Compile_ori(CompileFlags cf)
void CPU::X64Recompiler::Compile_ori(CompileFlags cf)
{
const Reg32 rt = MoveSToT(cf);
if (const u32 imm = inst->i.imm_zext32(); imm != 0)
cg->or_(rt, imm);
}
void CPU::Recompiler::X64Recompiler::Compile_xori(CompileFlags cf)
void CPU::X64Recompiler::Compile_xori(CompileFlags cf)
{
const Reg32 rt = MoveSToT(cf);
if (const u32 imm = inst->i.imm_zext32(); imm != 0)
cg->xor_(rt, imm);
}
void CPU::Recompiler::X64Recompiler::Compile_sll(CompileFlags cf)
void CPU::X64Recompiler::Compile_sll(CompileFlags cf)
{
const Reg32 rd = MoveTToD(cf);
if (inst->r.shamt > 0)
cg->shl(rd, inst->r.shamt);
}
void CPU::Recompiler::X64Recompiler::Compile_srl(CompileFlags cf)
void CPU::X64Recompiler::Compile_srl(CompileFlags cf)
{
const Reg32 rd = MoveTToD(cf);
if (inst->r.shamt > 0)
cg->shr(rd, inst->r.shamt);
}
void CPU::Recompiler::X64Recompiler::Compile_sra(CompileFlags cf)
void CPU::X64Recompiler::Compile_sra(CompileFlags cf)
{
const Reg32 rd = MoveTToD(cf);
if (inst->r.shamt > 0)
cg->sar(rd, inst->r.shamt);
}
void CPU::Recompiler::X64Recompiler::Compile_variable_shift(
CompileFlags cf, void (Xbyak::CodeGenerator::*op)(const Xbyak::Operand&, const Xbyak::Reg8&),
void CPU::X64Recompiler::Compile_variable_shift(CompileFlags cf,
void (Xbyak::CodeGenerator::*op)(const Xbyak::Operand&,
const Xbyak::Reg8&),
void (Xbyak::CodeGenerator::*op_const)(const Xbyak::Operand&, int))
{
const Reg32 rd = CFGetRegD(cf);
@ -1165,22 +1194,22 @@ void CPU::Recompiler::X64Recompiler::Compile_variable_shift(
}
}
void CPU::Recompiler::X64Recompiler::Compile_sllv(CompileFlags cf)
void CPU::X64Recompiler::Compile_sllv(CompileFlags cf)
{
Compile_variable_shift(cf, &CodeGenerator::shl, &CodeGenerator::shl);
}
void CPU::Recompiler::X64Recompiler::Compile_srlv(CompileFlags cf)
void CPU::X64Recompiler::Compile_srlv(CompileFlags cf)
{
Compile_variable_shift(cf, &CodeGenerator::shr, &CodeGenerator::shr);
}
void CPU::Recompiler::X64Recompiler::Compile_srav(CompileFlags cf)
void CPU::X64Recompiler::Compile_srav(CompileFlags cf)
{
Compile_variable_shift(cf, &CodeGenerator::sar, &CodeGenerator::sar);
}
void CPU::Recompiler::X64Recompiler::Compile_mult(CompileFlags cf, bool sign)
void CPU::X64Recompiler::Compile_mult(CompileFlags cf, bool sign)
{
// RAX/RDX shouldn't be allocatable..
DebugAssert(!(m_host_regs[Xbyak::Operand::RAX].flags & HR_USABLE) &&
@ -1212,17 +1241,17 @@ void CPU::Recompiler::X64Recompiler::Compile_mult(CompileFlags cf, bool sign)
cg->mov(MipsPtr(Reg::hi), cg->edx);
}
void CPU::Recompiler::X64Recompiler::Compile_mult(CompileFlags cf)
void CPU::X64Recompiler::Compile_mult(CompileFlags cf)
{
Compile_mult(cf, true);
}
void CPU::Recompiler::X64Recompiler::Compile_multu(CompileFlags cf)
void CPU::X64Recompiler::Compile_multu(CompileFlags cf)
{
Compile_mult(cf, false);
}
void CPU::Recompiler::X64Recompiler::Compile_div(CompileFlags cf)
void CPU::X64Recompiler::Compile_div(CompileFlags cf)
{
// not supported without registers for now..
DebugAssert(cf.valid_host_lo && cf.valid_host_hi);
@ -1268,7 +1297,7 @@ void CPU::Recompiler::X64Recompiler::Compile_div(CompileFlags cf)
cg->L(done);
}
void CPU::Recompiler::X64Recompiler::Compile_divu(CompileFlags cf)
void CPU::X64Recompiler::Compile_divu(CompileFlags cf)
{
// not supported without registers for now..
DebugAssert(cf.valid_host_lo && cf.valid_host_hi);
@ -1299,7 +1328,7 @@ void CPU::Recompiler::X64Recompiler::Compile_divu(CompileFlags cf)
cg->L(done);
}
void CPU::Recompiler::X64Recompiler::TestOverflow(const Xbyak::Reg32& result)
void CPU::X64Recompiler::TestOverflow(const Xbyak::Reg32& result)
{
SwitchToFarCode(true, &Xbyak::CodeGenerator::jo);
@ -1315,9 +1344,10 @@ void CPU::Recompiler::X64Recompiler::TestOverflow(const Xbyak::Reg32& result)
SwitchToNearCode(false);
}
void CPU::Recompiler::X64Recompiler::Compile_dst_op(
CompileFlags cf, void (Xbyak::CodeGenerator::*op)(const Xbyak::Operand&, const Xbyak::Operand&),
void (Xbyak::CodeGenerator::*op_const)(const Xbyak::Operand&, u32), bool commutative, bool overflow)
void CPU::X64Recompiler::Compile_dst_op(CompileFlags cf,
void (Xbyak::CodeGenerator::*op)(const Xbyak::Operand&, const Xbyak::Operand&),
void (Xbyak::CodeGenerator::*op_const)(const Xbyak::Operand&, u32),
bool commutative, bool overflow)
{
if (cf.valid_host_s && cf.valid_host_t)
{
@ -1401,27 +1431,27 @@ void CPU::Recompiler::X64Recompiler::Compile_dst_op(
}
}
void CPU::Recompiler::X64Recompiler::Compile_add(CompileFlags cf)
void CPU::X64Recompiler::Compile_add(CompileFlags cf)
{
Compile_dst_op(cf, &CodeGenerator::add, &CodeGenerator::add, true, g_settings.cpu_recompiler_memory_exceptions);
}
void CPU::Recompiler::X64Recompiler::Compile_addu(CompileFlags cf)
void CPU::X64Recompiler::Compile_addu(CompileFlags cf)
{
Compile_dst_op(cf, &CodeGenerator::add, &CodeGenerator::add, true, false);
}
void CPU::Recompiler::X64Recompiler::Compile_sub(CompileFlags cf)
void CPU::X64Recompiler::Compile_sub(CompileFlags cf)
{
Compile_dst_op(cf, &CodeGenerator::sub, &CodeGenerator::sub, false, g_settings.cpu_recompiler_memory_exceptions);
}
void CPU::Recompiler::X64Recompiler::Compile_subu(CompileFlags cf)
void CPU::X64Recompiler::Compile_subu(CompileFlags cf)
{
Compile_dst_op(cf, &CodeGenerator::sub, &CodeGenerator::sub, false, false);
}
void CPU::Recompiler::X64Recompiler::Compile_and(CompileFlags cf)
void CPU::X64Recompiler::Compile_and(CompileFlags cf)
{
// special cases - and with self -> self, and with 0 -> 0
const Reg32 regd = CFGetRegD(cf);
@ -1439,7 +1469,7 @@ void CPU::Recompiler::X64Recompiler::Compile_and(CompileFlags cf)
Compile_dst_op(cf, &CodeGenerator::and_, &CodeGenerator::and_, true, false);
}
void CPU::Recompiler::X64Recompiler::Compile_or(CompileFlags cf)
void CPU::X64Recompiler::Compile_or(CompileFlags cf)
{
// or/nor with 0 -> no effect
const Reg32 regd = CFGetRegD(cf);
@ -1452,7 +1482,7 @@ void CPU::Recompiler::X64Recompiler::Compile_or(CompileFlags cf)
Compile_dst_op(cf, &CodeGenerator::or_, &CodeGenerator::or_, true, false);
}
void CPU::Recompiler::X64Recompiler::Compile_xor(CompileFlags cf)
void CPU::X64Recompiler::Compile_xor(CompileFlags cf)
{
const Reg32 regd = CFGetRegD(cf);
if (cf.MipsS() == cf.MipsT())
@ -1471,23 +1501,23 @@ void CPU::Recompiler::X64Recompiler::Compile_xor(CompileFlags cf)
Compile_dst_op(cf, &CodeGenerator::xor_, &CodeGenerator::xor_, true, false);
}
void CPU::Recompiler::X64Recompiler::Compile_nor(CompileFlags cf)
void CPU::X64Recompiler::Compile_nor(CompileFlags cf)
{
Compile_or(cf);
cg->not_(CFGetRegD(cf));
}
void CPU::Recompiler::X64Recompiler::Compile_slt(CompileFlags cf)
void CPU::X64Recompiler::Compile_slt(CompileFlags cf)
{
Compile_slt(cf, true);
}
void CPU::Recompiler::X64Recompiler::Compile_sltu(CompileFlags cf)
void CPU::X64Recompiler::Compile_sltu(CompileFlags cf)
{
Compile_slt(cf, false);
}
void CPU::Recompiler::X64Recompiler::Compile_slt(CompileFlags cf, bool sign)
void CPU::X64Recompiler::Compile_slt(CompileFlags cf, bool sign)
{
const Reg32 rd = CFGetRegD(cf);
const Reg32 rs = cf.valid_host_s ? CFGetRegS(cf) : RWARG1;
@ -1513,8 +1543,8 @@ void CPU::Recompiler::X64Recompiler::Compile_slt(CompileFlags cf, bool sign)
sign ? cg->setl(rd.cvt8()) : cg->setb(rd.cvt8());
}
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::ComputeLoadStoreAddressArg(
CompileFlags cf, const std::optional<VirtualMemoryAddress>& address,
Xbyak::Reg32
CPU::X64Recompiler::ComputeLoadStoreAddressArg(CompileFlags cf, const std::optional<VirtualMemoryAddress>& address,
const std::optional<const Xbyak::Reg32>& reg /* = std::nullopt */)
{
const u32 imm = inst->i.imm_sext32();
@ -1546,8 +1576,8 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::ComputeLoadStoreAddressArg(
}
template<typename RegAllocFn>
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::GenerateLoad(const Xbyak::Reg32& addr_reg, MemoryAccessSize size,
bool sign, bool use_fastmem, const RegAllocFn& dst_reg_alloc)
Xbyak::Reg32 CPU::X64Recompiler::GenerateLoad(const Xbyak::Reg32& addr_reg, MemoryAccessSize size, bool sign,
bool use_fastmem, const RegAllocFn& dst_reg_alloc)
{
if (use_fastmem)
{
@ -1608,20 +1638,20 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::GenerateLoad(const Xbyak::Reg32& ad
{
case MemoryAccessSize::Byte:
{
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::ReadMemoryByte) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryByte));
cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryByte) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryByte));
}
break;
case MemoryAccessSize::HalfWord:
{
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::ReadMemoryHalfWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryHalfWord));
cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryHalfWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryHalfWord));
}
break;
case MemoryAccessSize::Word:
{
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::ReadMemoryWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryWord));
cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryWord));
}
break;
}
@ -1677,7 +1707,7 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::GenerateLoad(const Xbyak::Reg32& ad
return dst_reg;
}
void CPU::Recompiler::X64Recompiler::GenerateStore(const Xbyak::Reg32& addr_reg, const Xbyak::Reg32& value_reg,
void CPU::X64Recompiler::GenerateStore(const Xbyak::Reg32& addr_reg, const Xbyak::Reg32& value_reg,
MemoryAccessSize size, bool use_fastmem)
{
if (use_fastmem)
@ -1729,20 +1759,20 @@ void CPU::Recompiler::X64Recompiler::GenerateStore(const Xbyak::Reg32& addr_reg,
{
case MemoryAccessSize::Byte:
{
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::WriteMemoryByte) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryByte));
cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryByte) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryByte));
}
break;
case MemoryAccessSize::HalfWord:
{
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::WriteMemoryHalfWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryHalfWord));
cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryHalfWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryHalfWord));
}
break;
case MemoryAccessSize::Word:
{
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::WriteMemoryWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryWord));
cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryWord));
}
break;
}
@ -1774,7 +1804,7 @@ void CPU::Recompiler::X64Recompiler::GenerateStore(const Xbyak::Reg32& addr_reg,
}
}
void CPU::Recompiler::X64Recompiler::Compile_lxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::X64Recompiler::Compile_lxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
const std::optional<Reg32> addr_reg = g_settings.gpu_pgxp_enable ?
@ -1803,7 +1833,7 @@ void CPU::Recompiler::X64Recompiler::Compile_lxx(CompileFlags cf, MemoryAccessSi
}
}
void CPU::Recompiler::X64Recompiler::Compile_lwx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::X64Recompiler::Compile_lwx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
DebugAssert(size == MemoryAccessSize::Word && !sign);
@ -1907,7 +1937,7 @@ void CPU::Recompiler::X64Recompiler::Compile_lwx(CompileFlags cf, MemoryAccessSi
}
}
void CPU::Recompiler::X64Recompiler::Compile_lwc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::X64Recompiler::Compile_lwc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
const u32 index = static_cast<u32>(inst->r.rt.GetValue());
@ -1993,7 +2023,7 @@ void CPU::Recompiler::X64Recompiler::Compile_lwc2(CompileFlags cf, MemoryAccessS
}
}
void CPU::Recompiler::X64Recompiler::Compile_sxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::X64Recompiler::Compile_sxx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
const std::optional<Reg32> addr_reg = g_settings.gpu_pgxp_enable ?
@ -2018,7 +2048,7 @@ void CPU::Recompiler::X64Recompiler::Compile_sxx(CompileFlags cf, MemoryAccessSi
}
}
void CPU::Recompiler::X64Recompiler::Compile_swx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::X64Recompiler::Compile_swx(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
DebugAssert(size == MemoryAccessSize::Word && !sign);
@ -2098,7 +2128,7 @@ void CPU::Recompiler::X64Recompiler::Compile_swx(CompileFlags cf, MemoryAccessSi
}
}
void CPU::Recompiler::X64Recompiler::Compile_swc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
void CPU::X64Recompiler::Compile_swc2(CompileFlags cf, MemoryAccessSize size, bool sign, bool use_fastmem,
const std::optional<VirtualMemoryAddress>& address)
{
const u32 index = static_cast<u32>(inst->r.rt.GetValue());
@ -2154,7 +2184,7 @@ void CPU::Recompiler::X64Recompiler::Compile_swc2(CompileFlags cf, MemoryAccessS
FreeHostReg(data_backup.getIdx());
}
void CPU::Recompiler::X64Recompiler::Compile_mtc0(CompileFlags cf)
void CPU::X64Recompiler::Compile_mtc0(CompileFlags cf)
{
const Cop0Reg reg = static_cast<Cop0Reg>(MipsD());
const u32* ptr = GetCop0RegPtr(reg);
@ -2238,7 +2268,7 @@ void CPU::Recompiler::X64Recompiler::Compile_mtc0(CompileFlags cf)
}
}
void CPU::Recompiler::X64Recompiler::Compile_rfe(CompileFlags cf)
void CPU::X64Recompiler::Compile_rfe(CompileFlags cf)
{
// shift mode bits right two, preserving upper bits
static constexpr u32 mode_bits_mask = UINT32_C(0b1111);
@ -2253,7 +2283,7 @@ void CPU::Recompiler::X64Recompiler::Compile_rfe(CompileFlags cf)
TestInterrupts(RWARG1);
}
void CPU::Recompiler::X64Recompiler::TestInterrupts(const Xbyak::Reg32& sr)
void CPU::X64Recompiler::TestInterrupts(const Xbyak::Reg32& sr)
{
// if Iec == 0 then goto no_interrupt
Label no_interrupt;
@ -2301,7 +2331,7 @@ void CPU::Recompiler::X64Recompiler::TestInterrupts(const Xbyak::Reg32& sr)
cg->L(no_interrupt);
}
void CPU::Recompiler::X64Recompiler::Compile_mfc2(CompileFlags cf)
void CPU::X64Recompiler::Compile_mfc2(CompileFlags cf)
{
const u32 index = inst->cop.Cop2Index();
const Reg rt = inst->r.rt;
@ -2342,7 +2372,7 @@ void CPU::Recompiler::X64Recompiler::Compile_mfc2(CompileFlags cf)
}
}
void CPU::Recompiler::X64Recompiler::Compile_mtc2(CompileFlags cf)
void CPU::X64Recompiler::Compile_mtc2(CompileFlags cf)
{
const u32 index = inst->cop.Cop2Index();
const auto [ptr, action] = GetGTERegisterPointer(index, true);
@ -2416,7 +2446,7 @@ void CPU::Recompiler::X64Recompiler::Compile_mtc2(CompileFlags cf)
}
}
void CPU::Recompiler::X64Recompiler::Compile_cop2(CompileFlags cf)
void CPU::X64Recompiler::Compile_cop2(CompileFlags cf)
{
TickCount func_ticks;
GTE::InstructionImpl func = GTE::GetInstructionImpl(inst->bits, &func_ticks);
@ -2480,20 +2510,20 @@ u32 CPU::Recompiler::CompileLoadStoreThunk(void* thunk_code, u32 thunk_space, vo
{
case MemoryAccessSize::Byte:
{
cg->call(is_load ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryByte) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryByte));
cg->call(is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryByte) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryByte));
}
break;
case MemoryAccessSize::HalfWord:
{
cg->call(is_load ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryHalfWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryHalfWord));
cg->call(is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryHalfWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryHalfWord));
}
break;
case MemoryAccessSize::Word:
{
cg->call(is_load ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryWord));
cg->call(is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryWord) :
reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryWord));
}
break;
}

View File

@ -9,7 +9,15 @@
#ifdef CPU_ARCH_X64
namespace CPU::Recompiler {
// We need to include windows.h before xbyak does..
#ifdef _WIN32
#include "common/windows_headers.h"
#endif
#define XBYAK_NO_OP_NAMES 1
#include "xbyak.h"
namespace CPU {
class X64Recompiler final : public Recompiler
{
@ -141,6 +149,6 @@ private:
Xbyak::CodeGenerator* cg;
};
} // namespace CPU::Recompiler
} // namespace CPU
#endif // CPU_ARCH_X64