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 set(RECOMPILER_SRCS
cpu_recompiler.cpp cpu_recompiler.cpp
cpu_recompiler.h cpu_recompiler.h
cpu_recompiler_thunks.h
cpu_recompiler_types.h
) )
target_precompile_headers(core PRIVATE "pch.h") target_precompile_headers(core PRIVATE "pch.h")

View File

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

View File

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

View File

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

View File

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

View File

@ -132,4 +132,36 @@ ALWAYS_INLINE static void StallUntilGTEComplete()
void HandleA0Syscall(); void HandleA0Syscall();
void HandleB0Syscall(); 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 } // namespace CPU

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include "cpu_code_cache_private.h" #include "cpu_code_cache_private.h"
#include "cpu_recompiler_types.h"
#include "cpu_types.h" #include "cpu_types.h"
#include <array> #include <array>
@ -13,36 +12,72 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace CPU::Recompiler { namespace CPU {
// 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)
static constexpr u32 NUM_HOST_REGS = 16;
static constexpr bool HAS_MEMORY_OPERANDS = true;
#elif defined(CPU_ARCH_ARM32)
static constexpr u32 NUM_HOST_REGS = 16;
static constexpr bool HAS_MEMORY_OPERANDS = false;
#elif defined(CPU_ARCH_ARM64)
static constexpr u32 NUM_HOST_REGS = 32;
static constexpr bool HAS_MEMORY_OPERANDS = false;
#elif defined(CPU_ARCH_RISCV64)
static constexpr u32 NUM_HOST_REGS = 32;
static constexpr bool HAS_MEMORY_OPERANDS = false;
#endif
// TODO: Get rid of the virtuals... somehow. // TODO: Get rid of the virtuals... somehow.
class Recompiler 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
public: public:
Recompiler(); Recompiler();
virtual ~Recompiler(); virtual ~Recompiler();
const void* CompileBlock(CodeCache::Block* block, u32* host_code_size, u32* host_far_code_size); 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: protected:
enum FlushFlags : u32 enum FlushFlags : u32
{ {
@ -274,7 +309,7 @@ protected:
void CompileTemplate(void (Recompiler::*const_func)(CompileFlags), void (Recompiler::*func)(CompileFlags), void CompileTemplate(void (Recompiler::*const_func)(CompileFlags), void (Recompiler::*func)(CompileFlags),
const void* pgxp_cpu_func, u32 tflags); const void* pgxp_cpu_func, u32 tflags);
void CompileLoadStoreTemplate(void (Recompiler::*func)(CompileFlags, MemoryAccessSize, bool, bool, void CompileLoadStoreTemplate(void (Recompiler::*func)(CompileFlags, MemoryAccessSize, bool, bool,
const std::optional<VirtualMemoryAddress>&), const std::optional<VirtualMemoryAddress>&),
MemoryAccessSize size, bool store, bool sign, u32 tflags); MemoryAccessSize size, bool store, bool sign, u32 tflags);
void FlushForLoadStore(const std::optional<VirtualMemoryAddress>& address, bool store, bool use_fastmem); void FlushForLoadStore(const std::optional<VirtualMemoryAddress>& address, bool store, bool use_fastmem);
void CompileMoveRegTemplate(Reg dst, Reg src, bool pgxp_move); void CompileMoveRegTemplate(Reg dst, Reg src, bool pgxp_move);
@ -533,11 +568,5 @@ protected:
static const std::array<const void*, 3> s_pgxp_mem_store_functions; 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; extern Recompiler* g_compiler;
} // namespace CPU::Recompiler } // namespace CPU

View File

@ -4,8 +4,6 @@
#include "cpu_recompiler_arm32.h" #include "cpu_recompiler_arm32.h"
#include "cpu_core_private.h" #include "cpu_core_private.h"
#include "cpu_pgxp.h" #include "cpu_pgxp.h"
#include "cpu_recompiler_thunks.h"
#include "cpu_recompiler_types.h"
#include "gte.h" #include "gte.h"
#include "settings.h" #include "settings.h"
#include "timing_event.h" #include "timing_event.h"
@ -20,6 +18,9 @@
#ifdef CPU_ARCH_ARM32 #ifdef CPU_ARCH_ARM32
#include "vixl/aarch32/constants-aarch32.h"
#include "vixl/aarch32/instructions-aarch32.h"
#ifdef ENABLE_HOST_DISASSEMBLY #ifdef ENABLE_HOST_DISASSEMBLY
#include "vixl/aarch32/disasm-aarch32.h" #include "vixl/aarch32/disasm-aarch32.h"
#include <iostream> #include <iostream>
@ -30,43 +31,64 @@ LOG_CHANNEL(Recompiler);
#define PTR(x) vixl::aarch32::MemOperand(RSTATE, (((u8*)(x)) - ((u8*)&g_state))) #define PTR(x) vixl::aarch32::MemOperand(RSTATE, (((u8*)(x)) - ((u8*)&g_state)))
#define RMEMBASE vixl::aarch32::r3 #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 static bool armIsCallerSavedRegister(u32 id);
constexpr u32 FUNCTION_CALLER_SAVED_SPACE_RESERVE = 144; // 18 registers -> 224 bytes static s32 armGetPCDisplacement(const void* current, const void* target);
constexpr u32 FUNCTION_STACK_SIZE = FUNCTION_CALLEE_SAVED_SPACE_RESERVE + FUNCTION_CALLER_SAVED_SPACE_RESERVE; 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 constexpr u32 TRAMPOLINE_AREA_SIZE = 4 * 1024;
static std::unordered_map<const void*, u32> s_trampoline_targets; static std::unordered_map<const void*, u32> s_trampoline_targets;
static u8* s_trampoline_start_ptr = nullptr; static u8* s_trampoline_start_ptr = nullptr;
static u32 s_trampoline_used = 0; static u32 s_trampoline_used = 0;
namespace CPU {
using namespace vixl::aarch32;
static ARM32Recompiler s_instance; static ARM32Recompiler s_instance;
Recompiler* g_compiler = &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 return ((id >= 0 && id <= 3) || // r0-r3
(id == 12 || id == 14)); // sp, pc (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>(current), 4));
Assert(Common::IsAlignedPow2(reinterpret_cast<size_t>(target), 4)); Assert(Common::IsAlignedPow2(reinterpret_cast<size_t>(target), 4));
return static_cast<s32>((reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(current))); 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); 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)) 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); armAsm->movt(al, rd, imm >> 16);
} }
void CPU::Recompiler::armMoveAddressToReg(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, void armMoveAddressToReg(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr)
const void* addr)
{ {
armEmitMov(armAsm, reg, static_cast<u32>(reinterpret_cast<uintptr_t>(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*>(); const void* cur = armAsm->GetCursorAddress<const void*>();
s32 displacement = armGetPCDisplacement(cur, ptr); 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*>(); const void* cur = armAsm->GetCursorAddress<const void*>();
s32 displacement = armGetPCDisplacement(cur, ptr); 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, void armEmitCondBranch(vixl::aarch32::Assembler* armAsm, vixl::aarch32::Condition cond, const void* ptr)
const void* ptr)
{ {
const s32 displacement = armGetPCDisplacement(armAsm->GetCursorAddress<const void*>(), ptr); const s32 displacement = armGetPCDisplacement(armAsm->GetCursorAddress<const void*>(), ptr);
if (!armIsPCDisplacementInImmediateRange(displacement)) 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, void armEmitFarLoad(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr)
const void* addr)
{ {
armMoveAddressToReg(armAsm, reg, addr); armMoveAddressToReg(armAsm, reg, addr);
armAsm->ldr(reg, MemOperand(reg)); armAsm->ldr(reg, MemOperand(reg));
} }
void CPU::Recompiler::armEmitFarStore(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, void armEmitFarStore(vixl::aarch32::Assembler* armAsm, const vixl::aarch32::Register& reg, const void* addr,
const void* addr, const vixl::aarch32::Register& tempreg) const vixl::aarch32::Register& tempreg)
{ {
armMoveAddressToReg(armAsm, tempreg, addr); armMoveAddressToReg(armAsm, tempreg, addr);
armAsm->str(reg, MemOperand(tempreg)); 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*/; 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*>(); return armAsm->GetCursorAddress<const void*>();
} }
void CPU::Recompiler::ARM32Recompiler::Reset(CodeCache::Block* block, u8* code_buffer, u32 code_buffer_space, void CPU::ARM32Recompiler::Reset(CodeCache::Block* block, u8* code_buffer, u32 code_buffer_space, u8* far_code_buffer,
u8* far_code_buffer, u32 far_code_space) u32 far_code_space)
{ {
Recompiler::Reset(block, code_buffer, code_buffer_space, far_code_buffer, 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); DebugAssert(armAsm == &m_emitter);
if (emit_jump) if (emit_jump)
@ -395,7 +414,7 @@ void CPU::Recompiler::ARM32Recompiler::SwitchToFarCode(bool emit_jump, vixl::aar
armAsm = &m_far_emitter; 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); armAsm->tst(reg, 1u << bit);
@ -416,8 +435,7 @@ void CPU::Recompiler::ARM32Recompiler::SwitchToFarCodeIfBitSet(const vixl::aarch
armAsm = &m_far_emitter; armAsm = &m_far_emitter;
} }
void CPU::Recompiler::ARM32Recompiler::SwitchToFarCodeIfRegZeroOrNonZero(const vixl::aarch32::Register& reg, void CPU::ARM32Recompiler::SwitchToFarCodeIfRegZeroOrNonZero(const vixl::aarch32::Register& reg, bool nonzero)
bool nonzero)
{ {
armAsm->cmp(reg, 0); armAsm->cmp(reg, 0);
@ -438,7 +456,7 @@ void CPU::Recompiler::ARM32Recompiler::SwitchToFarCodeIfRegZeroOrNonZero(const v
armAsm = &m_far_emitter; 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); DebugAssert(armAsm == &m_far_emitter);
if (emit_jump) if (emit_jump)
@ -464,17 +482,17 @@ void CPU::Recompiler::ARM32Recompiler::SwitchToNearCode(bool emit_jump, vixl::aa
armAsm = &m_emitter; 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); 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); 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))) if (ImmediateA32::IsImmediateA32(static_cast<u32>(val)))
return vixl::aarch32::Operand(static_cast<int32_t>(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); 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)); 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); return armCheckAddSubConstant(val);
} }
vixl::aarch32::Operand CPU::Recompiler::ARM32Recompiler::armCheckLogicalConstant(u32 val) vixl::aarch32::Operand CPU::ARM32Recompiler::armCheckLogicalConstant(u32 val)
{ {
return armCheckAddSubConstant(val); return armCheckAddSubConstant(val);
} }
void CPU::Recompiler::ARM32Recompiler::BeginBlock() void CPU::ARM32Recompiler::BeginBlock()
{ {
Recompiler::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 // store it first to reduce code size, because we can offset
armMoveAddressToReg(armAsm, RARG1, ram_ptr); armMoveAddressToReg(armAsm, RARG1, ram_ptr);
@ -579,7 +597,7 @@ bool foo(const void* a, const void* b)
armAsm->bind(&block_unchanged); armAsm->bind(&block_unchanged);
} }
void CPU::Recompiler::ARM32Recompiler::GenerateICacheCheckAndUpdate() void CPU::ARM32Recompiler::GenerateICacheCheckAndUpdate()
{ {
if (!m_block->HasFlag(CodeCache::BlockFlags::IsUsingICache)) if (!m_block->HasFlag(CodeCache::BlockFlags::IsUsingICache))
{ {
@ -635,8 +653,8 @@ 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*/) s32 arg3reg /*= -1*/)
{ {
if (arg1reg >= 0 && arg1reg != static_cast<s32>(RARG1.GetCode())) if (arg1reg >= 0 && arg1reg != static_cast<s32>(RARG1.GetCode()))
armAsm->mov(RARG1, Register(arg1reg)); armAsm->mov(RARG1, Register(arg1reg));
@ -647,7 +665,7 @@ void CPU::Recompiler::ARM32Recompiler::GenerateCall(const void* func, s32 arg1re
EmitCall(func); 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()) if (newpc.has_value())
{ {
@ -664,7 +682,7 @@ void CPU::Recompiler::ARM32Recompiler::EndBlock(const std::optional<u32>& newpc,
EndAndLinkBlock(newpc, do_event_test, false); 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 regs, but not pc, it's going to get overwritten
// flush cycles because of the GTE instruction stuff... // flush cycles because of the GTE instruction stuff...
@ -682,8 +700,7 @@ void CPU::Recompiler::ARM32Recompiler::EndBlockWithException(Exception excode)
EndAndLinkBlock(std::nullopt, true, false); EndAndLinkBlock(std::nullopt, true, false);
} }
void CPU::Recompiler::ARM32Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, void CPU::ARM32Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, bool force_run_events)
bool force_run_events)
{ {
// event test // event test
// pc should've been flushed // 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 #ifdef VIXL_DEBUG
m_emitter_check.reset(); m_emitter_check.reset();
@ -757,7 +774,7 @@ const void* CPU::Recompiler::ARM32Recompiler::EndCompile(u32* code_size, u32* fa
return code; 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 = { 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", {"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"; 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); 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)); 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)); 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); EmitMov(RSCRATCH, val);
armAsm->str(RSCRATCH, PTR(ptr)); 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) if (src != dst)
armAsm->mov(Register(dst), Register(src)); 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); 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); 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); DebugAssert(r < Reg::count);
return PTR(&g_state.regs.r[static_cast<u32>(r)]); 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); DebugAssert(cf.valid_host_d);
return Register(cf.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); DebugAssert(cf.valid_host_s);
return Register(cf.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); DebugAssert(cf.valid_host_t);
return Register(cf.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); DebugAssert(cf.valid_host_lo);
return Register(cf.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); DebugAssert(cf.valid_host_hi);
return Register(cf.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(); const u32 code = RMEMBASE.GetCode();
if (!IsHostRegAllocated(code)) if (!IsHostRegAllocated(code))
@ -852,7 +869,7 @@ vixl::aarch32::Register CPU::Recompiler::ARM32Recompiler::GetMembaseReg()
return RMEMBASE; 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) 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) 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); DebugAssert(reg < Reg::count);
if (const std::optional<u32> hreg = CheckHostReg(0, Recompiler::HR_TYPE_CPU_REG, reg)) if (const std::optional<u32> hreg = CheckHostReg(0, Recompiler::HR_TYPE_CPU_REG, reg))
@ -901,9 +918,8 @@ void CPU::Recompiler::ARM32Recompiler::MoveMIPSRegToReg(const vixl::aarch32::Reg
armAsm->ldr(dst, MipsPtr(reg)); armAsm->ldr(dst, MipsPtr(reg));
} }
void CPU::Recompiler::ARM32Recompiler::GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val, void CPU::ARM32Recompiler::GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val, Reg arg2reg /* = Reg::count */,
Reg arg2reg /* = Reg::count */, Reg arg3reg /* = Reg::count */)
Reg arg3reg /* = Reg::count */)
{ {
DebugAssert(g_settings.gpu_pgxp_enable); DebugAssert(g_settings.gpu_pgxp_enable);
@ -918,7 +934,7 @@ void CPU::Recompiler::ARM32Recompiler::GeneratePGXPCallWithMIPSRegs(const void*
EmitCall(func); EmitCall(func);
} }
void CPU::Recompiler::ARM32Recompiler::Flush(u32 flags) void CPU::ARM32Recompiler::Flush(u32 flags)
{ {
Recompiler::Flush(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); WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", iinfo->pc, inst->bits);
Flush(FLUSH_FOR_INTERPRETER); 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: make me less garbage
// TODO: this is wrong, it flushes the load delay on the same cycle when we return. // 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; 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) if (!g_settings.cpu_recompiler_memory_exceptions)
return; return;
@ -1050,7 +1066,7 @@ void CPU::Recompiler::ARM32Recompiler::CheckBranchTarget(const vixl::aarch32::Re
SwitchToNearCode(false); SwitchToNearCode(false);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_jr(CompileFlags cf) void CPU::ARM32Recompiler::Compile_jr(CompileFlags cf)
{ {
const Register pcreg = CFGetRegS(cf); const Register pcreg = CFGetRegS(cf);
CheckBranchTarget(pcreg); CheckBranchTarget(pcreg);
@ -1061,7 +1077,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_jr(CompileFlags cf)
EndBlock(std::nullopt, true); EndBlock(std::nullopt, true);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_jalr(CompileFlags cf) void CPU::ARM32Recompiler::Compile_jalr(CompileFlags cf)
{ {
const Register pcreg = CFGetRegS(cf); const Register pcreg = CFGetRegS(cf);
if (MipsD() != Reg::zero) if (MipsD() != Reg::zero)
@ -1074,7 +1090,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_jalr(CompileFlags cf)
EndBlock(std::nullopt, true); 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); AssertRegOrConstS(cf);
@ -1148,7 +1164,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_bxx(CompileFlags cf, BranchCondit
EndBlock(taken_pc, true); 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 rs = CFGetRegS(cf);
const Register rt = CFGetRegT(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); 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); Compile_addi(cf, false);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_slti(CompileFlags cf) void CPU::ARM32Recompiler::Compile_slti(CompileFlags cf)
{ {
Compile_slti(cf, true); Compile_slti(cf, true);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_sltiu(CompileFlags cf) void CPU::ARM32Recompiler::Compile_sltiu(CompileFlags cf)
{ {
Compile_slti(cf, false); 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 rs = CFGetRegS(cf);
const Register rt = CFGetRegT(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); 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); const Register rt = CFGetRegT(cf);
if (const u32 imm = inst->i.imm_zext32(); imm != 0) 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); 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 rt = CFGetRegT(cf);
const Register rs = CFGetRegS(cf); const Register rs = CFGetRegS(cf);
@ -1218,7 +1234,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_ori(CompileFlags cf)
armAsm->mov(rt, rs); 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 rt = CFGetRegT(cf);
const Register rs = CFGetRegS(cf); const Register rs = CFGetRegS(cf);
@ -1228,10 +1244,9 @@ void CPU::Recompiler::ARM32Recompiler::Compile_xori(CompileFlags cf)
armAsm->mov(rt, rs); 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, void (vixl::aarch32::Assembler::*op)(vixl::aarch32::Register,
vixl::aarch32::Register, vixl::aarch32::Register, const Operand&))
const Operand&))
{ {
const Register rd = CFGetRegD(cf); const Register rd = CFGetRegD(cf);
const Register rt = CFGetRegT(cf); const Register rt = CFGetRegT(cf);
@ -1241,24 +1256,25 @@ void CPU::Recompiler::ARM32Recompiler::Compile_shift(CompileFlags cf,
armAsm->mov(rd, rt); armAsm->mov(rd, rt);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_sll(CompileFlags cf) void CPU::ARM32Recompiler::Compile_sll(CompileFlags cf)
{ {
Compile_shift(cf, &Assembler::lsl); 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); 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); Compile_shift(cf, &Assembler::asr);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_variable_shift( void CPU::ARM32Recompiler::Compile_variable_shift(CompileFlags cf,
CompileFlags cf, void (vixl::aarch32::Assembler::*op)(vixl::aarch32::Register,
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 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); 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); 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); 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; const Register rs = cf.valid_host_s ? CFGetRegS(cf) : RARG1;
if (!cf.valid_host_s) 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); (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); Compile_mult(cf, true);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_multu(CompileFlags cf) void CPU::ARM32Recompiler::Compile_multu(CompileFlags cf)
{ {
Compile_mult(cf, false); 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; const Register rs = cf.valid_host_s ? CFGetRegS(cf) : RARG1;
if (!cf.valid_host_s) if (!cf.valid_host_s)
@ -1371,7 +1387,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_div(CompileFlags cf)
armAsm->bind(&done); 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; const Register rs = cf.valid_host_s ? CFGetRegS(cf) : RARG1;
if (!cf.valid_host_s) if (!cf.valid_host_s)
@ -1402,7 +1418,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_divu(CompileFlags cf)
armAsm->bind(&done); 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); SwitchToFarCode(true, vs);
@ -1418,11 +1434,10 @@ void CPU::Recompiler::ARM32Recompiler::TestOverflow(const vixl::aarch32::Registe
SwitchToNearCode(false); 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, void (vixl::aarch32::Assembler::*op)(vixl::aarch32::Register,
vixl::aarch32::Register, vixl::aarch32::Register, const Operand&),
const Operand&), bool commutative, bool logical, bool overflow)
bool commutative, bool logical, bool overflow)
{ {
AssertRegOrConstS(cf); AssertRegOrConstS(cf);
AssertRegOrConstT(cf); AssertRegOrConstT(cf);
@ -1470,7 +1485,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_dst_op(CompileFlags cf,
TestOverflow(rd); TestOverflow(rd);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_add(CompileFlags cf) void CPU::ARM32Recompiler::Compile_add(CompileFlags cf)
{ {
if (g_settings.cpu_recompiler_memory_exceptions) if (g_settings.cpu_recompiler_memory_exceptions)
Compile_dst_op(cf, &Assembler::adds, true, false, true); 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); 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); 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) if (g_settings.cpu_recompiler_memory_exceptions)
Compile_dst_op(cf, &Assembler::subs, false, false, true); 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); 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); 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); AssertRegOrConstS(cf);
AssertRegOrConstT(cf); AssertRegOrConstT(cf);
@ -1517,7 +1532,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_and(CompileFlags cf)
Compile_dst_op(cf, &Assembler::and_, true, true, false); 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); AssertRegOrConstS(cf);
AssertRegOrConstT(cf); AssertRegOrConstT(cf);
@ -1533,7 +1548,7 @@ void CPU::Recompiler::ARM32Recompiler::Compile_or(CompileFlags cf)
Compile_dst_op(cf, &Assembler::orr, true, true, false); 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); AssertRegOrConstS(cf);
AssertRegOrConstT(cf); AssertRegOrConstT(cf);
@ -1555,23 +1570,23 @@ void CPU::Recompiler::ARM32Recompiler::Compile_xor(CompileFlags cf)
Compile_dst_op(cf, &Assembler::eor, true, true, false); 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); Compile_or(cf);
armAsm->mvn(CFGetRegD(cf), CFGetRegD(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); Compile_slt(cf, true);
} }
void CPU::Recompiler::ARM32Recompiler::Compile_sltu(CompileFlags cf) void CPU::ARM32Recompiler::Compile_sltu(CompileFlags cf)
{ {
Compile_slt(cf, false); 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); AssertRegOrConstS(cf);
AssertRegOrConstT(cf); AssertRegOrConstT(cf);
@ -1597,9 +1612,8 @@ void CPU::Recompiler::ARM32Recompiler::Compile_slt(CompileFlags cf, bool sign)
} }
vixl::aarch32::Register vixl::aarch32::Register
CPU::Recompiler::ARM32Recompiler::ComputeLoadStoreAddressArg(CompileFlags cf, CPU::ARM32Recompiler::ComputeLoadStoreAddressArg(CompileFlags cf, const std::optional<VirtualMemoryAddress>& address,
const std::optional<VirtualMemoryAddress>& address, const std::optional<const vixl::aarch32::Register>& reg)
const std::optional<const vixl::aarch32::Register>& reg)
{ {
const u32 imm = inst->i.imm_sext32(); const u32 imm = inst->i.imm_sext32();
if (cf.valid_host_s && imm == 0 && !reg.has_value()) if (cf.valid_host_s && imm == 0 && !reg.has_value())
@ -1639,9 +1653,9 @@ CPU::Recompiler::ARM32Recompiler::ComputeLoadStoreAddressArg(CompileFlags cf,
} }
template<typename RegAllocFn> template<typename RegAllocFn>
vixl::aarch32::Register vixl::aarch32::Register CPU::ARM32Recompiler::GenerateLoad(const vixl::aarch32::Register& addr_reg,
CPU::Recompiler::ARM32Recompiler::GenerateLoad(const vixl::aarch32::Register& addr_reg, MemoryAccessSize size, MemoryAccessSize size, bool sign, bool use_fastmem,
bool sign, bool use_fastmem, const RegAllocFn& dst_reg_alloc) const RegAllocFn& dst_reg_alloc)
{ {
if (use_fastmem) if (use_fastmem)
{ {
@ -1683,20 +1697,20 @@ CPU::Recompiler::ARM32Recompiler::GenerateLoad(const vixl::aarch32::Register& ad
{ {
case MemoryAccessSize::Byte: case MemoryAccessSize::Byte:
{ {
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::ReadMemoryByte) : EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryByte) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryByte)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryByte));
} }
break; break;
case MemoryAccessSize::HalfWord: case MemoryAccessSize::HalfWord:
{ {
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::ReadMemoryHalfWord) : EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryHalfWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryHalfWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryHalfWord));
} }
break; break;
case MemoryAccessSize::Word: case MemoryAccessSize::Word:
{ {
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::ReadMemoryWord) : EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryWord));
} }
break; break;
} }
@ -1751,9 +1765,9 @@ CPU::Recompiler::ARM32Recompiler::GenerateLoad(const vixl::aarch32::Register& ad
return dst_reg; 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, const vixl::aarch32::Register& value_reg, MemoryAccessSize size,
bool use_fastmem) bool use_fastmem)
{ {
if (use_fastmem) if (use_fastmem)
{ {
@ -1793,20 +1807,20 @@ void CPU::Recompiler::ARM32Recompiler::GenerateStore(const vixl::aarch32::Regist
{ {
case MemoryAccessSize::Byte: case MemoryAccessSize::Byte:
{ {
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::WriteMemoryByte) : EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryByte) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryByte)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryByte));
} }
break; break;
case MemoryAccessSize::HalfWord: case MemoryAccessSize::HalfWord:
{ {
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::WriteMemoryHalfWord) : EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryHalfWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryHalfWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryHalfWord));
} }
break; break;
case MemoryAccessSize::Word: case MemoryAccessSize::Word:
{ {
EmitCall(checked ? reinterpret_cast<const void*>(&Recompiler::Thunks::WriteMemoryWord) : EmitCall(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryWord));
} }
break; break;
} }
@ -1837,8 +1851,8 @@ 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<VirtualMemoryAddress>& address)
{ {
const std::optional<Register> addr_reg = g_settings.gpu_pgxp_enable ? const std::optional<Register> addr_reg = g_settings.gpu_pgxp_enable ?
std::optional<Register>(Register(AllocateTempHostReg(HR_CALLEE_SAVED))) : std::optional<Register>(Register(AllocateTempHostReg(HR_CALLEE_SAVED))) :
@ -1865,8 +1879,8 @@ 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) const std::optional<VirtualMemoryAddress>& address)
{ {
DebugAssert(size == MemoryAccessSize::Word && !sign); DebugAssert(size == MemoryAccessSize::Word && !sign);
@ -1959,8 +1973,8 @@ 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 std::optional<VirtualMemoryAddress>& address)
{ {
const u32 index = static_cast<u32>(inst->r.rt.GetValue()); const u32 index = static_cast<u32>(inst->r.rt.GetValue());
const auto [ptr, action] = GetGTERegisterPointer(index, true); const auto [ptr, action] = GetGTERegisterPointer(index, true);
@ -2045,8 +2059,8 @@ 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) const std::optional<VirtualMemoryAddress>& address)
{ {
AssertRegOrConstS(cf); AssertRegOrConstS(cf);
AssertRegOrConstT(cf); AssertRegOrConstT(cf);
@ -2073,8 +2087,8 @@ 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) const std::optional<VirtualMemoryAddress>& address)
{ {
DebugAssert(size == MemoryAccessSize::Word && !sign); DebugAssert(size == MemoryAccessSize::Word && !sign);
@ -2147,8 +2161,8 @@ 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 std::optional<VirtualMemoryAddress>& address)
{ {
const u32 index = static_cast<u32>(inst->r.rt.GetValue()); const u32 index = static_cast<u32>(inst->r.rt.GetValue());
const auto [ptr, action] = GetGTERegisterPointer(index, false); const auto [ptr, action] = GetGTERegisterPointer(index, false);
@ -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 // TODO: we need better constant setting here.. which will need backprop
AssertRegOrConstT(cf); 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 // shift mode bits right two, preserving upper bits
armAsm->ldr(RARG1, PTR(&g_state.cop0_regs.sr.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); 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 // if Iec == 0 then goto no_interrupt
Label no_interrupt; Label no_interrupt;
@ -2344,7 +2358,7 @@ void CPU::Recompiler::ARM32Recompiler::TestInterrupts(const vixl::aarch32::Regis
armAsm->bind(&no_interrupt); 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 u32 index = inst->cop.Cop2Index();
const Reg rt = inst->r.rt; 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 u32 index = inst->cop.Cop2Index();
const auto [ptr, action] = GetGTERegisterPointer(index, true); 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; TickCount func_ticks;
GTE::InstructionImpl func = GTE::GetInstructionImpl(inst->bits, &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: case MemoryAccessSize::Byte:
{ {
armEmitCall(armAsm, armEmitCall(armAsm,
is_load ? reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryByte) : is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryByte) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryByte), reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryByte),
false); false);
} }
break; break;
case MemoryAccessSize::HalfWord: case MemoryAccessSize::HalfWord:
{ {
armEmitCall(armAsm, armEmitCall(armAsm,
is_load ? reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryHalfWord) : is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryHalfWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryHalfWord), reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryHalfWord),
false); false);
} }
break; break;
case MemoryAccessSize::Word: case MemoryAccessSize::Word:
{ {
armEmitCall(armAsm, armEmitCall(armAsm,
is_load ? reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedReadMemoryWord) : is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryWord) :
reinterpret_cast<const void*>(&Recompiler::Thunks::UncheckedWriteMemoryWord), reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryWord),
false); false);
} }
break; break;

View File

@ -12,7 +12,7 @@
#include "vixl/aarch32/assembler-aarch32.h" #include "vixl/aarch32/assembler-aarch32.h"
#include "vixl/aarch32/operands-aarch32.h" #include "vixl/aarch32/operands-aarch32.h"
namespace CPU::Recompiler { namespace CPU {
class ARM32Recompiler final : public Recompiler class ARM32Recompiler final : public Recompiler
{ {
@ -165,6 +165,6 @@ private:
#endif #endif
}; };
} // namespace CPU::Recompiler } // namespace CPU
#endif // CPU_ARCH_ARM32 #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" #include "vixl/aarch64/assembler-aarch64.h"
namespace CPU::Recompiler { namespace CPU {
class ARM64Recompiler final : public Recompiler class ARM64Recompiler final : public Recompiler
{ {
@ -166,6 +166,6 @@ private:
#endif #endif
}; };
} // namespace CPU::Recompiler } // namespace CPU
#endif // CPU_ARCH_ARM64 #endif // CPU_ARCH_ARM64

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,9 @@
#ifdef CPU_ARCH_RISCV64 #ifdef CPU_ARCH_RISCV64
namespace CPU::Recompiler { #include "biscuit/assembler.hpp"
namespace CPU {
class RISCV64Recompiler final : public Recompiler class RISCV64Recompiler final : public Recompiler
{ {
@ -171,6 +173,6 @@ private:
biscuit::Assembler* rvAsm; biscuit::Assembler* rvAsm;
}; };
} // namespace CPU::Recompiler } // namespace CPU
#endif // CPU_ARCH_RISCV64 #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_code_cache_private.h"
#include "cpu_core_private.h" #include "cpu_core_private.h"
#include "cpu_pgxp.h" #include "cpu_pgxp.h"
#include "cpu_recompiler_thunks.h"
#include "cpu_recompiler_types.h"
#include "gte.h" #include "gte.h"
#include "settings.h" #include "settings.h"
#include "timing_event.h" #include "timing_event.h"
@ -37,25 +35,58 @@ LOG_CHANNEL(Recompiler);
// PGXP TODO: LWL etc, MFC0 // PGXP TODO: LWL etc, MFC0
// PGXP TODO: Spyro 1 level gates have issues. // PGXP TODO: Spyro 1 level gates have issues.
namespace CPU::Recompiler {
using namespace Xbyak;
static constexpr u32 BACKPATCH_JMP_SIZE = 5; 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 // 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; 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; static constexpr u32 STACK_SHADOW_SIZE = 0;
#else
#error Unknown ABI.
#endif #endif
namespace CPU {
using namespace Xbyak;
static X64Recompiler s_instance; static X64Recompiler s_instance;
Recompiler* g_compiler = &s_instance; Recompiler* g_compiler = &s_instance;
} // namespace CPU::Recompiler } // namespace CPU
bool CPU::Recompiler::IsCallerSavedRegister(u32 id) bool IsCallerSavedRegister(u32 id)
{ {
#ifdef _WIN32 #ifdef _WIN32
// The x64 ABI considers the registers RAX, RCX, RDX, R8, R9, R10, R11, and XMM0-XMM5 volatile. // 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 #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, void CPU::X64Recompiler::Reset(CodeCache::Block* block, u8* code_buffer, u32 code_buffer_space, u8* far_code_buffer,
u8* far_code_buffer, u32 far_code_space) u32 far_code_space)
{ {
Recompiler::Reset(block, code_buffer, code_buffer_space, far_code_buffer, 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()); DebugAssert(cg == m_emitter.get());
if (emit_jump) if (emit_jump)
@ -377,8 +408,7 @@ void CPU::Recompiler::X64Recompiler::SwitchToFarCode(bool emit_jump, void (Xbyak
cg = m_far_emitter.get(); cg = m_far_emitter.get();
} }
void CPU::Recompiler::X64Recompiler::SwitchToNearCode(bool emit_jump, void CPU::X64Recompiler::SwitchToNearCode(bool emit_jump, void (Xbyak::CodeGenerator::*jump_op)(const void*))
void (Xbyak::CodeGenerator::*jump_op)(const void*))
{ {
DebugAssert(cg == m_far_emitter.get()); DebugAssert(cg == m_far_emitter.get());
if (emit_jump) if (emit_jump)
@ -389,7 +419,7 @@ void CPU::Recompiler::X64Recompiler::SwitchToNearCode(bool emit_jump,
cg = m_emitter.get(); cg = m_emitter.get();
} }
void CPU::Recompiler::X64Recompiler::BeginBlock() void CPU::X64Recompiler::BeginBlock()
{ {
Recompiler::BeginBlock(); Recompiler::BeginBlock();
@ -408,7 +438,7 @@ void CPU::Recompiler::X64Recompiler::BeginBlock()
#endif #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 // store it first to reduce code size, because we can offset
cg->mov(RXARG1, static_cast<size_t>(reinterpret_cast<uintptr_t>(ram_ptr))); 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); DebugAssert(size == 0);
} }
void CPU::Recompiler::X64Recompiler::GenerateICacheCheckAndUpdate() void CPU::X64Recompiler::GenerateICacheCheckAndUpdate()
{ {
if (!m_block->HasFlag(CodeCache::BlockFlags::IsUsingICache)) if (!m_block->HasFlag(CodeCache::BlockFlags::IsUsingICache))
{ {
@ -500,8 +530,8 @@ 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*/) s32 arg3reg /*= -1*/)
{ {
if (arg1reg >= 0 && arg1reg != static_cast<s32>(RXARG1.getIdx())) if (arg1reg >= 0 && arg1reg != static_cast<s32>(RXARG1.getIdx()))
cg->mov(RXARG1, Reg64(arg1reg)); cg->mov(RXARG1, Reg64(arg1reg));
@ -512,7 +542,7 @@ void CPU::Recompiler::X64Recompiler::GenerateCall(const void* func, s32 arg1reg
cg->call(func); 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()) 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); 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 regs, but not pc, it's going to get overwritten
// flush cycles because of the GTE instruction stuff... // flush cycles because of the GTE instruction stuff...
@ -544,8 +574,7 @@ void CPU::Recompiler::X64Recompiler::EndBlockWithException(Exception excode)
EndAndLinkBlock(std::nullopt, true, false); EndAndLinkBlock(std::nullopt, true, false);
} }
void CPU::Recompiler::X64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, void CPU::X64Recompiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, bool force_run_events)
bool force_run_events)
{ {
// event test // event test
// pc should've been flushed // 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(); const void* code = m_emitter->getCode();
*code_size = static_cast<u32>(m_emitter->getSize()); *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; return code;
} }
const void* CPU::Recompiler::X64Recompiler::GetCurrentCodePointer() const void* CPU::X64Recompiler::GetCurrentCodePointer()
{ {
return cg->getCurr(); 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 = { 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"}}; {"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"; 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); 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)]); 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)); 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); 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) if (src != dst)
cg->mov(Reg32(dst), Reg32(src)); 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); DebugAssert(r < Reg::count);
return cg->dword[PTR(&g_state.regs.r[static_cast<u32>(r)])]; 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); DebugAssert(cf.valid_host_d);
return Reg32(cf.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); DebugAssert(cf.valid_host_s);
return Reg32(cf.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); DebugAssert(cf.valid_host_t);
return Reg32(cf.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); DebugAssert(cf.valid_host_lo);
return Reg32(cf.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); DebugAssert(cf.valid_host_hi);
return Reg32(cf.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_d);
DebugAssert(!cf.valid_host_t || cf.host_t != cf.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; return rd;
} }
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::MoveSToT(CompileFlags cf) Xbyak::Reg32 CPU::X64Recompiler::MoveSToT(CompileFlags cf)
{ {
DebugAssert(cf.valid_host_t); DebugAssert(cf.valid_host_t);
@ -736,7 +765,7 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::MoveSToT(CompileFlags cf)
return rt; 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_d);
DebugAssert(!cf.valid_host_s || cf.host_s != cf.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; 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) 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) 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); DebugAssert(reg < Reg::count);
if (const std::optional<u32> hreg = CheckHostReg(0, Recompiler::HR_TYPE_CPU_REG, reg)) if (const std::optional<u32> hreg = CheckHostReg(0, Recompiler::HR_TYPE_CPU_REG, reg))
@ -799,9 +828,8 @@ void CPU::Recompiler::X64Recompiler::MoveMIPSRegToReg(const Xbyak::Reg32& dst, R
cg->mov(dst, MipsPtr(reg)); cg->mov(dst, MipsPtr(reg));
} }
void CPU::Recompiler::X64Recompiler::GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val, void CPU::X64Recompiler::GeneratePGXPCallWithMIPSRegs(const void* func, u32 arg1val, Reg arg2reg /* = Reg::count */,
Reg arg2reg /* = Reg::count */, Reg arg3reg /* = Reg::count */)
Reg arg3reg /* = Reg::count */)
{ {
DebugAssert(g_settings.gpu_pgxp_enable); DebugAssert(g_settings.gpu_pgxp_enable);
@ -816,7 +844,7 @@ void CPU::Recompiler::X64Recompiler::GeneratePGXPCallWithMIPSRegs(const void* fu
cg->call(func); cg->call(func);
} }
void CPU::Recompiler::X64Recompiler::Flush(u32 flags) void CPU::X64Recompiler::Flush(u32 flags)
{ {
Recompiler::Flush(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); WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", iinfo->pc, inst->bits);
Flush(FLUSH_FOR_INTERPRETER); Flush(FLUSH_FOR_INTERPRETER);
cg->call(&CPU::Recompiler::Thunks::InterpretInstruction); cg->call(&CPU::RecompilerThunks::InterpretInstruction);
// TODO: make me less garbage // TODO: make me less garbage
// TODO: this is wrong, it flushes the load delay on the same cycle when we return. // 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; 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) if (!g_settings.cpu_recompiler_memory_exceptions)
return; return;
@ -938,7 +966,7 @@ void CPU::Recompiler::X64Recompiler::CheckBranchTarget(const Xbyak::Reg32& pcreg
SwitchToNearCode(false); SwitchToNearCode(false);
} }
void CPU::Recompiler::X64Recompiler::Compile_jr(CompileFlags cf) void CPU::X64Recompiler::Compile_jr(CompileFlags cf)
{ {
if (!cf.valid_host_s) if (!cf.valid_host_s)
cg->mov(RWARG1, MipsPtr(cf.MipsS())); cg->mov(RWARG1, MipsPtr(cf.MipsS()));
@ -952,7 +980,7 @@ void CPU::Recompiler::X64Recompiler::Compile_jr(CompileFlags cf)
EndBlock(std::nullopt, true); EndBlock(std::nullopt, true);
} }
void CPU::Recompiler::X64Recompiler::Compile_jalr(CompileFlags cf) void CPU::X64Recompiler::Compile_jalr(CompileFlags cf)
{ {
if (!cf.valid_host_s) if (!cf.valid_host_s)
cg->mov(RWARG1, MipsPtr(cf.MipsS())); cg->mov(RWARG1, MipsPtr(cf.MipsS()));
@ -969,7 +997,7 @@ void CPU::Recompiler::X64Recompiler::Compile_jalr(CompileFlags cf)
EndBlock(std::nullopt, true); 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); const u32 taken_pc = GetConditionalBranchTarget(cf);
@ -1045,7 +1073,7 @@ void CPU::Recompiler::X64Recompiler::Compile_bxx(CompileFlags cf, BranchConditio
EndBlock(taken_pc, true); EndBlock(taken_pc, true);
} }
void CPU::Recompiler::X64Recompiler::Compile_addi(CompileFlags cf) void CPU::X64Recompiler::Compile_addi(CompileFlags cf)
{ {
const Reg32 rt = MoveSToT(cf); const Reg32 rt = MoveSToT(cf);
if (const u32 imm = inst->i.imm_sext32(); imm != 0) 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); const Reg32 rt = MoveSToT(cf);
if (const u32 imm = inst->i.imm_sext32(); imm != 0) if (const u32 imm = inst->i.imm_sext32(); imm != 0)
cg->add(rt, imm); cg->add(rt, imm);
} }
void CPU::Recompiler::X64Recompiler::Compile_slti(CompileFlags cf) void CPU::X64Recompiler::Compile_slti(CompileFlags cf)
{ {
Compile_slti(cf, true); Compile_slti(cf, true);
} }
void CPU::Recompiler::X64Recompiler::Compile_sltiu(CompileFlags cf) void CPU::X64Recompiler::Compile_sltiu(CompileFlags cf)
{ {
Compile_slti(cf, false); 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; 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); 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) if (const u32 imm = inst->i.imm_zext32(); imm != 0)
{ {
@ -1112,44 +1140,45 @@ 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); const Reg32 rt = MoveSToT(cf);
if (const u32 imm = inst->i.imm_zext32(); imm != 0) if (const u32 imm = inst->i.imm_zext32(); imm != 0)
cg->or_(rt, imm); cg->or_(rt, imm);
} }
void CPU::Recompiler::X64Recompiler::Compile_xori(CompileFlags cf) void CPU::X64Recompiler::Compile_xori(CompileFlags cf)
{ {
const Reg32 rt = MoveSToT(cf); const Reg32 rt = MoveSToT(cf);
if (const u32 imm = inst->i.imm_zext32(); imm != 0) if (const u32 imm = inst->i.imm_zext32(); imm != 0)
cg->xor_(rt, imm); cg->xor_(rt, imm);
} }
void CPU::Recompiler::X64Recompiler::Compile_sll(CompileFlags cf) void CPU::X64Recompiler::Compile_sll(CompileFlags cf)
{ {
const Reg32 rd = MoveTToD(cf); const Reg32 rd = MoveTToD(cf);
if (inst->r.shamt > 0) if (inst->r.shamt > 0)
cg->shl(rd, inst->r.shamt); 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); const Reg32 rd = MoveTToD(cf);
if (inst->r.shamt > 0) if (inst->r.shamt > 0)
cg->shr(rd, inst->r.shamt); 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); const Reg32 rd = MoveTToD(cf);
if (inst->r.shamt > 0) if (inst->r.shamt > 0)
cg->sar(rd, inst->r.shamt); cg->sar(rd, inst->r.shamt);
} }
void CPU::Recompiler::X64Recompiler::Compile_variable_shift( void CPU::X64Recompiler::Compile_variable_shift(CompileFlags cf,
CompileFlags cf, void (Xbyak::CodeGenerator::*op)(const Xbyak::Operand&, const Xbyak::Reg8&), void (Xbyak::CodeGenerator::*op)(const Xbyak::Operand&,
void (Xbyak::CodeGenerator::*op_const)(const Xbyak::Operand&, int)) const Xbyak::Reg8&),
void (Xbyak::CodeGenerator::*op_const)(const Xbyak::Operand&, int))
{ {
const Reg32 rd = CFGetRegD(cf); const Reg32 rd = CFGetRegD(cf);
if (!cf.const_s) if (!cf.const_s)
@ -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); 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); 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); 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.. // RAX/RDX shouldn't be allocatable..
DebugAssert(!(m_host_regs[Xbyak::Operand::RAX].flags & HR_USABLE) && 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); 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); Compile_mult(cf, true);
} }
void CPU::Recompiler::X64Recompiler::Compile_multu(CompileFlags cf) void CPU::X64Recompiler::Compile_multu(CompileFlags cf)
{ {
Compile_mult(cf, false); 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.. // not supported without registers for now..
DebugAssert(cf.valid_host_lo && cf.valid_host_hi); DebugAssert(cf.valid_host_lo && cf.valid_host_hi);
@ -1268,7 +1297,7 @@ void CPU::Recompiler::X64Recompiler::Compile_div(CompileFlags cf)
cg->L(done); cg->L(done);
} }
void CPU::Recompiler::X64Recompiler::Compile_divu(CompileFlags cf) void CPU::X64Recompiler::Compile_divu(CompileFlags cf)
{ {
// not supported without registers for now.. // not supported without registers for now..
DebugAssert(cf.valid_host_lo && cf.valid_host_hi); DebugAssert(cf.valid_host_lo && cf.valid_host_hi);
@ -1299,7 +1328,7 @@ void CPU::Recompiler::X64Recompiler::Compile_divu(CompileFlags cf)
cg->L(done); 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); SwitchToFarCode(true, &Xbyak::CodeGenerator::jo);
@ -1315,9 +1344,10 @@ void CPU::Recompiler::X64Recompiler::TestOverflow(const Xbyak::Reg32& result)
SwitchToNearCode(false); SwitchToNearCode(false);
} }
void CPU::Recompiler::X64Recompiler::Compile_dst_op( void CPU::X64Recompiler::Compile_dst_op(CompileFlags cf,
CompileFlags cf, void (Xbyak::CodeGenerator::*op)(const Xbyak::Operand&, const Xbyak::Operand&), void (Xbyak::CodeGenerator::*op)(const Xbyak::Operand&, const Xbyak::Operand&),
void (Xbyak::CodeGenerator::*op_const)(const Xbyak::Operand&, u32), bool commutative, bool overflow) void (Xbyak::CodeGenerator::*op_const)(const Xbyak::Operand&, u32),
bool commutative, bool overflow)
{ {
if (cf.valid_host_s && cf.valid_host_t) 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); 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); 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); 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); 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 // special cases - and with self -> self, and with 0 -> 0
const Reg32 regd = CFGetRegD(cf); 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); 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 // or/nor with 0 -> no effect
const Reg32 regd = CFGetRegD(cf); 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); 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); const Reg32 regd = CFGetRegD(cf);
if (cf.MipsS() == cf.MipsT()) 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); 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); Compile_or(cf);
cg->not_(CFGetRegD(cf)); cg->not_(CFGetRegD(cf));
} }
void CPU::Recompiler::X64Recompiler::Compile_slt(CompileFlags cf) void CPU::X64Recompiler::Compile_slt(CompileFlags cf)
{ {
Compile_slt(cf, true); Compile_slt(cf, true);
} }
void CPU::Recompiler::X64Recompiler::Compile_sltu(CompileFlags cf) void CPU::X64Recompiler::Compile_sltu(CompileFlags cf)
{ {
Compile_slt(cf, false); 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 rd = CFGetRegD(cf);
const Reg32 rs = cf.valid_host_s ? CFGetRegS(cf) : RWARG1; const Reg32 rs = cf.valid_host_s ? CFGetRegS(cf) : RWARG1;
@ -1513,9 +1543,9 @@ void CPU::Recompiler::X64Recompiler::Compile_slt(CompileFlags cf, bool sign)
sign ? cg->setl(rd.cvt8()) : cg->setb(rd.cvt8()); sign ? cg->setl(rd.cvt8()) : cg->setb(rd.cvt8());
} }
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::ComputeLoadStoreAddressArg( Xbyak::Reg32
CompileFlags cf, const std::optional<VirtualMemoryAddress>& address, CPU::X64Recompiler::ComputeLoadStoreAddressArg(CompileFlags cf, const std::optional<VirtualMemoryAddress>& address,
const std::optional<const Xbyak::Reg32>& reg /* = std::nullopt */) const std::optional<const Xbyak::Reg32>& reg /* = std::nullopt */)
{ {
const u32 imm = inst->i.imm_sext32(); const u32 imm = inst->i.imm_sext32();
if (cf.valid_host_s && imm == 0 && !reg.has_value()) if (cf.valid_host_s && imm == 0 && !reg.has_value())
@ -1546,8 +1576,8 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::ComputeLoadStoreAddressArg(
} }
template<typename RegAllocFn> template<typename RegAllocFn>
Xbyak::Reg32 CPU::Recompiler::X64Recompiler::GenerateLoad(const Xbyak::Reg32& addr_reg, MemoryAccessSize size, Xbyak::Reg32 CPU::X64Recompiler::GenerateLoad(const Xbyak::Reg32& addr_reg, MemoryAccessSize size, bool sign,
bool sign, bool use_fastmem, const RegAllocFn& dst_reg_alloc) bool use_fastmem, const RegAllocFn& dst_reg_alloc)
{ {
if (use_fastmem) if (use_fastmem)
{ {
@ -1608,20 +1638,20 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::GenerateLoad(const Xbyak::Reg32& ad
{ {
case MemoryAccessSize::Byte: case MemoryAccessSize::Byte:
{ {
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::ReadMemoryByte) : cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryByte) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryByte)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryByte));
} }
break; break;
case MemoryAccessSize::HalfWord: case MemoryAccessSize::HalfWord:
{ {
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::ReadMemoryHalfWord) : cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryHalfWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryHalfWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryHalfWord));
} }
break; break;
case MemoryAccessSize::Word: case MemoryAccessSize::Word:
{ {
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::ReadMemoryWord) : cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::ReadMemoryWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryWord));
} }
break; break;
} }
@ -1677,8 +1707,8 @@ Xbyak::Reg32 CPU::Recompiler::X64Recompiler::GenerateLoad(const Xbyak::Reg32& ad
return dst_reg; 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) MemoryAccessSize size, bool use_fastmem)
{ {
if (use_fastmem) if (use_fastmem)
{ {
@ -1729,20 +1759,20 @@ void CPU::Recompiler::X64Recompiler::GenerateStore(const Xbyak::Reg32& addr_reg,
{ {
case MemoryAccessSize::Byte: case MemoryAccessSize::Byte:
{ {
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::WriteMemoryByte) : cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryByte) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryByte)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryByte));
} }
break; break;
case MemoryAccessSize::HalfWord: case MemoryAccessSize::HalfWord:
{ {
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::WriteMemoryHalfWord) : cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryHalfWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryHalfWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryHalfWord));
} }
break; break;
case MemoryAccessSize::Word: case MemoryAccessSize::Word:
{ {
cg->call(checked ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::WriteMemoryWord) : cg->call(checked ? reinterpret_cast<const void*>(&RecompilerThunks::WriteMemoryWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryWord));
} }
break; break;
} }
@ -1774,8 +1804,8 @@ 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<VirtualMemoryAddress>& address)
{ {
const std::optional<Reg32> addr_reg = g_settings.gpu_pgxp_enable ? const std::optional<Reg32> addr_reg = g_settings.gpu_pgxp_enable ?
std::optional<Reg32>(Reg32(AllocateTempHostReg(HR_CALLEE_SAVED))) : std::optional<Reg32>(Reg32(AllocateTempHostReg(HR_CALLEE_SAVED))) :
@ -1803,8 +1833,8 @@ 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) const std::optional<VirtualMemoryAddress>& address)
{ {
DebugAssert(size == MemoryAccessSize::Word && !sign); DebugAssert(size == MemoryAccessSize::Word && !sign);
@ -1907,8 +1937,8 @@ 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 std::optional<VirtualMemoryAddress>& address)
{ {
const u32 index = static_cast<u32>(inst->r.rt.GetValue()); const u32 index = static_cast<u32>(inst->r.rt.GetValue());
const auto [ptr, action] = GetGTERegisterPointer(index, true); const auto [ptr, action] = GetGTERegisterPointer(index, true);
@ -1993,8 +2023,8 @@ 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<VirtualMemoryAddress>& address)
{ {
const std::optional<Reg32> addr_reg = g_settings.gpu_pgxp_enable ? const std::optional<Reg32> addr_reg = g_settings.gpu_pgxp_enable ?
std::optional<Reg32>(Reg32(AllocateTempHostReg(HR_CALLEE_SAVED))) : std::optional<Reg32>(Reg32(AllocateTempHostReg(HR_CALLEE_SAVED))) :
@ -2018,8 +2048,8 @@ 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) const std::optional<VirtualMemoryAddress>& address)
{ {
DebugAssert(size == MemoryAccessSize::Word && !sign); DebugAssert(size == MemoryAccessSize::Word && !sign);
@ -2098,8 +2128,8 @@ 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 std::optional<VirtualMemoryAddress>& address)
{ {
const u32 index = static_cast<u32>(inst->r.rt.GetValue()); const u32 index = static_cast<u32>(inst->r.rt.GetValue());
const auto [ptr, action] = GetGTERegisterPointer(index, false); const auto [ptr, action] = GetGTERegisterPointer(index, false);
@ -2154,7 +2184,7 @@ void CPU::Recompiler::X64Recompiler::Compile_swc2(CompileFlags cf, MemoryAccessS
FreeHostReg(data_backup.getIdx()); 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 Cop0Reg reg = static_cast<Cop0Reg>(MipsD());
const u32* ptr = GetCop0RegPtr(reg); 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 // shift mode bits right two, preserving upper bits
static constexpr u32 mode_bits_mask = UINT32_C(0b1111); static constexpr u32 mode_bits_mask = UINT32_C(0b1111);
@ -2253,7 +2283,7 @@ void CPU::Recompiler::X64Recompiler::Compile_rfe(CompileFlags cf)
TestInterrupts(RWARG1); 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 // if Iec == 0 then goto no_interrupt
Label no_interrupt; Label no_interrupt;
@ -2301,7 +2331,7 @@ void CPU::Recompiler::X64Recompiler::TestInterrupts(const Xbyak::Reg32& sr)
cg->L(no_interrupt); 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 u32 index = inst->cop.Cop2Index();
const Reg rt = inst->r.rt; 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 u32 index = inst->cop.Cop2Index();
const auto [ptr, action] = GetGTERegisterPointer(index, true); 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; TickCount func_ticks;
GTE::InstructionImpl func = GTE::GetInstructionImpl(inst->bits, &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: case MemoryAccessSize::Byte:
{ {
cg->call(is_load ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryByte) : cg->call(is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryByte) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryByte)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryByte));
} }
break; break;
case MemoryAccessSize::HalfWord: case MemoryAccessSize::HalfWord:
{ {
cg->call(is_load ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryHalfWord) : cg->call(is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryHalfWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryHalfWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryHalfWord));
} }
break; break;
case MemoryAccessSize::Word: case MemoryAccessSize::Word:
{ {
cg->call(is_load ? reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedReadMemoryWord) : cg->call(is_load ? reinterpret_cast<const void*>(&RecompilerThunks::UncheckedReadMemoryWord) :
reinterpret_cast<const void*>(&CPU::Recompiler::Thunks::UncheckedWriteMemoryWord)); reinterpret_cast<const void*>(&RecompilerThunks::UncheckedWriteMemoryWord));
} }
break; break;
} }

View File

@ -9,7 +9,15 @@
#ifdef CPU_ARCH_X64 #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 class X64Recompiler final : public Recompiler
{ {
@ -141,6 +149,6 @@ private:
Xbyak::CodeGenerator* cg; Xbyak::CodeGenerator* cg;
}; };
} // namespace CPU::Recompiler } // namespace CPU
#endif // CPU_ARCH_X64 #endif // CPU_ARCH_X64