2021-07-29 21:31:45 +02:00
# include "stdafx.h"
2018-05-14 22:07:36 +02:00
# include "Utilities/JIT.h"
2021-01-29 11:32:19 +01:00
# include "Utilities/StrUtil.h"
2022-07-04 15:02:17 +02:00
# include "util/serialization.hpp"
2017-02-26 16:56:31 +01:00
# include "Crypto/sha1.h"
2021-01-29 11:32:19 +01:00
# include "Crypto/unself.h"
# include "Loader/ELF.h"
# include "Loader/mself.hpp"
2020-10-18 14:00:10 +02:00
# include "Emu/perf_meter.hpp"
2018-09-25 22:34:45 +02:00
# include "Emu/Memory/vm_reservation.h"
2020-10-30 14:32:49 +01:00
# include "Emu/Memory/vm_locking.h"
2020-04-07 19:29:11 +02:00
# include "Emu/RSX/RSXThread.h"
2020-02-15 23:36:20 +01:00
# include "Emu/VFS.h"
2021-04-21 22:12:21 +02:00
# include "Emu/system_progress.hpp"
# include "Emu/system_utils.hpp"
2016-04-14 01:09:41 +02:00
# include "PPUThread.h"
# include "PPUInterpreter.h"
2016-06-07 22:24:20 +02:00
# include "PPUAnalyser.h"
2016-04-14 01:09:41 +02:00
# include "PPUModule.h"
2020-11-10 15:57:06 +01:00
# include "PPUDisAsm.h"
2018-05-04 23:01:27 +02:00
# include "SPURecompiler.h"
2021-03-23 20:32:50 +01:00
# include "timers.hpp"
2017-02-06 19:36:46 +01:00
# include "lv2/sys_sync.h"
2017-02-26 16:56:31 +01:00
# include "lv2/sys_prx.h"
2021-01-30 14:08:22 +01:00
# include "lv2/sys_overlay.h"
# include "lv2/sys_process.h"
2022-07-14 21:07:02 +02:00
# include "lv2/sys_spu.h"
2015-03-16 22:38:21 +01:00
2016-06-22 15:37:51 +02:00
# ifdef LLVM_AVAILABLE
# ifdef _MSC_VER
# pragma warning(push, 0)
2019-11-30 00:11:28 +01:00
# else
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wall"
# pragma GCC diagnostic ignored "-Wextra"
# pragma GCC diagnostic ignored "-Wold-style-cast"
2021-03-05 20:05:37 +01:00
# pragma GCC diagnostic ignored "-Wunused-parameter"
2021-03-08 21:41:23 +01:00
# pragma GCC diagnostic ignored "-Wstrict-aliasing"
2021-03-30 17:31:46 +02:00
# pragma GCC diagnostic ignored "-Weffc++"
2021-04-07 23:52:18 +02:00
# pragma GCC diagnostic ignored "-Wmissing-noreturn"
2016-06-22 15:37:51 +02:00
# endif
# include "llvm/Support/FormattedStream.h"
2020-11-02 04:07:58 +01:00
# include "llvm/Support/Host.h"
2017-03-23 18:41:38 +01:00
# include "llvm/Object/ObjectFile.h"
# include "llvm/ADT/Triple.h"
2016-06-22 15:37:51 +02:00
# include "llvm/IR/Verifier.h"
2016-06-25 07:16:15 +02:00
# include "llvm/IR/InstIterator.h"
2016-06-22 15:37:51 +02:00
# include "llvm/IR/LegacyPassManager.h"
2016-06-25 07:16:15 +02:00
# include "llvm/Transforms/Utils/BasicBlockUtils.h"
2016-06-22 15:37:51 +02:00
# include "llvm/Transforms/Scalar.h"
# ifdef _MSC_VER
# pragma warning(pop)
2019-11-30 00:11:28 +01:00
# else
# pragma GCC diagnostic pop
2016-06-22 15:37:51 +02:00
# endif
# include "PPUTranslator.h"
# endif
2017-06-24 17:36:49 +02:00
# include <thread>
2017-02-07 14:14:44 +01:00
# include <cfenv>
2020-03-31 02:11:37 +02:00
# include <cctype>
2021-03-31 15:31:21 +02:00
# include <optional>
2020-11-24 06:18:31 +01:00
# include "util/asm.hpp"
2020-11-07 23:56:35 +01:00
# include "util/vm.hpp"
2020-12-13 14:34:45 +01:00
# include "util/v128.hpp"
2021-12-30 17:39:18 +01:00
# include "util/simd.hpp"
2020-12-21 15:12:05 +01:00
# include "util/sysinfo.hpp"
2017-02-07 14:14:44 +01:00
2022-06-14 14:28:38 +02:00
# ifdef __APPLE__
# include <libkern/OSCacheControl.h>
# endif
2020-06-01 01:27:33 +02:00
extern atomic_t < u64 > g_watchdog_hold_ctr ;
2020-04-07 19:29:11 +02:00
// Should be of the same type
2020-10-01 17:15:07 +02:00
using spu_rdata_t = decltype ( ppu_thread : : rdata ) ;
2020-04-07 19:29:11 +02:00
extern void mov_rdata ( spu_rdata_t & _dst , const spu_rdata_t & _src ) ;
2020-10-30 03:17:00 +01:00
extern void mov_rdata_nt ( spu_rdata_t & _dst , const spu_rdata_t & _src ) ;
2020-04-07 19:29:11 +02:00
extern bool cmp_rdata ( const spu_rdata_t & _lhs , const spu_rdata_t & _rhs ) ;
// Verify AVX availability for TSX transactions
static const bool s_tsx_avx = utils : : has_avx ( ) ;
2017-02-22 11:10:55 +01:00
template < >
2020-03-03 21:39:40 +01:00
void fmt_class_string < ppu_join_status > : : format ( std : : string & out , u64 arg )
2017-02-22 11:10:55 +01:00
{
2020-03-03 21:39:40 +01:00
format_enum ( out , arg , [ ] ( ppu_join_status js )
2017-02-22 11:10:55 +01:00
{
switch ( js )
{
2020-04-29 08:49:13 +02:00
case ppu_join_status : : joinable : return " none " ;
2020-03-03 21:39:40 +01:00
case ppu_join_status : : detached : return " detached " ;
case ppu_join_status : : zombie : return " zombie " ;
case ppu_join_status : : exited : return " exited " ;
2020-03-14 18:06:58 +01:00
case ppu_join_status : : max : break ;
2017-02-22 11:10:55 +01:00
}
return unknown ;
} ) ;
}
2021-05-21 07:48:37 +02:00
template < >
void fmt_class_string < ppu_thread_status > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( ppu_thread_status s )
{
switch ( s )
{
case PPU_THREAD_STATUS_IDLE : return " IDLE " ;
case PPU_THREAD_STATUS_RUNNABLE : return " RUN " ;
case PPU_THREAD_STATUS_ONPROC : return " ONPROC " ;
case PPU_THREAD_STATUS_SLEEP : return " SLEEP " ;
case PPU_THREAD_STATUS_STOP : return " STOP " ;
case PPU_THREAD_STATUS_ZOMBIE : return " Zombie " ;
case PPU_THREAD_STATUS_DELETED : return " Deleted " ;
case PPU_THREAD_STATUS_UNKNOWN : break ;
}
return unknown ;
} ) ;
}
2021-07-10 10:56:48 +02:00
template < >
void fmt_class_string < typename ppu_thread : : call_history_t > : : format ( std : : string & out , u64 arg )
{
const auto & history = get_object ( arg ) ;
2021-07-18 20:06:06 +02:00
PPUDisAsm dis_asm ( cpu_disasm_mode : : normal , vm : : g_sudo_addr ) ;
2021-07-10 10:56:48 +02:00
for ( u64 count = 0 , idx = history . index - 1 ; idx ! = umax & & count < ppu_thread : : call_history_max_size ; count + + , idx - - )
{
2021-07-18 20:06:06 +02:00
const u32 pc = history . data [ idx % ppu_thread : : call_history_max_size ] ;
dis_asm . disasm ( pc ) ;
fmt : : append ( out , " \n (%u) 0x%08x: %s " , count , pc , dis_asm . last_opcode ) ;
2021-07-10 10:56:48 +02:00
}
}
2021-12-30 17:39:18 +01:00
extern const ppu_decoder < ppu_itype > g_ppu_itype { } ;
extern const ppu_decoder < ppu_iname > g_ppu_iname { } ;
2012-11-15 00:39:56 +01:00
2022-07-04 15:02:17 +02:00
template < >
bool serialize < ppu_thread : : cr_bits > ( utils : : serial & ar , typename ppu_thread : : cr_bits & o )
{
if ( ar . is_writing ( ) )
{
ar ( o . pack ( ) ) ;
}
else
{
o . unpack ( ar ) ;
}
return true ;
}
2017-02-26 16:56:31 +01:00
extern void ppu_initialize ( ) ;
2021-01-27 14:08:43 +01:00
extern void ppu_finalize ( const ppu_module & info ) ;
2021-01-30 14:08:22 +01:00
extern bool ppu_initialize ( const ppu_module & info , bool = false ) ;
2018-05-30 19:34:36 +02:00
static void ppu_initialize2 ( class jit_compiler & jit , const ppu_module & module_part , const std : : string & cache_path , const std : : string & obj_name ) ;
2022-07-04 15:02:17 +02:00
extern std : : pair < std : : shared_ptr < lv2_overlay > , CellError > ppu_load_overlay ( const ppu_exec_object & , const std : : string & path , s64 file_offset , utils : : serial * = nullptr ) ;
2021-01-29 11:32:19 +01:00
extern void ppu_unload_prx ( const lv2_prx & ) ;
2022-07-04 15:02:17 +02:00
extern std : : shared_ptr < lv2_prx > ppu_load_prx ( const ppu_prx_object & , const std : : string & , s64 file_offset , utils : : serial * = nullptr ) ;
2016-07-27 23:43:22 +02:00
extern void ppu_execute_syscall ( ppu_thread & ppu , u64 code ) ;
2021-12-30 17:39:18 +01:00
static void ppu_break ( ppu_thread & , ppu_opcode_t , be_t < u32 > * , ppu_intrp_func * ) ;
2016-07-27 23:43:22 +02:00
2020-09-25 16:29:25 +02:00
extern void do_cell_atomic_128_store ( u32 addr , const void * to_write ) ;
2022-01-23 13:20:07 +01:00
const auto ppu_gateway = build_function_asm < void ( * ) ( ppu_thread * ) > ( " ppu_gateway " , [ ] ( native_asm & c , auto & args )
2021-01-31 19:38:47 +01:00
{
// Gateway for PPU, converts from native to GHC calling convention, also saves RSP value for escape
using namespace asmjit ;
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2021-01-31 19:38:47 +01:00
# ifdef _WIN32
c . push ( x86 : : r15 ) ;
c . push ( x86 : : r14 ) ;
c . push ( x86 : : r13 ) ;
c . push ( x86 : : r12 ) ;
c . push ( x86 : : rsi ) ;
c . push ( x86 : : rdi ) ;
c . push ( x86 : : rbp ) ;
c . push ( x86 : : rbx ) ;
c . sub ( x86 : : rsp , 0xa8 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x90 ) , x86 : : xmm15 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x80 ) , x86 : : xmm14 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x70 ) , x86 : : xmm13 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x60 ) , x86 : : xmm12 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x50 ) , x86 : : xmm11 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x40 ) , x86 : : xmm10 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x30 ) , x86 : : xmm9 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x20 ) , x86 : : xmm8 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0x10 ) , x86 : : xmm7 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rsp , 0 ) , x86 : : xmm6 ) ;
# else
c . push ( x86 : : rbp ) ;
c . push ( x86 : : r15 ) ;
c . push ( x86 : : r14 ) ;
c . push ( x86 : : r13 ) ;
c . push ( x86 : : r12 ) ;
c . push ( x86 : : rbx ) ;
c . push ( x86 : : rax ) ;
# endif
// Save native stack pointer for longjmp emulation
c . mov ( x86 : : qword_ptr ( args [ 0 ] , : : offset32 ( & ppu_thread : : saved_native_sp ) ) , x86 : : rsp ) ;
// Initialize args
c . mov ( x86 : : r13 , x86 : : qword_ptr ( reinterpret_cast < u64 > ( & vm : : g_exec_addr ) ) ) ;
c . mov ( x86 : : rbp , args [ 0 ] ) ;
c . mov ( x86 : : edx , x86 : : dword_ptr ( x86 : : rbp , : : offset32 ( & ppu_thread : : cia ) ) ) ; // Load PC
c . mov ( x86 : : rax , x86 : : qword_ptr ( x86 : : r13 , x86 : : edx , 1 , 0 ) ) ; // Load call target
c . mov ( x86 : : rdx , x86 : : rax ) ;
2021-12-30 17:39:18 +01:00
c . shl ( x86 : : rax , 16 ) ;
c . shr ( x86 : : rax , 16 ) ;
c . shr ( x86 : : rdx , 48 ) ;
c . shl ( x86 : : edx , 13 ) ;
2021-01-31 19:38:47 +01:00
c . mov ( x86 : : r12d , x86 : : edx ) ; // Load relocation base
c . mov ( x86 : : rbx , x86 : : qword_ptr ( reinterpret_cast < u64 > ( & vm : : g_base_addr ) ) ) ;
c . mov ( x86 : : r14 , x86 : : qword_ptr ( x86 : : rbp , : : offset32 ( & ppu_thread : : gpr , 0 ) ) ) ; // Load some registers
c . mov ( x86 : : rsi , x86 : : qword_ptr ( x86 : : rbp , : : offset32 ( & ppu_thread : : gpr , 1 ) ) ) ;
c . mov ( x86 : : rdi , x86 : : qword_ptr ( x86 : : rbp , : : offset32 ( & ppu_thread : : gpr , 2 ) ) ) ;
if ( utils : : has_avx ( ) )
{
c . vzeroupper ( ) ;
}
c . call ( x86 : : rax ) ;
if ( utils : : has_avx ( ) )
{
c . vzeroupper ( ) ;
}
# ifdef _WIN32
c . movaps ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rsp , 0 ) ) ;
c . movaps ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rsp , 0x10 ) ) ;
c . movaps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rsp , 0x20 ) ) ;
c . movaps ( x86 : : xmm9 , x86 : : oword_ptr ( x86 : : rsp , 0x30 ) ) ;
c . movaps ( x86 : : xmm10 , x86 : : oword_ptr ( x86 : : rsp , 0x40 ) ) ;
c . movaps ( x86 : : xmm11 , x86 : : oword_ptr ( x86 : : rsp , 0x50 ) ) ;
c . movaps ( x86 : : xmm12 , x86 : : oword_ptr ( x86 : : rsp , 0x60 ) ) ;
c . movaps ( x86 : : xmm13 , x86 : : oword_ptr ( x86 : : rsp , 0x70 ) ) ;
c . movaps ( x86 : : xmm14 , x86 : : oword_ptr ( x86 : : rsp , 0x80 ) ) ;
c . movaps ( x86 : : xmm15 , x86 : : oword_ptr ( x86 : : rsp , 0x90 ) ) ;
c . add ( x86 : : rsp , 0xa8 ) ;
c . pop ( x86 : : rbx ) ;
c . pop ( x86 : : rbp ) ;
c . pop ( x86 : : rdi ) ;
c . pop ( x86 : : rsi ) ;
c . pop ( x86 : : r12 ) ;
c . pop ( x86 : : r13 ) ;
c . pop ( x86 : : r14 ) ;
c . pop ( x86 : : r15 ) ;
# else
c . add ( x86 : : rsp , + 8 ) ;
c . pop ( x86 : : rbx ) ;
c . pop ( x86 : : r12 ) ;
c . pop ( x86 : : r13 ) ;
c . pop ( x86 : : r14 ) ;
c . pop ( x86 : : r15 ) ;
c . pop ( x86 : : rbp ) ;
# endif
c . ret ( ) ;
2021-12-30 17:39:18 +01:00
# else
2022-06-14 14:28:38 +02:00
// See https://github.com/ghc/ghc/blob/master/rts/include/stg/MachRegs.h
// for GHC calling convention definitions on Aarch64
// and https://developer.arm.com/documentation/den0024/a/The-ABI-for-ARM-64-bit-Architecture/Register-use-in-the-AArch64-Procedure-Call-Standard/Parameters-in-general-purpose-registers
// for AArch64 calling convention
2022-07-19 06:33:28 +02:00
// Save sp for native longjmp emulation
Label native_sp_offset = c . newLabel ( ) ;
c . ldr ( a64 : : x10 , arm : : Mem ( native_sp_offset ) ) ;
// sp not allowed to be used in load/stores directly
c . mov ( a64 : : x15 , a64 : : sp ) ;
c . str ( a64 : : x15 , arm : : Mem ( args [ 0 ] , a64 : : x10 ) ) ;
2022-06-14 14:28:38 +02:00
// Push callee saved registers to the stack
// We need to save x18-x30 = 13 x 8B each + 8 bytes for 16B alignment = 112B
c . sub ( a64 : : sp , a64 : : sp , Imm ( 112 ) ) ;
c . stp ( a64 : : x18 , a64 : : x19 , arm : : Mem ( a64 : : sp ) ) ;
c . stp ( a64 : : x20 , a64 : : x21 , arm : : Mem ( a64 : : sp , 16 ) ) ;
c . stp ( a64 : : x22 , a64 : : x23 , arm : : Mem ( a64 : : sp , 32 ) ) ;
c . stp ( a64 : : x24 , a64 : : x25 , arm : : Mem ( a64 : : sp , 48 ) ) ;
c . stp ( a64 : : x26 , a64 : : x27 , arm : : Mem ( a64 : : sp , 64 ) ) ;
c . stp ( a64 : : x28 , a64 : : x29 , arm : : Mem ( a64 : : sp , 80 ) ) ;
c . str ( a64 : : x30 , arm : : Mem ( a64 : : sp , 96 ) ) ;
// Load REG_Base - use absolute jump target to bypass rel jmp range limits
Label exec_addr = c . newLabel ( ) ;
c . ldr ( a64 : : x19 , arm : : Mem ( exec_addr ) ) ;
c . ldr ( a64 : : x19 , arm : : Mem ( a64 : : x19 ) ) ;
// Load PPUThread struct base -> REG_Sp
const arm : : GpX ppu_t_base = a64 : : x20 ;
c . mov ( ppu_t_base , args [ 0 ] ) ;
// Load PC
2022-07-19 06:33:28 +02:00
const arm : : GpX pc = a64 : : x15 ;
2022-06-14 14:28:38 +02:00
Label cia_offset = c . newLabel ( ) ;
const arm : : GpX cia_addr_reg = a64 : : x11 ;
// Load offset value
c . ldr ( cia_addr_reg , arm : : Mem ( cia_offset ) ) ;
// Load cia
2022-07-19 06:33:28 +02:00
c . ldr ( a64 : : w15 , arm : : Mem ( ppu_t_base , cia_addr_reg ) ) ;
2022-06-14 14:28:38 +02:00
// Multiply by 2 to index into ptr table
2022-07-19 06:33:28 +02:00
const arm : : GpX index_shift = a64 : : x12 ;
2022-06-14 14:28:38 +02:00
c . mov ( index_shift , Imm ( 2 ) ) ;
c . mul ( pc , pc , index_shift ) ;
// Load call target
2022-07-19 06:33:28 +02:00
const arm : : GpX call_target = a64 : : x13 ;
2022-06-14 14:28:38 +02:00
c . ldr ( call_target , arm : : Mem ( a64 : : x19 , pc ) ) ;
// Compute REG_Hp
const arm : : GpX reg_hp = a64 : : x21 ;
c . mov ( reg_hp , call_target ) ;
c . lsr ( reg_hp , reg_hp , 48 ) ;
2022-07-19 06:33:28 +02:00
c . lsl ( a64 : : w21 , a64 : : w21 , 13 ) ;
2022-06-14 14:28:38 +02:00
// Zero top 16 bits of call target
c . lsl ( call_target , call_target , Imm ( 16 ) ) ;
c . lsr ( call_target , call_target , Imm ( 16 ) ) ;
// Load registers
Label base_addr = c . newLabel ( ) ;
c . ldr ( a64 : : x22 , arm : : Mem ( base_addr ) ) ;
c . ldr ( a64 : : x22 , arm : : Mem ( a64 : : x22 ) ) ;
Label gpr_addr_offset = c . newLabel ( ) ;
const arm : : GpX gpr_addr_reg = a64 : : x9 ;
c . ldr ( gpr_addr_reg , arm : : Mem ( gpr_addr_offset ) ) ;
c . add ( gpr_addr_reg , gpr_addr_reg , ppu_t_base ) ;
c . ldr ( a64 : : x23 , arm : : Mem ( gpr_addr_reg ) ) ;
c . ldr ( a64 : : x24 , arm : : Mem ( gpr_addr_reg , 8 ) ) ;
c . ldr ( a64 : : x25 , arm : : Mem ( gpr_addr_reg , 16 ) ) ;
// Execute LLE call
c . blr ( call_target ) ;
// Restore registers from the stack
c . ldp ( a64 : : x18 , a64 : : x19 , arm : : Mem ( a64 : : sp ) ) ;
c . ldp ( a64 : : x20 , a64 : : x21 , arm : : Mem ( a64 : : sp , 16 ) ) ;
c . ldp ( a64 : : x22 , a64 : : x23 , arm : : Mem ( a64 : : sp , 32 ) ) ;
c . ldp ( a64 : : x24 , a64 : : x25 , arm : : Mem ( a64 : : sp , 48 ) ) ;
c . ldp ( a64 : : x26 , a64 : : x27 , arm : : Mem ( a64 : : sp , 64 ) ) ;
c . ldp ( a64 : : x28 , a64 : : x29 , arm : : Mem ( a64 : : sp , 80 ) ) ;
c . ldr ( a64 : : x30 , arm : : Mem ( a64 : : sp , 96 ) ) ;
// Restore stack ptr
c . add ( a64 : : sp , a64 : : sp , Imm ( 112 ) ) ;
// Return
2021-12-30 17:39:18 +01:00
c . ret ( a64 : : x30 ) ;
2022-06-14 14:28:38 +02:00
c . bind ( exec_addr ) ;
c . embedUInt64 ( reinterpret_cast < u64 > ( & vm : : g_exec_addr ) ) ;
c . bind ( base_addr ) ;
c . embedUInt64 ( reinterpret_cast < u64 > ( & vm : : g_base_addr ) ) ;
c . bind ( cia_offset ) ;
c . embedUInt64 ( static_cast < u64 > ( : : offset32 ( & ppu_thread : : cia ) ) ) ;
c . bind ( gpr_addr_offset ) ;
c . embedUInt64 ( static_cast < u64 > ( : : offset32 ( & ppu_thread : : gpr ) ) ) ;
c . bind ( native_sp_offset ) ;
c . embedUInt64 ( static_cast < u64 > ( : : offset32 ( & ppu_thread : : saved_native_sp ) ) ) ;
2021-12-30 17:39:18 +01:00
# endif
2021-01-31 19:38:47 +01:00
} ) ;
2021-12-30 17:39:18 +01:00
const extern auto ppu_escape = build_function_asm < void ( * ) ( ppu_thread * ) > ( " ppu_escape " , [ ] ( native_asm & c , auto & args )
2021-01-31 19:38:47 +01:00
{
using namespace asmjit ;
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2021-01-31 19:38:47 +01:00
// Restore native stack pointer (longjmp emulation)
c . mov ( x86 : : rsp , x86 : : qword_ptr ( args [ 0 ] , : : offset32 ( & ppu_thread : : saved_native_sp ) ) ) ;
// Return to the return location
2021-12-24 18:33:32 +01:00
c . sub ( x86 : : rsp , 8 ) ;
c . ret ( ) ;
2021-12-30 17:39:18 +01:00
# endif
2021-01-31 19:38:47 +01:00
} ) ;
void ppu_recompiler_fallback ( ppu_thread & ppu ) ;
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2022-01-23 13:20:07 +01:00
const auto ppu_recompiler_fallback_ghc = build_function_asm < void ( * ) ( ppu_thread & ppu ) > ( " " , [ ] ( native_asm & c , auto & args )
2021-01-31 19:38:47 +01:00
{
using namespace asmjit ;
c . mov ( args [ 0 ] , x86 : : rbp ) ;
2022-01-17 17:24:53 +01:00
c . jmp ( ppu_recompiler_fallback ) ;
2021-01-31 19:38:47 +01:00
} ) ;
2021-12-30 17:39:18 +01:00
# elif defined(ARCH_ARM64)
const auto ppu_recompiler_fallback_ghc = & ppu_recompiler_fallback ;
# endif
2021-01-31 19:38:47 +01:00
2017-03-25 16:53:45 +01:00
// Get pointer to executable cache
2021-12-30 17:39:18 +01:00
static ppu_intrp_func_t & ppu_ref ( u32 addr )
2017-02-10 20:56:16 +01:00
{
2021-12-30 17:39:18 +01:00
return * reinterpret_cast < ppu_intrp_func_t * > ( vm : : g_exec_addr + u64 { addr } * 2 ) ;
2017-02-10 20:56:16 +01:00
}
2017-02-10 13:20:54 +01:00
// Get interpreter cache value
2021-12-30 17:39:18 +01:00
static ppu_intrp_func_t ppu_cache ( u32 addr )
2016-06-07 22:24:20 +02:00
{
2021-12-30 17:39:18 +01:00
if ( g_cfg . core . ppu_decoder ! = ppu_decoder_type : : _static )
2021-01-18 19:34:54 +01:00
{
fmt : : throw_exception ( " Invalid PPU decoder " ) ;
}
2021-12-30 17:39:18 +01:00
return g_fxo - > get < ppu_interpreter_rt > ( ) . decode ( vm : : read32 ( addr ) ) ;
2017-02-10 13:20:54 +01:00
}
2021-12-30 17:39:18 +01:00
static ppu_intrp_func ppu_ret = { [ ] ( ppu_thread & ppu , ppu_opcode_t , be_t < u32 > * this_op , ppu_intrp_func * )
2017-02-11 00:42:59 +01:00
{
2021-12-30 17:39:18 +01:00
// Fix PC and return (step execution)
ppu . cia = vm : : get_addr ( this_op ) ;
return ;
} } ;
2017-02-11 15:17:07 +01:00
2021-12-30 17:39:18 +01:00
static void ppu_fallback ( ppu_thread & ppu , ppu_opcode_t op , be_t < u32 > * this_op , ppu_intrp_func * next_fn )
{
const auto _pc = vm : : get_addr ( this_op ) ;
const auto _fn = ppu_cache ( _pc ) ;
ppu_ref ( _pc ) = _fn ;
return _fn ( ppu , op , this_op , next_fn ) ;
2019-03-20 16:20:13 +01:00
}
// TODO: Make this a dispatch call
void ppu_recompiler_fallback ( ppu_thread & ppu )
{
2021-12-30 17:39:18 +01:00
perf_meter < " PPUFALL1 " _u64 > perf0 ;
2017-05-20 13:45:02 +02:00
if ( g_cfg . core . ppu_debug )
2017-04-08 22:58:00 +02:00
{
2021-12-30 17:39:18 +01:00
ppu_log . error ( " Unregistered PPU Function (LR=0x%x) " , ppu . lr ) ;
2017-04-08 22:58:00 +02:00
}
2021-12-30 17:39:18 +01:00
const auto & table = g_fxo - > get < ppu_interpreter_rt > ( ) ;
2021-01-31 19:38:47 +01:00
2019-03-20 16:20:13 +01:00
while ( true )
{
2021-12-30 17:39:18 +01:00
if ( uptr func = uptr ( ppu_ref ( ppu . cia ) ) ; ( func < < 16 > > 16 ) ! = reinterpret_cast < uptr > ( ppu_recompiler_fallback_ghc ) )
2021-09-01 12:38:17 +02:00
{
// We found a recompiler function at cia, return
break ;
}
2021-12-30 17:39:18 +01:00
// Run one instruction in interpreter (TODO)
const u32 op = vm : : read32 ( ppu . cia ) ;
table . decode ( op ) ( ppu , { op } , vm : : _ptr < u32 > ( ppu . cia ) , & ppu_ret ) ;
2019-03-20 16:20:13 +01:00
if ( ppu . test_stopped ( ) )
{
2021-01-31 19:38:47 +01:00
break ;
2019-03-20 16:20:13 +01:00
}
}
2017-04-08 22:58:00 +02:00
}
2020-10-13 21:36:00 +02:00
void ppu_reservation_fallback ( ppu_thread & ppu )
{
2021-12-30 17:39:18 +01:00
perf_meter < " PPUFALL2 " _u64 > perf0 ;
const auto & table = g_fxo - > get < ppu_interpreter_rt > ( ) ;
2020-10-13 21:36:00 +02:00
while ( true )
{
2021-12-30 17:39:18 +01:00
// Run one instruction in interpreter (TODO)
2020-10-13 21:36:00 +02:00
const u32 op = vm : : read32 ( ppu . cia ) ;
2021-12-30 17:39:18 +01:00
table . decode ( op ) ( ppu , { op } , vm : : _ptr < u32 > ( ppu . cia ) , & ppu_ret ) ;
2020-10-13 21:36:00 +02:00
if ( ! ppu . raddr | | ! ppu . use_full_rdata )
{
// We've escaped from reservation, return.
return ;
}
if ( ppu . test_stopped ( ) )
{
return ;
}
}
}
2017-04-22 15:00:23 +02:00
static std : : unordered_map < u32 , u32 > * s_ppu_toc ;
2017-04-08 22:58:00 +02:00
2021-12-30 17:39:18 +01:00
static void ppu_check_toc ( ppu_thread & ppu , ppu_opcode_t op , be_t < u32 > * this_op , ppu_intrp_func * next_fn )
2017-04-08 22:58:00 +02:00
{
// Compare TOC with expected value
2017-04-22 15:00:23 +02:00
const auto found = s_ppu_toc - > find ( ppu . cia ) ;
2017-04-08 22:58:00 +02:00
if ( ppu . gpr [ 2 ] ! = found - > second )
{
2020-02-01 09:31:27 +01:00
ppu_log . error ( " Unexpected TOC (0x%x, expected 0x%x) " , ppu . gpr [ 2 ] , found - > second ) ;
2017-12-19 22:01:03 +01:00
2017-04-08 22:58:00 +02:00
if ( ! ppu . state . test_and_set ( cpu_flag : : dbg_pause ) & & ppu . check_state ( ) )
{
2021-12-30 17:39:18 +01:00
return ;
2017-04-08 22:58:00 +02:00
}
}
// Fallback to the interpreter function
2021-12-30 17:39:18 +01:00
return ppu_cache ( ppu . cia ) ( ppu , op , this_op , next_fn ) ;
2017-02-11 00:42:59 +01:00
}
2017-02-10 13:20:54 +01:00
extern void ppu_register_range ( u32 addr , u32 size )
{
2017-02-11 00:42:59 +01:00
if ( ! size )
{
2020-02-01 09:31:27 +01:00
ppu_log . error ( " ppu_register_range(0x%x): empty range " , addr ) ;
2017-04-06 15:57:32 +02:00
return ;
2017-02-11 00:42:59 +01:00
}
2021-02-08 16:04:50 +01:00
size = utils : : align ( size + addr % 0x10000 , 0x10000 ) ;
addr & = - 0x10000 ;
2017-02-10 13:20:54 +01:00
// Register executable range at
2021-02-08 16:04:50 +01:00
utils : : memory_commit ( & ppu_ref ( addr ) , u64 { size } * 2 , utils : : protection : : rw ) ;
2021-09-01 12:38:17 +02:00
ensure ( vm : : page_protect ( addr , size , 0 , vm : : page_executable ) ) ;
2017-02-10 13:20:54 +01:00
2021-02-26 10:20:25 +01:00
if ( g_cfg . core . ppu_debug )
{
utils : : memory_commit ( vm : : g_stat_addr + addr , size ) ;
}
2021-01-31 19:38:47 +01:00
const u64 seg_base = addr ;
2017-02-11 00:42:59 +01:00
2017-02-10 13:20:54 +01:00
while ( size )
{
2021-01-31 19:38:47 +01:00
if ( g_cfg . core . ppu_decoder = = ppu_decoder_type : : llvm )
{
// Assume addr is the start of first segment of PRX
2021-12-30 17:39:18 +01:00
ppu_ref ( addr ) = reinterpret_cast < ppu_intrp_func_t > ( reinterpret_cast < uptr > ( ppu_recompiler_fallback_ghc ) | ( seg_base < < ( 32 + 3 ) ) ) ;
2021-01-31 19:38:47 +01:00
}
else
{
2021-12-30 17:39:18 +01:00
ppu_ref ( addr ) = ppu_fallback ;
2021-01-31 19:38:47 +01:00
}
2017-02-10 13:20:54 +01:00
addr + = 4 ;
size - = 4 ;
}
}
2021-12-30 17:39:18 +01:00
static void ppu_far_jump ( ppu_thread & , ppu_opcode_t , be_t < u32 > * , ppu_intrp_func * ) ;
2021-09-01 12:38:17 +02:00
2021-12-30 17:39:18 +01:00
extern void ppu_register_function_at ( u32 addr , u32 size , ppu_intrp_func_t ptr = nullptr )
2017-02-10 13:20:54 +01:00
{
2017-03-22 21:23:47 +01:00
// Initialize specific function
if ( ptr )
{
2021-12-30 17:39:18 +01:00
ppu_ref ( addr ) = reinterpret_cast < ppu_intrp_func_t > ( ( reinterpret_cast < uptr > ( ptr ) & 0xffff'ffff'ffffu ) | ( uptr ( ppu_ref ( addr ) ) & ~ 0xffff'ffff'ffffu ) ) ;
2017-03-22 21:23:47 +01:00
return ;
}
2017-02-11 00:42:59 +01:00
if ( ! size )
{
2017-05-20 13:45:02 +02:00
if ( g_cfg . core . ppu_debug )
2017-04-08 22:58:00 +02:00
{
2020-02-01 09:31:27 +01:00
ppu_log . error ( " ppu_register_function_at(0x%x): empty range " , addr ) ;
2017-04-08 22:58:00 +02:00
}
2017-12-19 22:01:03 +01:00
return ;
2017-02-11 00:42:59 +01:00
}
2017-05-20 13:45:02 +02:00
if ( g_cfg . core . ppu_decoder = = ppu_decoder_type : : llvm )
2016-06-07 22:24:20 +02:00
{
2017-02-10 13:20:54 +01:00
return ;
}
// Initialize interpreter cache
while ( size )
{
2021-12-30 17:39:18 +01:00
if ( ppu_ref ( addr ) ! = ppu_break & & ppu_ref ( addr ) ! = ppu_far_jump )
2017-03-22 21:23:47 +01:00
{
2017-03-25 16:53:45 +01:00
ppu_ref ( addr ) = ppu_cache ( addr ) ;
2017-03-22 21:23:47 +01:00
}
2017-02-10 13:20:54 +01:00
addr + = 4 ;
size - = 4 ;
2016-06-07 22:24:20 +02:00
}
2016-06-27 18:34:08 +02:00
}
2016-06-07 22:24:20 +02:00
2021-09-01 12:38:17 +02:00
extern void ppu_register_function_at ( u32 addr , u32 size , u64 ptr )
{
2021-12-30 17:39:18 +01:00
return ppu_register_function_at ( addr , size , reinterpret_cast < ppu_intrp_func_t > ( ptr ) ) ;
2021-09-01 12:38:17 +02:00
}
2021-09-06 09:33:44 +02:00
u32 ppu_get_exported_func_addr ( u32 fnid , const std : : string & module_name ) ;
2021-12-30 17:39:18 +01:00
void ppu_return_from_far_jump ( ppu_thread & ppu , ppu_opcode_t , be_t < u32 > * , ppu_intrp_func * )
2021-09-06 09:33:44 +02:00
{
auto & calls_info = ppu . hle_func_calls_with_toc_info ;
ensure ( ! calls_info . empty ( ) ) ;
// Branch to next instruction after far jump call entry with restored R2 and LR
const auto restore_info = & calls_info . back ( ) ;
ppu . cia = restore_info - > cia + 4 ;
ppu . lr = restore_info - > saved_lr ;
ppu . gpr [ 2 ] = restore_info - > saved_r2 ;
calls_info . pop_back ( ) ;
}
static const bool s_init_return_far_jump_func = [ ]
{
REG_HIDDEN_FUNC_PURE ( ppu_return_from_far_jump ) ;
return true ;
} ( ) ;
2021-09-01 12:38:17 +02:00
struct ppu_far_jumps_t
{
2021-09-06 09:33:44 +02:00
struct all_info_t
{
u32 target ;
bool link ;
bool with_toc ;
std : : string module_name ;
2022-08-17 15:53:05 +02:00
ppu_intrp_func_t func ;
2021-09-06 09:33:44 +02:00
} ;
2022-08-17 15:53:05 +02:00
ppu_far_jumps_t ( int ) noexcept { }
2021-09-06 09:33:44 +02:00
std : : unordered_map < u32 , all_info_t > vals ;
2022-08-17 15:53:05 +02:00
: : jit_runtime rt ;
2021-09-01 12:38:17 +02:00
mutable shared_mutex mutex ;
2021-09-06 09:33:44 +02:00
// Get target address, 'ppu' is used in ppu_far_jump in order to modify registers
u32 get_target ( const u32 pc , ppu_thread * ppu = nullptr )
2021-09-01 12:38:17 +02:00
{
reader_lock lock ( mutex ) ;
if ( auto it = vals . find ( pc ) ; it ! = vals . end ( ) )
{
2021-09-06 09:33:44 +02:00
all_info_t & all_info = it - > second ;
u32 target = all_info . target ;
bool link = all_info . link ;
bool from_opd = all_info . with_toc ;
if ( ! all_info . module_name . empty ( ) )
{
target = ppu_get_exported_func_addr ( target , all_info . module_name ) ;
}
if ( from_opd & & ! vm : : check_addr < sizeof ( ppu_func_opd_t ) > ( target ) )
{
// Avoid reading unmapped memory under mutex
from_opd = false ;
}
if ( from_opd )
{
auto & opd = vm : : _ref < ppu_func_opd_t > ( target ) ;
target = opd . addr ;
2021-11-25 19:15:24 +01:00
// We modify LR to custom values here
2021-09-06 09:33:44 +02:00
link = false ;
if ( ppu )
{
auto & calls_info = ppu - > hle_func_calls_with_toc_info ;
// Save LR and R2
// Set LR to the this ppu_return_from_far_jump branch for restoration of registers
// NOTE: In order to clean up this information all calls must return in order
auto & saved_info = calls_info . emplace_back ( ) ;
saved_info . cia = pc ;
saved_info . saved_lr = std : : exchange ( ppu - > lr , FIND_FUNC ( ppu_return_from_far_jump ) ) ;
saved_info . saved_r2 = std : : exchange ( ppu - > gpr [ 2 ] , opd . rtoc ) ;
}
2021-11-25 19:15:24 +01:00
2021-09-06 09:33:44 +02:00
}
if ( link & & ppu )
{
ppu - > lr = pc + 4 ;
}
return target ;
2021-09-01 12:38:17 +02:00
}
2021-09-02 17:14:26 +02:00
return { } ;
2021-09-01 12:38:17 +02:00
}
2022-08-17 15:53:05 +02:00
template < bool Locked = true >
ppu_intrp_func_t gen_jump ( u32 pc )
{
[[maybe_unused]] std : : conditional_t < Locked , std : : lock_guard < shared_mutex > , const shared_mutex & > lock ( mutex ) ;
auto it = vals . find ( pc ) ;
if ( it = = vals . end ( ) )
{
return nullptr ;
}
if ( ! it - > second . func )
{
it - > second . func = build_function_asm < ppu_intrp_func_t > ( " " , [ & ] ( native_asm & c , auto & args )
{
using namespace asmjit ;
# ifdef ARCH_X64
c . mov ( args [ 0 ] , x86 : : rbp ) ;
c . mov ( x86 : : dword_ptr ( args [ 0 ] , : : offset32 ( & ppu_thread : : cia ) ) , pc ) ;
c . jmp ( ppu_far_jump ) ;
# else
Label jmp_address = c . newLabel ( ) ;
Label imm_address = c . newLabel ( ) ;
2022-09-04 20:11:04 +02:00
c . ldr ( args [ 1 ] . w ( ) , arm : : ptr ( imm_address ) ) ;
c . str ( args [ 1 ] . w ( ) , arm : : Mem ( args [ 0 ] , : : offset32 ( & ppu_thread : : cia ) ) ) ;
2022-08-17 15:53:05 +02:00
c . ldr ( args [ 1 ] , arm : : ptr ( jmp_address ) ) ;
c . br ( args [ 1 ] ) ;
c . align ( AlignMode : : kCode , 16 ) ;
c . bind ( jmp_address ) ;
c . embedUInt64 ( reinterpret_cast < u64 > ( ppu_far_jump ) ) ;
c . bind ( imm_address ) ;
c . embedUInt32 ( pc ) ;
# endif
2022-09-13 15:08:55 +02:00
} , & rt ) ;
2022-08-17 15:53:05 +02:00
}
return it - > second . func ;
}
2021-09-01 12:38:17 +02:00
} ;
u32 ppu_get_far_jump ( u32 pc )
{
2022-08-17 15:53:05 +02:00
if ( ! g_fxo - > is_init < ppu_far_jumps_t > ( ) )
{
return 0 ;
}
2021-09-06 09:33:44 +02:00
return g_fxo - > get < ppu_far_jumps_t > ( ) . get_target ( pc ) ;
2021-09-01 12:38:17 +02:00
}
2021-09-01 13:38:20 +02:00
2022-08-17 15:53:05 +02:00
static void ppu_far_jump ( ppu_thread & ppu , ppu_opcode_t , be_t < u32 > * , ppu_intrp_func * )
2021-09-01 12:38:17 +02:00
{
2022-08-17 15:53:05 +02:00
const u32 cia = g_fxo - > get < ppu_far_jumps_t > ( ) . get_target ( ppu . cia , & ppu ) ;
2021-09-06 09:33:44 +02:00
if ( ! vm : : check_addr ( cia , vm : : page_executable ) )
{
fmt : : throw_exception ( " PPU far jump failed! (returned cia = 0x%08x) " , cia ) ;
}
2021-09-02 17:14:26 +02:00
ppu . cia = cia ;
2021-09-01 12:38:17 +02:00
}
2021-09-06 09:33:44 +02:00
bool ppu_form_branch_to_code ( u32 entry , u32 target , bool link , bool with_toc , std : : string module_name )
2021-09-01 12:38:17 +02:00
{
2021-09-06 09:33:44 +02:00
// Force align entry and target
2021-09-01 12:38:17 +02:00
entry & = - 4 ;
2021-09-06 09:33:44 +02:00
// Exported functions are using target as FNID, must not be changed
if ( module_name . empty ( ) )
{
target & = - 4 ;
u32 cia_target = target ;
if ( with_toc )
{
ppu_func_opd_t opd { } ;
if ( ! vm : : try_access ( target , & opd , sizeof ( opd ) , false ) )
{
// Cannot access function descriptor
return false ;
}
// For now allow situations where OPD is changed later by patches or by the program itself
//cia_target = opd.addr;
// So force a valid target (executable, yet not equal to entry)
cia_target = entry ^ 8 ;
}
// Target CIA must be aligned, executable and not equal with
if ( cia_target % 4 | | entry = = cia_target | | ! vm : : check_addr ( cia_target , vm : : page_executable ) )
{
return false ;
}
}
// Entry must be executable
if ( ! vm : : check_addr ( entry , vm : : page_executable ) )
2021-09-01 12:38:17 +02:00
{
return false ;
}
2022-08-17 15:53:05 +02:00
g_fxo - > init < ppu_far_jumps_t > ( 0 ) ;
2021-09-01 12:38:17 +02:00
2021-09-06 09:33:44 +02:00
if ( ! module_name . empty ( ) )
{
// Always use function descriptor for exported functions
with_toc = true ;
}
if ( with_toc )
{
// Always link for calls with function descriptor
link = true ;
}
2021-09-01 12:38:17 +02:00
// Register branch target in host memory, not guest memory
auto & jumps = g_fxo - > get < ppu_far_jumps_t > ( ) ;
std : : lock_guard lock ( jumps . mutex ) ;
2021-12-30 17:39:18 +01:00
jumps . vals . insert_or_assign ( entry , ppu_far_jumps_t : : all_info_t { target , link , with_toc , std : : move ( module_name ) } ) ;
2022-08-17 15:53:05 +02:00
ppu_register_function_at ( entry , 4 , g_cfg . core . ppu_decoder = = ppu_decoder_type : : _static ? & ppu_far_jump : ensure ( g_fxo - > get < ppu_far_jumps_t > ( ) . gen_jump < false > ( entry ) ) ) ;
2021-09-01 12:38:17 +02:00
return true ;
}
2021-09-06 09:33:44 +02:00
bool ppu_form_branch_to_code ( u32 entry , u32 target , bool link , bool with_toc )
{
return ppu_form_branch_to_code ( entry , target , link , with_toc , std : : string { } ) ;
}
bool ppu_form_branch_to_code ( u32 entry , u32 target , bool link )
{
return ppu_form_branch_to_code ( entry , target , link , false ) ;
}
bool ppu_form_branch_to_code ( u32 entry , u32 target )
{
return ppu_form_branch_to_code ( entry , target , false ) ;
}
2021-09-01 12:38:17 +02:00
void ppu_remove_hle_instructions ( u32 addr , u32 size )
{
2022-08-17 15:53:05 +02:00
if ( Emu . IsStopped ( ) | | ! g_fxo - > is_init < ppu_far_jumps_t > ( ) )
{
return ;
}
2021-09-01 15:56:38 +02:00
2021-09-01 12:38:17 +02:00
auto & jumps = g_fxo - > get < ppu_far_jumps_t > ( ) ;
std : : lock_guard lock ( jumps . mutex ) ;
for ( auto it = jumps . vals . begin ( ) ; it ! = jumps . vals . end ( ) ; )
{
if ( it - > first > = addr & & it - > first < = addr + size - 1 & & size )
{
it = jumps . vals . erase ( it ) ;
continue ;
}
it + + ;
}
}
2021-03-31 19:08:01 +02:00
atomic_t < bool > g_debugger_pause_all_threads_on_bp = true ;
2017-02-10 14:13:17 +01:00
// Breakpoint entry point
2021-12-30 17:39:18 +01:00
static void ppu_break ( ppu_thread & ppu , ppu_opcode_t , be_t < u32 > * this_op , ppu_intrp_func * next_fn )
2017-02-10 14:13:17 +01:00
{
2021-03-31 19:08:01 +02:00
const bool pause_all = g_debugger_pause_all_threads_on_bp ;
2021-12-30 17:39:18 +01:00
const u32 old_cia = vm : : get_addr ( this_op ) ;
ppu . cia = old_cia ;
2021-02-06 22:25:40 +01:00
// Pause
2021-03-31 19:08:01 +02:00
ppu . state . atomic_op ( [ & ] ( bs_t < cpu_flag > & state )
{
if ( pause_all ) state + = cpu_flag : : dbg_global_pause ;
if ( pause_all | | ! ( state & cpu_flag : : dbg_step ) ) state + = cpu_flag : : dbg_pause ;
} ) ;
if ( pause_all )
{
// Pause all other threads
2022-01-20 18:44:49 +01:00
Emu . CallFromMainThread ( [ ] ( ) { Emu . Pause ( ) ; } ) ;
2021-03-31 19:08:01 +02:00
}
2021-03-02 12:59:19 +01:00
2021-12-30 17:39:18 +01:00
if ( ppu . check_state ( ) | | old_cia ! = atomic_storage < u32 > : : load ( ppu . cia ) )
2017-02-10 14:13:17 +01:00
{
2021-12-30 17:39:18 +01:00
// Do not execute if PC changed
return ;
2017-02-10 14:13:17 +01:00
}
// Fallback to the interpreter function
2022-03-27 10:37:11 +02:00
return ppu_cache ( ppu . cia ) ( ppu , { * this_op } , this_op , ppu . state ? & ppu_ret : next_fn ) ;
2017-02-10 14:13:17 +01:00
}
// Set or remove breakpoint
2021-07-30 20:30:29 +02:00
extern bool ppu_breakpoint ( u32 addr , bool is_adding )
2017-02-10 14:13:17 +01:00
{
2021-08-12 20:58:18 +02:00
if ( addr % 4 | | ! vm : : check_addr ( addr , vm : : page_executable ) | | g_cfg . core . ppu_decoder = = ppu_decoder_type : : llvm )
2017-02-10 14:13:17 +01:00
{
2021-07-30 20:30:29 +02:00
return false ;
2017-02-10 14:13:17 +01:00
}
2021-07-30 20:30:29 +02:00
// Remove breakpoint parameters
2021-12-30 17:39:18 +01:00
ppu_intrp_func_t to_set = 0 ;
ppu_intrp_func_t expected = & ppu_break ;
2017-02-10 14:13:17 +01:00
2021-07-30 20:30:29 +02:00
if ( u32 hle_addr { } ; g_fxo - > is_init < ppu_function_manager > ( ) & & ( hle_addr = g_fxo - > get < ppu_function_manager > ( ) . addr ) )
2017-04-02 20:10:06 +02:00
{
2021-07-30 20:30:29 +02:00
// HLE function index
const u32 index = ( addr - hle_addr ) / 8 ;
2017-04-02 20:10:06 +02:00
2021-07-30 20:30:29 +02:00
if ( addr % 8 = = 4 & & index < ppu_function_manager : : get ( ) . size ( ) )
{
// HLE function placement
2021-12-30 17:39:18 +01:00
to_set = ppu_function_manager : : get ( ) [ index ] ;
2021-07-30 20:30:29 +02:00
}
2017-04-02 20:10:06 +02:00
}
2021-07-30 20:30:29 +02:00
if ( ! to_set )
2017-04-02 20:10:06 +02:00
{
2021-07-30 20:30:29 +02:00
// If not an HLE function use regular instruction function
to_set = ppu_cache ( addr ) ;
2017-04-02 20:10:06 +02:00
}
2021-12-30 17:39:18 +01:00
ppu_intrp_func_t & _ref = ppu_ref ( addr ) ;
2021-08-12 20:58:18 +02:00
2021-07-30 20:30:29 +02:00
if ( is_adding )
2017-04-02 20:10:06 +02:00
{
2021-07-30 20:30:29 +02:00
// Swap if adding
std : : swap ( to_set , expected ) ;
2021-08-12 20:58:18 +02:00
2021-12-30 17:39:18 +01:00
if ( _ref = = & ppu_fallback )
2021-08-12 20:58:18 +02:00
{
ppu_log . error ( " Unregistered instruction replaced with a breakpoint at 0x%08x " , addr ) ;
2021-12-30 17:39:18 +01:00
expected = ppu_fallback ;
2021-08-12 20:58:18 +02:00
}
2017-04-02 20:10:06 +02:00
}
2021-07-30 20:30:29 +02:00
2021-12-30 17:39:18 +01:00
return atomic_storage < ppu_intrp_func_t > : : compare_exchange ( _ref , expected , to_set ) ;
2017-04-02 20:10:06 +02:00
}
2017-10-06 17:39:15 +02:00
extern bool ppu_patch ( u32 addr , u32 value )
{
2020-08-21 11:10:00 +02:00
if ( addr % 4 )
2017-10-06 17:39:15 +02:00
{
2020-08-21 11:10:00 +02:00
ppu_log . fatal ( " Patch failed at 0x%x: unanligned memory address. " , addr ) ;
2018-05-07 20:57:06 +02:00
return false ;
}
2017-10-06 17:39:15 +02:00
2022-04-30 15:51:52 +02:00
vm : : writer_lock rlock ;
2020-08-21 11:10:00 +02:00
2020-11-10 18:09:28 +01:00
if ( ! vm : : check_addr ( addr ) )
2018-05-07 20:57:06 +02:00
{
2020-02-01 09:31:27 +01:00
ppu_log . fatal ( " Patch failed at 0x%x: invalid memory address. " , addr ) ;
2018-05-07 20:57:06 +02:00
return false ;
}
2017-10-06 17:39:15 +02:00
2020-11-10 18:09:28 +01:00
const bool is_exec = vm : : check_addr ( addr , vm : : page_executable ) ;
2020-08-21 11:10:00 +02:00
if ( is_exec & & g_cfg . core . ppu_decoder = = ppu_decoder_type : : llvm & & ! Emu . IsReady ( ) )
{
// TODO: support recompilers
ppu_log . fatal ( " Patch failed at 0x%x: LLVM recompiler is used. " , addr ) ;
return false ;
}
* vm : : get_super_ptr < u32 > ( addr ) = value ;
if ( is_exec )
2018-05-07 20:57:06 +02:00
{
2021-12-30 17:39:18 +01:00
if ( ppu_ref ( addr ) ! = ppu_break & & ppu_ref ( addr ) ! = ppu_fallback )
2020-08-21 11:10:00 +02:00
{
ppu_ref ( addr ) = ppu_cache ( addr ) ;
}
2017-10-06 17:39:15 +02:00
}
2018-05-07 20:57:06 +02:00
return true ;
2017-10-06 17:39:15 +02:00
}
2020-11-23 18:57:34 +01:00
std : : array < u32 , 2 > op_branch_targets ( u32 pc , ppu_opcode_t op )
{
2021-05-22 09:35:15 +02:00
std : : array < u32 , 2 > res { pc + 4 , umax } ;
2020-11-23 18:57:34 +01:00
2021-09-01 12:38:17 +02:00
g_fxo - > need < ppu_far_jumps_t > ( ) ;
2021-09-06 09:33:44 +02:00
if ( u32 target = g_fxo - > get < ppu_far_jumps_t > ( ) . get_target ( pc ) )
2021-09-01 12:38:17 +02:00
{
res [ 0 ] = target ;
return res ;
}
2020-11-23 18:57:34 +01:00
switch ( const auto type = g_ppu_itype . decode ( op . opcode ) )
{
case ppu_itype : : B :
case ppu_itype : : BC :
{
res [ type = = ppu_itype : : BC ? 1 : 0 ] = ( ( op . aa ? 0 : pc ) + ( type = = ppu_itype : : B ? + op . bt24 : + op . bt14 ) ) ;
break ;
}
case ppu_itype : : BCCTR :
case ppu_itype : : BCLR :
case ppu_itype : : UNK :
{
2021-05-22 09:35:15 +02:00
res [ 0 ] = umax ;
2020-11-23 18:57:34 +01:00
break ;
}
default : break ;
}
return res ;
}
2022-06-22 11:00:06 +02:00
void ppu_thread : : dump_regs ( std : : string & ret ) const
2020-03-31 02:11:37 +02:00
{
2021-10-12 22:12:30 +02:00
PPUDisAsm dis_asm ( cpu_disasm_mode : : normal , vm : : g_sudo_addr ) ;
2020-03-31 02:57:54 +02:00
for ( uint i = 0 ; i < 32 ; + + i )
{
auto reg = gpr [ i ] ;
2021-03-16 14:20:45 +01:00
// Fixup for syscall arguments
2021-03-16 14:41:32 +01:00
if ( current_function & & i > = 3 & & i < = 10 ) reg = syscall_args [ i - 3 ] ;
2021-03-16 14:20:45 +01:00
2022-05-08 12:40:21 +02:00
auto [ is_const , const_value ] = dis_asm . try_get_const_gpr_value ( i , cia ) ;
if ( const_value ! = reg )
{
// Expectation of pretictable code path has not been met (such as a branch directly to the instruction)
is_const = false ;
}
fmt : : append ( ret , " r%d%s%s 0x%-8llx " , i , i < = 9 ? " " : " " , is_const ? " © " : " : " , reg ) ;
2020-03-31 02:57:54 +02:00
2020-11-10 18:09:28 +01:00
constexpr u32 max_str_len = 32 ;
constexpr u32 hex_count = 8 ;
2020-03-31 02:57:54 +02:00
2021-05-22 09:35:15 +02:00
if ( reg < = u32 { umax } & & vm : : check_addr < max_str_len > ( static_cast < u32 > ( reg ) ) )
2020-03-31 02:57:54 +02:00
{
2020-04-17 08:15:30 +02:00
bool is_function = false ;
u32 toc = 0 ;
2022-05-18 15:09:27 +02:00
auto is_exec_code = [ & ] ( u32 addr )
{
return addr % 4 = = 0 & & vm : : check_addr ( addr , vm : : page_executable ) & & g_ppu_itype . decode ( * vm : : get_super_ptr < u32 > ( addr ) ) ! = ppu_itype : : UNK ;
} ;
2022-09-29 11:04:38 +02:00
if ( const u32 reg_ptr = * vm : : get_super_ptr < be_t < u32 , 1 > > ( static_cast < u32 > ( reg ) ) ;
2022-05-18 15:09:27 +02:00
vm : : check_addr < 8 > ( reg_ptr ) & & ! vm : : check_addr ( toc , vm : : page_executable ) )
2020-03-31 02:57:54 +02:00
{
2022-05-18 15:09:27 +02:00
// Check executability and alignment
if ( reg % 4 = = 0 & & is_exec_code ( reg_ptr ) )
2020-04-17 08:15:30 +02:00
{
toc = * vm : : get_super_ptr < u32 > ( static_cast < u32 > ( reg + 4 ) ) ;
2022-05-18 17:00:32 +02:00
if ( toc % 4 = = 0 & & ( toc > > 29 ) = = ( reg_ptr > > 29 ) & & vm : : check_addr ( toc ) & & ! vm : : check_addr ( toc , vm : : page_executable ) )
2020-04-17 08:15:30 +02:00
{
is_function = true ;
2020-08-21 19:58:38 +02:00
reg = reg_ptr ;
2020-04-17 08:15:30 +02:00
}
}
2020-03-31 02:57:54 +02:00
}
2022-05-18 15:09:27 +02:00
else if ( is_exec_code ( reg ) )
2020-04-17 08:15:30 +02:00
{
is_function = true ;
}
2020-03-31 02:57:54 +02:00
const auto gpr_buf = vm : : get_super_ptr < u8 > ( reg ) ;
std : : string buf_tmp ( gpr_buf , gpr_buf + max_str_len ) ;
2021-02-13 09:13:26 +01:00
std : : string_view sv ( buf_tmp . data ( ) , std : : min < usz > ( buf_tmp . size ( ) , buf_tmp . find_first_of ( " \0 \n " sv ) ) ) ;
2020-04-17 08:15:30 +02:00
if ( is_function )
{
if ( toc )
{
fmt : : append ( ret , " -> func(at=0x%x, toc=0x%x) " , reg , toc ) ;
}
else
{
2020-11-10 15:57:06 +01:00
dis_asm . disasm ( reg ) ;
fmt : : append ( ret , " -> %s " , dis_asm . last_opcode ) ;
2020-04-17 08:15:30 +02:00
}
}
2021-02-13 09:13:26 +01:00
// NTS: size of 3 and above is required
// If ends with a newline, only one character is required
else if ( ( sv . size ( ) = = buf_tmp . size ( ) | | ( sv . size ( ) > = ( buf_tmp [ sv . size ( ) ] = = ' \n ' ? 1 : 3 ) ) ) & &
std : : all_of ( sv . begin ( ) , sv . end ( ) , [ ] ( u8 c ) { return std : : isprint ( c ) ; } ) )
2020-03-31 02:57:54 +02:00
{
2021-02-13 09:13:26 +01:00
fmt : : append ( ret , " -> \" %s \" " , sv ) ;
2020-03-31 02:57:54 +02:00
}
else
{
2020-04-03 10:21:18 +02:00
fmt : : append ( ret , " -> " ) ;
2020-03-31 02:57:54 +02:00
for ( u32 j = 0 ; j < hex_count ; + + j )
{
fmt : : append ( ret , " %02x " , buf_tmp [ j ] ) ;
}
}
}
fmt : : append ( ret , " \n " ) ;
}
2020-04-03 10:21:18 +02:00
for ( uint i = 0 ; i < 32 ; + + i )
{
2022-05-31 07:36:10 +02:00
const f64 r = fpr [ i ] ;
if ( ! std : : bit_cast < u64 > ( r ) )
{
fmt : : append ( ret , " f%d%s: %-12.6G [%-18s] (f32=0x%x) \n " , i , i < = 9 ? " " : " " , r , " " , std : : bit_cast < u32 > ( f32 ( r ) ) ) ;
continue ;
}
fmt : : append ( ret , " f%d%s: %-12.6G [0x%016x] (f32=0x%x) \n " , i , i < = 9 ? " " : " " , r , std : : bit_cast < u64 > ( r ) , std : : bit_cast < u32 > ( f32 ( r ) ) ) ;
2020-04-03 10:21:18 +02:00
}
2020-11-11 04:59:24 +01:00
for ( uint i = 0 ; i < 32 ; + + i , ret + = ' \n ' )
2020-04-03 10:21:18 +02:00
{
2020-11-11 04:59:24 +01:00
fmt : : append ( ret , " v%d%s: " , i , i < = 9 ? " " : " " ) ;
2020-11-14 07:03:33 +01:00
2020-11-11 04:59:24 +01:00
const auto r = vr [ i ] ;
const u32 i3 = r . u32r [ 0 ] ;
if ( v128 : : from32p ( i3 ) = = r )
{
// Shortand formatting
fmt : : append ( ret , " %08x " , i3 ) ;
fmt : : append ( ret , " [x: %g] " , r . fr [ 0 ] ) ;
}
else
{
fmt : : append ( ret , " %08x %08x %08x %08x " , r . u32r [ 0 ] , r . u32r [ 1 ] , r . u32r [ 2 ] , r . u32r [ 3 ] ) ;
fmt : : append ( ret , " [x: %g y: %g z: %g w: %g] " , r . fr [ 0 ] , r . fr [ 1 ] , r . fr [ 2 ] , r . fr [ 3 ] ) ;
}
2020-04-03 10:21:18 +02:00
}
2017-04-28 15:28:37 +02:00
2020-05-05 06:23:12 +02:00
fmt : : append ( ret , " CR: 0x%08x \n " , cr . pack ( ) ) ;
fmt : : append ( ret , " LR: 0x%llx \n " , lr ) ;
fmt : : append ( ret , " CTR: 0x%llx \n " , ctr ) ;
fmt : : append ( ret , " VRSAVE: 0x%08x \n " , vrsave ) ;
fmt : : append ( ret , " XER: [CA=%u | OV=%u | SO=%u | CNT=%u] \n " , xer . ca , xer . ov , xer . so , xer . cnt ) ;
fmt : : append ( ret , " VSCR: [SAT=%u | NJ=%u] \n " , sat , nj ) ;
fmt : : append ( ret , " FPSCR: [FL=%u | FG=%u | FE=%u | FU=%u] \n " , fpscr . fl , fpscr . fg , fpscr . fe , fpscr . fu ) ;
2021-11-21 10:41:05 +01:00
const u32 addr = raddr ;
if ( addr )
2020-05-05 06:23:12 +02:00
fmt : : append ( ret , " Reservation Addr: 0x%x " , addr ) ;
2020-05-01 07:59:15 +02:00
else
2020-05-05 06:23:12 +02:00
fmt : : append ( ret , " Reservation Addr: none " ) ;
2017-04-28 15:28:37 +02:00
2022-05-18 15:09:27 +02:00
fmt : : append ( ret , " \n Reservation Data (entire cache line): \n " ) ;
2021-11-21 10:41:05 +01:00
be_t < u32 > data [ 32 ] { } ;
std : : memcpy ( data , rdata , sizeof ( rdata ) ) ; // Show the data even if the reservation was lost inside the atomic loop
if ( addr & & ! use_full_rdata )
{
const u32 offset = addr & 0x78 ;
fmt : : append ( ret , " [0x%02x] %08x %08x \n " , offset , data [ offset / sizeof ( u32 ) ] , data [ offset / sizeof ( u32 ) + 1 ] ) ;
// Asterisk marks the offset of data that had been given to the guest PPU code
* ( & ret . back ( ) - ( addr & 4 ? 9 : 18 ) ) = ' * ' ;
}
else
{
for ( usz i = 0 ; i < std : : size ( data ) ; i + = 4 )
{
fmt : : append ( ret , " [0x%02x] %08x %08x %08x %08x \n " , i * sizeof ( data [ 0 ] )
, data [ i + 0 ] , data [ i + 1 ] , data [ i + 2 ] , data [ i + 3 ] ) ;
}
if ( addr )
{
// See the note above
* ( & ret . back ( ) - ( 4 - ( addr % 16 / 4 ) ) * 9 - ( 8 - ( addr % 128 / 16 ) ) * std : : size ( " [0x00] " sv ) ) = ' * ' ;
}
}
2020-03-31 02:11:37 +02:00
}
std : : string ppu_thread : : dump_callstack ( ) const
{
std : : string ret ;
fmt : : append ( ret , " Call stack: \n ========= \n 0x%08x (0x0) called \n " , cia ) ;
2020-07-03 06:56:55 +02:00
for ( const auto & sp : dump_callstack_list ( ) )
2020-03-31 02:11:37 +02:00
{
// TODO: function addresses too
2020-11-07 17:12:52 +01:00
fmt : : append ( ret , " > from 0x%08x (sp=0x%08x) \n " , sp . first , sp . second ) ;
2020-03-31 02:11:37 +02:00
}
return ret ;
}
2020-07-03 06:56:55 +02:00
std : : vector < std : : pair < u32 , u32 > > ppu_thread : : dump_callstack_list ( ) const
2020-03-31 02:11:37 +02:00
{
2020-03-19 11:29:50 +01:00
//std::shared_lock rlock(vm::g_mutex); // Needs optimizations
2017-04-28 15:28:37 +02:00
// Determine stack range
2020-08-21 19:49:57 +02:00
const u64 r1 = gpr [ 1 ] ;
2021-05-22 09:35:15 +02:00
if ( r1 > u32 { umax } | | r1 % 0x10 )
2020-08-21 19:49:57 +02:00
{
return { } ;
}
const u32 stack_ptr = static_cast < u32 > ( r1 ) ;
2020-03-19 11:29:50 +01:00
2020-11-10 18:09:28 +01:00
if ( ! vm : : check_addr ( stack_ptr , vm : : page_writable ) )
2020-03-19 11:29:50 +01:00
{
// Normally impossible unless the code does not follow ABI rules
2020-03-31 02:11:37 +02:00
return { } ;
2020-03-19 11:29:50 +01:00
}
2017-04-28 15:28:37 +02:00
u32 stack_min = stack_ptr & ~ 0xfff ;
u32 stack_max = stack_min + 4096 ;
2020-11-10 18:09:28 +01:00
while ( stack_min & & vm : : check_addr ( stack_min - 4096 , vm : : page_writable ) )
2017-04-28 15:28:37 +02:00
{
stack_min - = 4096 ;
}
2020-11-10 18:09:28 +01:00
while ( stack_max + 4096 & & vm : : check_addr ( stack_max , vm : : page_writable ) )
2017-04-28 15:28:37 +02:00
{
stack_max + = 4096 ;
}
2020-07-03 06:56:55 +02:00
std : : vector < std : : pair < u32 , u32 > > call_stack_list ;
2020-03-31 02:11:37 +02:00
2020-11-07 17:12:52 +01:00
bool first = true ;
2020-03-31 02:11:37 +02:00
for (
2020-11-07 17:12:52 +01:00
u64 sp = r1 ;
2020-07-03 05:18:14 +02:00
sp % 0x10 = = 0u & & sp > = stack_min & & sp < = stack_max - ppu_stack_start_offset ;
2020-11-08 14:42:20 +01:00
sp = * vm : : get_super_ptr < u64 > ( static_cast < u32 > ( sp ) ) , first = false
2020-03-31 02:11:37 +02:00
)
2015-02-01 14:52:34 +01:00
{
2020-11-07 17:12:52 +01:00
u64 addr = * vm : : get_super_ptr < u64 > ( static_cast < u32 > ( sp + 16 ) ) ;
auto is_invalid = [ ] ( u64 addr )
{
2021-05-22 09:35:15 +02:00
if ( addr > u32 { umax } | | addr % 4 | | ! vm : : check_addr ( static_cast < u32 > ( addr ) , vm : : page_executable ) )
2020-11-07 20:05:44 +01:00
{
return true ;
}
// Ignore HLE stop address
2021-05-17 13:22:27 +02:00
return addr = = g_fxo - > get < ppu_function_manager > ( ) . func_addr ( 1 ) + 4 ;
2020-11-07 17:12:52 +01:00
} ;
2020-07-03 05:18:14 +02:00
2020-11-07 17:12:52 +01:00
if ( is_invalid ( addr ) )
2020-07-03 05:18:14 +02:00
{
2020-11-08 14:42:20 +01:00
if ( first )
2020-11-07 17:12:52 +01:00
{
// Function hasn't saved LR, could be because it's a leaf function
// Use LR directly instead
addr = lr ;
if ( is_invalid ( addr ) )
{
// Skip it, workaround
continue ;
}
}
else
{
break ;
}
2020-07-03 05:18:14 +02:00
}
2020-03-31 02:11:37 +02:00
// TODO: function addresses too
2020-07-03 06:56:55 +02:00
call_stack_list . emplace_back ( static_cast < u32 > ( addr ) , static_cast < u32 > ( sp ) ) ;
2015-02-01 14:52:34 +01:00
}
2020-03-31 02:11:37 +02:00
return call_stack_list ;
}
std : : string ppu_thread : : dump_misc ( ) const
{
2022-06-22 11:10:40 +02:00
std : : string ret = cpu_thread : : dump_misc ( ) ;
2020-03-31 02:11:37 +02:00
2022-09-25 13:10:59 +02:00
if ( ack_suspend )
{
if ( ret . ends_with ( " \n " ) )
{
ret . pop_back ( ) ;
}
fmt : : append ( ret , " (LV2 suspended) \n " ) ;
}
2020-03-31 02:11:37 +02:00
fmt : : append ( ret , " Priority: %d \n " , + prio ) ;
fmt : : append ( ret , " Stack: 0x%x..0x%x \n " , stack_addr , stack_addr + stack_size - 1 ) ;
fmt : : append ( ret , " Joiner: %s \n " , joiner . load ( ) ) ;
2020-04-03 10:21:18 +02:00
if ( const auto size = cmd_queue . size ( ) )
fmt : : append ( ret , " Commands: %u \n " , size ) ;
2020-03-31 02:11:37 +02:00
const char * _func = current_function ;
if ( _func )
{
2020-04-16 20:16:40 +02:00
ret + = " In function: " ;
2020-03-31 02:11:37 +02:00
ret + = _func ;
ret + = ' \n ' ;
2021-03-16 14:41:32 +01:00
for ( u32 i = 3 ; i < = 10 ; i + + )
2021-03-16 14:20:45 +01:00
if ( u64 v = gpr [ i ] ; v ! = syscall_args [ i - 3 ] )
fmt : : append ( ret , " ** r%d: 0x%llx \n " , i , v ) ;
2020-03-31 02:11:37 +02:00
}
2022-09-25 13:10:59 +02:00
else if ( is_paused ( ) | | is_stopped ( ) )
2020-03-31 02:11:37 +02:00
{
if ( const auto last_func = last_function )
{
_func = last_func ;
ret + = " Last function: " ;
ret + = _func ;
ret + = ' \n ' ;
}
}
if ( const auto _time = start_time )
{
fmt : : append ( ret , " Waiting: %fs \n " , ( get_guest_system_time ( ) - _time ) / 1000000. ) ;
}
else
{
ret + = ' \n ' ;
}
if ( ! _func )
{
ret + = ' \n ' ;
}
2016-07-27 23:43:22 +02:00
return ret ;
2015-08-10 21:39:52 +02:00
}
2022-06-22 11:00:06 +02:00
void ppu_thread : : dump_all ( std : : string & ret ) const
2021-07-10 10:56:48 +02:00
{
2022-06-22 11:00:06 +02:00
cpu_thread : : dump_all ( ret ) ;
2021-07-10 10:56:48 +02:00
if ( ! call_history . data . empty ( ) )
{
ret + =
" \n Calling History: "
" \n ================ " ;
fmt : : append ( ret , " %s " , call_history ) ;
}
}
2016-04-27 00:27:24 +02:00
extern thread_local std : : string ( * g_tls_log_prefix ) ( ) ;
2016-07-27 23:43:22 +02:00
void ppu_thread : : cpu_task ( )
2012-11-15 00:39:56 +01:00
{
2017-02-07 14:14:44 +01:00
std : : fesetround ( FE_TONEAREST ) ;
2015-03-16 19:44:49 +01:00
2021-12-30 17:39:18 +01:00
if ( g_cfg . core . set_daz_and_ftz )
{
gv_set_zeroing_denormals ( ) ;
}
else
2018-05-09 22:35:05 +02:00
{
2021-12-30 17:39:18 +01:00
gv_unset_zeroing_denormals ( ) ;
2018-05-09 22:35:05 +02:00
}
2016-07-27 23:43:22 +02:00
// Execute cmd_queue
2016-08-09 16:14:41 +02:00
while ( cmd64 cmd = cmd_wait ( ) )
2016-07-27 23:43:22 +02:00
{
const u32 arg = cmd . arg2 < u32 > ( ) ; // 32-bit arg extracted
2016-08-09 16:14:41 +02:00
switch ( auto type = cmd . arg1 < ppu_cmd > ( ) )
2016-07-27 23:43:22 +02:00
{
case ppu_cmd : : opcode :
{
2021-12-30 17:39:18 +01:00
cmd_pop ( ) , g_fxo - > get < ppu_interpreter_rt > ( ) . decode ( arg ) ( * this , { arg } , vm : : _ptr < u32 > ( cia - 4 ) , & ppu_ret ) ;
2016-07-27 23:43:22 +02:00
break ;
}
case ppu_cmd : : set_gpr :
{
if ( arg > = 32 )
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Invalid ppu_cmd::set_gpr arg (0x%x) " , arg ) ;
2016-07-27 23:43:22 +02:00
}
2016-08-09 16:14:41 +02:00
gpr [ arg % 32 ] = cmd_get ( 1 ) . as < u64 > ( ) ;
2016-07-27 23:43:22 +02:00
cmd_pop ( 1 ) ;
break ;
}
case ppu_cmd : : set_args :
{
if ( arg > 8 )
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Unsupported ppu_cmd::set_args size (0x%x) " , arg ) ;
2016-07-27 23:43:22 +02:00
}
for ( u32 i = 0 ; i < arg ; i + + )
{
2016-08-09 16:14:41 +02:00
gpr [ i + 3 ] = cmd_get ( 1 + i ) . as < u64 > ( ) ;
2016-07-27 23:43:22 +02:00
}
cmd_pop ( arg ) ;
break ;
}
case ppu_cmd : : lle_call :
{
2022-06-18 04:54:54 +02:00
# ifdef __APPLE__
pthread_jit_write_protect_np ( true ) ;
# endif
2016-07-27 23:43:22 +02:00
const vm : : ptr < u32 > opd ( arg < 32 ? vm : : cast ( gpr [ arg ] ) : vm : : cast ( arg ) ) ;
cmd_pop ( ) , fast_call ( opd [ 0 ] , opd [ 1 ] ) ;
break ;
}
case ppu_cmd : : hle_call :
{
2022-09-19 14:57:51 +02:00
cmd_pop ( ) , : : at32 ( ppu_function_manager : : get ( ) , arg ) ( * this , { arg } , vm : : _ptr < u32 > ( cia - 4 ) , & ppu_ret ) ;
2016-07-27 23:43:22 +02:00
break ;
}
2020-04-08 13:26:31 +02:00
case ppu_cmd : : opd_call :
{
2022-06-18 04:54:54 +02:00
# ifdef __APPLE__
pthread_jit_write_protect_np ( true ) ;
# endif
2020-04-11 10:16:28 +02:00
const ppu_func_opd_t opd = cmd_get ( 1 ) . as < ppu_func_opd_t > ( ) ;
2020-04-08 13:26:31 +02:00
cmd_pop ( 1 ) , fast_call ( opd . addr , opd . rtoc ) ;
break ;
}
2018-10-11 00:17:19 +02:00
case ppu_cmd : : ptr_call :
{
2021-12-30 17:39:18 +01:00
const ppu_intrp_func_t func = cmd_get ( 1 ) . as < ppu_intrp_func_t > ( ) ;
cmd_pop ( 1 ) , func ( * this , { } , vm : : _ptr < u32 > ( cia - 4 ) , & ppu_ret ) ;
2018-10-11 00:17:19 +02:00
break ;
}
2022-07-04 15:02:17 +02:00
case ppu_cmd : : cia_call :
{
loaded_from_savestate = true ;
cmd_pop ( ) , fast_call ( std : : exchange ( cia , 0 ) , gpr [ 2 ] ) ;
break ;
}
2017-01-22 20:03:57 +01:00
case ppu_cmd : : initialize :
{
2022-06-14 14:28:38 +02:00
# ifdef __APPLE__
pthread_jit_write_protect_np ( false ) ;
# endif
2021-03-21 17:55:47 +01:00
cmd_pop ( ) ;
ppu_initialize ( ) , spu_cache : : initialize ( ) ;
2021-04-06 20:05:16 +02:00
2022-06-14 14:28:38 +02:00
# ifdef __APPLE__
pthread_jit_write_protect_np ( true ) ;
# endif
# ifdef ARCH_ARM64
// Flush all cache lines after potentially writing executable code
asm ( " ISB " ) ;
asm ( " DSB ISH " ) ;
# endif
2022-07-04 15:02:17 +02:00
// Wait until the progress dialog is closed.
// We don't want to open a cell dialog while a native progress dialog is still open.
thread_ctrl : : wait_on < atomic_wait : : op_ne > ( g_progr_ptotal , 0 ) ;
g_fxo - > get < progress_dialog_workaround > ( ) . skip_the_progress_dialog = true ;
2022-07-05 13:12:21 +02:00
// Sadly we can't postpone initializing guest time because we need to run PPU threads
2022-07-04 15:02:17 +02:00
// (the farther it's postponed, the less accuracy of guest time has been lost)
Emu . FixGuestTime ( ) ;
2022-07-14 21:07:02 +02:00
// Run SPUs waiting on a syscall (savestates related)
idm : : select < named_thread < spu_thread > > ( [ & ] ( u32 , named_thread < spu_thread > & spu )
{
if ( spu . group & & spu . index = = spu . group - > waiter_spu_index )
{
if ( std : : exchange ( spu . stop_flag_removal_protection , false ) )
{
return ;
}
ensure ( spu . state . test_and_reset ( cpu_flag : : stop ) ) ;
spu . state . notify_one ( cpu_flag : : stop ) ;
}
} ) ;
2022-07-04 15:02:17 +02:00
// Check if this is the only PPU left to initialize (savestates related)
if ( lv2_obj : : is_scheduler_ready ( ) )
{
if ( Emu . IsStarting ( ) )
{
Emu . FinalizeRunRequest ( ) ;
}
}
2017-01-22 20:03:57 +01:00
break ;
}
2017-02-06 19:36:46 +01:00
case ppu_cmd : : sleep :
{
2017-02-22 11:10:55 +01:00
cmd_pop ( ) , lv2_obj : : sleep ( * this ) ;
2017-02-06 19:36:46 +01:00
break ;
}
2018-03-06 03:36:33 +01:00
case ppu_cmd : : reset_stack :
{
2020-07-03 05:18:14 +02:00
cmd_pop ( ) , gpr [ 1 ] = stack_addr + stack_size - ppu_stack_start_offset ;
2018-03-06 03:36:33 +01:00
break ;
}
2016-07-27 23:43:22 +02:00
default :
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Unknown ppu_cmd(0x%x) " , static_cast < u32 > ( type ) ) ;
2016-07-27 23:43:22 +02:00
}
}
}
2016-06-25 07:16:15 +02:00
}
2014-04-23 13:59:14 +02:00
2018-04-03 16:19:07 +02:00
void ppu_thread : : cpu_sleep ( )
{
2020-10-30 14:32:49 +01:00
// Clear reservation
raddr = 0 ;
2018-04-03 16:19:07 +02:00
2020-10-30 14:32:49 +01:00
// Setup wait flag and memory flags to relock itself
2020-11-14 07:03:33 +01:00
state + = g_use_rtm ? cpu_flag : : wait : cpu_flag : : wait + cpu_flag : : memory ;
2018-04-03 16:19:07 +02:00
2020-10-30 14:32:49 +01:00
if ( auto ptr = vm : : g_tls_locked )
{
ptr - > compare_and_swap ( this , nullptr ) ;
}
lv2_obj : : awake ( this ) ;
2018-04-03 16:19:07 +02:00
}
2021-02-13 15:50:07 +01:00
void ppu_thread : : cpu_on_stop ( )
{
if ( current_function )
{
if ( start_time )
{
ppu_log . warning ( " '%s' aborted (%fs) " , current_function , ( get_guest_system_time ( ) - start_time ) / 1000000. ) ;
}
else
{
ppu_log . warning ( " '%s' aborted " , current_function ) ;
}
current_function = { } ;
}
2022-08-26 09:50:58 +02:00
// TODO: More conditions
if ( Emu . IsStopped ( ) & & g_cfg . core . spu_debug )
{
std : : string ret ;
dump_all ( ret ) ;
ppu_log . notice ( " thread context: %s " , ret ) ;
}
2021-02-13 15:50:07 +01:00
}
2016-07-27 23:43:22 +02:00
void ppu_thread : : exec_task ( )
2016-06-25 07:16:15 +02:00
{
2021-12-30 17:39:18 +01:00
if ( g_cfg . core . ppu_decoder ! = ppu_decoder_type : : _static )
2016-06-07 22:24:20 +02:00
{
2021-01-31 19:38:47 +01:00
while ( true )
2017-06-22 23:52:09 +02:00
{
2021-01-31 19:38:47 +01:00
if ( state ) [[unlikely]]
{
if ( check_state ( ) )
break ;
}
ppu_gateway ( this ) ;
2017-06-22 23:52:09 +02:00
}
2017-12-19 22:01:03 +01:00
2017-03-22 21:23:47 +01:00
return ;
2016-06-07 22:24:20 +02:00
}
2017-03-25 16:53:45 +01:00
const auto cache = vm : : g_exec_addr ;
2021-12-30 17:39:18 +01:00
const auto mem_ = vm : : g_base_addr ;
2012-11-15 00:39:56 +01:00
2016-04-14 01:09:41 +02:00
while ( true )
{
2021-12-30 17:39:18 +01:00
if ( test_stopped ( ) ) [[unlikely]]
2016-04-14 01:09:41 +02:00
{
2021-12-30 17:39:18 +01:00
return ;
2016-05-13 15:55:34 +02:00
}
2016-04-14 01:09:41 +02:00
2021-12-30 17:39:18 +01:00
gv_zeroupper ( ) ;
2017-02-10 13:20:54 +01:00
2021-12-30 17:39:18 +01:00
// Execute instruction (may be step; execute only one instruction if state)
const auto op = reinterpret_cast < be_t < u32 > * > ( mem_ + u64 { cia } ) ;
const auto fn = reinterpret_cast < ppu_intrp_func * > ( cache + u64 { cia } * 2 ) ;
fn - > fn ( * this , { * op } , op , state ? & ppu_ret : fn + 1 ) ;
2016-04-14 01:09:41 +02:00
}
2012-11-15 00:39:56 +01:00
}
2016-07-27 23:43:22 +02:00
ppu_thread : : ~ ppu_thread ( )
2012-11-15 00:39:56 +01:00
{
2021-01-21 17:29:37 +01:00
perf_log . notice ( " Perf stats for STCX reload: successs %u, failure %u " , last_succ , last_fail ) ;
2021-12-30 17:39:18 +01:00
perf_log . notice ( " Perf stats for instructions: total %u " , exec_bytes / 4 ) ;
2016-07-27 23:43:22 +02:00
}
2016-05-13 15:55:34 +02:00
2018-10-11 00:17:19 +02:00
ppu_thread : : ppu_thread ( const ppu_thread_params & param , std : : string_view name , u32 prio , int detached )
2017-01-25 18:50:30 +01:00
: cpu_thread ( idm : : last_id ( ) )
2016-07-27 23:43:22 +02:00
, prio ( prio )
2018-10-11 00:17:19 +02:00
, stack_size ( param . stack_size )
, stack_addr ( param . stack_addr )
2020-03-03 21:39:40 +01:00
, joiner ( detached ! = 0 ? ppu_join_status : : detached : ppu_join_status : : joinable )
2020-04-08 13:26:31 +02:00
, entry_func ( param . entry )
2020-02-21 13:20:10 +01:00
, start_time ( get_guest_system_time ( ) )
2022-07-04 15:02:17 +02:00
, is_interrupt_thread ( detached < 0 )
2020-11-26 10:30:51 +01:00
, ppu_tname ( make_single < std : : string > ( name ) )
2016-07-27 23:43:22 +02:00
{
2020-07-03 05:18:14 +02:00
gpr [ 1 ] = stack_addr + stack_size - ppu_stack_start_offset ;
2018-10-11 00:17:19 +02:00
gpr [ 13 ] = param . tls_addr ;
2020-04-29 07:03:07 +02:00
if ( detached > = 0 )
{
// Initialize thread args
gpr [ 3 ] = param . arg0 ;
gpr [ 4 ] = param . arg1 ;
2018-10-11 00:17:19 +02:00
}
2022-07-05 13:12:21 +02:00
optional_savestate_state = std : : make_shared < utils : : serial > ( ) ;
2017-02-06 19:36:46 +01:00
// Trigger the scheduler
2018-05-14 22:07:36 +02:00
state + = cpu_flag : : suspend ;
if ( ! g_use_rtm )
{
state + = cpu_flag : : memory ;
}
2021-07-10 10:56:48 +02:00
if ( g_cfg . core . ppu_call_history )
{
call_history . data . resize ( call_history_max_size ) ;
}
2022-06-14 14:28:38 +02:00
# ifdef __APPLE__
pthread_jit_write_protect_np ( true ) ;
# endif
# ifdef ARCH_ARM64
// Flush all cache lines after potentially writing executable code
asm ( " ISB " ) ;
asm ( " DSB ISH " ) ;
# endif
2016-07-27 23:43:22 +02:00
}
2016-05-13 15:55:34 +02:00
2022-07-04 15:02:17 +02:00
struct disable_precomp_t
{
atomic_t < bool > disable = false ;
} ;
void vdecEntry ( ppu_thread & ppu , u32 vid ) ;
bool ppu_thread : : savable ( ) const
{
if ( joiner = = ppu_join_status : : exited )
{
return false ;
}
if ( cia = = g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( vdecEntry ) ) )
{
// Do not attempt to save the state of HLE VDEC threads
return false ;
}
return true ;
}
void ppu_thread : : serialize_common ( utils : : serial & ar )
{
2022-07-06 16:52:31 +02:00
ar ( gpr , fpr , cr , fpscr . bits , lr , ctr , vrsave , cia , xer , sat , nj , prio , optional_savestate_state , vr ) ;
2022-07-05 13:12:21 +02:00
if ( optional_savestate_state - > data . empty ( ) )
{
optional_savestate_state - > clear ( ) ;
}
2022-07-04 15:02:17 +02:00
}
ppu_thread : : ppu_thread ( utils : : serial & ar )
: cpu_thread ( idm : : last_id ( ) ) // last_id() is showed to constructor on serialization
, stack_size ( ar )
, stack_addr ( ar )
, joiner ( ar . operator ppu_join_status ( ) )
, entry_func ( std : : bit_cast < ppu_func_opd_t , u64 > ( ar ) )
, is_interrupt_thread ( ar )
{
struct init_pushed
{
bool pushed = false ;
atomic_t < bool > inited = false ;
} ;
serialize_common ( ar ) ;
// Restore jm_mask
jm_mask = nj ? 0x7F800000 : 0x7fff'ffff ;
auto queue_intr_entry = [ & ] ( )
{
if ( is_interrupt_thread )
{
void ppu_interrupt_thread_entry ( ppu_thread & , ppu_opcode_t , be_t < u32 > * , struct ppu_intrp_func * ) ;
cmd_list
( {
{ ppu_cmd : : ptr_call , 0 } ,
std : : bit_cast < u64 > ( & ppu_interrupt_thread_entry )
} ) ;
}
} ;
switch ( const u32 status = ar . operator u32 ( ) )
{
case PPU_THREAD_STATUS_IDLE :
{
stop_flag_removal_protection = true ;
break ;
}
case PPU_THREAD_STATUS_RUNNABLE :
case PPU_THREAD_STATUS_ONPROC :
{
lv2_obj : : awake ( this ) ;
[[fallthrough]] ;
}
case PPU_THREAD_STATUS_SLEEP :
{
if ( std : : exchange ( g_fxo - > get < init_pushed > ( ) . pushed , true ) )
{
cmd_list
( {
2022-09-13 15:08:55 +02:00
{ ppu_cmd : : ptr_call , 0 } , + [ ] ( ppu_thread & ) - > bool
2022-07-04 15:02:17 +02:00
{
while ( ! Emu . IsStopped ( ) & & ! g_fxo - > get < init_pushed > ( ) . inited )
{
thread_ctrl : : wait_on ( g_fxo - > get < init_pushed > ( ) . inited , false ) ;
}
return false ;
}
} ) ;
}
else
{
g_fxo - > init < disable_precomp_t > ( ) ;
g_fxo - > get < disable_precomp_t > ( ) . disable = true ;
cmd_push ( { ppu_cmd : : initialize , 0 } ) ;
cmd_list
( {
{ ppu_cmd : : ptr_call , 0 } , + [ ] ( ppu_thread & ) - > bool
{
auto & inited = g_fxo - > get < init_pushed > ( ) . inited ;
inited = true ;
inited . notify_all ( ) ;
return true ;
}
} ) ;
}
if ( status = = PPU_THREAD_STATUS_SLEEP )
{
cmd_list
( {
{ ppu_cmd : : ptr_call , 0 } ,
+ [ ] ( ppu_thread & ppu ) - > bool
{
2022-07-05 13:12:21 +02:00
const u32 op = vm : : read32 ( ppu . cia ) ;
const auto & table = g_fxo - > get < ppu_interpreter_rt > ( ) ;
2022-07-04 15:02:17 +02:00
ppu . loaded_from_savestate = true ;
2022-07-05 13:12:21 +02:00
table . decode ( op ) ( ppu , { op } , vm : : _ptr < u32 > ( ppu . cia ) , & ppu_ret ) ;
ppu . optional_savestate_state - > clear ( ) ; // Reset to writing state
2022-07-04 15:02:17 +02:00
ppu . loaded_from_savestate = false ;
return true ;
}
} ) ;
lv2_obj : : set_future_sleep ( this ) ;
}
queue_intr_entry ( ) ;
cmd_push ( { ppu_cmd : : cia_call , 0 } ) ;
break ;
}
case PPU_THREAD_STATUS_ZOMBIE :
{
state + = cpu_flag : : exit ;
break ;
}
case PPU_THREAD_STATUS_STOP :
{
queue_intr_entry ( ) ;
break ;
}
}
// Trigger the scheduler
state + = cpu_flag : : suspend ;
if ( ! g_use_rtm )
{
state + = cpu_flag : : memory ;
}
ppu_tname = make_single < std : : string > ( ar . operator std : : string ( ) ) ;
}
void ppu_thread : : save ( utils : : serial & ar )
{
2022-07-05 13:12:21 +02:00
USING_SERIALIZATION_VERSION ( ppu ) ;
2022-07-04 15:02:17 +02:00
const u64 entry = std : : bit_cast < u64 > ( entry_func ) ;
ppu_join_status _joiner = joiner ;
if ( _joiner > = ppu_join_status : : max )
{
// Joining thread should recover this member properly
2022-09-13 15:08:55 +02:00
_joiner = ppu_join_status : : joinable ;
2022-07-04 15:02:17 +02:00
}
if ( state & cpu_flag : : again )
{
std : : memcpy ( & gpr [ 3 ] , syscall_args , sizeof ( syscall_args ) ) ;
cia - = 4 ;
}
ar ( stack_size , stack_addr , _joiner , entry , is_interrupt_thread ) ;
serialize_common ( ar ) ;
ppu_thread_status status = lv2_obj : : ppu_state ( this , false ) ;
if ( status = = PPU_THREAD_STATUS_SLEEP & & cpu_flag : : again - state )
{
// Hack for sys_fs
status = PPU_THREAD_STATUS_RUNNABLE ;
}
ar ( status ) ;
ar ( * ppu_tname . load ( ) ) ;
}
2021-05-01 08:34:52 +02:00
ppu_thread : : thread_name_t : : operator std : : string ( ) const
{
std : : string thread_name = fmt : : format ( " PPU[0x%x] " , _this - > id ) ;
if ( const std : : string name = * _this - > ppu_tname . load ( ) ; ! name . empty ( ) )
{
fmt : : append ( thread_name , " %s " , name ) ;
}
return thread_name ;
}
2016-08-09 16:14:41 +02:00
void ppu_thread : : cmd_push ( cmd64 cmd )
2016-07-27 23:43:22 +02:00
{
// Reserve queue space
const u32 pos = cmd_queue . push_begin ( ) ;
2016-05-13 15:55:34 +02:00
2016-07-27 23:43:22 +02:00
// Write single command
cmd_queue [ pos ] = cmd ;
}
2016-05-13 15:55:34 +02:00
2016-08-09 16:14:41 +02:00
void ppu_thread : : cmd_list ( std : : initializer_list < cmd64 > list )
2016-07-27 23:43:22 +02:00
{
// Reserve queue space
const u32 pos = cmd_queue . push_begin ( static_cast < u32 > ( list . size ( ) ) ) ;
2016-05-13 15:55:34 +02:00
2016-07-27 23:43:22 +02:00
// Write command tail in relaxed manner
for ( u32 i = 1 ; i < list . size ( ) ; i + + )
2016-05-13 15:55:34 +02:00
{
2016-07-27 23:43:22 +02:00
cmd_queue [ pos + i ] . raw ( ) = list . begin ( ) [ i ] ;
2016-05-13 15:55:34 +02:00
}
2016-07-27 23:43:22 +02:00
// Write command head after all
cmd_queue [ pos ] = * list . begin ( ) ;
2012-11-15 00:39:56 +01:00
}
2016-07-27 23:43:22 +02:00
void ppu_thread : : cmd_pop ( u32 count )
2012-11-15 00:39:56 +01:00
{
2016-07-27 23:43:22 +02:00
// Get current position
const u32 pos = cmd_queue . peek ( ) ;
// Clean command buffer for command tail
for ( u32 i = 1 ; i < = count ; i + + )
2016-04-14 01:09:41 +02:00
{
2016-08-09 16:14:41 +02:00
cmd_queue [ pos + i ] . raw ( ) = cmd64 { } ;
2016-04-14 01:09:41 +02:00
}
2016-07-27 23:43:22 +02:00
// Free
cmd_queue . pop_end ( count + 1 ) ;
2014-04-10 00:54:32 +02:00
}
2014-08-15 14:50:59 +02:00
2016-08-09 16:14:41 +02:00
cmd64 ppu_thread : : cmd_wait ( )
2016-04-25 12:49:12 +02:00
{
2016-07-27 23:43:22 +02:00
while ( true )
{
2021-02-13 15:50:07 +01:00
if ( cmd64 result = cmd_queue [ cmd_queue . peek ( ) ] . exchange ( cmd64 { } ) )
2016-07-27 23:43:22 +02:00
{
2021-02-13 15:50:07 +01:00
return result ;
2016-07-27 23:43:22 +02:00
}
2021-02-13 15:50:07 +01:00
if ( is_stopped ( ) )
2016-07-27 23:43:22 +02:00
{
2021-02-13 15:50:07 +01:00
return { } ;
2016-07-27 23:43:22 +02:00
}
2021-02-13 15:50:07 +01:00
thread_ctrl : : wait_on ( cmd_notify , 0 ) ;
cmd_notify = 0 ;
2016-07-27 23:43:22 +02:00
}
2016-04-25 12:49:12 +02:00
}
2016-07-27 23:43:22 +02:00
be_t < u64 > * ppu_thread : : get_stack_arg ( s32 i , u64 align )
2014-08-23 16:51:51 +02:00
{
2020-12-09 16:04:52 +01:00
if ( align ! = 1 & & align ! = 2 & & align ! = 4 & & align ! = 8 & & align ! = 16 ) fmt : : throw_exception ( " Unsupported alignment: 0x%llx " , align ) ;
return vm : : _ptr < u64 > ( vm : : cast ( ( gpr [ 1 ] + 0x30 + 0x8 * ( i - 1 ) ) & ( 0 - align ) ) ) ;
2014-08-23 16:51:51 +02:00
}
2022-07-04 15:02:17 +02:00
void ppu_thread : : fast_call ( u32 addr , u64 rtoc )
2014-08-19 20:17:20 +02:00
{
2017-02-09 23:51:29 +01:00
const auto old_cia = cia ;
2016-07-27 23:43:22 +02:00
const auto old_rtoc = gpr [ 2 ] ;
const auto old_lr = lr ;
2019-07-09 19:44:07 +02:00
const auto old_func = current_function ;
2016-07-27 23:43:22 +02:00
const auto old_fmt = g_tls_log_prefix ;
2015-07-19 13:36:32 +02:00
2022-07-04 15:02:17 +02:00
interrupt_thread_executing = true ;
2016-07-27 23:43:22 +02:00
cia = addr ;
gpr [ 2 ] = rtoc ;
2021-05-17 13:22:27 +02:00
lr = g_fxo - > get < ppu_function_manager > ( ) . func_addr ( 1 ) + 4 ; // HLE stop address
2019-07-09 19:44:07 +02:00
current_function = nullptr ;
2014-08-19 20:17:20 +02:00
2022-07-04 15:02:17 +02:00
if ( std : : exchange ( loaded_from_savestate , false ) )
{
lr = old_lr ;
}
2016-07-27 23:43:22 +02:00
g_tls_log_prefix = [ ]
{
2017-02-09 23:51:29 +01:00
const auto _this = static_cast < ppu_thread * > ( get_current_cpu_thread ( ) ) ;
2020-02-28 08:43:37 +01:00
2020-11-26 10:30:51 +01:00
static thread_local shared_ptr < std : : string > name_cache ;
2020-02-28 08:43:37 +01:00
if ( ! _this - > ppu_tname . is_equal ( name_cache ) ) [[unlikely]]
{
2020-12-06 09:13:34 +01:00
_this - > ppu_tname . peek_op ( [ & ] ( const shared_ptr < std : : string > & ptr )
{
if ( ptr ! = name_cache )
{
name_cache = ptr ;
}
} ) ;
2020-02-28 08:43:37 +01:00
}
2020-08-27 17:40:13 +02:00
const auto cia = _this - > cia ;
if ( _this - > current_function & & vm : : read32 ( cia ) ! = ppu_instructions : : SC ( 0 ) )
{
2020-10-08 15:13:55 +02:00
return fmt : : format ( " PPU[0x%x] Thread (%s) [HLE:0x%08x, LR:0x%08x] " , _this - > id , * name_cache . get ( ) , cia , _this - > lr ) ;
2020-08-27 17:40:13 +02:00
}
2022-10-02 11:59:41 +02:00
extern const char * get_prx_name_by_cia ( u32 addr ) ;
if ( auto name = get_prx_name_by_cia ( cia ) )
{
return fmt : : format ( " PPU[0x%x] Thread (%s) [%s: 0x%08x] " , _this - > id , * name_cache . get ( ) , name , cia ) ;
}
2020-08-27 17:40:13 +02:00
return fmt : : format ( " PPU[0x%x] Thread (%s) [0x%08x] " , _this - > id , * name_cache . get ( ) , cia ) ;
2016-07-27 23:43:22 +02:00
} ;
2019-11-09 17:11:01 +01:00
auto at_ret = [ & ] ( )
2015-07-01 00:25:52 +02:00
{
2018-09-04 19:59:38 +02:00
if ( std : : uncaught_exceptions ( ) )
2017-02-09 23:51:29 +01:00
{
2021-02-13 15:50:07 +01:00
cpu_on_stop ( ) ;
2019-07-09 19:44:07 +02:00
current_function = old_func ;
2017-02-09 23:51:29 +01:00
}
2022-07-04 15:02:17 +02:00
else if ( old_cia )
2016-06-25 07:16:15 +02:00
{
2022-07-05 13:12:21 +02:00
if ( state & cpu_flag : : again )
2022-07-04 15:02:17 +02:00
{
ppu_log . error ( " HLE callstack savestate is not implemented! " ) ;
}
2017-02-09 23:51:29 +01:00
cia = old_cia ;
gpr [ 2 ] = old_rtoc ;
lr = old_lr ;
2016-06-25 07:16:15 +02:00
}
2022-07-04 15:02:17 +02:00
current_function = old_func ;
g_tls_log_prefix = old_fmt ;
state - = cpu_flag : : ret ;
2019-11-09 17:11:01 +01:00
} ;
2020-03-09 17:18:39 +01:00
exec_task ( ) ;
2019-11-09 17:11:01 +01:00
at_ret ( ) ;
2014-08-19 20:17:20 +02:00
}
2016-06-07 22:24:20 +02:00
2021-06-26 13:15:10 +02:00
std : : pair < vm : : addr_t , u32 > ppu_thread : : stack_push ( u32 size , u32 align_v )
2016-08-09 16:14:41 +02:00
{
2021-05-20 06:00:22 +02:00
if ( auto cpu = get_current_cpu_thread < ppu_thread > ( ) )
2016-08-09 16:14:41 +02:00
{
ppu_thread & context = static_cast < ppu_thread & > ( * cpu ) ;
2020-12-09 16:04:52 +01:00
const u32 old_pos = vm : : cast ( context . gpr [ 1 ] ) ;
2021-06-26 13:15:10 +02:00
context . gpr [ 1 ] - = size ; // room minimal possible size
2019-12-02 22:31:34 +01:00
context . gpr [ 1 ] & = ~ ( u64 { align_v } - 1 ) ; // fix stack alignment
2016-08-09 16:14:41 +02:00
2021-06-26 13:15:10 +02:00
auto is_stack = [ & ] ( u64 addr )
{
return addr > = context . stack_addr & & addr < context . stack_addr + context . stack_size ;
} ;
// TODO: This check does not care about custom stack memory
if ( is_stack ( old_pos ) ! = is_stack ( context . gpr [ 1 ] ) )
2016-08-09 16:14:41 +02:00
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Stack overflow (size=0x%x, align=0x%x, SP=0x%llx, stack=*0x%x) " , size , align_v , old_pos , context . stack_addr ) ;
2016-08-09 16:14:41 +02:00
}
else
{
const u32 addr = static_cast < u32 > ( context . gpr [ 1 ] ) ;
std : : memset ( vm : : base ( addr ) , 0 , size ) ;
2021-06-26 13:15:10 +02:00
return { vm : : cast ( addr ) , old_pos - addr } ;
2016-08-09 16:14:41 +02:00
}
}
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " Invalid thread " ) ;
2016-08-09 16:14:41 +02:00
}
void ppu_thread : : stack_pop_verbose ( u32 addr , u32 size ) noexcept
{
2021-05-20 06:00:22 +02:00
if ( auto cpu = get_current_cpu_thread < ppu_thread > ( ) )
2016-08-09 16:14:41 +02:00
{
ppu_thread & context = static_cast < ppu_thread & > ( * cpu ) ;
if ( context . gpr [ 1 ] ! = addr )
{
2020-02-01 09:31:27 +01:00
ppu_log . error ( " Stack inconsistency (addr=0x%x, SP=0x%llx, size=0x%x) " , addr , context . gpr [ 1 ] , size ) ;
2016-08-09 16:14:41 +02:00
return ;
}
2021-06-26 13:15:10 +02:00
context . gpr [ 1 ] + = size ;
2016-08-09 16:14:41 +02:00
return ;
}
2020-12-09 16:04:52 +01:00
ppu_log . error ( " Invalid thread " ) ;
2016-08-09 16:14:41 +02:00
}
2021-12-30 17:39:18 +01:00
extern ppu_intrp_func_t ppu_get_syscall ( u64 code ) ;
2016-06-07 22:24:20 +02:00
2020-05-15 17:57:48 +02:00
void ppu_trap ( ppu_thread & ppu , u64 addr )
2016-06-07 22:24:20 +02:00
{
2021-05-22 09:35:15 +02:00
ensure ( ( addr & ( ~ u64 { 0xffff'ffff } | 0x3 ) ) = = 0 ) ;
2020-05-15 17:57:48 +02:00
ppu . cia = static_cast < u32 > ( addr ) ;
u32 add = static_cast < u32 > ( g_cfg . core . stub_ppu_traps ) * 4 ;
// If stubbing is enabled, check current instruction and the following
2020-11-10 18:09:28 +01:00
if ( ! add | | ! vm : : check_addr ( ppu . cia , vm : : page_executable ) | | ! vm : : check_addr ( ppu . cia + add , vm : : page_executable ) )
2020-05-15 17:57:48 +02:00
{
2021-09-30 20:33:55 +02:00
fmt : : throw_exception ( " PPU Trap! Sometimes tweaking the setting \" Stub PPU Traps \" can be a workaround to this crash. \n Best values depend on game code, if unsure try 1. " ) ;
2020-05-15 17:57:48 +02:00
}
ppu_log . error ( " PPU Trap: Stubbing %d instructions %s. " , std : : abs ( static_cast < s32 > ( add ) / 4 ) , add > > 31 ? " backwards " : " forwards " ) ;
ppu . cia + = add ; // Skip instructions, hope for valid code (interprter may be invoked temporarily)
2016-06-22 15:37:51 +02:00
}
2016-06-07 22:24:20 +02:00
2021-09-22 22:42:40 +02:00
static void ppu_error ( ppu_thread & ppu , u64 addr , u32 /*op*/ )
2016-07-07 20:42:39 +02:00
{
2017-06-22 23:52:09 +02:00
ppu . cia = : : narrow < u32 > ( addr ) ;
2021-09-01 12:38:17 +02:00
ppu_recompiler_fallback ( ppu ) ;
2016-07-07 20:42:39 +02:00
}
2017-02-13 18:51:37 +01:00
static void ppu_check ( ppu_thread & ppu , u64 addr )
{
2017-06-22 23:52:09 +02:00
ppu . cia = : : narrow < u32 > ( addr ) ;
2018-10-11 00:17:19 +02:00
if ( ppu . test_stopped ( ) )
{
return ;
}
2017-02-13 18:51:37 +01:00
}
2016-06-22 15:37:51 +02:00
static void ppu_trace ( u64 addr )
2016-06-07 22:24:20 +02:00
{
2020-02-01 09:31:27 +01:00
ppu_log . notice ( " Trace: 0x%llx " , addr ) ;
2016-06-22 15:37:51 +02:00
}
2016-06-07 22:24:20 +02:00
2018-04-28 19:09:35 +02:00
template < typename T >
static T ppu_load_acquire_reservation ( ppu_thread & ppu , u32 addr )
2016-06-22 15:37:51 +02:00
{
2020-10-29 19:46:50 +01:00
perf_meter < " LARX " _u32 > perf0 ;
2020-09-14 07:08:26 +02:00
// Do not allow stores accessed from the same cache line to past reservation load
2020-12-06 10:10:00 +01:00
atomic_fence_seq_cst ( ) ;
2020-09-14 07:08:26 +02:00
2020-09-10 05:27:55 +02:00
if ( addr % sizeof ( T ) )
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " PPU %s: Unaligned address: 0x%08x " , sizeof ( T ) = = 4 ? " LWARX " : " LDARX " , addr ) ;
2020-09-10 05:27:55 +02:00
}
// Always load aligned 64-bit value
2018-09-14 12:53:50 +02:00
auto & data = vm : : _ref < const atomic_be_t < u64 > > ( addr & - 8 ) ;
const u64 size_off = ( sizeof ( T ) * 8 ) & 63 ;
const u64 data_off = ( addr & 7 ) * 8 ;
2018-04-28 19:09:35 +02:00
2017-02-17 20:35:57 +01:00
ppu . raddr = addr ;
2018-04-28 19:09:35 +02:00
2020-10-29 23:22:28 +01:00
u32 addr_mask = - 1 ;
2020-04-07 19:29:11 +02:00
if ( const s32 max = g_cfg . core . ppu_128_reservations_loop_max_length )
{
// If we use it in HLE it means we want the accurate version
ppu . use_full_rdata = max < 0 | | ppu . current_function | | [ & ] ( )
{
const u32 cia = ppu . cia ;
if ( ( cia & 0xffff ) > = 0x10000u - max * 4 )
{
// Do not cross 64k boundary
2020-10-29 23:22:28 +01:00
return false ;
2020-04-07 19:29:11 +02:00
}
const auto inst = vm : : _ptr < const nse_t < u32 > > ( cia ) ;
// Search for STWCX or STDCX nearby (LDARX-STWCX and LWARX-STDCX loops will use accurate 128-byte reservations)
2020-12-13 14:34:45 +01:00
constexpr u32 store_cond = stx : : se_storage < u32 > : : swap ( sizeof ( T ) = = 8 ? 0x7C00012D : 0x7C0001AD ) ;
constexpr u32 mask = stx : : se_storage < u32 > : : swap ( 0xFC0007FF ) ;
2020-04-07 19:29:11 +02:00
const auto store_vec = v128 : : from32p ( store_cond ) ;
const auto mask_vec = v128 : : from32p ( mask ) ;
s32 i = 2 ;
for ( const s32 _max = max - 3 ; i < _max ; i + = 4 )
{
const auto _inst = v128 : : loadu ( inst + i ) & mask_vec ;
2021-12-30 17:39:18 +01:00
if ( ! gv_testz ( gv_eq32 ( _inst , store_vec ) ) )
2020-04-07 19:29:11 +02:00
{
return false ;
}
}
for ( ; i < max ; i + + )
{
const u32 val = inst [ i ] & mask ;
if ( val = = store_cond )
{
return false ;
}
}
return true ;
} ( ) ;
2020-10-29 23:22:28 +01:00
if ( ppu . use_full_rdata )
{
addr_mask = - 128 ;
}
2020-04-07 19:29:11 +02:00
}
else
{
ppu . use_full_rdata = false ;
}
2020-10-29 23:22:28 +01:00
if ( ( addr & addr_mask ) = = ( ppu . last_faddr & addr_mask ) )
{
ppu_log . trace ( u8 " LARX after fail: addr=0x%x, faddr=0x%x, time=%u c " , addr , ppu . last_faddr , ( perf0 . get ( ) - ppu . last_ftsc ) ) ;
}
2021-03-05 20:05:37 +01:00
if ( ( addr & addr_mask ) = = ( ppu . last_faddr & addr_mask ) & & ( perf0 . get ( ) - ppu . last_ftsc ) < 600 & & ( vm : : reservation_acquire ( addr ) & - 128 ) = = ppu . last_ftime )
2020-10-29 23:22:28 +01:00
{
be_t < u64 > rdata ;
std : : memcpy ( & rdata , & ppu . rdata [ addr & 0x78 ] , 8 ) ;
if ( rdata = = data . load ( ) )
{
ppu . rtime = ppu . last_ftime ;
ppu . raddr = ppu . last_faddr ;
2020-10-30 23:52:24 +01:00
ppu . last_ftime = 0 ;
2020-10-29 23:22:28 +01:00
return static_cast < T > ( rdata < < data_off > > size_off ) ;
}
ppu . last_fail + + ;
ppu . last_faddr = 0 ;
}
else
{
// Silent failure
ppu . last_faddr = 0 ;
}
2021-03-05 20:05:37 +01:00
ppu . rtime = vm : : reservation_acquire ( addr ) & - 128 ;
2020-10-30 03:17:00 +01:00
2020-10-30 07:40:58 +01:00
be_t < u64 > rdata ;
2020-10-17 13:55:31 +02:00
2020-10-30 07:40:58 +01:00
if ( ! ppu . use_full_rdata )
2018-05-18 17:51:48 +02:00
{
2020-10-30 07:40:58 +01:00
rdata = data . load ( ) ;
2020-09-02 23:58:29 +02:00
2020-10-30 07:40:58 +01:00
// Store only 64 bits of reservation data
std : : memcpy ( & ppu . rdata [ addr & 0x78 ] , & rdata , 8 ) ;
}
else
{
mov_rdata ( ppu . rdata , vm : : _ref < spu_rdata_t > ( addr & - 128 ) ) ;
2020-12-06 10:10:00 +01:00
atomic_fence_acquire ( ) ;
2020-10-13 19:23:10 +02:00
2020-10-30 07:40:58 +01:00
// Load relevant 64 bits of reservation data
std : : memcpy ( & rdata , & ppu . rdata [ addr & 0x78 ] , 8 ) ;
2018-05-18 17:51:48 +02:00
}
2020-10-17 13:55:31 +02:00
return static_cast < T > ( rdata < < data_off > > size_off ) ;
2018-04-28 19:09:35 +02:00
}
extern u32 ppu_lwarx ( ppu_thread & ppu , u32 addr )
{
return ppu_load_acquire_reservation < u32 > ( ppu , addr ) ;
2016-06-22 15:37:51 +02:00
}
2016-06-07 22:24:20 +02:00
2017-02-26 16:56:31 +01:00
extern u64 ppu_ldarx ( ppu_thread & ppu , u32 addr )
2016-06-22 15:37:51 +02:00
{
2018-04-28 19:09:35 +02:00
return ppu_load_acquire_reservation < u64 > ( ppu , addr ) ;
2016-06-22 15:37:51 +02:00
}
2016-06-07 22:24:20 +02:00
2022-01-23 13:20:07 +01:00
const auto ppu_stcx_accurate_tx = build_function_asm < u64 ( * ) ( u32 raddr , u64 rtime , const void * _old , u64 _new ) > ( " ppu_stcx_accurate_tx " , [ ] ( native_asm & c , auto & args )
2020-04-07 19:29:11 +02:00
{
using namespace asmjit ;
2021-12-30 17:39:18 +01:00
# if defined(ARCH_X64)
2020-04-07 19:29:11 +02:00
Label fall = c . newLabel ( ) ;
Label fail = c . newLabel ( ) ;
Label _ret = c . newLabel ( ) ;
2020-10-29 23:22:28 +01:00
Label load = c . newLabel ( ) ;
2020-04-07 19:29:11 +02:00
//if (utils::has_avx() && !s_tsx_avx)
//{
// c.vzeroupper();
//}
// Create stack frame if necessary (Windows ABI has only 6 volatile vector registers)
c . push ( x86 : : rbp ) ;
2020-10-30 23:52:24 +01:00
c . push ( x86 : : r14 ) ;
2020-04-07 19:29:11 +02:00
c . sub ( x86 : : rsp , 40 ) ;
# ifdef _WIN32
if ( ! s_tsx_avx )
{
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 0 ) , x86 : : xmm6 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 16 ) , x86 : : xmm7 ) ;
}
# endif
// Prepare registers
2021-12-24 18:33:32 +01:00
build_swap_rdx_with ( c , args , x86 : : r10 ) ;
2020-11-01 12:28:56 +01:00
c . mov ( x86 : : rbp , x86 : : qword_ptr ( reinterpret_cast < u64 > ( & vm : : g_sudo_addr ) ) ) ;
2020-04-07 19:29:11 +02:00
c . lea ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rbp , args [ 0 ] ) ) ;
c . and_ ( x86 : : rbp , - 128 ) ;
2020-10-19 14:20:53 +02:00
c . prefetchw ( x86 : : byte_ptr ( x86 : : rbp , 0 ) ) ;
c . prefetchw ( x86 : : byte_ptr ( x86 : : rbp , 64 ) ) ;
2020-04-07 19:29:11 +02:00
c . movzx ( args [ 0 ] . r32 ( ) , args [ 0 ] . r16 ( ) ) ;
c . shr ( args [ 0 ] . r32 ( ) , 1 ) ;
2021-12-24 18:33:32 +01:00
c . lea ( x86 : : r11 , x86 : : qword_ptr ( reinterpret_cast < u64 > ( + vm : : g_reservations ) , args [ 0 ] ) ) ;
c . and_ ( x86 : : r11 , - 128 / 2 ) ;
2020-10-15 18:24:00 +02:00
c . and_ ( args [ 0 ] . r32 ( ) , 63 ) ;
2020-04-07 19:29:11 +02:00
// Prepare data
if ( s_tsx_avx )
{
2021-12-28 20:25:36 +01:00
c . vmovups ( x86 : : ymm0 , x86 : : ymmword_ptr ( args [ 2 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm1 , x86 : : ymmword_ptr ( args [ 2 ] , 32 ) ) ;
c . vmovups ( x86 : : ymm2 , x86 : : ymmword_ptr ( args [ 2 ] , 64 ) ) ;
c . vmovups ( x86 : : ymm3 , x86 : : ymmword_ptr ( args [ 2 ] , 96 ) ) ;
2020-04-07 19:29:11 +02:00
}
else
{
c . movaps ( x86 : : xmm0 , x86 : : oword_ptr ( args [ 2 ] , 0 ) ) ;
c . movaps ( x86 : : xmm1 , x86 : : oword_ptr ( args [ 2 ] , 16 ) ) ;
c . movaps ( x86 : : xmm2 , x86 : : oword_ptr ( args [ 2 ] , 32 ) ) ;
c . movaps ( x86 : : xmm3 , x86 : : oword_ptr ( args [ 2 ] , 48 ) ) ;
c . movaps ( x86 : : xmm4 , x86 : : oword_ptr ( args [ 2 ] , 64 ) ) ;
c . movaps ( x86 : : xmm5 , x86 : : oword_ptr ( args [ 2 ] , 80 ) ) ;
c . movaps ( x86 : : xmm6 , x86 : : oword_ptr ( args [ 2 ] , 96 ) ) ;
c . movaps ( x86 : : xmm7 , x86 : : oword_ptr ( args [ 2 ] , 112 ) ) ;
}
2020-10-30 23:52:24 +01:00
// Alloc r14 to stamp0
const auto stamp0 = x86 : : r14 ;
build_get_tsc ( c , stamp0 ) ;
2020-04-07 19:29:11 +02:00
Label fail2 = c . newLabel ( ) ;
2021-12-18 16:12:37 +01:00
Label tx1 = build_transaction_enter ( c , fall , [ & ] ( )
2020-10-30 23:52:24 +01:00
{
build_get_tsc ( c ) ;
2021-12-18 16:12:37 +01:00
c . sub ( x86 : : rax , stamp0 ) ;
2020-11-01 12:45:16 +01:00
c . cmp ( x86 : : rax , x86 : : qword_ptr ( reinterpret_cast < u64 > ( & g_rtm_tx_limit2 ) ) ) ;
2021-12-18 16:12:37 +01:00
c . jae ( fall ) ;
2020-10-30 23:52:24 +01:00
} ) ;
2020-10-15 18:24:00 +02:00
// Check pause flag
2020-10-09 19:33:12 +02:00
c . bt ( x86 : : dword_ptr ( args [ 2 ] , : : offset32 ( & ppu_thread : : state ) - : : offset32 ( & ppu_thread : : rdata ) ) , static_cast < u32 > ( cpu_flag : : pause ) ) ;
2021-12-18 16:12:37 +01:00
c . jc ( fall ) ;
2020-10-09 19:33:12 +02:00
c . xbegin ( tx1 ) ;
2020-04-07 19:29:11 +02:00
if ( s_tsx_avx )
{
2021-12-28 20:25:36 +01:00
c . vxorps ( x86 : : ymm0 , x86 : : ymm0 , x86 : : ymmword_ptr ( x86 : : rbp , 0 ) ) ;
c . vxorps ( x86 : : ymm1 , x86 : : ymm1 , x86 : : ymmword_ptr ( x86 : : rbp , 32 ) ) ;
c . vxorps ( x86 : : ymm2 , x86 : : ymm2 , x86 : : ymmword_ptr ( x86 : : rbp , 64 ) ) ;
c . vxorps ( x86 : : ymm3 , x86 : : ymm3 , x86 : : ymmword_ptr ( x86 : : rbp , 96 ) ) ;
2020-04-07 19:29:11 +02:00
c . vorps ( x86 : : ymm0 , x86 : : ymm0 , x86 : : ymm1 ) ;
c . vorps ( x86 : : ymm1 , x86 : : ymm2 , x86 : : ymm3 ) ;
c . vorps ( x86 : : ymm0 , x86 : : ymm1 , x86 : : ymm0 ) ;
c . vptest ( x86 : : ymm0 , x86 : : ymm0 ) ;
}
else
{
c . xorps ( x86 : : xmm0 , x86 : : oword_ptr ( x86 : : rbp , 0 ) ) ;
c . xorps ( x86 : : xmm1 , x86 : : oword_ptr ( x86 : : rbp , 16 ) ) ;
c . xorps ( x86 : : xmm2 , x86 : : oword_ptr ( x86 : : rbp , 32 ) ) ;
c . xorps ( x86 : : xmm3 , x86 : : oword_ptr ( x86 : : rbp , 48 ) ) ;
c . xorps ( x86 : : xmm4 , x86 : : oword_ptr ( x86 : : rbp , 64 ) ) ;
c . xorps ( x86 : : xmm5 , x86 : : oword_ptr ( x86 : : rbp , 80 ) ) ;
c . xorps ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rbp , 96 ) ) ;
c . xorps ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rbp , 112 ) ) ;
c . orps ( x86 : : xmm0 , x86 : : xmm1 ) ;
c . orps ( x86 : : xmm2 , x86 : : xmm3 ) ;
c . orps ( x86 : : xmm4 , x86 : : xmm5 ) ;
c . orps ( x86 : : xmm6 , x86 : : xmm7 ) ;
c . orps ( x86 : : xmm0 , x86 : : xmm2 ) ;
c . orps ( x86 : : xmm4 , x86 : : xmm6 ) ;
c . orps ( x86 : : xmm0 , x86 : : xmm4 ) ;
c . ptest ( x86 : : xmm0 , x86 : : xmm0 ) ;
}
2021-12-18 16:12:37 +01:00
c . jnz ( fail ) ;
2020-04-07 19:29:11 +02:00
2020-10-15 18:24:00 +02:00
// Store 8 bytes
c . mov ( x86 : : qword_ptr ( x86 : : rbp , args [ 0 ] , 1 , 0 ) , args [ 3 ] ) ;
2020-04-07 19:29:11 +02:00
c . xend ( ) ;
2021-12-24 18:33:32 +01:00
c . lock ( ) . add ( x86 : : qword_ptr ( x86 : : r11 ) , 64 ) ;
2020-10-30 23:52:24 +01:00
build_get_tsc ( c ) ;
c . sub ( x86 : : rax , stamp0 ) ;
2020-04-07 19:29:11 +02:00
c . jmp ( _ret ) ;
2020-10-20 07:22:25 +02:00
// XABORT is expensive so try to finish with xend instead
2021-12-18 16:12:37 +01:00
c . bind ( fail ) ;
2020-04-07 19:29:11 +02:00
2020-10-29 23:22:28 +01:00
// Load old data to store back in rdata
2020-10-20 07:22:25 +02:00
if ( s_tsx_avx )
{
2021-12-28 20:25:36 +01:00
c . vmovaps ( x86 : : ymm0 , x86 : : ymmword_ptr ( x86 : : rbp , 0 ) ) ;
c . vmovaps ( x86 : : ymm1 , x86 : : ymmword_ptr ( x86 : : rbp , 32 ) ) ;
c . vmovaps ( x86 : : ymm2 , x86 : : ymmword_ptr ( x86 : : rbp , 64 ) ) ;
c . vmovaps ( x86 : : ymm3 , x86 : : ymmword_ptr ( x86 : : rbp , 96 ) ) ;
2020-10-20 07:22:25 +02:00
}
else
{
c . movaps ( x86 : : xmm0 , x86 : : oword_ptr ( x86 : : rbp , 0 ) ) ;
c . movaps ( x86 : : xmm1 , x86 : : oword_ptr ( x86 : : rbp , 16 ) ) ;
c . movaps ( x86 : : xmm2 , x86 : : oword_ptr ( x86 : : rbp , 32 ) ) ;
c . movaps ( x86 : : xmm3 , x86 : : oword_ptr ( x86 : : rbp , 48 ) ) ;
c . movaps ( x86 : : xmm4 , x86 : : oword_ptr ( x86 : : rbp , 64 ) ) ;
c . movaps ( x86 : : xmm5 , x86 : : oword_ptr ( x86 : : rbp , 80 ) ) ;
c . movaps ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rbp , 96 ) ) ;
c . movaps ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rbp , 112 ) ) ;
}
c . xend ( ) ;
c . jmp ( fail2 ) ;
2021-12-18 16:12:37 +01:00
c . bind ( fall ) ;
2020-10-30 23:52:24 +01:00
c . mov ( x86 : : rax , - 1 ) ;
2020-04-07 19:29:11 +02:00
c . jmp ( _ret ) ;
c . bind ( fail2 ) ;
2021-12-24 18:33:32 +01:00
c . lock ( ) . sub ( x86 : : qword_ptr ( x86 : : r11 ) , 64 ) ;
2020-10-29 23:22:28 +01:00
c . bind ( load ) ;
// Store previous data back to rdata
if ( s_tsx_avx )
{
2021-12-28 20:25:36 +01:00
c . vmovaps ( x86 : : ymmword_ptr ( args [ 2 ] , 0 ) , x86 : : ymm0 ) ;
c . vmovaps ( x86 : : ymmword_ptr ( args [ 2 ] , 32 ) , x86 : : ymm1 ) ;
c . vmovaps ( x86 : : ymmword_ptr ( args [ 2 ] , 64 ) , x86 : : ymm2 ) ;
c . vmovaps ( x86 : : ymmword_ptr ( args [ 2 ] , 96 ) , x86 : : ymm3 ) ;
2020-10-29 23:22:28 +01:00
}
else
{
c . movaps ( x86 : : oword_ptr ( args [ 2 ] , 0 ) , x86 : : xmm0 ) ;
c . movaps ( x86 : : oword_ptr ( args [ 2 ] , 16 ) , x86 : : xmm1 ) ;
c . movaps ( x86 : : oword_ptr ( args [ 2 ] , 32 ) , x86 : : xmm2 ) ;
c . movaps ( x86 : : oword_ptr ( args [ 2 ] , 48 ) , x86 : : xmm3 ) ;
c . movaps ( x86 : : oword_ptr ( args [ 2 ] , 64 ) , x86 : : xmm4 ) ;
c . movaps ( x86 : : oword_ptr ( args [ 2 ] , 80 ) , x86 : : xmm5 ) ;
c . movaps ( x86 : : oword_ptr ( args [ 2 ] , 96 ) , x86 : : xmm6 ) ;
c . movaps ( x86 : : oword_ptr ( args [ 2 ] , 112 ) , x86 : : xmm7 ) ;
}
2020-10-30 23:52:24 +01:00
c . mov ( x86 : : rax , - 1 ) ;
2021-01-31 18:00:04 +01:00
c . mov ( x86 : : qword_ptr ( args [ 2 ] , : : offset32 ( & ppu_thread : : last_ftime ) - : : offset32 ( & ppu_thread : : rdata ) ) , x86 : : rax ) ;
2020-04-07 19:29:11 +02:00
c . xor_ ( x86 : : eax , x86 : : eax ) ;
//c.jmp(_ret);
c . bind ( _ret ) ;
# ifdef _WIN32
2020-10-15 18:24:00 +02:00
if ( ! s_tsx_avx )
2020-04-07 19:29:11 +02:00
{
c . vmovups ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rsp , 0 ) ) ;
c . vmovups ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rsp , 16 ) ) ;
}
# endif
if ( s_tsx_avx )
{
c . vzeroupper ( ) ;
}
c . add ( x86 : : rsp , 40 ) ;
2020-10-30 23:52:24 +01:00
c . pop ( x86 : : r14 ) ;
2020-04-07 19:29:11 +02:00
c . pop ( x86 : : rbp ) ;
2021-12-24 18:33:32 +01:00
2022-08-24 18:36:37 +02:00
maybe_flush_lbr ( c ) ;
2020-04-07 19:29:11 +02:00
c . ret ( ) ;
2021-12-30 17:39:18 +01:00
# else
2022-06-14 14:28:38 +02:00
// Unimplemented should fail.
c . brk ( Imm ( 0x42 ) ) ;
2021-12-30 17:39:18 +01:00
c . ret ( a64 : : x30 ) ;
# endif
2020-04-07 19:29:11 +02:00
} ) ;
2020-04-13 12:29:01 +02:00
template < typename T >
2020-05-01 22:52:10 +02:00
static bool ppu_store_reservation ( ppu_thread & ppu , u32 addr , u64 reg_value )
2016-06-22 15:37:51 +02:00
{
2020-10-18 14:00:10 +02:00
perf_meter < " STCX " _u32 > perf0 ;
2020-09-10 05:27:55 +02:00
if ( addr % sizeof ( T ) )
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " PPU %s: Unaligned address: 0x%08x " , sizeof ( T ) = = 4 ? " STWCX " : " STDCX " , addr ) ;
2020-09-10 05:27:55 +02:00
}
2020-05-01 22:52:10 +02:00
auto & data = vm : : _ref < atomic_be_t < u64 > > ( addr & - 8 ) ;
2021-03-05 20:05:37 +01:00
auto & res = vm : : reservation_acquire ( addr ) ;
2020-04-07 19:29:11 +02:00
const u64 rtime = ppu . rtime ;
2017-02-17 20:35:57 +01:00
2020-10-29 19:46:50 +01:00
be_t < u64 > old_data = 0 ;
std : : memcpy ( & old_data , & ppu . rdata [ addr & 0x78 ] , sizeof ( old_data ) ) ;
be_t < u64 > new_data = old_data ;
2020-05-01 22:52:10 +02:00
if constexpr ( sizeof ( T ) = = sizeof ( u32 ) )
{
// Rebuild reg_value to be 32-bits of new data and 32-bits of old data
2020-10-29 19:46:50 +01:00
const be_t < u32 > reg32 = static_cast < u32 > ( reg_value ) ;
std : : memcpy ( reinterpret_cast < char * > ( & new_data ) + ( addr & 4 ) , & reg32 , sizeof ( u32 ) ) ;
}
else
{
new_data = reg_value ;
2020-05-01 22:52:10 +02:00
}
2020-04-07 19:29:11 +02:00
// Test if store address is on the same aligned 8-bytes memory as load
if ( const u32 raddr = std : : exchange ( ppu . raddr , 0 ) ; raddr / 8 ! = addr / 8 )
2017-02-17 20:35:57 +01:00
{
2020-04-07 19:29:11 +02:00
// If not and it is on the same aligned 128-byte memory, proceed only if 128-byte reservations are enabled
// In realhw the store address can be at any address of the 128-byte cache line
if ( raddr / 128 ! = addr / 128 | | ! ppu . use_full_rdata )
2020-09-10 05:27:55 +02:00
{
2020-04-07 19:29:11 +02:00
// Even when the reservation address does not match the target address must be valid
2020-11-10 18:09:28 +01:00
if ( ! vm : : check_addr ( addr , vm : : page_writable ) )
2020-04-07 19:29:11 +02:00
{
// Access violate
data + = 0 ;
}
return false ;
2020-09-10 05:27:55 +02:00
}
2020-04-07 19:29:11 +02:00
}
2020-09-10 05:27:55 +02:00
2020-04-07 19:29:11 +02:00
if ( old_data ! = data | | rtime ! = ( res & - 128 ) )
{
2020-05-08 19:41:15 +02:00
return false ;
}
2020-04-07 19:29:11 +02:00
if ( [ & ] ( )
2020-05-08 19:41:15 +02:00
{
2020-04-07 19:29:11 +02:00
if ( ppu . use_full_rdata ) [[unlikely]]
2020-05-08 19:41:15 +02:00
{
2021-12-18 16:12:37 +01:00
auto [ _oldd , _ok ] = res . fetch_op ( [ & ] ( u64 & r )
{
if ( ( r & - 128 ) ! = rtime | | ( r & 127 ) )
{
return false ;
}
r + = vm : : rsrv_unique_lock ;
return true ;
} ) ;
if ( ! _ok )
{
// Already locked or updated: give up
return false ;
}
2020-04-07 19:29:11 +02:00
if ( g_use_rtm ) [[likely]]
{
2020-10-30 23:52:24 +01:00
switch ( u64 count = ppu_stcx_accurate_tx ( addr & - 8 , rtime , ppu . rdata , std : : bit_cast < u64 > ( new_data ) ) )
2020-04-07 19:29:11 +02:00
{
2021-05-22 09:35:15 +02:00
case umax :
2020-04-07 19:29:11 +02:00
{
2020-10-30 03:17:00 +01:00
auto & all_data = * vm : : get_super_ptr < spu_rdata_t > ( addr & - 128 ) ;
auto & sdata = * vm : : get_super_ptr < atomic_be_t < u64 > > ( addr & - 8 ) ;
2020-10-29 23:22:28 +01:00
2020-11-09 01:41:56 +01:00
const bool ok = cpu_thread : : suspend_all < + 3 > ( & ppu , { all_data , all_data + 64 , & res } , [ & ]
2020-10-30 03:17:00 +01:00
{
2020-10-29 23:22:28 +01:00
if ( ( res & - 128 ) = = rtime & & cmp_rdata ( ppu . rdata , all_data ) )
{
2020-10-30 03:17:00 +01:00
sdata . release ( new_data ) ;
2021-12-18 16:12:37 +01:00
res + = 64 ;
2020-10-29 23:22:28 +01:00
return true ;
}
2020-10-30 03:17:00 +01:00
mov_rdata_nt ( ppu . rdata , all_data ) ;
2021-12-18 16:12:37 +01:00
res - = 64 ;
2020-10-29 23:22:28 +01:00
return false ;
} ) ;
if ( ok )
{
break ;
}
2020-10-30 23:52:24 +01:00
ppu . last_ftime = - 1 ;
2020-10-29 23:22:28 +01:00
[[fallthrough]] ;
2020-04-07 19:29:11 +02:00
}
2020-10-29 23:22:28 +01:00
case 0 :
2020-10-19 14:31:10 +02:00
{
2020-10-29 23:22:28 +01:00
if ( ppu . last_faddr = = addr )
{
ppu . last_fail + + ;
}
2020-10-30 23:52:24 +01:00
if ( ppu . last_ftime ! = umax )
{
ppu . last_faddr = 0 ;
return false ;
}
2020-11-24 06:18:31 +01:00
utils : : prefetch_read ( ppu . rdata ) ;
utils : : prefetch_read ( ppu . rdata + 64 ) ;
2020-10-29 23:22:28 +01:00
ppu . last_faddr = addr ;
ppu . last_ftime = res . load ( ) & - 128 ;
2021-12-30 17:39:18 +01:00
ppu . last_ftsc = utils : : get_tsc ( ) ;
2020-10-29 23:22:28 +01:00
return false ;
2020-10-19 14:31:10 +02:00
}
default :
2020-04-07 19:29:11 +02:00
{
2020-10-30 23:52:24 +01:00
if ( count > 20000 & & g_cfg . core . perf_report ) [[unlikely]]
2020-10-19 14:31:10 +02:00
{
2020-10-30 23:52:24 +01:00
perf_log . warning ( u8 " STCX: took too long: %.3fµs (%u c) " , count / ( utils : : get_tsc_freq ( ) / 1000'000 . ) , count ) ;
2020-10-19 14:31:10 +02:00
}
2020-10-29 23:22:28 +01:00
break ;
2020-04-07 19:29:11 +02:00
}
}
2020-05-08 19:41:15 +02:00
2020-10-29 23:22:28 +01:00
if ( ppu . last_faddr = = addr )
2020-10-08 15:13:55 +02:00
{
2020-10-29 23:22:28 +01:00
ppu . last_succ + + ;
}
2020-05-01 22:52:10 +02:00
2020-10-29 23:22:28 +01:00
ppu . last_faddr = 0 ;
return true ;
2020-04-07 19:29:11 +02:00
}
// Align address: we do not need the lower 7 bits anymore
addr & = - 128 ;
// Cache line data
2021-01-12 11:01:06 +01:00
//auto& cline_data = vm::_ref<spu_rdata_t>(addr);
2020-04-07 19:29:11 +02:00
data + = 0 ;
2020-09-14 22:38:17 +02:00
rsx : : reservation_lock rsx_lock ( addr , 128 ) ;
2020-04-07 19:29:11 +02:00
auto & super_data = * vm : : get_super_ptr < spu_rdata_t > ( addr ) ;
const bool success = [ & ] ( )
{
// Full lock (heavyweight)
// TODO: vm::check_addr
vm : : writer_lock lock ( addr ) ;
2020-10-01 17:15:07 +02:00
if ( cmp_rdata ( ppu . rdata , super_data ) )
2020-04-07 19:29:11 +02:00
{
2020-10-29 19:46:50 +01:00
data . release ( new_data ) ;
2020-10-13 10:07:43 +02:00
res + = 64 ;
2020-04-07 19:29:11 +02:00
return true ;
}
2020-10-13 10:07:43 +02:00
res - = 64 ;
2020-04-07 19:29:11 +02:00
return false ;
} ( ) ;
return success ;
2019-05-18 22:47:35 +02:00
}
2020-04-07 19:29:11 +02:00
2020-10-29 19:46:50 +01:00
if ( new_data = = old_data )
2017-07-18 19:03:47 +02:00
{
2020-10-29 23:22:28 +01:00
ppu . last_faddr = 0 ;
2020-04-07 19:29:11 +02:00
return res . compare_and_swap_test ( rtime , rtime + 128 ) ;
2019-05-18 22:47:35 +02:00
}
2020-04-07 19:29:11 +02:00
// Aligned 8-byte reservations will be used here
addr & = - 8 ;
2021-12-17 19:48:01 +01:00
const u64 lock_bits = vm : : rsrv_unique_lock ;
2020-10-31 18:27:28 +01:00
2020-10-15 11:40:53 +02:00
auto [ _oldd , _ok ] = res . fetch_op ( [ & ] ( u64 & r )
2020-04-07 19:29:11 +02:00
{
2020-10-15 11:40:53 +02:00
if ( ( r & - 128 ) ! = rtime | | ( r & 127 ) )
2020-10-08 15:13:55 +02:00
{
return false ;
}
2020-10-31 18:27:28 +01:00
r + = lock_bits ;
2020-10-15 11:40:53 +02:00
return true ;
} ) ;
2020-10-08 15:13:55 +02:00
2020-10-15 11:40:53 +02:00
// Give up if reservation has been locked or updated
if ( ! _ok )
{
2020-10-29 23:22:28 +01:00
ppu . last_faddr = 0 ;
2020-10-15 11:40:53 +02:00
return false ;
2020-04-07 19:29:11 +02:00
}
2017-02-17 20:35:57 +01:00
2020-10-29 19:46:50 +01:00
// Store previous value in old_data on failure
if ( data . compare_exchange ( old_data , new_data ) )
2020-10-08 15:13:55 +02:00
{
2020-10-31 18:27:28 +01:00
res + = 128 - lock_bits ;
2020-10-08 15:13:55 +02:00
return true ;
}
2020-10-31 18:27:28 +01:00
const u64 old_rtime = res . fetch_sub ( lock_bits ) ;
2020-10-29 23:22:28 +01:00
// TODO: disabled with this setting on, since it's dangerous to mix
if ( ! g_cfg . core . ppu_128_reservations_loop_max_length )
{
// Store old_data on failure
if ( ppu . last_faddr = = addr )
{
ppu . last_fail + + ;
}
ppu . last_faddr = addr ;
ppu . last_ftime = old_rtime & - 128 ;
2021-12-30 17:39:18 +01:00
ppu . last_ftsc = utils : : get_tsc ( ) ;
2020-10-29 23:22:28 +01:00
std : : memcpy ( & ppu . rdata [ addr & 0x78 ] , & old_data , 8 ) ;
}
2020-10-08 15:13:55 +02:00
return false ;
2020-04-07 19:29:11 +02:00
} ( ) )
2017-02-17 20:35:57 +01:00
{
2022-08-18 15:10:24 +02:00
// Test a common pattern in lwmutex
2022-09-03 05:46:16 +02:00
extern atomic_t < u32 > liblv2_begin , liblv2_end ;
2022-08-18 15:10:24 +02:00
2022-09-03 05:46:16 +02:00
if ( ppu . cia < liblv2_begin | | ppu . cia > = liblv2_end )
2022-08-18 15:10:24 +02:00
{
res . notify_all ( - 128 ) ;
}
2020-10-29 23:22:28 +01:00
if ( addr = = ppu . last_faddr )
{
ppu . last_succ + + ;
}
ppu . last_faddr = 0 ;
2020-04-07 19:29:11 +02:00
return true ;
2018-05-21 19:25:05 +02:00
}
2017-02-17 20:35:57 +01:00
2020-04-07 19:29:11 +02:00
return false ;
2016-06-22 15:37:51 +02:00
}
2016-06-07 22:24:20 +02:00
2020-04-13 12:29:01 +02:00
extern bool ppu_stwcx ( ppu_thread & ppu , u32 addr , u32 reg_value )
{
return ppu_store_reservation < u32 > ( ppu , addr , reg_value ) ;
}
extern bool ppu_stdcx ( ppu_thread & ppu , u32 addr , u64 reg_value )
{
return ppu_store_reservation < u64 > ( ppu , addr , reg_value ) ;
}
2021-01-27 14:08:43 +01:00
# ifdef LLVM_AVAILABLE
namespace
{
// Compiled PPU module info
struct jit_module
{
2021-12-30 17:39:18 +01:00
std : : vector < ppu_intrp_func_t > funcs ;
2021-01-27 14:08:43 +01:00
std : : shared_ptr < jit_compiler > pjit ;
2021-01-31 19:38:47 +01:00
bool init = false ;
2021-01-27 14:08:43 +01:00
} ;
struct jit_module_manager
{
shared_mutex mutex ;
std : : unordered_map < std : : string , jit_module > map ;
jit_module & get ( const std : : string & name )
{
std : : lock_guard lock ( mutex ) ;
return map . emplace ( name , jit_module { } ) . first - > second ;
}
void remove ( const std : : string & name ) noexcept
{
std : : lock_guard lock ( mutex ) ;
const auto found = map . find ( name ) ;
if ( found = = map . end ( ) ) [[unlikely]]
{
ppu_log . error ( " Failed to remove module %s " , name ) ;
return ;
}
map . erase ( found ) ;
}
} ;
}
# endif
2021-01-29 11:32:19 +01:00
namespace
{
// Read-only file view starting with specified offset (for MSELF)
struct file_view : fs : : file_base
{
const fs : : file m_file ;
const u64 m_off ;
u64 m_pos ;
explicit file_view ( fs : : file & & _file , u64 offset )
: m_file ( std : : move ( _file ) )
, m_off ( offset )
, m_pos ( 0 )
{
}
~ file_view ( ) override
{
}
fs : : stat_t stat ( ) override
{
return m_file . stat ( ) ;
}
2021-03-05 20:05:37 +01:00
bool trunc ( u64 ) override
2021-01-29 11:32:19 +01:00
{
return false ;
}
u64 read ( void * buffer , u64 size ) override
{
const u64 old_pos = m_file . pos ( ) ;
m_file . seek ( m_off + m_pos ) ;
const u64 result = m_file . read ( buffer , size ) ;
ensure ( old_pos = = m_file . seek ( old_pos ) ) ;
m_pos + = result ;
return result ;
}
2021-03-05 20:05:37 +01:00
u64 write ( const void * , u64 ) override
2021-01-29 11:32:19 +01:00
{
return 0 ;
}
u64 seek ( s64 offset , fs : : seek_mode whence ) override
{
const s64 new_pos =
whence = = fs : : seek_set ? offset :
whence = = fs : : seek_cur ? offset + m_pos :
whence = = fs : : seek_end ? offset + size ( ) : - 1 ;
if ( new_pos < 0 )
{
fs : : g_tls_error = fs : : error : : inval ;
return - 1 ;
}
m_pos = new_pos ;
return m_pos ;
}
u64 size ( ) override
{
return m_file . size ( ) ;
}
} ;
}
2022-07-04 15:02:17 +02:00
extern fs : : file make_file_view ( fs : : file & & _file , u64 offset )
{
fs : : file file ;
file . reset ( std : : make_unique < file_view > ( std : : move ( _file ) , offset ) ) ;
return file ;
}
2021-01-27 14:08:43 +01:00
extern void ppu_finalize ( const ppu_module & info )
{
// Get cache path for this executable
std : : string cache_path ;
if ( info . name . empty ( ) )
{
// Don't remove main module from memory
return ;
}
else
{
// Get PPU cache location
cache_path = fs : : get_cache_dir ( ) + " cache/ " ;
2022-05-12 07:08:36 +02:00
const std : : string dev_flash = vfs : : get ( " /dev_flash/sys/ " ) ;
2021-01-27 14:08:43 +01:00
if ( info . path . starts_with ( dev_flash ) | | Emu . GetCat ( ) = = " 1P " )
{
// Don't remove dev_flash prx from memory
return ;
}
else if ( ! Emu . GetTitleID ( ) . empty ( ) )
{
cache_path + = Emu . GetTitleID ( ) ;
cache_path + = ' / ' ;
}
// Add PPU hash and filename
fmt : : append ( cache_path , " ppu-%s-%s/ " , fmt : : base57 ( info . sha1 ) , info . path . substr ( info . path . find_last_of ( ' / ' ) + 1 ) ) ;
}
# ifdef LLVM_AVAILABLE
2021-03-02 12:59:19 +01:00
g_fxo - > get < jit_module_manager > ( ) . remove ( cache_path + info . name ) ;
2021-01-27 14:08:43 +01:00
# endif
}
2022-07-04 15:02:17 +02:00
extern void ppu_precompile ( std : : vector < std : : string > & dir_queue , std : : vector < ppu_module * > * loaded_modules )
2021-01-29 11:32:19 +01:00
{
2021-02-23 15:09:23 +01:00
if ( g_cfg . core . ppu_decoder ! = ppu_decoder_type : : llvm )
{
return ;
}
2022-07-04 15:02:17 +02:00
if ( auto dis = g_fxo - > try_get < disable_precomp_t > ( ) ; dis & & dis - > disable )
{
return ;
}
2021-01-30 16:18:10 +01:00
// Make sure we only have one '/' at the end and remove duplicates.
for ( std : : string & dir : dir_queue )
{
while ( dir . back ( ) = = ' / ' | | dir . back ( ) = = ' \\ ' )
dir . pop_back ( ) ;
dir + = ' / ' ;
}
2021-01-30 14:08:22 +01:00
std : : sort ( dir_queue . begin ( ) , dir_queue . end ( ) ) ;
dir_queue . erase ( std : : unique ( dir_queue . begin ( ) , dir_queue . end ( ) ) , dir_queue . end ( ) ) ;
const std : : string firmware_sprx_path = vfs : : get ( " /dev_flash/sys/external/ " ) ;
// Map fixed address executables area, fake overlay support
2021-01-30 17:05:02 +01:00
const bool had_ovl = ! vm : : map ( 0x3000'0000 , 0x1000'0000 , 0x202 ) . operator bool ( ) ;
2021-01-30 14:08:22 +01:00
const u32 ppc_seg = std : : exchange ( g_ps3_process_info . ppc_seg , 0x3 ) ;
2021-01-29 11:32:19 +01:00
std : : vector < std : : pair < std : : string , u64 > > file_queue ;
file_queue . reserve ( 2000 ) ;
2021-01-30 15:25:21 +01:00
// Find all .sprx files recursively
2021-01-29 11:32:19 +01:00
for ( usz i = 0 ; i < dir_queue . size ( ) ; i + + )
{
if ( Emu . IsStopped ( ) )
{
2021-01-30 14:08:22 +01:00
file_queue . clear ( ) ;
2021-01-29 11:32:19 +01:00
break ;
}
ppu_log . notice ( " Scanning directory: %s " , dir_queue [ i ] ) ;
for ( auto & & entry : fs : : dir ( dir_queue [ i ] ) )
{
if ( Emu . IsStopped ( ) )
{
2021-01-30 14:08:22 +01:00
file_queue . clear ( ) ;
2021-01-29 11:32:19 +01:00
break ;
}
if ( entry . is_directory )
{
if ( entry . name ! = " . " & & entry . name ! = " .. " )
{
dir_queue . emplace_back ( dir_queue [ i ] + entry . name + ' / ' ) ;
}
continue ;
}
2021-01-30 14:08:22 +01:00
std : : string upper = fmt : : to_upper ( entry . name ) ;
2022-07-04 15:02:17 +02:00
// Skip already loaded modules or HLEd ones
2022-09-13 15:08:55 +02:00
auto is_ignored = [ & ] ( s64 /*offset*/ ) - > bool
2021-01-29 11:32:19 +01:00
{
2022-07-04 15:02:17 +02:00
if ( dir_queue [ i ] ! = firmware_sprx_path )
2021-01-30 14:08:22 +01:00
{
2022-07-04 15:02:17 +02:00
return false ;
}
2021-01-30 14:08:22 +01:00
2022-07-04 15:02:17 +02:00
if ( loaded_modules )
{
if ( std : : any_of ( loaded_modules - > begin ( ) , loaded_modules - > end ( ) , [ & ] ( ppu_module * obj )
2021-01-30 14:08:22 +01:00
{
2022-07-04 15:02:17 +02:00
return obj - > name = = entry . name ;
} ) )
2021-01-30 14:08:22 +01:00
{
2022-07-04 15:02:17 +02:00
return true ;
2021-01-30 14:08:22 +01:00
}
2022-07-04 15:02:17 +02:00
}
2021-01-30 14:08:22 +01:00
2022-07-04 15:02:17 +02:00
if ( g_cfg . core . libraries_control . get_set ( ) . count ( entry . name + " :lle " ) )
{
// Force LLE
return false ;
}
else if ( g_cfg . core . libraries_control . get_set ( ) . count ( entry . name + " :hle " ) )
{
// Force HLE
return true ;
}
2021-01-30 14:08:22 +01:00
2022-07-04 15:02:17 +02:00
extern const std : : map < std : : string_view , int > g_prx_list ;
// Use list
2022-09-19 14:57:51 +02:00
return g_prx_list . count ( entry . name ) & & : : at32 ( g_prx_list , entry . name ) ! = 0 ;
2022-07-04 15:02:17 +02:00
} ;
// Check .sprx filename
if ( upper . ends_with ( " .SPRX " ) & & entry . name ! = " libfs_utility_init.sprx " sv )
{
if ( is_ignored ( 0 ) )
{
continue ;
2021-01-30 14:08:22 +01:00
}
2021-01-29 11:32:19 +01:00
// Get full path
file_queue . emplace_back ( dir_queue [ i ] + entry . name , 0 ) ;
2021-01-30 14:08:22 +01:00
continue ;
}
// Check .self filename
if ( upper . ends_with ( " .SELF " ) )
{
// Get full path
file_queue . emplace_back ( dir_queue [ i ] + entry . name , 0 ) ;
2021-01-29 11:32:19 +01:00
continue ;
}
// Check .mself filename
2021-01-30 14:08:22 +01:00
if ( upper . ends_with ( " .MSELF " ) )
2021-01-29 11:32:19 +01:00
{
if ( fs : : file mself { dir_queue [ i ] + entry . name } )
{
mself_header hdr { } ;
if ( mself . read ( hdr ) & & hdr . get_count ( mself . size ( ) ) )
{
2021-01-29 19:06:49 +01:00
for ( u32 j = 0 ; j < hdr . count ; j + + )
2021-01-29 11:32:19 +01:00
{
mself_record rec { } ;
if ( mself . read ( rec ) & & rec . get_pos ( mself . size ( ) ) )
{
std : : string name = rec . name ;
2021-01-30 14:08:22 +01:00
upper = fmt : : to_upper ( name ) ;
if ( upper . ends_with ( " .SPRX " ) )
2021-01-29 11:32:19 +01:00
{
// .sprx inside .mself found
file_queue . emplace_back ( dir_queue [ i ] + entry . name , rec . off ) ;
2021-01-30 14:08:22 +01:00
continue ;
}
if ( upper . ends_with ( " .SELF " ) )
{
// .self inside .mself found
file_queue . emplace_back ( dir_queue [ i ] + entry . name , rec . off ) ;
continue ;
2021-01-29 11:32:19 +01:00
}
}
else
{
ppu_log . error ( " MSELF file is possibly truncated " ) ;
break ;
}
}
}
}
}
}
}
2021-01-30 14:08:22 +01:00
g_progr_ftotal + = file_queue . size ( ) ;
2021-03-31 15:31:21 +02:00
scoped_progress_dialog progr = " Compiling PPU modules... " ;
2021-01-30 14:08:22 +01:00
2021-01-29 11:32:19 +01:00
atomic_t < usz > fnext = 0 ;
2021-01-30 14:08:22 +01:00
shared_mutex sprx_mtx , ovl_mtx ;
2021-01-29 11:32:19 +01:00
2021-01-30 14:08:22 +01:00
named_thread_group workers ( " SPRX Worker " , std : : min < u32 > ( utils : : get_thread_count ( ) , : : size32 ( file_queue ) ) , [ & ]
2021-01-29 11:32:19 +01:00
{
2022-06-14 14:28:38 +02:00
# ifdef __APPLE__
pthread_jit_write_protect_np ( false ) ;
# endif
2021-01-30 16:18:10 +01:00
// Set low priority
thread_ctrl : : scoped_priority low_prio ( - 1 ) ;
2021-01-30 14:08:22 +01:00
for ( usz func_i = fnext + + ; func_i < file_queue . size ( ) ; func_i = fnext + + , g_progr_fdone + + )
2021-01-29 11:32:19 +01:00
{
2021-01-30 14:08:22 +01:00
if ( Emu . IsStopped ( ) )
{
continue ;
}
2021-01-29 11:32:19 +01:00
2021-01-30 14:08:22 +01:00
auto [ path , offset ] = std : : as_const ( file_queue ) [ func_i ] ;
2021-01-29 11:32:19 +01:00
2021-01-30 14:08:22 +01:00
ppu_log . notice ( " Trying to load: %s " , path ) ;
// Load MSELF, SPRX or SELF
2021-01-29 11:32:19 +01:00
fs : : file src { path } ;
2021-01-30 14:08:22 +01:00
if ( ! src )
{
ppu_log . error ( " Failed to open '%s' (%s) " , path , fs : : g_tls_error ) ;
continue ;
}
if ( u64 off = offset )
2021-01-29 11:32:19 +01:00
{
// Adjust offset for MSELF
src . reset ( std : : make_unique < file_view > ( std : : move ( src ) , off ) ) ;
2021-01-29 19:06:49 +01:00
// Adjust path for MSELF too
fmt : : append ( path , " _x%x " , off ) ;
2021-01-29 11:32:19 +01:00
}
// Some files may fail to decrypt due to the lack of klic
src = decrypt_self ( std : : move ( src ) ) ;
2021-01-30 14:08:22 +01:00
if ( ! src )
{
2021-04-19 19:45:38 +02:00
ppu_log . notice ( " Failed to decrypt '%s' " , path ) ;
2021-01-30 16:18:10 +01:00
continue ;
2021-01-30 14:08:22 +01:00
}
elf_error prx_err { } , ovl_err { } ;
2021-01-29 11:32:19 +01:00
2021-02-12 12:40:55 +01:00
if ( ppu_prx_object obj = src ; ( prx_err = obj , obj = = elf_error : : ok ) )
2021-01-29 11:32:19 +01:00
{
std : : unique_lock lock ( sprx_mtx ) ;
2021-05-26 22:38:17 +02:00
if ( auto prx = ppu_load_prx ( obj , path , offset ) )
2021-01-29 11:32:19 +01:00
{
lock . unlock ( ) ;
2021-02-12 12:40:55 +01:00
obj . clear ( ) , src . close ( ) ; // Clear decrypted file and elf object memory
2021-01-29 11:32:19 +01:00
ppu_initialize ( * prx ) ;
idm : : remove < lv2_obj , lv2_prx > ( idm : : last_id ( ) ) ;
lock . lock ( ) ;
ppu_unload_prx ( * prx ) ;
lock . unlock ( ) ;
ppu_finalize ( * prx ) ;
continue ;
}
2021-01-30 14:08:22 +01:00
// Log error
prx_err = elf_error : : header_type ;
2021-01-29 11:32:19 +01:00
}
2021-01-30 16:18:10 +01:00
2021-02-12 12:40:55 +01:00
if ( ppu_exec_object obj = src ; ( ovl_err = obj , obj = = elf_error : : ok ) )
2021-01-30 14:08:22 +01:00
{
while ( ovl_err = = elf_error : : ok )
{
// Only one thread compiles OVL atm, other can compile PRX cuncurrently
std : : unique_lock lock ( ovl_mtx ) ;
2021-05-26 22:38:17 +02:00
auto [ ovlm , error ] = ppu_load_overlay ( obj , path , offset ) ;
2021-01-30 14:08:22 +01:00
if ( error )
{
// Abort
ovl_err = elf_error : : header_type ;
break ;
}
2021-01-29 11:32:19 +01:00
2021-02-12 12:40:55 +01:00
obj . clear ( ) , src . close ( ) ; // Clear decrypted file and elf object memory
2021-01-30 14:08:22 +01:00
ppu_initialize ( * ovlm ) ;
for ( auto & seg : ovlm - > segs )
{
vm : : dealloc ( seg . addr ) ;
}
lock . unlock ( ) ;
idm : : remove < lv2_obj , lv2_overlay > ( idm : : last_id ( ) ) ;
ppu_finalize ( * ovlm ) ;
break ;
}
if ( ovl_err = = elf_error : : ok )
{
continue ;
}
}
2021-04-19 19:45:38 +02:00
ppu_log . notice ( " Failed to precompile '%s' (prx: %s, ovl: %s) " , path , prx_err , ovl_err ) ;
2021-01-29 11:32:19 +01:00
continue ;
}
} ) ;
// Join every thread
workers . join ( ) ;
2021-01-30 14:08:22 +01:00
// Revert changes
if ( ! had_ovl )
{
2021-09-11 07:26:08 +02:00
ensure ( vm : : unmap ( 0x3000'0000 ) . second ) ;
2021-01-30 14:08:22 +01:00
}
g_ps3_process_info . ppc_seg = ppc_seg ;
2021-01-29 11:32:19 +01:00
}
2017-02-26 16:56:31 +01:00
extern void ppu_initialize ( )
2016-06-22 15:37:51 +02:00
{
2021-03-02 12:59:19 +01:00
if ( ! g_fxo - > is_init < ppu_module > ( ) )
2017-02-26 16:56:31 +01:00
{
return ;
}
2017-01-22 20:03:57 +01:00
2018-05-04 23:01:27 +02:00
if ( Emu . IsStopped ( ) )
{
return ;
}
2021-01-31 18:00:04 +01:00
2022-07-04 15:02:17 +02:00
auto & _main = g_fxo - > get < ppu_module > ( ) ;
2021-03-31 15:31:21 +02:00
scoped_progress_dialog progr = " Scanning PPU modules... " ;
2019-12-04 23:17:57 +01:00
2021-01-30 14:08:22 +01:00
bool compile_main = false ;
2018-05-04 23:01:27 +02:00
2021-01-30 14:08:22 +01:00
// Check main module cache
2021-03-02 12:59:19 +01:00
if ( ! _main . segs . empty ( ) )
2019-09-26 16:06:43 +02:00
{
2021-03-02 12:59:19 +01:00
compile_main = ppu_initialize ( _main , true ) ;
2019-09-26 16:06:43 +02:00
}
2017-02-26 16:56:31 +01:00
2022-07-04 15:02:17 +02:00
std : : vector < ppu_module * > module_list ;
2017-04-22 15:00:23 +02:00
2022-07-04 15:02:17 +02:00
const std : : string firmware_sprx_path = vfs : : get ( " /dev_flash/sys/external/ " ) ;
// If empty we have no indication for firmware cache state, check everything
bool compile_fw = true ;
idm : : select < lv2_obj , lv2_prx > ( [ & ] ( u32 , lv2_prx & _module )
2017-04-22 15:00:23 +02:00
{
2022-07-04 15:02:17 +02:00
if ( _module . path . starts_with ( firmware_sprx_path ) )
{
// Postpone testing
compile_fw = false ;
}
module_list . emplace_back ( & _module ) ;
2017-04-22 15:00:23 +02:00
} ) ;
2022-07-04 15:02:17 +02:00
idm : : select < lv2_obj , lv2_overlay > ( [ & ] ( u32 , lv2_overlay & _module )
{
module_list . emplace_back ( & _module ) ;
} ) ;
2021-01-30 14:08:22 +01:00
// Check preloaded libraries cache
2022-07-04 15:02:17 +02:00
if ( ! compile_fw )
2021-01-30 14:08:22 +01:00
{
2022-07-04 15:02:17 +02:00
for ( auto ptr : module_list )
{
if ( ptr - > path . starts_with ( firmware_sprx_path ) )
{
compile_fw | = ppu_initialize ( * ptr , true ) ;
}
}
2021-01-30 14:08:22 +01:00
}
std : : vector < std : : string > dir_queue ;
2022-05-12 07:08:36 +02:00
const std : : string mount_point = vfs : : get ( " /dev_flash/ " ) ;
bool dev_flash_located = Emu . GetCat ( ) . back ( ) ! = ' P ' & & Emu . IsPathInsideDir ( Emu . GetBoot ( ) , mount_point ) ;
if ( compile_fw | | dev_flash_located )
2021-01-30 14:08:22 +01:00
{
2022-05-12 07:08:36 +02:00
if ( dev_flash_located )
{
const std : : string eseibrd = mount_point + " /vsh/module/eseibrd.sprx " ;
if ( auto prx = ppu_load_prx ( ppu_prx_object { decrypt_self ( fs : : file { eseibrd } ) } , eseibrd , 0 ) )
{
// Check if cache exists for this infinitesimally small prx
dev_flash_located = ppu_initialize ( * prx , true ) ;
idm : : remove < lv2_obj , lv2_prx > ( idm : : last_id ( ) ) ;
ppu_unload_prx ( * prx ) ;
}
}
const std : : string firmware_sprx_path = vfs : : get ( dev_flash_located ? " /dev_flash/ " sv : " /dev_flash/sys/ " sv ) ;
2021-01-30 14:08:22 +01:00
dir_queue . emplace_back ( firmware_sprx_path ) ;
}
// Avoid compilation if main's cache exists or it is a standalone SELF with no PARAM.SFO
2021-04-10 13:43:50 +02:00
if ( compile_main & & g_cfg . core . ppu_llvm_precompilation & & ! Emu . GetTitleID ( ) . empty ( ) )
2021-01-30 14:08:22 +01:00
{
2021-01-30 16:18:10 +01:00
// Try to add all related directories
const std : : set < std : : string > dirs = Emu . GetGameDirs ( ) ;
dir_queue . insert ( std : : end ( dir_queue ) , std : : begin ( dirs ) , std : : end ( dirs ) ) ;
2021-01-30 14:08:22 +01:00
}
2022-07-04 15:02:17 +02:00
ppu_precompile ( dir_queue , & module_list ) ;
2021-01-30 14:08:22 +01:00
if ( Emu . IsStopped ( ) )
{
return ;
}
// Initialize main module cache
2021-03-02 12:59:19 +01:00
if ( ! _main . segs . empty ( ) )
2021-01-30 14:08:22 +01:00
{
2021-03-02 12:59:19 +01:00
ppu_initialize ( _main ) ;
2021-01-30 14:08:22 +01:00
}
2017-06-22 23:52:09 +02:00
// Initialize preloaded libraries
2022-07-04 15:02:17 +02:00
for ( auto ptr : module_list )
2017-02-26 16:56:31 +01:00
{
2021-01-30 14:08:22 +01:00
if ( Emu . IsStopped ( ) )
{
return ;
}
2017-06-22 23:52:09 +02:00
ppu_initialize ( * ptr ) ;
2017-04-22 15:00:23 +02:00
}
2017-02-26 16:56:31 +01:00
}
2021-03-02 17:22:39 +01:00
struct ppu_toc_manager
{
std : : unordered_map < u32 , u32 > toc_map ;
shared_mutex mutex ;
} ;
2021-01-30 14:08:22 +01:00
bool ppu_initialize ( const ppu_module & info , bool check_only )
2017-02-26 16:56:31 +01:00
{
2017-05-20 13:45:02 +02:00
if ( g_cfg . core . ppu_decoder ! = ppu_decoder_type : : llvm )
2017-02-26 16:56:31 +01:00
{
2021-01-30 14:08:22 +01:00
if ( check_only )
{
return false ;
}
2017-04-22 15:00:23 +02:00
// Temporarily
2021-03-02 17:22:39 +01:00
s_ppu_toc = & g_fxo - > get < ppu_toc_manager > ( ) . toc_map ;
2017-04-22 15:00:23 +02:00
2017-02-26 16:56:31 +01:00
for ( const auto & func : info . funcs )
{
2017-04-08 22:58:00 +02:00
for ( auto & block : func . blocks )
{
2021-09-01 12:38:17 +02:00
ppu_register_function_at ( block . first , block . second ) ;
2017-04-08 22:58:00 +02:00
}
2020-02-19 18:03:59 +01:00
if ( g_cfg . core . ppu_debug & & func . size & & func . toc ! = umax )
2017-04-08 22:58:00 +02:00
{
2017-04-22 15:00:23 +02:00
s_ppu_toc - > emplace ( func . addr , func . toc ) ;
2021-12-30 17:39:18 +01:00
ppu_ref ( func . addr ) = & ppu_check_toc ;
2017-04-08 22:58:00 +02:00
}
2017-02-26 16:56:31 +01:00
}
2021-01-30 14:08:22 +01:00
return false ;
2017-02-26 16:56:31 +01:00
}
2017-06-24 17:36:49 +02:00
// Link table
static const std : : unordered_map < std : : string , u64 > s_link_table = [ ] ( )
2017-02-26 16:56:31 +01:00
{
2017-06-22 23:52:09 +02:00
std : : unordered_map < std : : string , u64 > link_table
2017-02-26 16:56:31 +01:00
{
2022-10-16 07:23:48 +02:00
{ " sys_game_watchdog_start " , reinterpret_cast < u64 > ( ppu_execute_syscall ) } ,
{ " sys_game_watchdog_stop " , reinterpret_cast < u64 > ( ppu_execute_syscall ) } ,
{ " sys_game_watchdog_clear " , reinterpret_cast < u64 > ( ppu_execute_syscall ) } ,
2022-10-10 19:14:40 +02:00
{ " sys_game_get_system_sw_version " , reinterpret_cast < u64 > ( ppu_execute_syscall ) } ,
2022-10-16 07:23:48 +02:00
{ " sys_game_board_storage_read " , reinterpret_cast < u64 > ( ppu_execute_syscall ) } ,
2019-12-02 22:31:34 +01:00
{ " __trap " , reinterpret_cast < u64 > ( & ppu_trap ) } ,
{ " __error " , reinterpret_cast < u64 > ( & ppu_error ) } ,
{ " __check " , reinterpret_cast < u64 > ( & ppu_check ) } ,
{ " __trace " , reinterpret_cast < u64 > ( & ppu_trace ) } ,
{ " __syscall " , reinterpret_cast < u64 > ( ppu_execute_syscall ) } ,
{ " __get_tb " , reinterpret_cast < u64 > ( get_timebased_time ) } ,
{ " __lwarx " , reinterpret_cast < u64 > ( ppu_lwarx ) } ,
{ " __ldarx " , reinterpret_cast < u64 > ( ppu_ldarx ) } ,
{ " __stwcx " , reinterpret_cast < u64 > ( ppu_stwcx ) } ,
{ " __stdcx " , reinterpret_cast < u64 > ( ppu_stdcx ) } ,
2020-09-25 16:29:25 +02:00
{ " __dcbz " , reinterpret_cast < u64 > ( + [ ] ( u32 addr ) { alignas ( 64 ) static constexpr u8 z [ 128 ] { } ; do_cell_atomic_128_store ( addr , z ) ; } ) } ,
2019-12-02 22:31:34 +01:00
{ " __resupdate " , reinterpret_cast < u64 > ( vm : : reservation_update ) } ,
2020-10-13 21:36:00 +02:00
{ " __resinterp " , reinterpret_cast < u64 > ( ppu_reservation_fallback ) } ,
2017-02-26 16:56:31 +01:00
} ;
for ( u64 index = 0 ; index < 1024 ; index + + )
{
2021-01-12 11:01:06 +01:00
if ( ppu_get_syscall ( index ) )
2017-02-26 16:56:31 +01:00
{
2020-03-02 18:17:48 +01:00
link_table . emplace ( fmt : : format ( " %s " , ppu_syscall_code ( index ) ) , reinterpret_cast < u64 > ( ppu_execute_syscall ) ) ;
link_table . emplace ( fmt : : format ( " syscall_%u " , index ) , reinterpret_cast < u64 > ( ppu_execute_syscall ) ) ;
2017-02-26 16:56:31 +01:00
}
}
2017-06-24 17:36:49 +02:00
return link_table ;
} ( ) ;
2017-07-10 21:22:54 +02:00
// Get cache path for this executable
std : : string cache_path ;
if ( info . name . empty ( ) )
{
2019-01-13 18:06:30 +01:00
cache_path = info . cache ;
2017-07-10 21:22:54 +02:00
}
else
{
2019-01-13 18:06:30 +01:00
// New PPU cache location
cache_path = fs : : get_cache_dir ( ) + " cache/ " ;
const std : : string dev_flash = vfs : : get ( " /dev_flash/ " ) ;
2017-07-10 21:22:54 +02:00
2020-02-17 22:43:23 +01:00
if ( ! info . path . starts_with ( dev_flash ) & & ! Emu . GetTitleID ( ) . empty ( ) & & Emu . GetCat ( ) ! = " 1P " )
2017-07-10 21:22:54 +02:00
{
2019-01-13 18:06:30 +01:00
// Add prefix for anything except dev_flash files, standalone elfs or PS1 classics
cache_path + = Emu . GetTitleID ( ) ;
cache_path + = ' / ' ;
2017-07-10 21:22:54 +02:00
}
2019-01-13 18:06:30 +01:00
// Add PPU hash and filename
fmt : : append ( cache_path , " ppu-%s-%s/ " , fmt : : base57 ( info . sha1 ) , info . path . substr ( info . path . find_last_of ( ' / ' ) + 1 ) ) ;
if ( ! fs : : create_path ( cache_path ) )
2017-07-10 21:22:54 +02:00
{
2019-01-13 18:06:30 +01:00
fmt : : throw_exception ( " Failed to create cache directory: %s (%s) " , cache_path , fs : : g_tls_error ) ;
2017-07-10 21:22:54 +02:00
}
}
2017-06-24 17:36:49 +02:00
# ifdef LLVM_AVAILABLE
2021-03-31 15:31:21 +02:00
std : : optional < scoped_progress_dialog > progr ;
2019-12-04 23:17:57 +01:00
if ( ! check_only )
{
// Initialize progress dialog
2021-03-31 15:31:21 +02:00
progr . emplace ( " Loading PPU modules... " ) ;
2019-12-04 23:17:57 +01:00
}
2018-05-30 19:34:36 +02:00
2018-03-26 23:54:38 +02:00
struct jit_core_allocator
{
2019-08-25 16:27:38 +02:00
const s32 thread_count = g_cfg . core . llvm_threads ? std : : min < s32 > ( g_cfg . core . llvm_threads , limit ( ) ) : limit ( ) ;
2018-03-26 23:54:38 +02:00
2019-08-25 16:27:38 +02:00
// Initialize global semaphore with the max number of threads
: : semaphore < 0x7fffffff > sem { std : : max < s32 > ( thread_count , 1 ) } ;
static s32 limit ( )
2018-03-26 23:54:38 +02:00
{
2021-01-28 19:33:50 +01:00
return static_cast < s32 > ( utils : : get_thread_count ( ) ) ;
2018-03-26 23:54:38 +02:00
}
} ;
2017-07-10 21:22:54 +02:00
// Permanently loaded compiled PPU modules (name -> data)
2021-03-02 12:59:19 +01:00
jit_module & jit_mod = g_fxo - > get < jit_module_manager > ( ) . get ( cache_path + info . name ) ;
2017-07-10 21:22:54 +02:00
// Compiler instance (deferred initialization)
2020-05-19 18:09:27 +02:00
std : : shared_ptr < jit_compiler > & jit = jit_mod . pjit ;
2017-06-24 17:36:49 +02:00
2017-06-22 23:52:09 +02:00
// Split module into fragments <= 1 MiB
2020-12-18 08:39:54 +01:00
usz fpos = 0 ;
2017-02-26 16:56:31 +01:00
2017-07-01 01:08:51 +02:00
// Difference between function name and current location
2022-09-19 14:57:51 +02:00
const u32 reloc = info . relocs . empty ( ) ? 0 : : : at32 ( info . segs , 0 ) . addr ;
2016-06-07 22:24:20 +02:00
2020-03-03 20:37:29 +01:00
// Info sent to threads
std : : vector < std : : pair < std : : string , ppu_module > > workload ;
// Info to load to main JIT instance (true - compiled)
std : : vector < std : : pair < std : : string , bool > > link_workload ;
// Sync variable to acquire workloads
atomic_t < u32 > work_cv = 0 ;
2021-01-30 14:08:22 +01:00
bool compiled_new = false ;
2021-06-25 09:50:42 +02:00
bool has_mfvscr = false ;
for ( auto & func : info . funcs )
{
if ( func . size = = 0 )
{
continue ;
}
for ( const auto & [ addr , size ] : func . blocks )
{
if ( size = = 0 )
{
continue ;
}
for ( u32 i = addr ; i < addr + size ; i + = 4 )
{
if ( g_ppu_itype . decode ( vm : : read32 ( i ) ) = = ppu_itype : : MFVSCR )
{
ppu_log . warning ( " MFVSCR found " ) ;
has_mfvscr = true ;
break ;
}
}
if ( has_mfvscr )
{
break ;
}
}
if ( has_mfvscr )
{
break ;
}
}
2021-01-31 19:38:47 +01:00
while ( ! jit_mod . init & & fpos < info . funcs . size ( ) )
2017-06-22 23:52:09 +02:00
{
2017-07-10 21:22:54 +02:00
// Initialize compiler instance
2018-03-17 18:41:35 +01:00
if ( ! jit & & get_current_cpu_thread ( ) )
2017-12-31 13:45:12 +01:00
{
2018-01-01 08:40:57 +01:00
jit = std : : make_shared < jit_compiler > ( s_link_table , g_cfg . core . llvm_cpu ) ;
2017-12-31 13:45:12 +01:00
}
2017-07-10 21:22:54 +02:00
2017-07-01 01:08:51 +02:00
// Copy module information (TODO: optimize)
2017-10-10 15:40:46 +02:00
ppu_module part ;
part . copy_part ( info ) ;
2017-06-24 17:36:49 +02:00
part . funcs . reserve ( 16000 ) ;
2017-06-22 23:52:09 +02:00
2017-07-01 01:08:51 +02:00
// Overall block size in bytes
2020-12-18 08:39:54 +01:00
usz bsize = 0 ;
2021-01-19 18:40:15 +01:00
usz bcount = 0 ;
2017-07-01 01:08:51 +02:00
2017-06-22 23:52:09 +02:00
while ( fpos < info . funcs . size ( ) )
{
auto & func = info . funcs [ fpos ] ;
2021-01-19 18:40:15 +01:00
if ( ! func . size )
{
fpos + + ;
continue ;
}
2019-01-13 18:06:30 +01:00
if ( bsize + func . size > 100 * 1024 & & bsize )
2017-06-22 23:52:09 +02:00
{
2021-01-19 18:40:15 +01:00
if ( bcount > = 1000 )
{
break ;
}
2017-06-22 23:52:09 +02:00
}
2022-08-17 15:53:05 +02:00
if ( jit )
{
const auto far_jump = ppu_get_far_jump ( func . addr ) ? g_fxo - > get < ppu_far_jumps_t > ( ) . gen_jump ( func . addr ) : nullptr ;
if ( far_jump )
{
// Replace the function with ppu_far_jump
jit - > update_global_mapping ( fmt : : format ( " __0x%x " , func . addr - reloc ) , reinterpret_cast < u64 > ( far_jump ) ) ;
fpos + + ;
continue ;
}
}
2021-01-19 18:40:15 +01:00
// Copy block or function entry
ppu_function & entry = part . funcs . emplace_back ( func ) ;
// Fixup some information
entry . name = fmt : : format ( " __0x%x " , entry . addr - reloc ) ;
2021-12-30 17:39:18 +01:00
if ( has_mfvscr & & g_cfg . core . ppu_set_sat_bit )
2021-06-25 09:50:42 +02:00
{
// TODO
entry . attr + = ppu_attr : : has_mfvscr ;
}
2021-01-19 18:40:15 +01:00
if ( entry . blocks . empty ( ) )
2017-06-22 23:52:09 +02:00
{
2021-01-19 18:40:15 +01:00
entry . blocks . emplace ( func . addr , func . size ) ;
2017-06-22 23:52:09 +02:00
}
2021-01-19 18:40:15 +01:00
bsize + = func . size ;
2017-06-22 23:52:09 +02:00
fpos + + ;
2021-01-19 18:40:15 +01:00
bcount + + ;
2017-06-22 23:52:09 +02:00
}
2019-01-13 18:06:30 +01:00
// Compute module hash to generate (hopefully) unique object name
std : : string obj_name ;
2017-06-24 17:36:49 +02:00
{
sha1_context ctx ;
u8 output [ 20 ] ;
sha1_starts ( & ctx ) ;
2017-06-22 23:52:09 +02:00
2020-09-25 16:29:25 +02:00
int has_dcbz = ! ! g_cfg . core . accurate_cache_line_stores ;
2017-06-24 17:36:49 +02:00
for ( const auto & func : part . funcs )
{
if ( func . size = = 0 )
{
continue ;
}
2017-06-22 23:52:09 +02:00
2017-07-01 01:08:51 +02:00
const be_t < u32 > addr = func . addr - reloc ;
2017-06-24 17:36:49 +02:00
const be_t < u32 > size = func . size ;
sha1_update ( & ctx , reinterpret_cast < const u8 * > ( & addr ) , sizeof ( addr ) ) ;
sha1_update ( & ctx , reinterpret_cast < const u8 * > ( & size ) , sizeof ( size ) ) ;
2017-06-22 23:52:09 +02:00
2017-06-24 17:36:49 +02:00
for ( const auto & block : func . blocks )
{
2017-07-01 01:08:51 +02:00
if ( block . second = = 0 | | reloc )
2017-06-24 17:36:49 +02:00
{
continue ;
}
2017-06-22 23:52:09 +02:00
2018-03-17 18:41:35 +01:00
// Find relevant relocations
auto low = std : : lower_bound ( part . relocs . cbegin ( ) , part . relocs . cend ( ) , block . first ) ;
auto high = std : : lower_bound ( low , part . relocs . cend ( ) , block . first + block . second ) ;
auto addr = block . first ;
for ( ; low ! = high ; + + low )
{
// Aligned relocation address
const u32 roff = low - > addr & ~ 3 ;
if ( roff > addr )
{
// Hash from addr to the beginning of the relocation
sha1_update ( & ctx , vm : : _ptr < const u8 > ( addr ) , roff - addr ) ;
}
// Hash relocation type instead
const be_t < u32 > type = low - > type ;
sha1_update ( & ctx , reinterpret_cast < const u8 * > ( & type ) , sizeof ( type ) ) ;
// Set the next addr
addr = roff + 4 ;
}
2020-09-25 16:29:25 +02:00
if ( has_dcbz = = 1 )
{
for ( u32 i = addr , end = block . second + block . first - 1 ; i < = end ; i + = 4 )
{
if ( g_ppu_itype . decode ( vm : : read32 ( i ) ) = = ppu_itype : : DCBZ )
{
has_dcbz = 2 ;
break ;
}
}
}
2018-03-17 18:41:35 +01:00
// Hash from addr to the end of the block
sha1_update ( & ctx , vm : : _ptr < const u8 > ( addr ) , block . second - ( addr - block . first ) ) ;
2017-06-24 17:36:49 +02:00
}
2017-07-01 01:08:51 +02:00
if ( reloc )
{
continue ;
}
2020-09-25 16:29:25 +02:00
if ( has_dcbz = = 1 )
{
for ( u32 i = func . addr , end = func . addr + func . size - 1 ; i < = end ; i + = 4 )
{
if ( g_ppu_itype . decode ( vm : : read32 ( i ) ) = = ppu_itype : : DCBZ )
{
has_dcbz = 2 ;
break ;
}
}
}
2018-02-09 15:49:37 +01:00
sha1_update ( & ctx , vm : : _ptr < const u8 > ( func . addr ) , func . size ) ;
2017-06-22 23:52:09 +02:00
}
2017-02-26 16:56:31 +01:00
2019-01-13 18:06:30 +01:00
if ( false )
2017-07-22 15:39:39 +02:00
{
2017-09-25 17:52:34 +02:00
const be_t < u64 > forced_upd = 3 ;
2017-07-22 15:39:39 +02:00
sha1_update ( & ctx , reinterpret_cast < const u8 * > ( & forced_upd ) , sizeof ( forced_upd ) ) ;
}
2017-06-24 17:36:49 +02:00
sha1_finish ( & ctx , output ) ;
2019-01-13 18:06:30 +01:00
// Settings: should be populated by settings which affect codegen (TODO)
enum class ppu_settings : u32
{
non_win32 ,
2021-12-30 17:39:18 +01:00
accurate_dfma ,
fixup_vnan ,
2022-01-15 12:30:13 +01:00
fixup_nj_denormals ,
2020-09-25 16:29:25 +02:00
accurate_cache_line_stores ,
2020-04-07 19:29:11 +02:00
reservations_128_byte ,
2021-01-19 18:40:15 +01:00
greedy_mode ,
2021-12-30 17:39:18 +01:00
accurate_sat ,
accurate_fpcc ,
accurate_vnan ,
2022-01-15 12:30:13 +01:00
accurate_nj_mode ,
2019-01-13 18:06:30 +01:00
__bitset_enum_max
} ;
be_t < bs_t < ppu_settings > > settings { } ;
# ifndef _WIN32
settings + = ppu_settings : : non_win32 ;
# endif
2021-12-30 17:39:18 +01:00
if ( g_cfg . core . use_accurate_dfma )
settings + = ppu_settings : : accurate_dfma ;
if ( g_cfg . core . ppu_fix_vnan )
settings + = ppu_settings : : fixup_vnan ;
2022-01-15 12:30:13 +01:00
if ( g_cfg . core . ppu_llvm_nj_fixup )
settings + = ppu_settings : : fixup_nj_denormals ;
2020-09-25 16:29:25 +02:00
if ( has_dcbz = = 2 )
settings + = ppu_settings : : accurate_cache_line_stores ;
2020-10-13 21:36:00 +02:00
if ( g_cfg . core . ppu_128_reservations_loop_max_length )
2020-04-07 19:29:11 +02:00
settings + = ppu_settings : : reservations_128_byte ;
2021-01-19 18:40:15 +01:00
if ( g_cfg . core . ppu_llvm_greedy_mode )
settings + = ppu_settings : : greedy_mode ;
2021-12-30 17:39:18 +01:00
if ( has_mfvscr & & g_cfg . core . ppu_set_sat_bit )
settings + = ppu_settings : : accurate_sat ;
if ( g_cfg . core . ppu_set_fpcc )
settings + = ppu_settings : : accurate_fpcc , fmt : : throw_exception ( " FPCC Not implemented " ) ;
if ( g_cfg . core . ppu_set_vnan )
2022-01-15 12:30:13 +01:00
settings + = ppu_settings : : accurate_vnan , settings - = ppu_settings : : fixup_vnan , fmt : : throw_exception ( " VNAN Not implemented " ) ;
if ( g_cfg . core . ppu_use_nj_bit )
settings + = ppu_settings : : accurate_nj_mode , settings - = ppu_settings : : fixup_nj_denormals , fmt : : throw_exception ( " NJ Not implemented " ) ;
2019-01-13 18:06:30 +01:00
// Write version, hash, CPU, settings
2021-02-10 18:20:14 +01:00
fmt : : append ( obj_name , " v5-kusa-%s-%s-%s.obj " , fmt : : base57 ( output , 16 ) , fmt : : base57 ( settings ) , jit_compiler : : cpu ( g_cfg . core . llvm_cpu ) ) ;
2017-06-24 17:36:49 +02:00
}
2017-06-22 23:52:09 +02:00
2017-06-24 17:36:49 +02:00
if ( Emu . IsStopped ( ) )
2017-02-26 16:56:31 +01:00
{
2017-06-24 17:36:49 +02:00
break ;
}
2017-06-22 23:52:09 +02:00
2021-01-30 23:04:07 +01:00
if ( ! check_only )
2017-07-01 01:08:51 +02:00
{
2019-12-04 23:17:57 +01:00
// Update progress dialog
g_progr_ptotal + + ;
2021-01-30 23:04:07 +01:00
link_workload . emplace_back ( obj_name , false ) ;
}
2020-03-03 20:37:29 +01:00
2017-06-24 17:36:49 +02:00
// Check object file
2020-04-07 15:09:47 +02:00
if ( jit_compiler : : check ( cache_path + obj_name ) )
2017-06-24 17:36:49 +02:00
{
2021-01-30 14:08:22 +01:00
if ( ! jit & & ! check_only )
2018-03-17 18:41:35 +01:00
{
2020-03-03 20:37:29 +01:00
ppu_log . success ( " LLVM: Module exists: %s " , obj_name ) ;
2018-03-17 18:41:35 +01:00
2021-07-29 21:31:45 +02:00
// Update progress dialog
g_progr_pdone + + ;
}
2017-06-24 17:36:49 +02:00
continue ;
}
2017-02-26 16:56:31 +01:00
2021-01-30 14:08:22 +01:00
if ( check_only )
{
return true ;
}
2021-01-30 23:04:07 +01:00
// Remember, used in ppu_initialize(void)
compiled_new = true ;
2020-03-03 20:37:29 +01:00
// Adjust information (is_compiled)
link_workload . back ( ) . second = true ;
// Fill workload list for compilation
workload . emplace_back ( std : : move ( obj_name ) , std : : move ( part ) ) ;
}
2021-01-30 23:04:59 +01:00
if ( check_only )
{
return false ;
}
2019-12-04 23:17:57 +01:00
if ( ! workload . empty ( ) )
{
g_progr = " Compiling PPU modules... " ;
}
2020-03-03 20:37:29 +01:00
// Create worker threads for compilation (TODO: how many threads)
{
2021-04-21 22:12:21 +02:00
u32 thread_count = rpcs3 : : utils : : get_max_threads ( ) ;
2020-03-03 20:37:29 +01:00
if ( workload . size ( ) < thread_count )
{
thread_count = : : size32 ( workload ) ;
}
struct thread_index_allocator
{
atomic_t < u64 > index = 0 ;
} ;
2018-05-30 19:34:36 +02:00
2020-06-01 01:27:33 +02:00
// Prevent watchdog thread from terminating
g_watchdog_hold_ctr + + ;
2021-03-02 12:59:19 +01:00
named_thread_group threads ( fmt : : format ( " PPUW.%u. " , + + g_fxo - > get < thread_index_allocator > ( ) . index ) , thread_count , [ & ] ( )
2017-06-24 17:36:49 +02:00
{
// Set low priority
2021-01-25 19:49:16 +01:00
thread_ctrl : : scoped_priority low_prio ( - 1 ) ;
2017-12-19 22:01:03 +01:00
2022-06-14 14:28:38 +02:00
# ifdef __APPLE__
pthread_jit_write_protect_np ( false ) ;
# endif
2021-01-30 23:03:20 +01:00
for ( u32 i = work_cv + + ; i < workload . size ( ) ; i = work_cv + + , g_progr_pdone + + )
2017-02-26 16:56:31 +01:00
{
2021-01-30 23:03:20 +01:00
if ( Emu . IsStopped ( ) )
{
continue ;
}
2020-03-03 20:37:29 +01:00
// Keep allocating workload
2021-03-02 17:22:39 +01:00
const auto & [ obj_name , part ] = std : : as_const ( workload ) [ i ] ;
2020-03-03 20:37:29 +01:00
// Allocate "core"
2021-03-02 12:59:19 +01:00
std : : lock_guard jlock ( g_fxo - > get < jit_core_allocator > ( ) . sem ) ;
2017-06-24 17:36:49 +02:00
2022-10-03 22:20:03 +02:00
if ( Emu . IsStopped ( ) )
{
continue ;
}
2021-01-30 23:03:20 +01:00
ppu_log . warning ( " LLVM: Compiling module %s%s " , cache_path , obj_name ) ;
2020-03-03 20:37:29 +01:00
2021-01-30 23:03:20 +01:00
// Use another JIT instance
jit_compiler jit2 ( { } , g_cfg . core . llvm_cpu , 0x1 ) ;
ppu_initialize2 ( jit2 , part , cache_path , obj_name ) ;
2017-06-22 23:52:09 +02:00
2021-01-30 23:03:20 +01:00
ppu_log . success ( " LLVM: Compiled module %s " , obj_name ) ;
2017-02-26 16:56:31 +01:00
}
2020-03-03 20:37:29 +01:00
} ) ;
threads . join ( ) ;
2020-06-01 01:27:33 +02:00
g_watchdog_hold_ctr - - ;
2020-03-03 20:37:29 +01:00
if ( Emu . IsStopped ( ) | | ! get_current_cpu_thread ( ) )
{
2021-01-30 14:08:22 +01:00
return compiled_new ;
2020-03-03 20:37:29 +01:00
}
2019-12-04 23:17:57 +01:00
if ( workload . size ( ) < link_workload . size ( ) )
{
// Only show this message if this task is relevant
g_progr = " Linking PPU modules... " ;
}
2020-03-03 20:37:29 +01:00
for ( auto [ obj_name , is_compiled ] : link_workload )
{
if ( Emu . IsStopped ( ) )
2017-06-25 14:16:07 +02:00
{
2020-03-03 20:37:29 +01:00
break ;
2017-06-25 14:16:07 +02:00
}
2017-07-15 11:20:40 +02:00
jit - > add ( cache_path + obj_name ) ;
2018-12-31 19:25:19 +01:00
2020-03-03 20:37:29 +01:00
if ( ! is_compiled )
{
ppu_log . success ( " LLVM: Loaded module %s " , obj_name ) ;
2019-12-04 23:17:57 +01:00
g_progr_pdone + + ;
2020-03-03 20:37:29 +01:00
}
}
2016-06-07 22:24:20 +02:00
}
2018-03-17 18:41:35 +01:00
if ( Emu . IsStopped ( ) | | ! get_current_cpu_thread ( ) )
2017-06-25 14:16:07 +02:00
{
2021-01-30 14:08:22 +01:00
return compiled_new ;
2017-06-25 14:16:07 +02:00
}
2017-09-10 06:04:29 +02:00
// Jit can be null if the loop doesn't ever enter.
2022-06-14 14:28:38 +02:00
# ifdef __APPLE__
pthread_jit_write_protect_np ( false ) ;
# endif
2021-01-31 19:38:47 +01:00
if ( jit & & ! jit_mod . init )
2017-12-19 22:01:03 +01:00
{
2017-07-10 21:22:54 +02:00
jit - > fin ( ) ;
2017-12-31 13:45:12 +01:00
2017-07-10 21:22:54 +02:00
// Get and install function addresses
for ( const auto & func : info . funcs )
2017-06-24 17:36:49 +02:00
{
2017-07-10 21:22:54 +02:00
if ( ! func . size ) continue ;
2021-01-31 19:38:47 +01:00
const auto name = fmt : : format ( " __0x%x " , func . addr - reloc ) ;
2021-12-30 17:39:18 +01:00
const auto addr = ensure ( reinterpret_cast < ppu_intrp_func_t > ( jit - > get ( name ) ) ) ;
2021-09-02 17:14:26 +02:00
jit_mod . funcs . emplace_back ( addr ) ;
2022-08-17 15:53:05 +02:00
ppu_register_function_at ( func . addr , 4 , addr ) ;
2017-06-29 16:25:39 +02:00
2021-01-31 19:38:47 +01:00
if ( g_cfg . core . ppu_debug )
ppu_log . notice ( " Installing function %s at 0x%x: %p (reloc = 0x%x) " , name , func . addr , ppu_ref ( func . addr ) , reloc ) ;
2017-07-10 21:22:54 +02:00
}
2021-01-31 19:38:47 +01:00
jit_mod . init = true ;
2017-07-10 21:22:54 +02:00
}
else
2017-06-29 16:25:39 +02:00
{
2020-12-18 08:39:54 +01:00
usz index = 0 ;
2017-07-10 21:22:54 +02:00
// Locate existing functions
for ( const auto & func : info . funcs )
2017-06-29 16:25:39 +02:00
{
2017-07-10 21:22:54 +02:00
if ( ! func . size ) continue ;
2021-09-02 17:14:26 +02:00
const u64 addr = reinterpret_cast < uptr > ( ensure ( jit_mod . funcs [ index + + ] ) ) ;
2022-08-17 15:53:05 +02:00
ppu_register_function_at ( func . addr , 4 , addr ) ;
2021-01-31 19:38:47 +01:00
if ( g_cfg . core . ppu_debug )
ppu_log . notice ( " Reinstalling function at 0x%x: %p (reloc=0x%x) " , func . addr , ppu_ref ( func . addr ) , reloc ) ;
2017-07-10 21:22:54 +02:00
}
index = 0 ;
2017-06-29 16:25:39 +02:00
}
2021-01-30 14:08:22 +01:00
return compiled_new ;
2017-07-23 09:54:00 +02:00
# else
fmt : : throw_exception ( " LLVM is not available in this build. " ) ;
2017-06-24 21:01:27 +02:00
# endif
2017-06-24 17:36:49 +02:00
}
2018-05-30 19:34:36 +02:00
static void ppu_initialize2 ( jit_compiler & jit , const ppu_module & module_part , const std : : string & cache_path , const std : : string & obj_name )
2017-06-24 17:36:49 +02:00
{
2017-06-24 21:01:27 +02:00
# ifdef LLVM_AVAILABLE
2017-06-24 17:36:49 +02:00
using namespace llvm ;
2017-06-22 23:52:09 +02:00
// Create LLVM module
2020-05-06 17:18:30 +02:00
std : : unique_ptr < Module > _module = std : : make_unique < Module > ( obj_name , jit . get_context ( ) ) ;
2017-06-22 23:52:09 +02:00
// Initialize target
2022-06-14 14:28:38 +02:00
# if defined(__APPLE__) && defined(ARCH_ARM64)
// Force target linux on macOS arm64 to bypass some 64-bit address space linking issues
2022-06-14 17:13:43 +02:00
_module - > setTargetTriple ( Triple : : normalize ( utils : : c_llvm_default_triple ) ) ;
2022-06-14 14:28:38 +02:00
# else
2020-05-06 17:18:30 +02:00
_module - > setTargetTriple ( Triple : : normalize ( sys : : getProcessTriple ( ) ) ) ;
2022-06-14 14:28:38 +02:00
# endif
2020-05-06 17:18:30 +02:00
_module - > setDataLayout ( jit . get_engine ( ) . getTargetMachine ( ) - > createDataLayout ( ) ) ;
2017-12-19 22:01:03 +01:00
2017-06-22 23:52:09 +02:00
// Initialize translator
2020-05-06 17:18:30 +02:00
PPUTranslator translator ( jit . get_context ( ) , _module . get ( ) , module_part , jit . get_engine ( ) ) ;
2017-06-22 23:52:09 +02:00
// Define some types
2021-01-31 19:38:47 +01:00
const auto _func = FunctionType : : get ( translator . get_type < void > ( ) , {
translator . get_type < u8 * > ( ) , // Exec base
translator . GetContextType ( ) - > getPointerTo ( ) , // PPU context
translator . get_type < u64 > ( ) , // Segment address (for PRX)
translator . get_type < u8 * > ( ) , // Memory base
translator . get_type < u64 > ( ) , // r0
translator . get_type < u64 > ( ) , // r1
translator . get_type < u64 > ( ) , // r2
} , false ) ;
2017-06-22 23:52:09 +02:00
// Initialize function list
for ( const auto & func : module_part . funcs )
2017-01-22 20:03:57 +01:00
{
2017-06-22 23:52:09 +02:00
if ( func . size )
2017-01-22 20:03:57 +01:00
{
2020-05-06 17:18:30 +02:00
const auto f = cast < Function > ( _module - > getOrInsertFunction ( func . name , _func ) . getCallee ( ) ) ;
2021-01-31 19:38:47 +01:00
f - > setCallingConv ( CallingConv : : GHC ) ;
f - > addAttribute ( 2 , Attribute : : NoAlias ) ;
2021-01-19 18:40:15 +01:00
f - > addFnAttr ( Attribute : : NoUnwind ) ;
2017-06-22 23:52:09 +02:00
}
}
2017-01-22 20:03:57 +01:00
2016-06-07 22:24:20 +02:00
{
2020-05-06 17:18:30 +02:00
legacy : : FunctionPassManager pm ( _module . get ( ) ) ;
2017-06-22 23:52:09 +02:00
// Basic optimizations
2017-06-25 23:29:09 +02:00
//pm.add(createCFGSimplificationPass());
//pm.add(createPromoteMemoryToRegisterPass());
2017-06-22 23:52:09 +02:00
pm . add ( createEarlyCSEPass ( ) ) ;
2017-06-28 13:56:35 +02:00
//pm.add(createTailCallEliminationPass());
2017-06-25 23:29:09 +02:00
//pm.add(createInstructionCombiningPass());
2017-06-22 23:52:09 +02:00
//pm.add(createBasicAAWrapperPass());
//pm.add(new MemoryDependenceAnalysis());
2017-06-28 13:56:35 +02:00
//pm.add(createLICMPass());
//pm.add(createLoopInstSimplifyPass());
2017-06-25 23:29:09 +02:00
//pm.add(createNewGVNPass());
2017-06-22 23:52:09 +02:00
pm . add ( createDeadStoreEliminationPass ( ) ) ;
2017-06-28 13:56:35 +02:00
//pm.add(createSCCPPass());
//pm.add(createReassociatePass());
//pm.add(createInstructionCombiningPass());
//pm.add(createInstructionSimplifierPass());
//pm.add(createAggressiveDCEPass());
//pm.add(createCFGSimplificationPass());
2017-06-22 23:52:09 +02:00
//pm.add(createLintPass()); // Check
// Translate functions
2020-12-18 08:39:54 +01:00
for ( usz fi = 0 , fmax = module_part . funcs . size ( ) ; fi < fmax ; fi + + )
2016-06-07 22:24:20 +02:00
{
2017-06-22 23:52:09 +02:00
if ( Emu . IsStopped ( ) )
2017-01-22 20:03:57 +01:00
{
2020-02-01 09:31:27 +01:00
ppu_log . success ( " LLVM: Translation cancelled " ) ;
2017-06-22 23:52:09 +02:00
return ;
}
2017-01-22 20:03:57 +01:00
2017-06-28 13:54:18 +02:00
if ( module_part . funcs [ fi ] . size )
2017-06-22 23:52:09 +02:00
{
// Translate
2017-07-01 01:08:51 +02:00
if ( const auto func = translator . Translate ( module_part . funcs [ fi ] ) )
{
// Run optimization passes
pm . run ( * func ) ;
}
else
{
Emu . Pause ( ) ;
return ;
2017-12-19 22:01:03 +01:00
}
2016-06-25 07:16:15 +02:00
}
2016-06-07 22:24:20 +02:00
}
2020-06-04 08:19:56 +02:00
//legacy::PassManager mpm;
2016-06-07 22:24:20 +02:00
2017-06-22 23:52:09 +02:00
// Remove unused functions, structs, global variables, etc
2017-06-25 23:29:09 +02:00
//mpm.add(createStripDeadPrototypesPass());
2017-06-22 23:52:09 +02:00
//mpm.add(createFunctionInliningPass());
2017-06-25 23:29:09 +02:00
//mpm.add(createDeadInstEliminationPass());
//mpm.run(*module);
2017-01-22 20:03:57 +01:00
2017-06-22 23:52:09 +02:00
std : : string result ;
raw_string_ostream out ( result ) ;
2016-06-07 22:24:20 +02:00
2017-06-22 23:52:09 +02:00
if ( g_cfg . core . llvm_logs )
{
2020-05-06 17:18:30 +02:00
out < < * _module ; // print IR
2017-06-24 17:36:49 +02:00
fs : : file ( cache_path + obj_name + " .log " , fs : : rewrite ) . write ( out . str ( ) ) ;
2017-06-22 23:52:09 +02:00
result . clear ( ) ;
}
2016-06-07 22:24:20 +02:00
2020-05-06 17:18:30 +02:00
if ( verifyModule ( * _module , & out ) )
2016-06-07 22:24:20 +02:00
{
2017-06-22 23:52:09 +02:00
out . flush ( ) ;
2020-02-01 09:31:27 +01:00
ppu_log . error ( " LLVM: Verification failed for %s: \n %s " , obj_name , result ) ;
2022-01-20 18:44:49 +01:00
Emu . CallFromMainThread ( [ ] { Emu . GracefulShutdown ( false , true ) ; } ) ;
2017-06-22 23:52:09 +02:00
return ;
2016-06-07 22:24:20 +02:00
}
2017-06-22 23:52:09 +02:00
2020-05-06 17:18:30 +02:00
ppu_log . notice ( " LLVM: %zu functions generated " , _module - > getFunctionList ( ) . size ( ) ) ;
2016-06-07 22:24:20 +02:00
}
2017-06-24 17:36:49 +02:00
// Load or compile module
2020-05-06 17:18:30 +02:00
jit . add ( std : : move ( _module ) , cache_path ) ;
2017-06-24 17:36:49 +02:00
# endif // LLVM_AVAILABLE
2016-06-22 15:37:51 +02:00
}