2018-11-05 12:38:35 +01:00
# include " stdafx.h "
2018-05-14 22:07:36 +02:00
# include "Utilities/JIT.h"
2017-07-18 19:03:47 +02:00
# include "Utilities/sysinfo.h"
2018-09-25 22:34:45 +02:00
# include "Emu/Memory/vm_ptr.h"
# include "Emu/Memory/vm_reservation.h"
2014-06-02 19:27:24 +02:00
2014-08-26 01:55:37 +02:00
# include "Emu/IdManager.h"
2020-02-11 22:36:46 +01:00
# include "Emu/RSX/RSXThread.h"
2014-08-26 01:55:37 +02:00
# include "Emu/Cell/PPUThread.h"
2016-04-14 01:09:41 +02:00
# include "Emu/Cell/ErrorCodes.h"
# include "Emu/Cell/lv2/sys_spu.h"
# include "Emu/Cell/lv2/sys_event_flag.h"
# include "Emu/Cell/lv2/sys_event.h"
# include "Emu/Cell/lv2/sys_interrupt.h"
2014-08-22 23:15:02 +02:00
2014-08-26 01:55:37 +02:00
# include "Emu/Cell/SPUDisAsm.h"
2012-11-15 00:39:56 +01:00
# include "Emu/Cell/SPUThread.h"
# include "Emu/Cell/SPUInterpreter.h"
2014-04-06 21:23:32 +02:00
# include "Emu/Cell/SPURecompiler.h"
2017-02-17 20:35:57 +01:00
# include "Emu/Cell/RawSPUThread.h"
2016-04-25 12:49:12 +02:00
2016-05-13 15:55:34 +02:00
# include <cmath>
2014-07-16 14:09:20 +02:00
# include <cfenv>
2017-04-23 22:09:27 +02:00
# include <atomic>
# include <thread>
2017-07-18 19:03:47 +02:00
2019-04-15 14:52:43 +02:00
// Verify AVX availability for TSX transactions
static const bool s_tsx_avx = utils : : has_avx ( ) ;
2019-05-18 22:47:35 +02:00
// For special case
static const bool s_tsx_haswell = utils : : has_rtm ( ) & & ! utils : : has_mpx ( ) ;
2019-06-06 20:32:35 +02:00
static FORCE_INLINE bool cmp_rdata ( const decltype ( spu_thread : : rdata ) & lhs , const decltype ( spu_thread : : rdata ) & rhs )
2017-02-17 20:35:57 +01:00
{
2019-06-06 20:32:35 +02:00
const v128 a = ( lhs [ 0 ] ^ rhs [ 0 ] ) | ( lhs [ 1 ] ^ rhs [ 1 ] ) ;
const v128 b = ( lhs [ 2 ] ^ rhs [ 2 ] ) | ( lhs [ 3 ] ^ rhs [ 3 ] ) ;
const v128 c = ( lhs [ 4 ] ^ rhs [ 4 ] ) | ( lhs [ 5 ] ^ rhs [ 5 ] ) ;
const v128 d = ( lhs [ 6 ] ^ rhs [ 6 ] ) | ( lhs [ 7 ] ^ rhs [ 7 ] ) ;
const v128 r = ( a | b ) | ( c | d ) ;
return ! ( r . _u64 [ 0 ] | r . _u64 [ 1 ] ) ;
2017-02-17 20:35:57 +01:00
}
2014-07-16 14:09:20 +02:00
2019-06-06 20:32:35 +02:00
static FORCE_INLINE void mov_rdata ( decltype ( spu_thread : : rdata ) & dst , const decltype ( spu_thread : : rdata ) & src )
2019-01-15 16:31:21 +01:00
{
{
2019-06-06 20:32:35 +02:00
const v128 data0 = src [ 0 ] ;
const v128 data1 = src [ 1 ] ;
const v128 data2 = src [ 2 ] ;
2019-01-15 16:31:21 +01:00
dst [ 0 ] = data0 ;
dst [ 1 ] = data1 ;
dst [ 2 ] = data2 ;
}
{
2019-06-06 20:32:35 +02:00
const v128 data0 = src [ 3 ] ;
const v128 data1 = src [ 4 ] ;
const v128 data2 = src [ 5 ] ;
2019-01-15 16:31:21 +01:00
dst [ 3 ] = data0 ;
dst [ 4 ] = data1 ;
dst [ 5 ] = data2 ;
}
{
2019-06-06 20:32:35 +02:00
const v128 data0 = src [ 6 ] ;
const v128 data1 = src [ 7 ] ;
2019-01-15 16:31:21 +01:00
dst [ 6 ] = data0 ;
dst [ 7 ] = data1 ;
}
2019-05-12 23:01:28 +02:00
}
2019-01-15 16:31:21 +01:00
2020-02-11 22:36:46 +01:00
// Returns nullptr if rsx does not need pausing on reservations op, rsx ptr otherwise
static FORCE_INLINE rsx : : thread * get_rsx_if_needs_res_pause ( u32 addr )
{
if ( ! g_cfg . core . rsx_accurate_res_access ) [[likely]]
{
return { } ;
}
const auto render = rsx : : get_current_renderer ( ) ;
ASSUME ( render ) ;
2020-02-19 18:03:59 +01:00
if ( render - > iomap_table . io [ addr > > 20 ] . load ( ) = = umax ) [[likely]]
2020-02-11 22:36:46 +01:00
{
return { } ;
}
return render ;
}
2015-07-06 01:21:15 +02:00
extern u64 get_timebased_time ( ) ;
2017-07-24 19:55:11 +02:00
extern u64 get_system_time ( ) ;
2015-07-06 01:21:15 +02:00
2018-05-27 22:37:01 +02:00
extern thread_local u64 g_tls_fault_spu ;
2017-04-23 22:09:27 +02:00
2017-05-20 13:45:02 +02:00
template < >
void fmt_class_string < spu_decoder_type > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( spu_decoder_type type )
{
switch ( type )
{
case spu_decoder_type : : precise : return " Interpreter (precise) " ;
case spu_decoder_type : : fast : return " Interpreter (fast) " ;
case spu_decoder_type : : asmjit : return " Recompiler (ASMJIT) " ;
case spu_decoder_type : : llvm : return " Recompiler (LLVM) " ;
}
return unknown ;
} ) ;
}
2018-04-30 18:39:06 +02:00
template < >
void fmt_class_string < spu_block_size_type > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( spu_block_size_type type )
{
switch ( type )
{
case spu_block_size_type : : safe : return " Safe " ;
case spu_block_size_type : : mega : return " Mega " ;
case spu_block_size_type : : giga : return " Giga " ;
}
return unknown ;
} ) ;
}
2017-07-11 21:21:22 +02:00
namespace spu
{
namespace scheduler
{
2017-07-18 00:12:13 +02:00
std : : array < std : : atomic < u8 > , 65536 > atomic_instruction_table = { } ;
2017-07-24 19:55:11 +02:00
constexpr u32 native_jiffy_duration_us = 1500 ; //About 1ms resolution with a half offset
2017-07-11 21:21:22 +02:00
2020-02-05 05:44:10 +01:00
void acquire_pc_address ( spu_thread & spu , u32 pc , u32 timeout_ms , u32 max_concurrent_instructions )
2017-07-11 21:21:22 +02:00
{
2017-07-18 00:12:13 +02:00
const u32 pc_offset = pc > > 2 ;
2017-07-11 21:21:22 +02:00
2017-07-24 19:55:11 +02:00
if ( atomic_instruction_table [ pc_offset ] . load ( std : : memory_order_consume ) > = max_concurrent_instructions )
2017-07-14 16:00:49 +02:00
{
2019-06-29 22:41:52 +02:00
spu . state + = cpu_flag : : wait ;
2017-07-24 19:55:11 +02:00
if ( timeout_ms > 0 )
2017-07-14 16:00:49 +02:00
{
2017-10-21 13:21:37 +02:00
const u64 timeout = timeout_ms * 1000u ; //convert to microseconds
const u64 start = get_system_time ( ) ;
2017-07-24 19:55:11 +02:00
auto remaining = timeout ;
while ( atomic_instruction_table [ pc_offset ] . load ( std : : memory_order_consume ) > = max_concurrent_instructions )
{
if ( remaining > = native_jiffy_duration_us )
std : : this_thread : : sleep_for ( 1 ms ) ;
else
2018-05-28 15:52:21 +02:00
std : : this_thread : : yield ( ) ;
2017-07-24 19:55:11 +02:00
const auto now = get_system_time ( ) ;
const auto elapsed = now - start ;
if ( elapsed > timeout ) break ;
remaining = timeout - elapsed ;
}
}
else
{
//Slight pause if function is overburdened
2017-10-21 13:21:37 +02:00
const auto count = atomic_instruction_table [ pc_offset ] . load ( std : : memory_order_consume ) * 100ull ;
busy_wait ( count ) ;
2017-07-14 16:00:49 +02:00
}
2019-06-29 22:41:52 +02:00
if ( spu . test_stopped ( ) )
{
spu_runtime : : g_escape ( & spu ) ;
}
2017-07-11 21:21:22 +02:00
}
2017-07-18 00:12:13 +02:00
atomic_instruction_table [ pc_offset ] + + ;
2017-07-11 21:21:22 +02:00
}
void release_pc_address ( u32 pc )
{
2017-07-18 00:12:13 +02:00
const u32 pc_offset = pc > > 2 ;
2017-07-11 21:21:22 +02:00
2017-07-18 00:12:13 +02:00
atomic_instruction_table [ pc_offset ] - - ;
2017-07-11 21:21:22 +02:00
}
struct concurrent_execution_watchdog
{
u32 pc = 0 ;
2017-07-14 16:00:49 +02:00
bool active = false ;
2017-07-11 21:21:22 +02:00
2018-10-11 00:17:19 +02:00
concurrent_execution_watchdog ( spu_thread & spu )
2017-07-11 21:21:22 +02:00
: pc ( spu . pc )
{
2020-02-05 05:44:10 +01:00
if ( const u32 max_concurrent_instructions = g_cfg . core . preferred_spu_threads )
2017-07-14 16:00:49 +02:00
{
2020-02-05 05:44:10 +01:00
acquire_pc_address ( spu , pc , g_cfg . core . spu_delay_penalty , max_concurrent_instructions ) ;
2017-07-14 16:00:49 +02:00
active = true ;
}
2017-07-11 21:21:22 +02:00
}
~ concurrent_execution_watchdog ( )
{
2017-07-14 16:00:49 +02:00
if ( active )
release_pc_address ( pc ) ;
2017-07-11 21:21:22 +02:00
}
} ;
}
}
2019-06-06 20:32:35 +02:00
const auto spu_putllc_tx = build_function_asm < u32 ( * ) ( u32 raddr , u64 rtime , const void * _old , const void * _new ) > ( [ ] ( asmjit : : X86Assembler & c , auto & args )
2018-05-14 22:07:36 +02:00
{
using namespace asmjit ;
Label fall = c . newLabel ( ) ;
Label fail = c . newLabel ( ) ;
2019-04-15 14:52:43 +02:00
Label _ret = c . newLabel ( ) ;
2019-06-06 20:32:35 +02:00
Label skip = c . newLabel ( ) ;
Label next = c . newLabel ( ) ;
2019-04-15 14:52:43 +02:00
if ( utils : : has_avx ( ) & & ! s_tsx_avx )
{
c . vzeroupper ( ) ;
}
// Create stack frame if necessary (Windows ABI has only 6 volatile vector registers)
2019-05-18 22:47:35 +02:00
c . push ( x86 : : rbp ) ;
c . push ( x86 : : r13 ) ;
c . push ( x86 : : r12 ) ;
c . push ( x86 : : rbx ) ;
c . sub ( x86 : : rsp , 168 ) ;
2019-04-15 14:52:43 +02:00
# ifdef _WIN32
if ( s_tsx_avx )
{
c . vmovups ( x86 : : oword_ptr ( x86 : : rsp , 0 ) , x86 : : xmm6 ) ;
c . vmovups ( x86 : : oword_ptr ( x86 : : rsp , 16 ) , x86 : : xmm7 ) ;
}
else
{
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 0 ) , x86 : : xmm6 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 16 ) , x86 : : xmm7 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 32 ) , x86 : : xmm8 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 48 ) , x86 : : xmm9 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 64 ) , x86 : : xmm10 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 80 ) , x86 : : xmm11 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 96 ) , x86 : : xmm12 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 112 ) , x86 : : xmm13 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 128 ) , x86 : : xmm14 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 144 ) , x86 : : xmm15 ) ;
}
# endif
2018-05-14 22:07:36 +02:00
// Prepare registers
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_reservations ) ) ;
2019-05-18 22:47:35 +02:00
c . mov ( x86 : : rbx , x86 : : qword_ptr ( x86 : : rax ) ) ;
2018-05-14 22:07:36 +02:00
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_base_addr ) ) ;
2019-05-18 22:47:35 +02:00
c . mov ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . lea ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rbp , args [ 0 ] ) ) ;
2018-05-14 22:07:36 +02:00
c . shr ( args [ 0 ] , 4 ) ;
2019-05-18 22:47:35 +02:00
c . lea ( x86 : : rbx , x86 : : qword_ptr ( x86 : : rbx , args [ 0 ] ) ) ;
c . xor_ ( x86 : : r12d , x86 : : r12d ) ;
c . mov ( x86 : : r13 , args [ 1 ] ) ;
2018-05-14 22:07:36 +02:00
2019-04-15 14:52:43 +02:00
// Prepare data
if ( s_tsx_avx )
{
c . vmovups ( x86 : : ymm0 , x86 : : yword_ptr ( args [ 2 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm1 , x86 : : yword_ptr ( args [ 2 ] , 32 ) ) ;
c . vmovups ( x86 : : ymm2 , x86 : : yword_ptr ( args [ 2 ] , 64 ) ) ;
c . vmovups ( x86 : : ymm3 , x86 : : yword_ptr ( args [ 2 ] , 96 ) ) ;
c . vmovups ( x86 : : ymm4 , x86 : : yword_ptr ( args [ 3 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm5 , x86 : : yword_ptr ( args [ 3 ] , 32 ) ) ;
c . vmovups ( x86 : : ymm6 , x86 : : yword_ptr ( args [ 3 ] , 64 ) ) ;
c . vmovups ( x86 : : ymm7 , x86 : : yword_ptr ( args [ 3 ] , 96 ) ) ;
}
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 ) ) ;
c . movaps ( x86 : : xmm8 , x86 : : oword_ptr ( args [ 3 ] , 0 ) ) ;
c . movaps ( x86 : : xmm9 , x86 : : oword_ptr ( args [ 3 ] , 16 ) ) ;
c . movaps ( x86 : : xmm10 , x86 : : oword_ptr ( args [ 3 ] , 32 ) ) ;
c . movaps ( x86 : : xmm11 , x86 : : oword_ptr ( args [ 3 ] , 48 ) ) ;
c . movaps ( x86 : : xmm12 , x86 : : oword_ptr ( args [ 3 ] , 64 ) ) ;
c . movaps ( x86 : : xmm13 , x86 : : oword_ptr ( args [ 3 ] , 80 ) ) ;
c . movaps ( x86 : : xmm14 , x86 : : oword_ptr ( args [ 3 ] , 96 ) ) ;
c . movaps ( x86 : : xmm15 , x86 : : oword_ptr ( args [ 3 ] , 112 ) ) ;
}
2018-05-14 22:07:36 +02:00
// Begin transaction
2019-06-06 20:32:35 +02:00
build_transaction_enter ( c , fall , x86 : : r12 , 4 ) ;
2019-05-18 22:47:35 +02:00
c . mov ( x86 : : rax , x86 : : qword_ptr ( x86 : : rbx ) ) ;
c . and_ ( x86 : : rax , - 128 ) ;
c . cmp ( x86 : : rax , x86 : : r13 ) ;
2019-06-06 20:32:35 +02:00
c . jne ( fail ) ;
c . test ( x86 : : qword_ptr ( x86 : : rbx ) , 127 ) ;
c . jnz ( skip ) ;
2019-04-15 14:52:43 +02:00
if ( s_tsx_avx )
{
2019-05-18 22:47:35 +02:00
c . vxorps ( x86 : : ymm0 , x86 : : ymm0 , x86 : : yword_ptr ( x86 : : rbp , 0 ) ) ;
c . vxorps ( x86 : : ymm1 , x86 : : ymm1 , x86 : : yword_ptr ( x86 : : rbp , 32 ) ) ;
c . vxorps ( x86 : : ymm2 , x86 : : ymm2 , x86 : : yword_ptr ( x86 : : rbp , 64 ) ) ;
c . vxorps ( x86 : : ymm3 , x86 : : ymm3 , x86 : : yword_ptr ( x86 : : rbp , 96 ) ) ;
2019-04-15 14:52:43 +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
{
2019-05-18 22:47:35 +02:00
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 ) ) ;
2019-04-15 14:52:43 +02:00
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 ) ;
}
2018-05-14 22:07:36 +02:00
c . jnz ( fail ) ;
2019-04-15 14:52:43 +02:00
if ( s_tsx_avx )
{
2019-05-18 22:47:35 +02:00
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 0 ) , x86 : : ymm4 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 32 ) , x86 : : ymm5 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 64 ) , x86 : : ymm6 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 96 ) , x86 : : ymm7 ) ;
2019-04-15 14:52:43 +02:00
}
else
{
2019-05-18 22:47:35 +02:00
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 0 ) , x86 : : xmm8 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 16 ) , x86 : : xmm9 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 32 ) , x86 : : xmm10 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 48 ) , x86 : : xmm11 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 64 ) , x86 : : xmm12 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 80 ) , x86 : : xmm13 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 96 ) , x86 : : xmm14 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 112 ) , x86 : : xmm15 ) ;
2019-04-15 14:52:43 +02:00
}
2019-05-18 22:47:35 +02:00
c . sub ( x86 : : qword_ptr ( x86 : : rbx ) , - 128 ) ;
2018-05-14 22:07:36 +02:00
c . xend ( ) ;
2019-06-06 20:32:35 +02:00
c . mov ( x86 : : eax , 1 ) ;
2019-04-15 14:52:43 +02:00
c . jmp ( _ret ) ;
2018-05-14 22:07:36 +02:00
2019-06-06 20:32:35 +02:00
c . bind ( skip ) ;
c . xor_ ( x86 : : eax , x86 : : eax ) ;
c . xor_ ( x86 : : r12d , x86 : : r12d ) ;
build_transaction_abort ( c , 0 ) ;
//c.jmp(fall);
2018-05-14 22:07:36 +02:00
c . bind ( fall ) ;
2018-05-18 17:51:48 +02:00
c . sar ( x86 : : eax , 24 ) ;
c . js ( fail ) ;
2019-06-06 20:32:35 +02:00
c . lock ( ) . bts ( x86 : : dword_ptr ( args [ 2 ] , : : offset32 ( & spu_thread : : state ) - : : offset32 ( & spu_thread : : rdata ) ) , static_cast < u32 > ( cpu_flag : : wait ) ) ;
// Touch memory if transaction failed without RETRY flag on the first attempt
c . cmp ( x86 : : r12 , 1 ) ;
c . jne ( next ) ;
2019-05-18 22:47:35 +02:00
c . xor_ ( x86 : : rbp , 0xf80 ) ;
2019-06-06 20:32:35 +02:00
c . lock ( ) . add ( x86 : : dword_ptr ( x86 : : rbp ) , 0 ) ;
2019-05-18 22:47:35 +02:00
c . xor_ ( x86 : : rbp , 0xf80 ) ;
Label fall2 = c . newLabel ( ) ;
2019-06-06 20:32:35 +02:00
Label fail2 = c . newLabel ( ) ;
2019-05-18 22:47:35 +02:00
// Lightened transaction: only compare and swap data
2019-06-06 20:32:35 +02:00
c . bind ( next ) ;
2019-10-11 15:57:20 +02:00
// Try to "lock" reservation
c . mov ( x86 : : rax , x86 : : r13 ) ;
c . add ( x86 : : r13 , 1 ) ;
c . lock ( ) . cmpxchg ( x86 : : qword_ptr ( x86 : : rbx ) , x86 : : r13 ) ;
c . jne ( fail ) ;
2019-06-06 20:32:35 +02:00
build_transaction_enter ( c , fall2 , x86 : : r12 , 666 ) ;
2019-05-18 22:47:35 +02:00
if ( s_tsx_avx )
{
c . vxorps ( x86 : : ymm0 , x86 : : ymm0 , x86 : : yword_ptr ( x86 : : rbp , 0 ) ) ;
c . vxorps ( x86 : : ymm1 , x86 : : ymm1 , x86 : : yword_ptr ( x86 : : rbp , 32 ) ) ;
c . vxorps ( x86 : : ymm2 , x86 : : ymm2 , x86 : : yword_ptr ( x86 : : rbp , 64 ) ) ;
c . vxorps ( x86 : : ymm3 , x86 : : ymm3 , x86 : : yword_ptr ( x86 : : rbp , 96 ) ) ;
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 ) ;
}
2019-06-06 20:32:35 +02:00
c . jnz ( fail2 ) ;
2019-05-18 22:47:35 +02:00
if ( s_tsx_avx )
{
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 0 ) , x86 : : ymm4 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 32 ) , x86 : : ymm5 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 64 ) , x86 : : ymm6 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 96 ) , x86 : : ymm7 ) ;
}
else
{
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 0 ) , x86 : : xmm8 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 16 ) , x86 : : xmm9 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 32 ) , x86 : : xmm10 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 48 ) , x86 : : xmm11 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 64 ) , x86 : : xmm12 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 80 ) , x86 : : xmm13 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 96 ) , x86 : : xmm14 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 112 ) , x86 : : xmm15 ) ;
}
c . xend ( ) ;
c . lock ( ) . add ( x86 : : qword_ptr ( x86 : : rbx ) , 127 ) ;
2019-06-06 20:32:35 +02:00
c . mov ( x86 : : eax , 1 ) ;
2019-05-18 22:47:35 +02:00
c . jmp ( _ret ) ;
c . bind ( fall2 ) ;
2019-06-06 20:32:35 +02:00
c . sar ( x86 : : eax , 24 ) ;
c . js ( fail2 ) ;
c . mov ( x86 : : eax , 2 ) ;
c . jmp ( _ret ) ;
2018-05-14 22:07:36 +02:00
c . bind ( fail ) ;
build_transaction_abort ( c , 0xff ) ;
2019-06-06 20:32:35 +02:00
c . xor_ ( x86 : : eax , x86 : : eax ) ;
c . jmp ( _ret ) ;
c . bind ( fail2 ) ;
build_transaction_abort ( c , 0xff ) ;
2019-05-18 22:47:35 +02:00
c . lock ( ) . sub ( x86 : : qword_ptr ( x86 : : rbx ) , 1 ) ;
2019-06-06 20:32:35 +02:00
c . xor_ ( x86 : : eax , x86 : : eax ) ;
2019-05-18 22:47:35 +02:00
//c.jmp(_ret);
2019-04-15 14:52:43 +02:00
c . bind ( _ret ) ;
# ifdef _WIN32
if ( s_tsx_avx )
{
c . vmovups ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rsp , 0 ) ) ;
c . vmovups ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rsp , 16 ) ) ;
}
else
{
c . movups ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rsp , 0 ) ) ;
c . movups ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rsp , 16 ) ) ;
c . movups ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rsp , 32 ) ) ;
c . movups ( x86 : : xmm9 , x86 : : oword_ptr ( x86 : : rsp , 48 ) ) ;
c . movups ( x86 : : xmm10 , x86 : : oword_ptr ( x86 : : rsp , 64 ) ) ;
c . movups ( x86 : : xmm11 , x86 : : oword_ptr ( x86 : : rsp , 80 ) ) ;
c . movups ( x86 : : xmm12 , x86 : : oword_ptr ( x86 : : rsp , 96 ) ) ;
c . movups ( x86 : : xmm13 , x86 : : oword_ptr ( x86 : : rsp , 112 ) ) ;
c . movups ( x86 : : xmm14 , x86 : : oword_ptr ( x86 : : rsp , 128 ) ) ;
c . movups ( x86 : : xmm15 , x86 : : oword_ptr ( x86 : : rsp , 144 ) ) ;
}
# endif
if ( s_tsx_avx )
{
c . vzeroupper ( ) ;
}
2019-05-18 22:47:35 +02:00
c . add ( x86 : : rsp , 168 ) ;
c . pop ( x86 : : rbx ) ;
c . pop ( x86 : : r12 ) ;
c . pop ( x86 : : r13 ) ;
c . pop ( x86 : : rbp ) ;
2019-01-15 16:31:21 +01:00
c . ret ( ) ;
2018-05-14 22:07:36 +02:00
} ) ;
2019-06-06 20:32:35 +02:00
const auto spu_getll_tx = build_function_asm < u64 ( * ) ( u32 raddr , void * rdata ) > ( [ ] ( asmjit : : X86Assembler & c , auto & args )
2018-05-14 22:07:36 +02:00
{
using namespace asmjit ;
Label fall = c . newLabel ( ) ;
2019-04-15 14:52:43 +02:00
Label _ret = c . newLabel ( ) ;
if ( utils : : has_avx ( ) & & ! s_tsx_avx )
{
c . vzeroupper ( ) ;
}
// Create stack frame if necessary (Windows ABI has only 6 volatile vector registers)
2019-05-25 18:56:24 +02:00
c . push ( x86 : : rbp ) ;
c . push ( x86 : : r13 ) ;
c . push ( x86 : : r12 ) ;
c . push ( x86 : : rbx ) ;
c . sub ( x86 : : rsp , 72 ) ;
2019-04-15 14:52:43 +02:00
# 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
2018-05-14 22:07:36 +02:00
// Prepare registers
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_reservations ) ) ;
2019-05-25 18:56:24 +02:00
c . mov ( x86 : : rbx , x86 : : qword_ptr ( x86 : : rax ) ) ;
2018-05-14 22:07:36 +02:00
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_base_addr ) ) ;
2019-05-25 18:56:24 +02:00
c . mov ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . lea ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rbp , args [ 0 ] ) ) ;
2018-05-14 22:07:36 +02:00
c . shr ( args [ 0 ] , 4 ) ;
2019-05-25 18:56:24 +02:00
c . lea ( x86 : : rbx , x86 : : qword_ptr ( x86 : : rbx , args [ 0 ] ) ) ;
c . xor_ ( x86 : : r12d , x86 : : r12d ) ;
c . mov ( x86 : : r13 , args [ 1 ] ) ;
2018-05-14 22:07:36 +02:00
// Begin transaction
2019-06-06 20:32:35 +02:00
build_transaction_enter ( c , fall , x86 : : r12 , 16 ) ;
2019-05-25 18:56:24 +02:00
c . mov ( x86 : : rax , x86 : : qword_ptr ( x86 : : rbx ) ) ;
2019-04-15 14:52:43 +02:00
if ( s_tsx_avx )
{
2019-05-25 18:56:24 +02:00
c . vmovaps ( x86 : : ymm0 , x86 : : yword_ptr ( x86 : : rbp , 0 ) ) ;
c . vmovaps ( x86 : : ymm1 , x86 : : yword_ptr ( x86 : : rbp , 32 ) ) ;
c . vmovaps ( x86 : : ymm2 , x86 : : yword_ptr ( x86 : : rbp , 64 ) ) ;
c . vmovaps ( x86 : : ymm3 , x86 : : yword_ptr ( x86 : : rbp , 96 ) ) ;
2019-04-15 14:52:43 +02:00
}
else
{
2019-05-25 18:56:24 +02:00
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 ) ) ;
2019-04-15 14:52:43 +02:00
}
2018-05-14 22:07:36 +02:00
c . xend ( ) ;
2019-04-15 14:52:43 +02:00
if ( s_tsx_avx )
{
2019-05-25 18:56:24 +02:00
c . vmovups ( x86 : : yword_ptr ( x86 : : r13 , 0 ) , x86 : : ymm0 ) ;
c . vmovups ( x86 : : yword_ptr ( x86 : : r13 , 32 ) , x86 : : ymm1 ) ;
c . vmovups ( x86 : : yword_ptr ( x86 : : r13 , 64 ) , x86 : : ymm2 ) ;
c . vmovups ( x86 : : yword_ptr ( x86 : : r13 , 96 ) , x86 : : ymm3 ) ;
2019-04-15 14:52:43 +02:00
}
else
{
2019-05-25 18:56:24 +02:00
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 0 ) , x86 : : xmm0 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 16 ) , x86 : : xmm1 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 32 ) , x86 : : xmm2 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 48 ) , x86 : : xmm3 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 64 ) , x86 : : xmm4 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 80 ) , x86 : : xmm5 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 96 ) , x86 : : xmm6 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 112 ) , x86 : : xmm7 ) ;
2019-04-15 14:52:43 +02:00
}
2019-05-18 19:56:22 +02:00
c . and_ ( x86 : : rax , - 128 ) ;
2019-04-15 14:52:43 +02:00
c . jmp ( _ret ) ;
2018-05-14 22:07:36 +02:00
c . bind ( fall ) ;
2019-06-06 20:32:35 +02:00
c . mov ( x86 : : eax , 1 ) ;
//c.jmp(_ret);
2019-05-25 18:56:24 +02:00
2019-04-15 14:52:43 +02:00
c . bind ( _ret ) ;
# ifdef _WIN32
if ( ! s_tsx_avx )
{
c . movups ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rsp , 0 ) ) ;
c . movups ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rsp , 16 ) ) ;
}
# endif
if ( s_tsx_avx )
{
c . vzeroupper ( ) ;
}
2019-05-25 18:56:24 +02:00
c . add ( x86 : : rsp , 72 ) ;
c . pop ( x86 : : rbx ) ;
c . pop ( x86 : : r12 ) ;
c . pop ( x86 : : r13 ) ;
c . pop ( x86 : : rbp ) ;
2018-06-11 01:30:43 +02:00
c . ret ( ) ;
2018-05-14 22:07:36 +02:00
} ) ;
2019-06-06 20:32:35 +02:00
const auto spu_getll_inexact = build_function_asm < u64 ( * ) ( u32 raddr , void * rdata ) > ( [ ] ( asmjit : : X86Assembler & c , auto & args )
2018-05-14 22:07:36 +02:00
{
using namespace asmjit ;
2019-04-15 14:52:43 +02:00
Label _ret = c . newLabel ( ) ;
if ( utils : : has_avx ( ) & & ! s_tsx_avx )
{
c . vzeroupper ( ) ;
}
// Create stack frame if necessary (Windows ABI has only 6 volatile vector registers)
2019-05-25 18:56:24 +02:00
c . push ( x86 : : rbp ) ;
c . push ( x86 : : r13 ) ;
c . push ( x86 : : r12 ) ;
c . push ( x86 : : rbx ) ;
c . sub ( x86 : : rsp , 72 ) ;
2019-04-15 14:52:43 +02:00
# 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 ) ;
2019-05-18 22:47:35 +02:00
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 32 ) , x86 : : xmm8 ) ;
c . movups ( x86 : : oword_ptr ( x86 : : rsp , 48 ) , x86 : : xmm9 ) ;
2019-04-15 14:52:43 +02:00
}
# endif
2018-05-14 22:07:36 +02:00
// Prepare registers
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_reservations ) ) ;
2019-05-25 18:56:24 +02:00
c . mov ( x86 : : rbx , x86 : : qword_ptr ( x86 : : rax ) ) ;
2018-05-14 22:07:36 +02:00
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_base_addr ) ) ;
2019-05-25 18:56:24 +02:00
c . mov ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . lea ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rbp , args [ 0 ] ) ) ;
2018-05-14 22:07:36 +02:00
c . shr ( args [ 0 ] , 4 ) ;
2019-05-25 18:56:24 +02:00
c . lea ( x86 : : rbx , x86 : : qword_ptr ( x86 : : rbx , args [ 0 ] ) ) ;
c . xor_ ( x86 : : r12d , x86 : : r12d ) ;
c . mov ( x86 : : r13 , args [ 1 ] ) ;
2019-05-18 22:47:35 +02:00
// Begin copying
Label begin = c . newLabel ( ) ;
Label test0 = c . newLabel ( ) ;
c . bind ( begin ) ;
2019-05-25 18:56:24 +02:00
c . mov ( x86 : : rax , x86 : : qword_ptr ( x86 : : rbx ) ) ;
2019-05-18 22:47:35 +02:00
if ( s_tsx_avx )
{
2019-05-25 18:56:24 +02:00
c . vmovaps ( x86 : : ymm0 , x86 : : yword_ptr ( x86 : : rbp , 0 ) ) ;
c . vmovaps ( x86 : : ymm1 , x86 : : yword_ptr ( x86 : : rbp , 32 ) ) ;
c . vmovaps ( x86 : : ymm2 , x86 : : yword_ptr ( x86 : : rbp , 64 ) ) ;
c . vmovaps ( x86 : : ymm3 , x86 : : yword_ptr ( x86 : : rbp , 96 ) ) ;
2019-05-18 22:47:35 +02:00
}
else
{
2019-05-25 18:56:24 +02:00
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 ) ) ;
2019-05-18 22:47:35 +02:00
}
// Verify and retry if necessary.
2019-06-06 20:32:35 +02:00
c . mov ( args [ 0 ] , x86 : : rax ) ;
c . xor_ ( args [ 0 ] , x86 : : qword_ptr ( x86 : : rbx ) ) ;
c . test ( args [ 0 ] , - 128 ) ;
c . jz ( test0 ) ;
2019-05-25 18:56:24 +02:00
c . lea ( x86 : : r12 , x86 : : qword_ptr ( x86 : : r12 , 1 ) ) ;
2019-05-18 22:47:35 +02:00
c . jmp ( begin ) ;
c . bind ( test0 ) ;
2019-06-06 20:32:35 +02:00
c . test ( x86 : : eax , 127 ) ;
2019-05-18 22:47:35 +02:00
c . jz ( _ret ) ;
c . and_ ( x86 : : rax , - 128 ) ;
// If there are lock bits set, verify data as well.
if ( s_tsx_avx )
{
2019-05-25 18:56:24 +02:00
c . vxorps ( x86 : : ymm4 , x86 : : ymm0 , x86 : : yword_ptr ( x86 : : rbp , 0 ) ) ;
c . vxorps ( x86 : : ymm5 , x86 : : ymm1 , x86 : : yword_ptr ( x86 : : rbp , 32 ) ) ;
2019-05-18 22:47:35 +02:00
c . vorps ( x86 : : ymm5 , x86 : : ymm5 , x86 : : ymm4 ) ;
2019-05-25 18:56:24 +02:00
c . vxorps ( x86 : : ymm4 , x86 : : ymm2 , x86 : : yword_ptr ( x86 : : rbp , 64 ) ) ;
2019-05-18 22:47:35 +02:00
c . vorps ( x86 : : ymm5 , x86 : : ymm5 , x86 : : ymm4 ) ;
2019-05-25 18:56:24 +02:00
c . vxorps ( x86 : : ymm4 , x86 : : ymm3 , x86 : : yword_ptr ( x86 : : rbp , 96 ) ) ;
2019-05-18 22:47:35 +02:00
c . vorps ( x86 : : ymm5 , x86 : : ymm5 , x86 : : ymm4 ) ;
c . vptest ( x86 : : ymm5 , x86 : : ymm5 ) ;
}
else
{
c . xorps ( x86 : : xmm9 , x86 : : xmm9 ) ;
c . movaps ( x86 : : xmm8 , x86 : : xmm0 ) ;
2019-05-25 18:56:24 +02:00
c . xorps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rbp , 0 ) ) ;
2019-05-18 22:47:35 +02:00
c . orps ( x86 : : xmm9 , x86 : : xmm8 ) ;
c . movaps ( x86 : : xmm8 , x86 : : xmm1 ) ;
2019-05-25 18:56:24 +02:00
c . xorps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rbp , 16 ) ) ;
2019-05-18 22:47:35 +02:00
c . orps ( x86 : : xmm9 , x86 : : xmm8 ) ;
c . movaps ( x86 : : xmm8 , x86 : : xmm2 ) ;
2019-05-25 18:56:24 +02:00
c . xorps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rbp , 32 ) ) ;
2019-05-18 22:47:35 +02:00
c . orps ( x86 : : xmm9 , x86 : : xmm8 ) ;
c . movaps ( x86 : : xmm8 , x86 : : xmm3 ) ;
2019-05-25 18:56:24 +02:00
c . xorps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rbp , 48 ) ) ;
2019-05-18 22:47:35 +02:00
c . orps ( x86 : : xmm9 , x86 : : xmm8 ) ;
c . movaps ( x86 : : xmm8 , x86 : : xmm4 ) ;
2019-05-25 18:56:24 +02:00
c . xorps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rbp , 64 ) ) ;
2019-05-18 22:47:35 +02:00
c . orps ( x86 : : xmm9 , x86 : : xmm8 ) ;
c . movaps ( x86 : : xmm8 , x86 : : xmm5 ) ;
2019-05-25 18:56:24 +02:00
c . xorps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rbp , 80 ) ) ;
2019-05-18 22:47:35 +02:00
c . orps ( x86 : : xmm9 , x86 : : xmm8 ) ;
c . movaps ( x86 : : xmm8 , x86 : : xmm6 ) ;
2019-05-25 18:56:24 +02:00
c . xorps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rbp , 96 ) ) ;
2019-05-18 22:47:35 +02:00
c . orps ( x86 : : xmm9 , x86 : : xmm8 ) ;
c . movaps ( x86 : : xmm8 , x86 : : xmm7 ) ;
2019-05-25 18:56:24 +02:00
c . xorps ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rbp , 112 ) ) ;
2019-05-18 22:47:35 +02:00
c . orps ( x86 : : xmm9 , x86 : : xmm8 ) ;
c . ptest ( x86 : : xmm9 , x86 : : xmm9 ) ;
}
c . jz ( _ret ) ;
2019-05-25 18:56:24 +02:00
c . lea ( x86 : : r12 , x86 : : qword_ptr ( x86 : : r12 , 2 ) ) ;
2019-05-18 22:47:35 +02:00
c . jmp ( begin ) ;
c . bind ( _ret ) ;
if ( s_tsx_avx )
{
2019-05-25 18:56:24 +02:00
c . vmovups ( x86 : : yword_ptr ( x86 : : r13 , 0 ) , x86 : : ymm0 ) ;
c . vmovups ( x86 : : yword_ptr ( x86 : : r13 , 32 ) , x86 : : ymm1 ) ;
c . vmovups ( x86 : : yword_ptr ( x86 : : r13 , 64 ) , x86 : : ymm2 ) ;
c . vmovups ( x86 : : yword_ptr ( x86 : : r13 , 96 ) , x86 : : ymm3 ) ;
2019-05-18 22:47:35 +02:00
}
else
{
2019-05-25 18:56:24 +02:00
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 0 ) , x86 : : xmm0 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 16 ) , x86 : : xmm1 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 32 ) , x86 : : xmm2 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 48 ) , x86 : : xmm3 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 64 ) , x86 : : xmm4 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 80 ) , x86 : : xmm5 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 96 ) , x86 : : xmm6 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : r13 , 112 ) , x86 : : xmm7 ) ;
2019-05-18 22:47:35 +02:00
}
# ifdef _WIN32
if ( ! s_tsx_avx )
{
c . movups ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rsp , 0 ) ) ;
c . movups ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rsp , 16 ) ) ;
c . movups ( x86 : : xmm8 , x86 : : oword_ptr ( x86 : : rsp , 32 ) ) ;
c . movups ( x86 : : xmm9 , x86 : : oword_ptr ( x86 : : rsp , 48 ) ) ;
}
# endif
if ( s_tsx_avx )
{
c . vzeroupper ( ) ;
}
2019-05-25 18:56:24 +02:00
c . add ( x86 : : rsp , 72 ) ;
c . pop ( x86 : : rbx ) ;
c . pop ( x86 : : r12 ) ;
c . pop ( x86 : : r13 ) ;
c . pop ( x86 : : rbp ) ;
2019-05-18 22:47:35 +02:00
c . ret ( ) ;
} ) ;
2019-06-06 20:32:35 +02:00
const auto spu_putlluc_tx = build_function_asm < u32 ( * ) ( u32 raddr , const void * rdata , spu_thread * _spu ) > ( [ ] ( asmjit : : X86Assembler & c , auto & args )
2019-05-18 22:47:35 +02:00
{
using namespace asmjit ;
Label fall = c . newLabel ( ) ;
Label _ret = c . newLabel ( ) ;
2019-06-06 20:32:35 +02:00
Label skip = c . newLabel ( ) ;
Label next = c . newLabel ( ) ;
2019-05-18 22:47:35 +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 ) ;
c . push ( x86 : : r13 ) ;
c . push ( x86 : : r12 ) ;
c . push ( x86 : : rbx ) ;
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
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_reservations ) ) ;
c . mov ( x86 : : rbx , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . mov ( x86 : : rax , imm_ptr ( & vm : : g_base_addr ) ) ;
c . mov ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rax ) ) ;
c . lea ( x86 : : rbp , x86 : : qword_ptr ( x86 : : rbp , args [ 0 ] ) ) ;
c . shr ( args [ 0 ] , 4 ) ;
c . lea ( x86 : : rbx , x86 : : qword_ptr ( x86 : : rbx , args [ 0 ] ) ) ;
c . xor_ ( x86 : : r12d , x86 : : r12d ) ;
c . mov ( x86 : : r13 , args [ 1 ] ) ;
2018-05-14 22:07:36 +02:00
// Prepare data
2019-04-15 14:52:43 +02:00
if ( s_tsx_avx )
{
c . vmovups ( x86 : : ymm0 , x86 : : yword_ptr ( args [ 1 ] , 0 ) ) ;
c . vmovups ( x86 : : ymm1 , x86 : : yword_ptr ( args [ 1 ] , 32 ) ) ;
c . vmovups ( x86 : : ymm2 , x86 : : yword_ptr ( args [ 1 ] , 64 ) ) ;
c . vmovups ( x86 : : ymm3 , x86 : : yword_ptr ( args [ 1 ] , 96 ) ) ;
}
else
{
c . movaps ( x86 : : xmm0 , x86 : : oword_ptr ( args [ 1 ] , 0 ) ) ;
c . movaps ( x86 : : xmm1 , x86 : : oword_ptr ( args [ 1 ] , 16 ) ) ;
c . movaps ( x86 : : xmm2 , x86 : : oword_ptr ( args [ 1 ] , 32 ) ) ;
c . movaps ( x86 : : xmm3 , x86 : : oword_ptr ( args [ 1 ] , 48 ) ) ;
c . movaps ( x86 : : xmm4 , x86 : : oword_ptr ( args [ 1 ] , 64 ) ) ;
c . movaps ( x86 : : xmm5 , x86 : : oword_ptr ( args [ 1 ] , 80 ) ) ;
c . movaps ( x86 : : xmm6 , x86 : : oword_ptr ( args [ 1 ] , 96 ) ) ;
c . movaps ( x86 : : xmm7 , x86 : : oword_ptr ( args [ 1 ] , 112 ) ) ;
}
2018-05-14 22:07:36 +02:00
// Begin transaction
2019-06-06 20:32:35 +02:00
build_transaction_enter ( c , fall , x86 : : r12 , 8 ) ;
c . test ( x86 : : dword_ptr ( x86 : : rbx ) , 127 ) ;
c . jnz ( skip ) ;
2019-04-15 14:52:43 +02:00
if ( s_tsx_avx )
{
2019-05-18 22:47:35 +02:00
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 0 ) , x86 : : ymm0 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 32 ) , x86 : : ymm1 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 64 ) , x86 : : ymm2 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 96 ) , x86 : : ymm3 ) ;
2019-04-15 14:52:43 +02:00
}
else
{
2019-05-18 22:47:35 +02:00
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 0 ) , x86 : : xmm0 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 16 ) , x86 : : xmm1 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 32 ) , x86 : : xmm2 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 48 ) , x86 : : xmm3 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 64 ) , x86 : : xmm4 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 80 ) , x86 : : xmm5 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 96 ) , x86 : : xmm6 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 112 ) , x86 : : xmm7 ) ;
2019-04-15 14:52:43 +02:00
}
2019-05-18 22:47:35 +02:00
c . sub ( x86 : : qword_ptr ( x86 : : rbx ) , - 128 ) ;
2018-05-14 22:07:36 +02:00
c . xend ( ) ;
2019-06-06 20:32:35 +02:00
c . mov ( x86 : : eax , 1 ) ;
2019-04-15 14:52:43 +02:00
c . jmp ( _ret ) ;
2018-05-14 22:07:36 +02:00
2019-06-06 20:32:35 +02:00
c . bind ( skip ) ;
c . xor_ ( x86 : : eax , x86 : : eax ) ;
c . xor_ ( x86 : : r12d , x86 : : r12d ) ;
build_transaction_abort ( c , 0 ) ;
//c.jmp(fall);
2018-05-14 22:07:36 +02:00
c . bind ( fall ) ;
2019-06-06 20:32:35 +02:00
c . lock ( ) . bts ( x86 : : dword_ptr ( args [ 2 ] , : : offset32 ( & spu_thread : : state ) ) , static_cast < u32 > ( cpu_flag : : wait ) ) ;
// Touch memory if transaction failed without RETRY flag on the first attempt
c . cmp ( x86 : : r12 , 1 ) ;
c . jne ( next ) ;
2019-05-18 22:47:35 +02:00
c . xor_ ( x86 : : rbp , 0xf80 ) ;
2019-06-06 20:32:35 +02:00
c . lock ( ) . add ( x86 : : dword_ptr ( x86 : : rbp ) , 0 ) ;
2019-05-18 22:47:35 +02:00
c . xor_ ( x86 : : rbp , 0xf80 ) ;
Label fall2 = c . newLabel ( ) ;
2019-10-11 15:57:20 +02:00
Label fail2 = c . newLabel ( ) ;
2019-05-18 22:47:35 +02:00
// Lightened transaction
2019-06-06 20:32:35 +02:00
c . bind ( next ) ;
2019-10-11 15:57:20 +02:00
// Try to acquire "PUTLLUC lock"
c . lock ( ) . bts ( x86 : : qword_ptr ( x86 : : rbx ) , 6 ) ;
c . jc ( fail2 ) ;
2019-06-06 20:32:35 +02:00
build_transaction_enter ( c , fall2 , x86 : : r12 , 666 ) ;
2019-05-18 22:47:35 +02:00
if ( s_tsx_avx )
{
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 0 ) , x86 : : ymm0 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 32 ) , x86 : : ymm1 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 64 ) , x86 : : ymm2 ) ;
c . vmovaps ( x86 : : yword_ptr ( x86 : : rbp , 96 ) , x86 : : ymm3 ) ;
}
else
{
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 0 ) , x86 : : xmm0 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 16 ) , x86 : : xmm1 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 32 ) , x86 : : xmm2 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 48 ) , x86 : : xmm3 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 64 ) , x86 : : xmm4 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 80 ) , x86 : : xmm5 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 96 ) , x86 : : xmm6 ) ;
c . movaps ( x86 : : oword_ptr ( x86 : : rbp , 112 ) , x86 : : xmm7 ) ;
}
c . xend ( ) ;
2019-10-11 15:57:20 +02:00
c . lock ( ) . add ( x86 : : qword_ptr ( x86 : : rbx ) , 64 ) ;
2019-06-06 20:32:35 +02:00
c . mov ( x86 : : eax , 1 ) ;
2019-04-15 14:52:43 +02:00
c . jmp ( _ret ) ;
2019-10-11 15:57:20 +02:00
c . bind ( fail2 ) ;
c . xor_ ( x86 : : eax , x86 : : eax ) ;
c . jmp ( _ret ) ;
2019-05-18 22:47:35 +02:00
c . bind ( fall2 ) ;
2019-06-06 20:32:35 +02:00
c . mov ( x86 : : eax , 2 ) ;
//c.jmp(_ret);
2019-05-18 22:47:35 +02:00
2019-04-15 14:52:43 +02:00
c . bind ( _ret ) ;
# ifdef _WIN32
if ( ! s_tsx_avx )
{
c . movups ( x86 : : xmm6 , x86 : : oword_ptr ( x86 : : rsp , 0 ) ) ;
c . movups ( x86 : : xmm7 , x86 : : oword_ptr ( x86 : : rsp , 16 ) ) ;
}
# endif
if ( s_tsx_avx )
{
c . vzeroupper ( ) ;
}
2019-05-18 22:47:35 +02:00
c . add ( x86 : : rsp , 40 ) ;
c . pop ( x86 : : rbx ) ;
c . pop ( x86 : : r12 ) ;
c . pop ( x86 : : r13 ) ;
c . pop ( x86 : : rbp ) ;
2018-06-11 01:30:43 +02:00
c . ret ( ) ;
2018-05-14 22:07:36 +02:00
} ) ;
2015-07-12 23:02:02 +02:00
void spu_int_ctrl_t : : set ( u64 ints )
{
// leave only enabled interrupts
2015-09-18 00:41:14 +02:00
ints & = mask ;
2015-07-12 23:02:02 +02:00
// notify if at least 1 bit was set
2019-12-21 07:16:47 +01:00
if ( ints & & ~ stat . fetch_or ( ints ) & ints & & ! tag . expired ( ) )
2015-07-12 23:02:02 +02:00
{
2017-02-05 00:26:57 +01:00
reader_lock rlock ( id_manager : : g_mutex ) ;
2015-07-12 23:02:02 +02:00
2019-12-21 07:16:47 +01:00
if ( const auto tag_ptr = tag . lock ( ) )
2015-07-12 23:02:02 +02:00
{
2019-12-21 07:16:47 +01:00
if ( auto handler = tag_ptr - > handler . lock ( ) )
2017-02-04 17:30:21 +01:00
{
handler - > exec ( ) ;
}
2015-07-12 23:02:02 +02:00
}
}
}
2015-08-26 04:54:06 +02:00
const spu_imm_table_t g_spu_imm ;
2015-03-20 17:53:54 +01:00
2016-05-13 15:55:34 +02:00
spu_imm_table_t : : scale_table_t : : scale_table_t ( )
{
for ( s32 i = - 155 ; i < 174 ; i + + )
{
2018-05-28 12:40:31 +02:00
m_data [ i + 155 ] . vf = _mm_set1_ps ( static_cast < float > ( std : : exp2 ( i ) ) ) ;
2016-05-13 15:55:34 +02:00
}
}
spu_imm_table_t : : spu_imm_table_t ( )
{
2018-09-05 22:52:31 +02:00
for ( u32 i = 0 ; i < std : : size ( sldq_pshufb ) ; i + + )
2016-05-13 15:55:34 +02:00
{
for ( u32 j = 0 ; j < 16 ; j + + )
{
sldq_pshufb [ i ] . _u8 [ j ] = static_cast < u8 > ( j - i ) ;
}
}
2018-09-05 22:52:31 +02:00
for ( u32 i = 0 ; i < std : : size ( srdq_pshufb ) ; i + + )
2016-05-13 15:55:34 +02:00
{
2017-12-09 15:57:43 +01:00
const u32 im = ( 0u - i ) & 0x1f ;
2016-05-13 15:55:34 +02:00
for ( u32 j = 0 ; j < 16 ; j + + )
{
2017-12-09 15:57:43 +01:00
srdq_pshufb [ i ] . _u8 [ j ] = ( j + im > 15 ) ? 0xff : static_cast < u8 > ( j + im ) ;
2016-05-13 15:55:34 +02:00
}
}
2018-09-05 22:52:31 +02:00
for ( u32 i = 0 ; i < std : : size ( rldq_pshufb ) ; i + + )
2016-05-13 15:55:34 +02:00
{
for ( u32 j = 0 ; j < 16 ; j + + )
{
rldq_pshufb [ i ] . _u8 [ j ] = static_cast < u8 > ( ( j - i ) & 0xf ) ;
}
}
}
2018-10-11 00:17:19 +02:00
std : : string spu_thread : : get_name ( ) const
2017-04-23 22:09:27 +02:00
{
2019-11-03 00:44:02 +01:00
return fmt : : format ( " %sSPU[0x%07x] Thread (%s) " , offset > = RAW_SPU_BASE_ADDR ? " Raw " : " " , lv2_id , spu_name . get ( ) ) ;
2012-11-15 00:39:56 +01:00
}
2018-10-11 00:17:19 +02:00
std : : string spu_thread : : dump ( ) const
2014-07-16 14:09:20 +02:00
{
2018-03-24 22:03:32 +01:00
std : : string ret = cpu_thread : : dump ( ) ;
2015-07-01 00:25:52 +02:00
2018-07-04 11:14:56 +02:00
fmt : : append ( ret , " \n Block Weight: %u (Retreats: %u) " , block_counter , block_failure ) ;
2018-05-18 17:53:01 +02:00
fmt : : append ( ret , " \n [%s] " , ch_mfc_cmd ) ;
2020-01-21 14:08:45 +01:00
fmt : : append ( ret , " \n Local Storage: 0x%08x..0x%08x " , offset , offset + 0x3ffff ) ;
2018-03-24 22:03:32 +01:00
fmt : : append ( ret , " \n Tag Mask: 0x%08x " , ch_tag_mask ) ;
fmt : : append ( ret , " \n MFC Stall: 0x%08x " , ch_stall_mask ) ;
fmt : : append ( ret , " \n MFC Queue Size: %u " , mfc_size ) ;
for ( u32 i = 0 ; i < 16 ; i + + )
{
if ( i < mfc_size )
{
2018-05-18 17:53:01 +02:00
fmt : : append ( ret , " \n %s " , mfc_queue [ i ] ) ;
2018-03-24 22:03:32 +01:00
}
else
{
fmt : : append ( ret , " \n [-] " ) ;
}
}
ret + = " \n Registers: \n ========= " ;
for ( u32 i = 0 ; i < 128 ; i + + )
{
fmt : : append ( ret , " \n GPR[%d] = %s " , i , gpr [ i ] ) ;
}
2014-07-16 14:09:20 +02:00
2016-04-14 01:09:41 +02:00
return ret ;
2012-11-15 00:39:56 +01:00
}
2018-10-11 00:17:19 +02:00
void spu_thread : : cpu_init ( )
2012-11-15 00:39:56 +01:00
{
2020-01-11 20:36:03 +01:00
std : : memset ( gpr . data ( ) , 0 , gpr . size ( ) * sizeof ( gpr [ 0 ] ) ) ;
2015-08-26 04:54:06 +02:00
fpscr . Reset ( ) ;
2015-03-02 03:10:41 +01:00
2017-02-17 20:35:57 +01:00
ch_mfc_cmd = { } ;
2013-11-03 20:23:16 +01:00
2017-02-13 14:12:24 +01:00
srr0 = 0 ;
2018-03-24 22:03:32 +01:00
mfc_size = 0 ;
2018-04-03 21:42:47 +02:00
mfc_barrier = 0 ;
mfc_fence = 0 ;
2017-02-17 20:35:57 +01:00
ch_tag_upd = 0 ;
2015-03-13 02:59:25 +01:00
ch_tag_mask = 0 ;
2019-07-09 06:30:06 +02:00
ch_tag_stat . data . raw ( ) = { } ;
2017-02-17 20:35:57 +01:00
ch_stall_mask = 0 ;
2019-07-09 06:30:06 +02:00
ch_stall_stat . data . raw ( ) = { } ;
ch_atomic_stat . data . raw ( ) = { } ;
2013-12-22 18:40:50 +01:00
2019-07-09 06:30:06 +02:00
ch_out_mbox . data . raw ( ) = { } ;
ch_out_intr_mbox . data . raw ( ) = { } ;
2014-07-16 18:10:18 +02:00
2019-07-09 06:30:06 +02:00
ch_event_mask . raw ( ) = 0 ;
ch_event_stat . raw ( ) = 0 ;
interrupts_enabled . raw ( ) = false ;
2017-02-17 20:35:57 +01:00
raddr = 0 ;
2015-03-02 03:10:41 +01:00
2015-07-06 01:21:15 +02:00
ch_dec_start_timestamp = get_timebased_time ( ) ; // ???
2015-03-13 02:59:25 +01:00
ch_dec_value = 0 ;
2015-03-02 03:10:41 +01:00
2018-11-19 07:07:44 +01:00
if ( offset > = RAW_SPU_BASE_ADDR )
{
2019-10-24 20:03:17 +02:00
ch_in_mbox . clear ( ) ;
ch_snr1 . data . raw ( ) = { } ;
ch_snr2 . data . raw ( ) = { } ;
2018-11-19 07:07:44 +01:00
snr_config = 0 ;
2020-01-07 22:00:06 +01:00
mfc_prxy_mask . raw ( ) = 0 ;
mfc_prxy_write_state = { } ;
2018-11-19 07:07:44 +01:00
}
2019-07-09 06:30:06 +02:00
run_ctrl . raw ( ) = 0 ;
2020-01-21 19:05:45 +01:00
status_npc . raw ( ) = { } ;
2015-03-02 03:10:41 +01:00
2015-09-18 00:41:14 +02:00
int_ctrl [ 0 ] . clear ( ) ;
int_ctrl [ 1 ] . clear ( ) ;
int_ctrl [ 2 ] . clear ( ) ;
2015-03-02 03:10:41 +01:00
2015-08-26 04:54:06 +02:00
gpr [ 1 ] . _u32 [ 3 ] = 0x3FFF0 ; // initial stack frame pointer
2012-11-15 00:39:56 +01:00
}
2018-11-13 16:40:41 +01:00
void spu_thread : : cpu_stop ( )
2015-02-01 14:52:34 +01:00
{
2018-11-13 16:40:41 +01:00
if ( ! group & & offset > = RAW_SPU_BASE_ADDR )
2018-11-05 12:38:35 +01:00
{
2020-01-21 19:05:45 +01:00
status_npc . fetch_op ( [ this ] ( status_npc_sync_var & state )
2020-01-21 13:51:55 +01:00
{
2020-01-21 19:05:45 +01:00
if ( state . status & SPU_STATUS_RUNNING )
{
// Save next PC and current SPU Interrupt Status
// Used only by RunCtrl stop requests
state . status & = ~ SPU_STATUS_RUNNING ;
state . npc = pc | + interrupts_enabled ;
return true ;
}
return false ;
} ) ;
2018-11-13 16:40:41 +01:00
}
else if ( group & & is_stopped ( ) )
{
2019-10-29 20:32:34 +01:00
ch_in_mbox . clear ( ) ;
2018-11-13 16:40:41 +01:00
if ( verify ( HERE , group - > running - - ) = = 1 )
2018-11-05 12:38:35 +01:00
{
2018-11-15 04:56:28 +01:00
{
std : : lock_guard lock ( group - > mutex ) ;
2018-12-25 18:23:03 +01:00
group - > stop_count + + ;
2018-11-15 04:56:28 +01:00
group - > run_state = SPU_THREAD_GROUP_STATUS_INITIALIZED ;
2018-12-25 18:23:03 +01:00
2019-02-08 18:23:49 +01:00
if ( ! group - > join_state )
{
group - > join_state = SYS_SPU_THREAD_GROUP_JOIN_ALL_THREADS_EXIT ;
}
2018-12-25 18:23:03 +01:00
if ( const auto ppu = std : : exchange ( group - > waiter , nullptr ) )
{
// Send exit status directly to the joining thread
ppu - > gpr [ 4 ] = group - > join_state ;
ppu - > gpr [ 5 ] = group - > exit_status ;
2019-02-10 16:22:48 +01:00
group - > join_state . release ( 0 ) ;
2018-12-25 18:23:03 +01:00
}
2018-11-15 04:56:28 +01:00
}
2018-11-13 16:40:41 +01:00
// Notify on last thread stopped
group - > cond . notify_all ( ) ;
2018-11-05 12:38:35 +01:00
}
2018-11-13 16:40:41 +01:00
}
}
2018-11-05 12:38:35 +01:00
2018-11-13 16:40:41 +01:00
extern thread_local std : : string ( * g_tls_log_prefix ) ( ) ;
2018-11-05 12:38:35 +01:00
2018-11-13 16:40:41 +01:00
void spu_thread : : cpu_task ( )
{
2018-10-11 00:17:19 +02:00
// Get next PC and SPU Interrupt status
2020-01-21 19:05:45 +01:00
pc = status_npc . load ( ) . npc ;
2020-01-21 13:51:55 +01:00
2020-01-21 17:40:12 +01:00
// Note: works both on RawSPU and threaded SPU!
2018-10-11 00:17:19 +02:00
set_interrupt_status ( ( pc & 1 ) ! = 0 ) ;
pc & = 0x3fffc ;
2016-04-14 01:09:41 +02:00
std : : fesetround ( FE_TOWARDZERO ) ;
2017-12-09 15:57:43 +01:00
2016-04-27 00:27:24 +02:00
g_tls_log_prefix = [ ]
2015-03-20 17:53:54 +01:00
{
2018-10-11 00:17:19 +02:00
const auto cpu = static_cast < spu_thread * > ( get_current_cpu_thread ( ) ) ;
return fmt : : format ( " %s [0x%05x] " , thread_ctrl : : get_name ( ) , cpu - > pc ) ;
2016-04-14 01:09:41 +02:00
} ;
2018-04-09 16:45:37 +02:00
if ( jit )
{
2019-03-18 21:01:16 +01:00
while ( true )
2018-04-09 16:45:37 +02:00
{
2020-02-05 08:00:08 +01:00
if ( state ) [[unlikely]]
2019-03-18 21:01:16 +01:00
{
if ( check_state ( ) )
break ;
}
2020-02-19 16:26:41 +01:00
if ( _ref < u32 > ( pc ) = = 0x0u )
2020-01-20 21:40:10 +01:00
{
2020-01-21 13:51:55 +01:00
if ( spu_thread : : stop_and_signal ( 0x0 ) )
pc + = 4 ;
2020-01-20 21:40:10 +01:00
continue ;
}
2019-05-11 18:21:07 +02:00
spu_runtime : : g_gateway ( * this , vm : : _ptr < u8 > ( offset ) , nullptr ) ;
2018-04-09 16:45:37 +02:00
}
2018-06-10 14:46:01 +02:00
// Print some stats
2020-02-01 09:36:09 +01:00
spu_log . notice ( " Stats: Block Weight: %u (Retreats: %u); " , block_counter , block_failure ) ;
2018-04-09 16:45:37 +02:00
}
2019-07-15 15:16:30 +02:00
else
2019-03-25 19:31:16 +01:00
{
2019-07-15 15:16:30 +02:00
ASSERT ( spu_runtime : : g_interpreter ) ;
2019-03-25 19:31:16 +01:00
while ( true )
{
2020-02-05 08:00:08 +01:00
if ( state ) [[unlikely]]
2019-03-25 19:31:16 +01:00
{
if ( check_state ( ) )
break ;
}
spu_runtime : : g_interpreter ( * this , vm : : _ptr < u8 > ( offset ) , nullptr ) ;
}
2015-03-20 17:53:54 +01:00
}
2018-10-11 00:17:19 +02:00
2018-11-13 16:40:41 +01:00
cpu_stop ( ) ;
2012-11-15 00:39:56 +01:00
}
2018-10-11 00:17:19 +02:00
void spu_thread : : cpu_mem ( )
2018-04-03 16:19:07 +02:00
{
2018-05-14 22:07:36 +02:00
//vm::passive_lock(*this);
2018-04-03 16:19:07 +02:00
}
2018-10-11 00:17:19 +02:00
void spu_thread : : cpu_unmem ( )
2018-04-03 16:19:07 +02:00
{
2018-05-14 22:07:36 +02:00
//state.test_and_set(cpu_flag::memory);
2018-04-03 16:19:07 +02:00
}
2018-10-11 00:17:19 +02:00
spu_thread : : ~ spu_thread ( )
2016-04-25 12:49:12 +02:00
{
// Deallocate Local Storage
vm : : dealloc_verbose_nothrow ( offset ) ;
2018-10-11 00:17:19 +02:00
// Deallocate RawSPU ID
if ( ! group & & offset > = RAW_SPU_BASE_ADDR )
{
g_raw_spu_id [ index ] = 0 ;
g_raw_spu_ctr - - ;
}
2016-04-25 12:49:12 +02:00
}
2019-11-03 00:44:02 +01:00
spu_thread : : spu_thread ( vm : : addr_t ls , lv2_spu_group * group , u32 index , std : : string_view name , u32 lv2_id )
2017-01-25 18:50:30 +01:00
: cpu_thread ( idm : : last_id ( ) )
2018-10-11 00:17:19 +02:00
, spu_name ( name )
2016-04-25 12:49:12 +02:00
, index ( index )
2018-10-11 00:17:19 +02:00
, offset ( ls )
2017-02-05 00:26:57 +01:00
, group ( group )
2019-11-03 00:44:02 +01:00
, lv2_id ( lv2_id )
2016-04-14 01:09:41 +02:00
{
2018-04-09 16:45:37 +02:00
if ( g_cfg . core . spu_decoder = = spu_decoder_type : : asmjit )
{
2018-05-03 14:55:45 +02:00
jit = spu_recompiler_base : : make_asmjit_recompiler ( ) ;
2018-04-09 16:45:37 +02:00
}
if ( g_cfg . core . spu_decoder = = spu_decoder_type : : llvm )
{
2019-05-17 22:54:47 +02:00
jit = spu_recompiler_base : : make_fast_llvm_recompiler ( ) ;
2018-04-09 16:45:37 +02:00
}
2018-05-03 14:55:45 +02:00
2018-06-03 23:20:14 +02:00
if ( g_cfg . core . spu_decoder ! = spu_decoder_type : : fast & & g_cfg . core . spu_decoder ! = spu_decoder_type : : precise )
{
if ( g_cfg . core . spu_block_size ! = spu_block_size_type : : safe )
{
// Initialize stack mirror
std : : memset ( stack_mirror . data ( ) , 0xff , sizeof ( stack_mirror ) ) ;
}
}
2018-10-11 00:17:19 +02:00
if ( ! group & & offset > = RAW_SPU_BASE_ADDR )
{
cpu_init ( ) ;
}
2016-04-14 01:09:41 +02:00
}
2014-09-24 20:44:26 +02:00
2018-10-11 00:17:19 +02:00
void spu_thread : : push_snr ( u32 number , u32 value )
2016-04-14 01:09:41 +02:00
{
2016-04-19 15:04:02 +02:00
// Get channel
const auto channel = number & 1 ? & ch_snr2 : & ch_snr1 ;
2014-09-24 20:44:26 +02:00
2016-04-19 15:04:02 +02:00
// Check corresponding SNR register settings
2016-04-14 01:09:41 +02:00
if ( ( snr_config > > number ) & 1 )
2015-07-01 00:25:52 +02:00
{
2016-04-19 15:04:02 +02:00
channel - > push_or ( * this , value ) ;
2015-07-01 00:25:52 +02:00
}
2016-04-14 01:09:41 +02:00
else
2015-07-01 00:25:52 +02:00
{
2016-04-19 15:04:02 +02:00
channel - > push ( * this , value ) ;
2016-04-14 01:09:41 +02:00
}
2014-10-02 12:29:20 +02:00
}
2018-10-11 00:17:19 +02:00
void spu_thread : : do_dma_transfer ( const spu_mfc_cmd & args )
2014-08-22 23:15:02 +02:00
{
2018-03-10 05:16:22 +01:00
const bool is_get = ( args . cmd & ~ ( MFC_BARRIER_MASK | MFC_FENCE_MASK | MFC_START_MASK ) ) = = MFC_GET_CMD ;
2014-08-22 23:15:02 +02:00
2017-02-17 20:35:57 +01:00
u32 eal = args . eal ;
u32 lsa = args . lsa & 0x3ffff ;
2015-02-15 18:13:06 +01:00
2018-04-01 20:48:58 +02:00
// SPU Thread Group MMIO (LS and SNR) and RawSPU MMIO
if ( eal > = RAW_SPU_BASE_ADDR )
2014-08-22 23:15:02 +02:00
{
2015-03-04 22:51:14 +01:00
const u32 index = ( eal - SYS_SPU_THREAD_BASE_LOW ) / SYS_SPU_THREAD_OFFSET ; // thread number in group
const u32 offset = ( eal - SYS_SPU_THREAD_BASE_LOW ) % SYS_SPU_THREAD_OFFSET ; // LS offset or MMIO register
2015-02-15 18:13:06 +01:00
2018-04-01 20:48:58 +02:00
if ( eal < SYS_SPU_THREAD_BASE_LOW )
{
// RawSPU MMIO
2018-10-11 00:17:19 +02:00
auto thread = idm : : get < named_thread < spu_thread > > ( find_raw_spu ( ( eal - RAW_SPU_BASE_ADDR ) / RAW_SPU_OFFSET ) ) ;
2018-04-01 20:48:58 +02:00
if ( ! thread )
{
fmt : : throw_exception ( " RawSPU not found (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
}
u32 value ;
if ( ( eal - RAW_SPU_BASE_ADDR ) % RAW_SPU_OFFSET + args . size - 1 < 0x40000 ) // LS access
{
}
else if ( args . size = = 4 & & is_get & & thread - > read_reg ( eal , value ) )
{
_ref < u32 > ( lsa ) = value ;
return ;
}
else if ( args . size = = 4 & & ! is_get & & thread - > write_reg ( eal , _ref < u32 > ( lsa ) ) )
{
return ;
}
else
{
fmt : : throw_exception ( " Invalid RawSPU MMIO offset (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
}
}
else if ( this - > offset > = RAW_SPU_BASE_ADDR )
{
fmt : : throw_exception ( " SPU MMIO used for RawSPU (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
}
2019-11-03 00:44:02 +01:00
else if ( group & & group - > threads_map [ index ] ! = - 1 )
2015-02-15 18:13:06 +01:00
{
2019-11-03 00:44:02 +01:00
auto & spu = static_cast < spu_thread & > ( * group - > threads [ group - > threads_map [ index ] ] ) ;
2014-09-19 02:19:22 +02:00
2015-03-02 03:10:41 +01:00
if ( offset + args . size - 1 < 0x40000 ) // LS access
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
eal = spu . offset + offset ; // redirect access
2014-08-22 23:15:02 +02:00
}
2017-02-17 20:35:57 +01:00
else if ( ! is_get & & args . size = = 4 & & ( offset = = SYS_SPU_THREAD_SNR1 | | offset = = SYS_SPU_THREAD_SNR2 ) )
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
spu . push_snr ( SYS_SPU_THREAD_SNR2 = = offset , _ref < u32 > ( lsa ) ) ;
2014-08-22 23:15:02 +02:00
return ;
}
else
{
2017-02-17 20:35:57 +01:00
fmt : : throw_exception ( " Invalid MMIO offset (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
2014-08-22 23:15:02 +02:00
}
}
else
{
2017-02-17 20:35:57 +01:00
fmt : : throw_exception ( " Invalid thread type (cmd=0x%x, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE , args . cmd , args . lsa , args . eal , args . tag , args . size ) ;
2014-08-22 23:15:02 +02:00
}
}
2019-06-29 00:27:49 +02:00
u8 * dst = vm : : _ptr < u8 > ( eal ) ;
u8 * src = vm : : _ptr < u8 > ( offset + lsa ) ;
2017-02-17 20:35:57 +01:00
2019-06-11 21:45:11 +02:00
if ( is_get )
{
std : : swap ( dst , src ) ;
}
2020-02-05 08:00:08 +01:00
if ( ! g_use_rtm & & ( ! is_get | | g_cfg . core . spu_accurate_putlluc ) ) [[unlikely]]
2018-05-21 19:25:05 +02:00
{
switch ( u32 size = args . size )
{
case 1 :
{
auto & res = vm : : reservation_lock ( eal , 1 ) ;
2019-01-15 16:31:21 +01:00
* reinterpret_cast < u8 * > ( dst ) = * reinterpret_cast < const u8 * > ( src ) ;
2019-06-11 21:45:11 +02:00
res . release ( res . load ( ) - 1 ) ;
2018-05-21 19:25:05 +02:00
break ;
}
case 2 :
{
auto & res = vm : : reservation_lock ( eal , 2 ) ;
2019-01-15 16:31:21 +01:00
* reinterpret_cast < u16 * > ( dst ) = * reinterpret_cast < const u16 * > ( src ) ;
2019-06-11 21:45:11 +02:00
res . release ( res . load ( ) - 1 ) ;
2018-05-21 19:25:05 +02:00
break ;
}
case 4 :
{
auto & res = vm : : reservation_lock ( eal , 4 ) ;
2019-01-15 16:31:21 +01:00
* reinterpret_cast < u32 * > ( dst ) = * reinterpret_cast < const u32 * > ( src ) ;
2019-06-11 21:45:11 +02:00
res . release ( res . load ( ) - 1 ) ;
2018-05-21 19:25:05 +02:00
break ;
}
case 8 :
{
auto & res = vm : : reservation_lock ( eal , 8 ) ;
2019-01-15 16:31:21 +01:00
* reinterpret_cast < u64 * > ( dst ) = * reinterpret_cast < const u64 * > ( src ) ;
2019-06-11 21:45:11 +02:00
res . release ( res . load ( ) - 1 ) ;
2018-05-21 19:25:05 +02:00
break ;
}
default :
{
2019-01-15 16:31:21 +01:00
if ( ( ( eal & 127 ) + size ) < = 128 )
2018-05-21 19:25:05 +02:00
{
2019-01-15 16:31:21 +01:00
// Lock one cache line
auto & res = vm : : reservation_lock ( eal , 128 ) ;
2018-05-21 19:25:05 +02:00
2019-01-15 16:31:21 +01:00
while ( size )
2018-05-21 19:25:05 +02:00
{
2019-06-06 20:32:35 +02:00
* reinterpret_cast < v128 * > ( dst ) = * reinterpret_cast < const v128 * > ( src ) ;
2018-05-21 19:25:05 +02:00
2019-01-15 16:31:21 +01:00
dst + = 16 ;
src + = 16 ;
size - = 16 ;
2018-05-21 19:25:05 +02:00
}
2019-06-11 21:45:11 +02:00
res . release ( res . load ( ) - 1 ) ;
2019-01-15 16:31:21 +01:00
break ;
2018-05-21 19:25:05 +02:00
}
2019-06-28 07:24:28 +02:00
auto lock = vm : : passive_lock ( eal & - 128 , : : align ( eal + size , 128 ) ) ;
2019-01-15 16:31:21 +01:00
while ( size > = 128 )
{
2019-06-06 20:32:35 +02:00
mov_rdata ( * reinterpret_cast < decltype ( spu_thread : : rdata ) * > ( dst ) , * reinterpret_cast < const decltype ( spu_thread : : rdata ) * > ( src ) ) ;
2019-01-15 16:31:21 +01:00
dst + = 128 ;
src + = 128 ;
size - = 128 ;
}
while ( size )
{
2019-06-06 20:32:35 +02:00
* reinterpret_cast < v128 * > ( dst ) = * reinterpret_cast < const v128 * > ( src ) ;
2019-01-15 16:31:21 +01:00
dst + = 16 ;
src + = 16 ;
size - = 16 ;
}
2019-02-09 10:34:36 +01:00
lock - > release ( 0 ) ;
2018-05-21 19:25:05 +02:00
break ;
}
}
return ;
}
2017-02-17 20:35:57 +01:00
switch ( u32 size = args . size )
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
case 1 :
{
2019-01-15 16:31:21 +01:00
* reinterpret_cast < u8 * > ( dst ) = * reinterpret_cast < const u8 * > ( src ) ;
2017-02-17 20:35:57 +01:00
break ;
2014-08-22 23:15:02 +02:00
}
2017-02-17 20:35:57 +01:00
case 2 :
{
2019-01-15 16:31:21 +01:00
* reinterpret_cast < u16 * > ( dst ) = * reinterpret_cast < const u16 * > ( src ) ;
2017-02-17 20:35:57 +01:00
break ;
}
case 4 :
{
2019-01-15 16:31:21 +01:00
* reinterpret_cast < u32 * > ( dst ) = * reinterpret_cast < const u32 * > ( src ) ;
2017-02-17 20:35:57 +01:00
break ;
}
case 8 :
2014-08-22 23:15:02 +02:00
{
2019-01-15 16:31:21 +01:00
* reinterpret_cast < u64 * > ( dst ) = * reinterpret_cast < const u64 * > ( src ) ;
2017-02-17 20:35:57 +01:00
break ;
}
default :
{
2019-01-15 16:31:21 +01:00
while ( size > = 128 )
{
2019-06-06 20:32:35 +02:00
mov_rdata ( * reinterpret_cast < decltype ( spu_thread : : rdata ) * > ( dst ) , * reinterpret_cast < const decltype ( spu_thread : : rdata ) * > ( src ) ) ;
2017-02-17 20:35:57 +01:00
2019-01-15 16:31:21 +01:00
dst + = 128 ;
src + = 128 ;
size - = 128 ;
}
while ( size )
2017-02-17 20:35:57 +01:00
{
2019-06-06 20:32:35 +02:00
* reinterpret_cast < v128 * > ( dst ) = * reinterpret_cast < const v128 * > ( src ) ;
2019-01-15 16:31:21 +01:00
dst + = 16 ;
src + = 16 ;
size - = 16 ;
2017-02-17 20:35:57 +01:00
}
2019-01-15 16:31:21 +01:00
2018-05-21 19:25:05 +02:00
break ;
2014-08-22 23:15:02 +02:00
}
}
2015-03-02 03:10:41 +01:00
}
2014-08-22 23:15:02 +02:00
2018-10-11 00:17:19 +02:00
bool spu_thread : : do_dma_check ( const spu_mfc_cmd & args )
2014-08-22 23:15:02 +02:00
{
2019-01-07 07:13:17 +01:00
const u32 mask = utils : : rol32 ( 1 , args . tag ) ;
2018-04-03 15:09:43 +02:00
2020-02-05 08:00:08 +01:00
if ( mfc_barrier & mask | | ( args . cmd & ( MFC_BARRIER_MASK | MFC_FENCE_MASK ) & & mfc_fence & mask ) ) [[unlikely]]
2018-03-24 22:03:32 +01:00
{
2018-04-03 15:09:43 +02:00
// Check for special value combination (normally impossible)
2018-04-28 19:11:16 +02:00
if ( false )
2018-03-24 22:03:32 +01:00
{
2018-04-03 15:09:43 +02:00
// Update barrier/fence masks if necessary
mfc_barrier = 0 ;
mfc_fence = 0 ;
2018-03-24 22:03:32 +01:00
2018-04-03 15:09:43 +02:00
for ( u32 i = 0 ; i < mfc_size ; i + + )
{
2018-04-03 21:42:47 +02:00
if ( ( mfc_queue [ i ] . cmd & ~ 0xc ) = = MFC_BARRIER_CMD )
2018-04-03 15:09:43 +02:00
{
mfc_barrier | = - 1 ;
2019-07-09 06:30:06 +02:00
mfc_fence | = utils : : rol32 ( 1 , mfc_queue [ i ] . tag ) ;
2018-04-03 15:09:43 +02:00
continue ;
}
2018-04-03 21:42:47 +02:00
if ( true )
2018-04-03 15:09:43 +02:00
{
2019-01-07 07:13:17 +01:00
const u32 _mask = utils : : rol32 ( 1u , mfc_queue [ i ] . tag ) ;
2018-04-03 15:09:43 +02:00
// A command with barrier hard blocks that tag until it's been dealt with
if ( mfc_queue [ i ] . cmd & MFC_BARRIER_MASK )
{
mfc_barrier | = _mask ;
}
// A new command that has a fence can't be executed until the stalled list has been dealt with
mfc_fence | = _mask ;
}
}
if ( mfc_barrier & mask | | ( args . cmd & MFC_FENCE_MASK & & mfc_fence & mask ) )
2018-03-24 22:03:32 +01:00
{
return false ;
}
2018-04-03 15:09:43 +02:00
return true ;
2018-03-24 22:03:32 +01:00
}
2018-04-03 15:09:43 +02:00
return false ;
2018-03-24 22:03:32 +01:00
}
2015-03-02 03:10:41 +01:00
2018-03-24 22:03:32 +01:00
return true ;
}
2018-10-11 00:17:19 +02:00
bool spu_thread : : do_list_transfer ( spu_mfc_cmd & args )
2018-03-24 22:03:32 +01:00
{
2019-07-24 00:13:45 +02:00
// Amount of elements to fetch in one go
constexpr u32 fetch_size = 6 ;
struct alignas ( 8 ) list_element
2018-03-24 22:03:32 +01:00
{
be_t < u16 > sb ; // Stall-and-Notify bit (0x8000)
be_t < u16 > ts ; // List Transfer Size
be_t < u32 > ea ; // External Address Low
2019-07-24 00:13:45 +02:00
} ;
2014-08-22 23:15:02 +02:00
2019-07-24 00:13:45 +02:00
union
2014-08-22 23:15:02 +02:00
{
2019-07-24 00:13:45 +02:00
list_element items [ fetch_size ] ;
alignas ( v128 ) char bufitems [ sizeof ( items ) ] ;
} ;
2018-03-24 22:03:32 +01:00
2019-07-24 00:13:45 +02:00
spu_mfc_cmd transfer ;
transfer . eah = 0 ;
transfer . tag = args . tag ;
transfer . cmd = MFC ( args . cmd & ~ MFC_LIST_MASK ) ;
2014-08-22 23:15:02 +02:00
2019-07-24 00:13:45 +02:00
args . lsa & = 0x3fff0 ;
2019-01-07 07:13:17 +01:00
2019-07-24 00:13:45 +02:00
u32 index = fetch_size ;
// Assume called with size greater than 0
while ( true )
{
// Check if fetching is needed
if ( index = = fetch_size )
{
// Reset to elements array head
index = 0 ;
const auto src = _ptr < const __m128i > ( args . eal & 0x3fff8 ) ;
const v128 data0 = v128 : : fromV ( _mm_loadu_si128 ( src + 0 ) ) ;
const v128 data1 = v128 : : fromV ( _mm_loadu_si128 ( src + 1 ) ) ;
const v128 data2 = v128 : : fromV ( _mm_loadu_si128 ( src + 2 ) ) ;
2017-02-17 20:35:57 +01:00
2019-12-02 22:31:34 +01:00
reinterpret_cast < v128 * > ( bufitems ) [ 0 ] = data0 ;
reinterpret_cast < v128 * > ( bufitems ) [ 1 ] = data1 ;
reinterpret_cast < v128 * > ( bufitems ) [ 2 ] = data2 ;
2019-07-24 00:13:45 +02:00
}
2018-03-24 22:03:32 +01:00
2019-07-24 00:13:45 +02:00
const u32 size = items [ index ] . ts & 0x7fff ;
const u32 addr = items [ index ] . ea ;
2018-03-24 22:03:32 +01:00
2020-02-01 09:36:09 +01:00
spu_log . trace ( " LIST: addr=0x%x, size=0x%x, lsa=0x%05x, sb=0x%x " , addr , size , args . lsa | ( addr & 0xf ) , items [ index ] . sb ) ;
2018-03-24 22:03:32 +01:00
if ( size )
{
transfer . eal = addr ;
transfer . lsa = args . lsa | ( addr & 0xf ) ;
transfer . size = size ;
do_dma_transfer ( transfer ) ;
const u32 add_size = std : : max < u32 > ( size , 16 ) ;
args . lsa + = add_size ;
}
args . size - = 8 ;
2019-07-24 00:13:45 +02:00
if ( ! args . size )
{
// No more elements
break ;
}
args . eal + = 8 ;
2020-02-05 08:00:08 +01:00
if ( items [ index ] . sb & 0x8000 ) [[unlikely]]
2019-07-24 00:13:45 +02:00
{
ch_stall_mask | = utils : : rol32 ( 1 , args . tag ) ;
if ( ! ch_stall_stat . get_count ( ) )
{
ch_event_stat | = SPU_EVENT_SN ;
}
ch_stall_stat . set_value ( utils : : rol32 ( 1 , args . tag ) | ch_stall_stat . get_value ( ) ) ;
args . tag | = 0x80 ; // Set stalled status
return false ;
}
index + + ;
2018-03-24 22:03:32 +01:00
}
return true ;
}
2018-10-11 00:17:19 +02:00
void spu_thread : : do_putlluc ( const spu_mfc_cmd & args )
2018-03-24 22:03:32 +01:00
{
2019-06-28 07:24:28 +02:00
const u32 addr = args . eal & - 128 ;
2018-06-21 14:24:47 +02:00
if ( raddr & & addr = = raddr )
2018-04-03 21:42:47 +02:00
{
2019-01-15 16:31:21 +01:00
// Last check for event before we clear the reservation
2019-06-06 20:32:35 +02:00
if ( ( vm : : reservation_acquire ( addr , 128 ) & - 128 ) ! = rtime | | ! cmp_rdata ( rdata , vm : : _ref < decltype ( rdata ) > ( addr ) ) )
2019-01-15 16:31:21 +01:00
{
ch_event_stat | = SPU_EVENT_LR ;
}
2018-04-03 21:42:47 +02:00
raddr = 0 ;
}
2018-12-04 19:09:55 +01:00
const auto & to_write = _ref < decltype ( rdata ) > ( args . lsa & 0x3ff80 ) ;
2018-03-24 22:03:32 +01:00
// Store unconditionally
2020-02-05 08:00:08 +01:00
if ( g_use_rtm ) [[likely]]
2018-03-24 22:03:32 +01:00
{
2019-06-06 20:32:35 +02:00
const u32 result = spu_putlluc_tx ( addr , to_write . data ( ) , this ) ;
2018-06-11 01:30:43 +02:00
2019-06-06 20:32:35 +02:00
if ( result = = 2 )
2018-05-19 22:14:02 +02:00
{
2019-06-06 20:32:35 +02:00
cpu_thread : : suspend_all cpu_lock ( this ) ;
2019-10-11 15:57:20 +02:00
if ( vm : : reservation_acquire ( addr , 128 ) & 64 )
2019-06-06 20:32:35 +02:00
{
2019-10-11 15:57:20 +02:00
// Wait for PUTLLC to complete
while ( vm : : reservation_acquire ( addr , 128 ) & 1 )
2019-06-06 20:32:35 +02:00
{
2019-10-11 15:57:20 +02:00
busy_wait ( 100 ) ;
2019-06-06 20:32:35 +02:00
}
2019-10-11 15:57:20 +02:00
mov_rdata ( vm : : _ref < decltype ( rdata ) > ( addr ) , to_write ) ;
vm : : reservation_acquire ( addr , 128 ) + = 64 ;
2019-06-06 20:32:35 +02:00
}
2018-05-19 22:14:02 +02:00
}
2018-05-21 19:25:05 +02:00
}
else
{
2018-12-04 19:09:55 +01:00
auto & data = vm : : _ref < decltype ( rdata ) > ( addr ) ;
2018-05-21 19:25:05 +02:00
auto & res = vm : : reservation_lock ( addr , 128 ) ;
2018-06-02 12:43:22 +02:00
2019-01-15 16:31:21 +01:00
* reinterpret_cast < atomic_t < u32 > * > ( & data ) + = 0 ;
2018-06-02 12:43:22 +02:00
2018-06-11 01:30:43 +02:00
if ( g_cfg . core . spu_accurate_putlluc )
{
2020-02-11 22:36:46 +01:00
const auto render = get_rsx_if_needs_res_pause ( addr ) ;
if ( render ) render - > pause ( ) ;
2020-02-01 09:15:29 +01:00
auto & super_data = * vm : : get_super_ptr < decltype ( rdata ) > ( addr ) ;
2020-02-11 22:36:46 +01:00
{
// Full lock (heavyweight)
// TODO: vm::check_addr
vm : : writer_lock lock ( addr ) ;
mov_rdata ( super_data , to_write ) ;
res . release ( res . load ( ) + 127 ) ;
}
if ( render ) render - > unpause ( ) ;
2018-06-11 01:30:43 +02:00
}
else
{
2019-06-06 20:32:35 +02:00
mov_rdata ( data , to_write ) ;
2019-05-18 19:56:22 +02:00
res . release ( res . load ( ) + 127 ) ;
2018-06-11 01:30:43 +02:00
}
2018-03-24 22:03:32 +01:00
}
2018-05-14 22:07:36 +02:00
vm : : reservation_notifier ( addr , 128 ) . notify_all ( ) ;
2018-03-24 22:03:32 +01:00
}
2018-10-11 00:17:19 +02:00
void spu_thread : : do_mfc ( bool wait )
2018-03-24 22:03:32 +01:00
{
u32 removed = 0 ;
u32 barrier = 0 ;
u32 fence = 0 ;
// Process enqueued commands
2019-10-25 12:32:21 +02:00
static_cast < void > ( std : : remove_if ( mfc_queue + 0 , mfc_queue + mfc_size , [ & ] ( spu_mfc_cmd & args )
2018-03-24 22:03:32 +01:00
{
2019-07-09 06:30:06 +02:00
// Select tag bit in the tag mask or the stall mask
const u32 mask = utils : : rol32 ( 1 , args . tag ) ;
2018-04-03 21:42:47 +02:00
if ( ( args . cmd & ~ 0xc ) = = MFC_BARRIER_CMD )
2018-03-24 22:03:32 +01:00
{
if ( & args - mfc_queue < = removed )
{
// Remove barrier-class command if it's the first in the queue
2019-06-29 17:48:42 +02:00
std : : atomic_thread_fence ( std : : memory_order_seq_cst ) ;
2018-03-24 22:03:32 +01:00
removed + + ;
return true ;
}
2018-04-03 21:42:47 +02:00
// Block all tags
barrier | = - 1 ;
2019-07-09 06:30:06 +02:00
fence | = mask ;
2018-03-24 22:03:32 +01:00
return false ;
}
if ( barrier & mask )
{
2018-04-03 15:09:43 +02:00
fence | = mask ;
2018-03-24 22:03:32 +01:00
return false ;
}
2018-07-10 10:25:11 +02:00
if ( args . cmd & ( MFC_BARRIER_MASK | MFC_FENCE_MASK ) & & fence & mask )
2018-04-03 21:42:47 +02:00
{
2018-07-10 10:25:11 +02:00
if ( args . cmd & MFC_BARRIER_MASK )
{
barrier | = mask ;
}
2018-04-03 21:42:47 +02:00
return false ;
}
2018-03-24 22:03:32 +01:00
if ( args . cmd & MFC_LIST_MASK )
{
2019-01-07 07:13:17 +01:00
if ( ! ( args . tag & 0x80 ) )
2018-03-24 22:03:32 +01:00
{
2018-05-23 10:55:15 +02:00
if ( do_list_transfer ( args ) )
2018-04-03 21:42:47 +02:00
{
2018-05-23 10:55:15 +02:00
removed + + ;
return true ;
2018-04-03 21:42:47 +02:00
}
2018-03-24 22:03:32 +01:00
}
if ( args . cmd & MFC_BARRIER_MASK )
{
barrier | = mask ;
}
2018-04-03 15:09:43 +02:00
fence | = mask ;
2018-03-24 22:03:32 +01:00
return false ;
}
2019-01-15 16:31:21 +01:00
if ( args . cmd = = MFC_PUTQLLUC_CMD )
2018-03-24 22:03:32 +01:00
{
2018-04-28 19:11:16 +02:00
if ( fence & mask )
{
return false ;
}
2018-04-03 21:42:47 +02:00
do_putlluc ( args ) ;
2018-03-24 22:03:32 +01:00
}
2019-01-15 16:31:21 +01:00
else if ( args . size )
{
do_dma_transfer ( args ) ;
}
2018-03-24 22:03:32 +01:00
removed + + ;
return true ;
2019-10-25 12:32:21 +02:00
} ) ) ;
2018-03-24 22:03:32 +01:00
mfc_size - = removed ;
2018-04-03 15:09:43 +02:00
mfc_barrier = barrier ;
mfc_fence = fence ;
2018-03-24 22:03:32 +01:00
if ( removed & & ch_tag_upd )
{
const u32 completed = get_mfc_completed ( ) ;
if ( completed & & ch_tag_upd = = 1 )
{
ch_tag_stat . set_value ( completed ) ;
ch_tag_upd = 0 ;
}
else if ( completed = = ch_tag_mask & & ch_tag_upd = = 2 )
{
ch_tag_stat . set_value ( completed ) ;
ch_tag_upd = 0 ;
}
}
}
2018-10-11 00:17:19 +02:00
u32 spu_thread : : get_mfc_completed ( )
2018-03-24 22:03:32 +01:00
{
2018-04-03 21:42:47 +02:00
return ch_tag_mask & ~ mfc_fence ;
2018-03-24 22:03:32 +01:00
}
2019-01-15 16:31:21 +01:00
bool spu_thread : : process_mfc_cmd ( )
2018-03-24 22:03:32 +01:00
{
// Stall infinitely if MFC queue is full
2020-02-05 08:00:08 +01:00
while ( mfc_size > = 16 ) [[unlikely]]
2018-03-24 22:03:32 +01:00
{
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2018-03-24 22:03:32 +01:00
{
return false ;
}
thread_ctrl : : wait ( ) ;
}
spu : : scheduler : : concurrent_execution_watchdog watchdog ( * this ) ;
2020-02-01 09:36:09 +01:00
spu_log . trace ( " DMAC: cmd=%s, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x " , ch_mfc_cmd . cmd , ch_mfc_cmd . lsa , ch_mfc_cmd . eal , ch_mfc_cmd . tag , ch_mfc_cmd . size ) ;
2018-03-24 22:03:32 +01:00
2019-01-15 16:31:21 +01:00
switch ( ch_mfc_cmd . cmd )
2017-02-17 20:35:57 +01:00
{
case MFC_GETLLAR_CMD :
2014-08-22 23:15:02 +02:00
{
2019-06-28 07:24:28 +02:00
const u32 addr = ch_mfc_cmd . eal & - 128 ;
2018-06-21 14:24:47 +02:00
auto & data = vm : : _ref < decltype ( rdata ) > ( addr ) ;
2019-01-15 16:31:21 +01:00
auto & dst = _ref < decltype ( rdata ) > ( ch_mfc_cmd . lsa & 0x3ff80 ) ;
u64 ntime ;
2014-08-22 23:15:02 +02:00
2018-04-03 21:42:47 +02:00
const bool is_polling = false ; // TODO
2015-03-02 03:10:41 +01:00
2017-02-17 20:35:57 +01:00
if ( is_polling )
2015-01-13 15:54:36 +01:00
{
2019-05-18 19:56:22 +02:00
rtime = vm : : reservation_acquire ( addr , 128 ) & - 128 ;
2017-02-17 20:35:57 +01:00
2019-06-06 20:32:35 +02:00
while ( cmp_rdata ( rdata , data ) & & ( vm : : reservation_acquire ( addr , 128 ) ) = = rtime )
2017-02-17 20:35:57 +01:00
{
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2017-02-17 20:35:57 +01:00
{
break ;
}
2014-09-24 20:44:26 +02:00
2019-06-19 21:01:48 +02:00
thread_ctrl : : wait_for ( 500 ) ;
2017-02-17 20:35:57 +01:00
}
2019-06-06 20:32:35 +02:00
if ( test_stopped ( ) )
{
return false ;
}
2017-02-17 20:35:57 +01:00
}
2018-04-03 21:42:47 +02:00
2020-02-05 08:00:08 +01:00
if ( g_use_rtm & & ! g_cfg . core . spu_accurate_getllar & & raddr ! = addr ) [[likely]]
2019-06-06 20:32:35 +02:00
{
// TODO: maybe always start from a transaction
ntime = spu_getll_inexact ( addr , dst . data ( ) ) ;
}
else if ( g_use_rtm )
2018-05-14 22:07:36 +02:00
{
2019-06-06 20:32:35 +02:00
ntime = spu_getll_tx ( addr , dst . data ( ) ) ;
2018-06-02 12:45:28 +02:00
2019-06-06 20:32:35 +02:00
if ( ntime = = 1 )
2018-05-19 22:20:41 +02:00
{
2019-06-06 20:32:35 +02:00
if ( ! g_cfg . core . spu_accurate_getllar )
{
ntime = spu_getll_inexact ( addr , dst . data ( ) ) ;
}
else
{
cpu_thread : : suspend_all cpu_lock ( this ) ;
while ( vm : : reservation_acquire ( addr , 128 ) & 127 )
{
busy_wait ( 100 ) ;
}
ntime = vm : : reservation_acquire ( addr , 128 ) ;
mov_rdata ( dst , data ) ;
}
2018-05-19 22:20:41 +02:00
}
2018-05-14 22:07:36 +02:00
}
2018-05-21 19:25:05 +02:00
else
2017-02-17 20:35:57 +01:00
{
2019-01-15 16:31:21 +01:00
auto & res = vm : : reservation_lock ( addr , 128 ) ;
2019-05-18 19:56:22 +02:00
const u64 old_time = res . load ( ) & - 128 ;
2018-06-02 12:45:28 +02:00
if ( g_cfg . core . spu_accurate_getllar )
{
2019-01-15 16:31:21 +01:00
* reinterpret_cast < atomic_t < u32 > * > ( & data ) + = 0 ;
2020-02-11 22:36:46 +01:00
const auto render = get_rsx_if_needs_res_pause ( addr ) ;
if ( render ) render - > pause ( ) ;
2020-02-01 09:15:29 +01:00
const auto & super_data = * vm : : get_super_ptr < decltype ( rdata ) > ( addr ) ;
2020-02-11 22:36:46 +01:00
{
// Full lock (heavyweight)
// TODO: vm::check_addr
vm : : writer_lock lock ( addr ) ;
2018-06-02 12:45:28 +02:00
2020-02-11 22:36:46 +01:00
ntime = old_time ;
mov_rdata ( dst , super_data ) ;
res . release ( old_time ) ;
}
2018-06-02 12:45:28 +02:00
2020-02-11 22:36:46 +01:00
if ( render ) render - > unpause ( ) ;
2018-06-02 12:45:28 +02:00
}
else
{
2019-02-09 10:34:36 +01:00
ntime = old_time ;
2019-06-06 20:32:35 +02:00
mov_rdata ( dst , data ) ;
2019-02-09 10:34:36 +01:00
res . release ( old_time ) ;
2018-06-02 12:45:28 +02:00
}
2015-01-13 15:54:36 +01:00
}
2014-08-22 23:15:02 +02:00
2019-06-06 20:32:35 +02:00
if ( raddr & & raddr ! = addr )
2019-01-15 16:31:21 +01:00
{
// Last check for event before we replace the reservation with a new one
2019-06-06 20:32:35 +02:00
if ( ( vm : : reservation_acquire ( raddr , 128 ) & - 128 ) ! = rtime | | ! cmp_rdata ( rdata , vm : : _ref < decltype ( rdata ) > ( raddr ) ) )
{
ch_event_stat | = SPU_EVENT_LR ;
}
}
else if ( raddr = = addr )
{
// Lost previous reservation on polling
if ( ntime ! = rtime | | ! cmp_rdata ( rdata , dst ) )
2019-01-15 16:31:21 +01:00
{
ch_event_stat | = SPU_EVENT_LR ;
}
}
raddr = addr ;
rtime = ntime ;
2019-06-06 20:32:35 +02:00
mov_rdata ( rdata , dst ) ;
2019-01-15 16:31:21 +01:00
2018-03-24 22:03:32 +01:00
ch_atomic_stat . set_value ( MFC_GETLLAR_SUCCESS ) ;
return true ;
2017-02-17 20:35:57 +01:00
}
case MFC_PUTLLC_CMD :
{
// Store conditionally
2019-06-28 07:24:28 +02:00
const u32 addr = ch_mfc_cmd . eal & - 128 ;
2019-01-15 16:31:21 +01:00
u32 result = 0 ;
2017-02-17 20:35:57 +01:00
2019-06-06 20:32:35 +02:00
if ( raddr = = addr )
2014-08-22 23:15:02 +02:00
{
2019-01-15 16:31:21 +01:00
const auto & to_write = _ref < decltype ( rdata ) > ( ch_mfc_cmd . lsa & 0x3ff80 ) ;
2018-12-04 19:09:55 +01:00
2020-02-05 08:00:08 +01:00
if ( g_use_rtm ) [[likely]]
2017-07-18 19:03:47 +02:00
{
2019-06-06 20:32:35 +02:00
result = spu_putllc_tx ( addr , rtime , rdata . data ( ) , to_write . data ( ) ) ;
2019-01-28 14:14:01 +01:00
2019-06-06 20:32:35 +02:00
if ( result = = 2 )
2019-05-18 22:47:35 +02:00
{
2019-06-06 20:32:35 +02:00
result = 0 ;
2018-05-18 17:51:48 +02:00
2019-06-06 20:32:35 +02:00
cpu_thread : : suspend_all cpu_lock ( this ) ;
2019-10-11 15:57:20 +02:00
// Give up if PUTLLUC happened
if ( vm : : reservation_acquire ( addr , 128 ) = = ( rtime | 1 ) )
2019-06-06 20:32:35 +02:00
{
auto & data = vm : : _ref < decltype ( rdata ) > ( addr ) ;
if ( ( vm : : reservation_acquire ( addr , 128 ) & - 128 ) = = rtime & & cmp_rdata ( rdata , data ) )
{
mov_rdata ( data , to_write ) ;
vm : : reservation_acquire ( addr , 128 ) + = 127 ;
result = 1 ;
}
else
{
vm : : reservation_acquire ( addr , 128 ) - = 1 ;
}
}
2019-10-11 15:57:20 +02:00
else
{
vm : : reservation_acquire ( addr , 128 ) - = 1 ;
}
2019-01-15 16:31:21 +01:00
}
2018-04-03 21:42:47 +02:00
}
2019-11-01 12:47:29 +01:00
else if ( auto & data = vm : : _ref < decltype ( rdata ) > ( addr ) ; rtime = = ( vm : : reservation_acquire ( raddr , 128 ) & - 128 ) )
2018-04-03 21:42:47 +02:00
{
2019-11-01 12:47:29 +01:00
if ( cmp_rdata ( rdata , to_write ) )
2019-01-15 16:31:21 +01:00
{
2019-11-01 12:47:29 +01:00
// Writeback of unchanged data. Only check memory change
result = cmp_rdata ( rdata , data ) & & vm : : reservation_acquire ( raddr , 128 ) . compare_and_swap_test ( rtime , rtime + 128 ) ;
}
else
{
auto & res = vm : : reservation_lock ( raddr , 128 ) ;
const u64 old_time = res . load ( ) & - 128 ;
2017-02-17 20:35:57 +01:00
2019-11-01 12:47:29 +01:00
if ( rtime = = old_time )
2019-01-15 16:31:21 +01:00
{
2019-11-01 12:47:29 +01:00
* reinterpret_cast < atomic_t < u32 > * > ( & data ) + = 0 ;
2020-02-11 22:36:46 +01:00
const auto render = get_rsx_if_needs_res_pause ( addr ) ;
2020-02-01 09:15:29 +01:00
2020-02-11 22:36:46 +01:00
if ( render ) render - > pause ( ) ;
2019-11-01 12:47:29 +01:00
2020-02-11 22:36:46 +01:00
auto & super_data = * vm : : get_super_ptr < decltype ( rdata ) > ( addr ) ;
2019-11-01 12:47:29 +01:00
{
2020-02-11 22:36:46 +01:00
// Full lock (heavyweight)
// TODO: vm::check_addr
vm : : writer_lock lock ( addr ) ;
if ( cmp_rdata ( rdata , super_data ) )
{
mov_rdata ( super_data , to_write ) ;
res . release ( old_time + 128 ) ;
result = 1 ;
}
else
{
res . release ( old_time ) ;
}
2019-11-01 12:47:29 +01:00
}
2020-02-11 22:36:46 +01:00
if ( render ) render - > unpause ( ) ;
2019-01-15 16:31:21 +01:00
}
else
{
2019-02-09 10:34:36 +01:00
res . release ( old_time ) ;
2019-01-15 16:31:21 +01:00
}
2018-05-21 19:25:05 +02:00
}
2017-02-17 20:35:57 +01:00
}
}
if ( result )
{
2019-01-15 16:31:21 +01:00
vm : : reservation_notifier ( addr , 128 ) . notify_all ( ) ;
2017-02-17 20:35:57 +01:00
ch_atomic_stat . set_value ( MFC_PUTLLC_SUCCESS ) ;
2014-08-22 23:15:02 +02:00
}
2017-02-17 20:35:57 +01:00
else
{
2019-01-15 16:31:21 +01:00
if ( raddr )
{
// Last check for event before we clear the reservation
2019-06-06 20:32:35 +02:00
if ( raddr = = addr | | rtime ! = ( vm : : reservation_acquire ( raddr , 128 ) & - 128 ) | | ! cmp_rdata ( rdata , vm : : _ref < decltype ( rdata ) > ( raddr ) ) )
2019-01-15 16:31:21 +01:00
{
ch_event_stat | = SPU_EVENT_LR ;
}
}
2017-02-17 20:35:57 +01:00
2019-01-15 16:31:21 +01:00
ch_atomic_stat . set_value ( MFC_PUTLLC_FAILURE ) ;
2017-02-17 20:35:57 +01:00
}
raddr = 0 ;
2018-03-24 22:03:32 +01:00
return true ;
2014-08-22 23:15:02 +02:00
}
2017-02-17 20:35:57 +01:00
case MFC_PUTLLUC_CMD :
{
2019-01-15 16:31:21 +01:00
do_putlluc ( ch_mfc_cmd ) ;
2017-02-17 20:35:57 +01:00
ch_atomic_stat . set_value ( MFC_PUTLLUC_SUCCESS ) ;
2018-03-24 22:03:32 +01:00
return true ;
2017-02-17 20:35:57 +01:00
}
case MFC_PUTQLLUC_CMD :
2014-08-22 23:15:02 +02:00
{
2019-01-15 16:31:21 +01:00
const u32 mask = utils : : rol32 ( 1 , ch_mfc_cmd . tag ) ;
2018-04-28 19:11:16 +02:00
2020-02-05 08:00:08 +01:00
if ( ( mfc_barrier | mfc_fence ) & mask ) [[unlikely]]
2018-03-24 22:03:32 +01:00
{
2019-01-15 16:31:21 +01:00
mfc_queue [ mfc_size + + ] = ch_mfc_cmd ;
2018-04-28 19:11:16 +02:00
mfc_fence | = mask ;
2018-04-03 21:42:47 +02:00
}
else
{
2019-01-15 16:31:21 +01:00
do_putlluc ( ch_mfc_cmd ) ;
2018-03-24 22:03:32 +01:00
}
return true ;
2017-02-17 20:35:57 +01:00
}
case MFC_SNDSIG_CMD :
case MFC_SNDSIGB_CMD :
case MFC_SNDSIGF_CMD :
{
2019-01-15 16:31:21 +01:00
if ( ch_mfc_cmd . size ! = 4 )
{
break ;
}
2017-02-17 20:35:57 +01:00
// Fallthrough
}
2014-08-22 23:15:02 +02:00
case MFC_PUT_CMD :
2015-03-02 03:10:41 +01:00
case MFC_PUTB_CMD :
case MFC_PUTF_CMD :
case MFC_PUTR_CMD :
case MFC_PUTRB_CMD :
case MFC_PUTRF_CMD :
2014-08-22 23:15:02 +02:00
case MFC_GET_CMD :
2015-03-02 03:10:41 +01:00
case MFC_GETB_CMD :
case MFC_GETF_CMD :
2014-08-22 23:15:02 +02:00
{
2020-02-05 08:00:08 +01:00
if ( ch_mfc_cmd . size < = 0x4000 ) [[likely]]
2017-02-17 20:35:57 +01:00
{
2020-02-05 08:00:08 +01:00
if ( do_dma_check ( ch_mfc_cmd ) ) [[likely]]
2017-02-17 20:35:57 +01:00
{
2019-01-15 16:31:21 +01:00
if ( ch_mfc_cmd . size )
2018-04-03 21:42:47 +02:00
{
2019-01-15 16:31:21 +01:00
do_dma_transfer ( ch_mfc_cmd ) ;
2018-04-03 21:42:47 +02:00
}
2018-05-23 10:55:15 +02:00
return true ;
2017-02-17 20:35:57 +01:00
}
2014-08-22 23:15:02 +02:00
2019-01-15 16:31:21 +01:00
mfc_queue [ mfc_size + + ] = ch_mfc_cmd ;
mfc_fence | = utils : : rol32 ( 1 , ch_mfc_cmd . tag ) ;
2018-04-03 21:42:47 +02:00
2019-01-15 16:31:21 +01:00
if ( ch_mfc_cmd . cmd & MFC_BARRIER_MASK )
2017-02-17 20:35:57 +01:00
{
2019-01-15 16:31:21 +01:00
mfc_barrier | = utils : : rol32 ( 1 , ch_mfc_cmd . tag ) ;
2017-02-17 20:35:57 +01:00
}
2018-03-24 22:03:32 +01:00
return true ;
2017-02-17 20:35:57 +01:00
}
2017-12-09 15:57:43 +01:00
2017-02-17 20:35:57 +01:00
break ;
}
2014-08-22 23:15:02 +02:00
case MFC_PUTL_CMD :
2015-03-02 03:10:41 +01:00
case MFC_PUTLB_CMD :
case MFC_PUTLF_CMD :
case MFC_PUTRL_CMD :
case MFC_PUTRLB_CMD :
case MFC_PUTRLF_CMD :
2014-08-22 23:15:02 +02:00
case MFC_GETL_CMD :
2015-03-02 03:10:41 +01:00
case MFC_GETLB_CMD :
case MFC_GETLF_CMD :
2014-08-22 23:15:02 +02:00
{
2020-02-05 08:00:08 +01:00
if ( ch_mfc_cmd . size < = 0x4000 ) [[likely]]
2015-07-21 22:14:04 +02:00
{
2019-01-15 16:31:21 +01:00
auto & cmd = mfc_queue [ mfc_size ] ;
cmd = ch_mfc_cmd ;
2020-02-05 08:00:08 +01:00
if ( do_dma_check ( cmd ) ) [[likely]]
2018-04-03 21:42:47 +02:00
{
2020-02-05 08:00:08 +01:00
if ( ! cmd . size | | do_list_transfer ( cmd ) ) [[likely]]
2018-04-03 21:42:47 +02:00
{
2018-05-23 10:55:15 +02:00
return true ;
2018-04-03 21:42:47 +02:00
}
}
2019-01-15 16:31:21 +01:00
mfc_size + + ;
mfc_fence | = utils : : rol32 ( 1 , cmd . tag ) ;
2018-04-03 21:42:47 +02:00
2019-01-15 16:31:21 +01:00
if ( cmd . cmd & MFC_BARRIER_MASK )
2015-07-21 22:14:04 +02:00
{
2019-01-15 16:31:21 +01:00
mfc_barrier | = utils : : rol32 ( 1 , cmd . tag ) ;
2015-07-21 22:14:04 +02:00
}
2018-03-24 22:03:32 +01:00
return true ;
2015-03-02 03:10:41 +01:00
}
2017-02-17 20:35:57 +01:00
break ;
2014-08-22 23:15:02 +02:00
}
2015-12-20 09:16:31 +01:00
case MFC_BARRIER_CMD :
case MFC_EIEIO_CMD :
2018-04-01 19:52:54 +02:00
case MFC_SYNC_CMD :
{
2018-04-03 21:42:47 +02:00
if ( mfc_size = = 0 )
2018-04-01 19:52:54 +02:00
{
2019-06-29 17:48:42 +02:00
std : : atomic_thread_fence ( std : : memory_order_seq_cst ) ;
2018-04-01 19:52:54 +02:00
}
else
{
2019-01-15 16:31:21 +01:00
mfc_queue [ mfc_size + + ] = ch_mfc_cmd ;
2018-04-03 21:42:47 +02:00
mfc_barrier | = - 1 ;
2019-07-09 06:30:06 +02:00
mfc_fence | = utils : : rol32 ( 1 , ch_mfc_cmd . tag ) ;
2018-04-01 19:52:54 +02:00
}
return true ;
}
2017-02-17 20:35:57 +01:00
default :
{
2018-03-24 22:03:32 +01:00
break ;
2017-02-17 20:35:57 +01:00
}
2015-02-16 02:53:53 +01:00
}
2015-03-02 03:10:41 +01:00
2018-03-24 22:03:32 +01:00
fmt : : throw_exception ( " Unknown command (cmd=%s, lsa=0x%x, ea=0x%llx, tag=0x%x, size=0x%x) " HERE ,
2019-01-15 16:31:21 +01:00
ch_mfc_cmd . cmd , ch_mfc_cmd . lsa , ch_mfc_cmd . eal , ch_mfc_cmd . tag , ch_mfc_cmd . size ) ;
2014-08-22 23:15:02 +02:00
}
2018-10-11 00:17:19 +02:00
u32 spu_thread : : get_events ( bool waiting )
2015-07-21 22:14:04 +02:00
{
2018-07-02 00:02:34 +02:00
const u32 mask1 = ch_event_mask ;
if ( mask1 & ~ SPU_EVENT_IMPLEMENTED )
{
fmt : : throw_exception ( " SPU Events not implemented (mask=0x%x) " HERE , mask1 ) ;
}
2017-02-17 20:35:57 +01:00
// Check reservation status and set SPU_EVENT_LR if lost
2019-06-06 20:32:35 +02:00
if ( raddr & & ( ( vm : : reservation_acquire ( raddr , sizeof ( rdata ) ) & - 128 ) ! = rtime | | ! cmp_rdata ( rdata , vm : : _ref < decltype ( rdata ) > ( raddr ) ) ) )
2015-07-21 22:14:04 +02:00
{
ch_event_stat | = SPU_EVENT_LR ;
2017-02-17 20:35:57 +01:00
raddr = 0 ;
2015-07-15 20:11:32 +02:00
}
2017-02-07 21:59:59 +01:00
// SPU Decrementer Event
2017-02-13 14:12:24 +01:00
if ( ! ch_dec_value | | ( ch_dec_value - ( get_timebased_time ( ) - ch_dec_start_timestamp ) ) > > 31 )
2017-02-07 21:59:59 +01:00
{
2017-02-13 14:12:24 +01:00
if ( ( ch_event_stat & SPU_EVENT_TM ) = = 0 )
{
ch_event_stat | = SPU_EVENT_TM ;
}
2017-02-07 21:59:59 +01:00
}
2017-02-17 20:35:57 +01:00
// Simple polling or polling with atomically set/removed SPU_EVENT_WAITING flag
2018-07-02 00:02:34 +02:00
return ! waiting ? ch_event_stat & mask1 : ch_event_stat . atomic_op ( [ & ] ( u32 & stat ) - > u32
2015-07-15 20:11:32 +02:00
{
2018-07-02 00:02:34 +02:00
if ( u32 res = stat & mask1 )
2015-07-15 20:11:32 +02:00
{
2017-02-17 20:35:57 +01:00
stat & = ~ SPU_EVENT_WAITING ;
return res ;
}
2015-07-21 22:14:04 +02:00
2017-02-17 20:35:57 +01:00
stat | = SPU_EVENT_WAITING ;
return 0 ;
} ) ;
2015-07-21 22:14:04 +02:00
}
2018-10-11 00:17:19 +02:00
void spu_thread : : set_events ( u32 mask )
2015-07-15 20:11:32 +02:00
{
2018-07-02 00:02:34 +02:00
if ( mask & ~ SPU_EVENT_IMPLEMENTED )
2015-07-15 20:11:32 +02:00
{
2018-07-02 00:02:34 +02:00
fmt : : throw_exception ( " SPU Events not implemented (mask=0x%x) " HERE , mask ) ;
2015-07-15 20:11:32 +02:00
}
2016-05-13 15:55:34 +02:00
// Set new events, get old event mask
2016-04-14 01:09:41 +02:00
const u32 old_stat = ch_event_stat . fetch_or ( mask ) ;
2015-07-15 20:11:32 +02:00
2016-05-13 15:55:34 +02:00
// Notify if some events were set
if ( ~ old_stat & mask & & old_stat & SPU_EVENT_WAITING & & ch_event_stat & SPU_EVENT_WAITING )
2015-07-15 20:11:32 +02:00
{
2016-09-07 00:38:52 +02:00
notify ( ) ;
2015-07-15 20:11:32 +02:00
}
}
2018-10-11 00:17:19 +02:00
void spu_thread : : set_interrupt_status ( bool enable )
2015-07-16 13:32:19 +02:00
{
if ( enable )
{
2018-07-02 00:02:34 +02:00
// Detect enabling interrupts with events masked
if ( ch_event_mask & ~ SPU_EVENT_INTR_IMPLEMENTED )
2015-07-16 13:32:19 +02:00
{
2018-07-02 00:02:34 +02:00
fmt : : throw_exception ( " SPU Interrupts not implemented (mask=0x%x) " HERE , + ch_event_mask ) ;
2015-07-16 13:32:19 +02:00
}
2018-07-02 00:02:34 +02:00
2017-12-01 21:11:06 +01:00
interrupts_enabled = true ;
2015-07-16 13:32:19 +02:00
}
else
{
2017-12-01 21:11:06 +01:00
interrupts_enabled = false ;
2015-07-16 13:32:19 +02:00
}
}
2018-10-11 00:17:19 +02:00
u32 spu_thread : : get_ch_count ( u32 ch )
2014-08-22 23:15:02 +02:00
{
2020-02-01 09:36:09 +01:00
spu_log . trace ( " get_ch_count(ch=%d [%s]) " , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " ) ;
2015-03-02 03:10:41 +01:00
switch ( ch )
{
2017-02-17 20:35:57 +01:00
case SPU_WrOutMbox : return ch_out_mbox . get_count ( ) ^ 1 ;
case SPU_WrOutIntrMbox : return ch_out_intr_mbox . get_count ( ) ^ 1 ;
case SPU_RdInMbox : return ch_in_mbox . get_count ( ) ;
case MFC_RdTagStat : return ch_tag_stat . get_count ( ) ;
case MFC_RdListStallStat : return ch_stall_stat . get_count ( ) ;
case MFC_WrTagUpdate : return ch_tag_upd = = 0 ;
case SPU_RdSigNotify1 : return ch_snr1 . get_count ( ) ;
case SPU_RdSigNotify2 : return ch_snr2 . get_count ( ) ;
case MFC_RdAtomicStat : return ch_atomic_stat . get_count ( ) ;
case SPU_RdEventStat : return get_events ( ) ! = 0 ;
2018-03-24 22:03:32 +01:00
case MFC_Cmd : return 16 - mfc_size ;
2015-03-02 03:10:41 +01:00
}
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown/illegal channel (ch=%d [%s]) " HERE , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " ) ;
2014-08-22 23:15:02 +02:00
}
2018-10-11 00:17:19 +02:00
s64 spu_thread : : get_ch_value ( u32 ch )
2014-08-22 23:15:02 +02:00
{
2020-02-01 09:36:09 +01:00
spu_log . trace ( " get_ch_value(ch=%d [%s]) " , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " ) ;
2014-10-02 12:29:20 +02:00
2018-04-30 18:39:06 +02:00
auto read_channel = [ & ] ( spu_channel & channel ) - > s64
2015-07-03 18:07:36 +02:00
{
2019-06-06 20:32:35 +02:00
if ( channel . get_count ( ) = = 0 )
{
state + = cpu_flag : : wait ;
}
2017-02-17 20:35:57 +01:00
for ( int i = 0 ; i < 10 & & channel . get_count ( ) = = 0 ; i + + )
{
2018-05-14 22:07:36 +02:00
busy_wait ( ) ;
2017-02-17 20:35:57 +01:00
}
2019-05-18 22:47:35 +02:00
u32 out = 0 ;
2018-04-30 18:39:06 +02:00
2017-02-17 20:35:57 +01:00
while ( ! channel . try_pop ( out ) )
2015-07-03 18:07:36 +02:00
{
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2017-02-17 20:35:57 +01:00
{
2018-04-30 18:39:06 +02:00
return - 1 ;
2017-02-17 20:35:57 +01:00
}
2015-07-17 18:27:12 +02:00
2018-03-24 22:03:32 +01:00
thread_ctrl : : wait ( ) ;
2015-07-03 18:07:36 +02:00
}
2016-05-13 15:55:34 +02:00
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2018-04-30 18:39:06 +02:00
return out ;
2015-07-03 18:07:36 +02:00
} ;
2014-08-22 23:15:02 +02:00
switch ( ch )
{
2017-02-13 14:12:24 +01:00
case SPU_RdSRR0 :
{
2018-04-30 18:39:06 +02:00
return srr0 ;
2017-02-13 14:12:24 +01:00
}
2015-03-02 03:10:41 +01:00
case SPU_RdInMbox :
{
2019-06-06 20:32:35 +02:00
if ( ch_in_mbox . get_count ( ) = = 0 )
{
state + = cpu_flag : : wait ;
}
2015-07-17 18:27:12 +02:00
while ( true )
2015-03-02 03:10:41 +01:00
{
2017-02-17 20:35:57 +01:00
for ( int i = 0 ; i < 10 & & ch_in_mbox . get_count ( ) = = 0 ; i + + )
{
2018-05-14 22:07:36 +02:00
busy_wait ( ) ;
2017-02-17 20:35:57 +01:00
}
2019-05-18 22:47:35 +02:00
u32 out = 0 ;
2018-04-30 18:39:06 +02:00
2016-04-19 15:04:02 +02:00
if ( const uint old_count = ch_in_mbox . try_pop ( out ) )
2015-07-17 18:27:12 +02:00
{
2016-04-19 15:04:02 +02:00
if ( old_count = = 4 /* SPU_IN_MBOX_THRESHOLD */ ) // TODO: check this
2015-07-17 18:27:12 +02:00
{
int_ctrl [ 2 ] . set ( SPU_INT2_STAT_SPU_MAILBOX_THRESHOLD_INT ) ;
}
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2018-04-30 18:39:06 +02:00
return out ;
2015-07-17 18:27:12 +02:00
}
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2016-04-14 01:09:41 +02:00
{
2018-04-30 18:39:06 +02:00
return - 1 ;
2016-04-14 01:09:41 +02:00
}
2015-07-03 18:07:36 +02:00
2016-07-27 23:43:22 +02:00
thread_ctrl : : wait ( ) ;
2015-03-02 03:10:41 +01:00
}
}
2014-08-22 23:15:02 +02:00
2015-03-02 03:10:41 +01:00
case MFC_RdTagStat :
{
2018-03-24 22:03:32 +01:00
if ( ch_tag_stat . get_count ( ) )
{
2018-04-30 18:39:06 +02:00
u32 out = ch_tag_stat . get_value ( ) ;
2018-03-24 22:03:32 +01:00
ch_tag_stat . set_value ( 0 , false ) ;
2018-04-30 18:39:06 +02:00
return out ;
2018-03-24 22:03:32 +01:00
}
// Will stall infinitely
2015-07-03 18:07:36 +02:00
return read_channel ( ch_tag_stat ) ;
2015-03-02 03:10:41 +01:00
}
case MFC_RdTagMask :
{
2018-04-30 18:39:06 +02:00
return ch_tag_mask ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdSigNotify1 :
{
2015-07-03 18:07:36 +02:00
return read_channel ( ch_snr1 ) ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdSigNotify2 :
{
2015-07-03 18:07:36 +02:00
return read_channel ( ch_snr2 ) ;
2015-03-02 03:10:41 +01:00
}
case MFC_RdAtomicStat :
{
2018-03-24 22:03:32 +01:00
if ( ch_atomic_stat . get_count ( ) )
{
2018-04-30 18:39:06 +02:00
u32 out = ch_atomic_stat . get_value ( ) ;
2018-03-24 22:03:32 +01:00
ch_atomic_stat . set_value ( 0 , false ) ;
2018-04-30 18:39:06 +02:00
return out ;
2018-03-24 22:03:32 +01:00
}
// Will stall infinitely
2015-07-03 18:07:36 +02:00
return read_channel ( ch_atomic_stat ) ;
2015-03-02 03:10:41 +01:00
}
case MFC_RdListStallStat :
{
2018-03-24 22:03:32 +01:00
if ( ch_stall_stat . get_count ( ) )
{
2018-04-30 18:39:06 +02:00
u32 out = ch_stall_stat . get_value ( ) ;
2018-03-24 22:03:32 +01:00
ch_stall_stat . set_value ( 0 , false ) ;
2018-04-30 18:39:06 +02:00
return out ;
2018-03-24 22:03:32 +01:00
}
// Will stall infinitely
2015-07-03 18:07:36 +02:00
return read_channel ( ch_stall_stat ) ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdDec :
{
2019-12-02 22:31:34 +01:00
u32 out = ch_dec_value - static_cast < u32 > ( get_timebased_time ( ) - ch_dec_start_timestamp ) ;
2017-07-14 16:00:49 +02:00
//Polling: We might as well hint to the scheduler to slot in another thread since this one is counting down
if ( g_cfg . core . spu_loop_detection & & out > spu : : scheduler : : native_jiffy_duration_us )
2019-06-20 03:32:19 +02:00
{
state + = cpu_flag : : wait ;
2017-07-14 16:00:49 +02:00
std : : this_thread : : yield ( ) ;
2019-06-20 03:32:19 +02:00
}
2017-07-14 16:00:49 +02:00
2018-04-30 18:39:06 +02:00
return out ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdEventMask :
{
2018-04-30 18:39:06 +02:00
return ch_event_mask ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdEventStat :
{
2017-02-17 20:35:57 +01:00
u32 res = get_events ( ) ;
if ( res )
2015-03-02 03:10:41 +01:00
{
2018-04-30 18:39:06 +02:00
return res ;
2015-07-15 20:11:32 +02:00
}
2015-07-04 01:22:24 +02:00
2018-05-14 22:07:36 +02:00
const u32 mask1 = ch_event_mask ;
2017-02-17 20:35:57 +01:00
2018-05-14 22:07:36 +02:00
if ( mask1 & SPU_EVENT_LR & & raddr )
2015-07-15 20:11:32 +02:00
{
2018-05-17 18:34:35 +02:00
if ( mask1 ! = SPU_EVENT_LR & & mask1 ! = SPU_EVENT_LR + SPU_EVENT_TM )
2018-05-14 22:07:36 +02:00
{
2018-05-17 18:34:35 +02:00
// Combining LR with other flags needs another solution
2018-05-14 22:07:36 +02:00
fmt : : throw_exception ( " Not supported: event mask 0x%x " HERE , mask1 ) ;
}
while ( res = get_events ( ) , ! res )
{
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2018-05-14 22:07:36 +02:00
{
return - 1 ;
}
2019-09-19 01:57:08 +02:00
vm : : reservation_notifier ( raddr , 128 ) . wait < UINT64_MAX & - 128 > ( rtime , atomic_wait_timeout { 100'000 } ) ;
2018-05-14 22:07:36 +02:00
}
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2018-05-14 22:07:36 +02:00
return res ;
2015-07-15 20:11:32 +02:00
}
2017-02-17 20:35:57 +01:00
2018-05-14 22:07:36 +02:00
while ( res = get_events ( true ) , ! res )
2015-07-15 20:11:32 +02:00
{
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2015-07-08 00:33:24 +02:00
{
2018-04-30 18:39:06 +02:00
return - 1 ;
2015-07-15 20:11:32 +02:00
}
2017-02-17 20:35:57 +01:00
thread_ctrl : : wait_for ( 100 ) ;
2016-04-14 01:09:41 +02:00
}
2017-12-09 15:57:43 +01:00
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2018-04-30 18:39:06 +02:00
return res ;
2015-03-02 03:10:41 +01:00
}
case SPU_RdMachStat :
2014-08-22 23:15:02 +02:00
{
2015-07-16 13:32:19 +02:00
// HACK: "Not isolated" status
// Return SPU Interrupt status in LSB
2018-04-30 18:39:06 +02:00
return interrupts_enabled = = true ;
2014-08-22 23:15:02 +02:00
}
}
2014-10-02 12:29:20 +02:00
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown/illegal channel (ch=%d [%s]) " HERE , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " ) ;
2014-08-22 23:15:02 +02:00
}
2018-10-11 00:17:19 +02:00
bool spu_thread : : set_ch_value ( u32 ch , u32 value )
2014-08-22 23:15:02 +02:00
{
2020-02-01 09:36:09 +01:00
spu_log . trace ( " set_ch_value(ch=%d [%s], value=0x%x) " , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " , value ) ;
2014-10-02 12:29:20 +02:00
2014-08-22 23:15:02 +02:00
switch ( ch )
{
2017-02-13 14:12:24 +01:00
case SPU_WrSRR0 :
{
2019-10-01 09:06:34 +02:00
srr0 = value & 0x3fffc ;
2018-01-15 14:18:13 +01:00
return true ;
2017-02-13 14:12:24 +01:00
}
2017-12-09 15:57:43 +01:00
2014-08-22 23:15:02 +02:00
case SPU_WrOutIntrMbox :
{
2016-04-14 01:09:41 +02:00
if ( offset > = RAW_SPU_BASE_ADDR )
2014-08-22 23:15:02 +02:00
{
2015-07-03 18:07:36 +02:00
while ( ! ch_out_intr_mbox . try_push ( value ) )
2014-08-22 23:15:02 +02:00
{
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2016-04-14 01:09:41 +02:00
{
2016-04-19 15:04:02 +02:00
return false ;
2016-04-14 01:09:41 +02:00
}
2015-07-03 18:07:36 +02:00
2016-07-27 23:43:22 +02:00
thread_ctrl : : wait ( ) ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2015-07-12 23:02:02 +02:00
int_ctrl [ 2 ] . set ( SPU_INT2_STAT_MAILBOX_INT ) ;
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2017-12-09 15:57:43 +01:00
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2017-02-05 00:26:57 +01:00
const u32 code = value > > 24 ;
2014-08-22 23:15:02 +02:00
{
if ( code < 64 )
{
/* ===== sys_spu_thread_send_event (used by spu_printf) ===== */
2017-02-05 00:26:57 +01:00
u32 spup = code & 63 ;
2019-05-18 22:47:35 +02:00
u32 data = 0 ;
2014-08-22 23:15:02 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( data ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_send_event(value=0x%x, spup=%d): Out_MBox is empty " HERE , value , spup ) ;
2014-08-22 23:15:02 +02:00
}
2015-07-17 18:27:12 +02:00
if ( u32 count = ch_in_mbox . get_count ( ) )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_send_event(value=0x%x, spup=%d): In_MBox is not empty (count=%d) " HERE , value , spup , count ) ;
2015-07-17 18:27:12 +02:00
}
2020-02-01 09:36:09 +01:00
spu_log . trace ( " sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x) " , spup , value & 0x00ffffff , data ) ;
2014-08-22 23:15:02 +02:00
2018-09-03 21:28:33 +02:00
const auto queue = ( std : : lock_guard { group - > mutex } , this - > spup [ spup ] . lock ( ) ) ;
2014-08-22 23:15:02 +02:00
2015-03-04 05:42:04 +01:00
if ( ! queue )
2014-08-22 23:15:02 +02:00
{
2020-02-01 09:36:09 +01:00
spu_log . warning ( " sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected " , spup , ( value & 0x00ffffff ) , data ) ;
2017-02-24 14:19:11 +01:00
ch_in_mbox . set_values ( 1 , CELL_ENOTCONN ) ;
return true ;
2014-08-22 23:15:02 +02:00
}
2017-02-24 14:19:11 +01:00
ch_in_mbox . set_values ( 1 , CELL_OK ) ;
2019-11-03 00:44:02 +01:00
if ( ! queue - > send ( SYS_SPU_THREAD_EVENT_USER_KEY , lv2_id , ( u64 { spup } < < 32 ) | ( value & 0x00ffffff ) , data ) )
2014-08-22 23:15:02 +02:00
{
2017-02-24 14:19:11 +01:00
ch_in_mbox . set_values ( 1 , CELL_EBUSY ) ;
2014-08-22 23:15:02 +02:00
}
2017-02-24 14:19:11 +01:00
return true ;
2014-08-22 23:15:02 +02:00
}
else if ( code < 128 )
{
/* ===== sys_spu_thread_throw_event ===== */
2017-02-05 00:26:57 +01:00
u32 spup = code & 63 ;
2019-05-18 22:47:35 +02:00
u32 data = 0 ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( data ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_throw_event(value=0x%x, spup=%d): Out_MBox is empty " HERE , value , spup ) ;
2014-08-22 23:15:02 +02:00
}
2020-02-01 09:36:09 +01:00
spu_log . trace ( " sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x) " , spup , value & 0x00ffffff , data ) ;
2014-08-22 23:15:02 +02:00
2018-09-03 21:28:33 +02:00
const auto queue = ( std : : lock_guard { group - > mutex } , this - > spup [ spup ] . lock ( ) ) ;
2014-08-22 23:15:02 +02:00
2015-03-04 05:42:04 +01:00
if ( ! queue )
2014-08-22 23:15:02 +02:00
{
2020-02-01 09:36:09 +01:00
spu_log . warning ( " sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected " , spup , ( value & 0x00ffffff ) , data ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
// TODO: check passing spup value
2019-11-03 00:44:02 +01:00
if ( ! queue - > send ( SYS_SPU_THREAD_EVENT_USER_KEY , lv2_id , ( u64 { spup } < < 32 ) | ( value & 0x00ffffff ) , data ) )
2014-08-22 23:15:02 +02:00
{
2020-02-01 09:36:09 +01:00
spu_log . warning ( " sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x) failed (queue is full) " , spup , ( value & 0x00ffffff ) , data ) ;
2014-08-22 23:15:02 +02:00
}
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
else if ( code = = 128 )
{
/* ===== sys_event_flag_set_bit ===== */
2017-02-05 00:26:57 +01:00
u32 flag = value & 0xffffff ;
2019-05-18 22:47:35 +02:00
u32 data = 0 ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( data ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_event_flag_set_bit(value=0x%x (flag=%d)): Out_MBox is empty " HERE , value , flag ) ;
2014-08-22 23:15:02 +02:00
}
2015-07-17 18:27:12 +02:00
if ( u32 count = ch_in_mbox . get_count ( ) )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_event_flag_set_bit(value=0x%x (flag=%d)): In_MBox is not empty (%d) " HERE , value , flag , count ) ;
2015-07-17 18:27:12 +02:00
}
2020-02-01 09:36:09 +01:00
spu_log . trace ( " sys_event_flag_set_bit(id=%d, value=0x%x (flag=%d)) " , data , value , flag ) ;
2014-08-22 23:15:02 +02:00
2017-02-24 14:19:11 +01:00
ch_in_mbox . set_values ( 1 , CELL_OK ) ;
2017-02-03 17:27:03 +01:00
// Use the syscall to set flag
2017-02-24 14:19:11 +01:00
if ( s32 res = sys_event_flag_set ( data , 1ull < < flag ) )
{
ch_in_mbox . set_values ( 1 , res ) ;
}
return true ;
2014-08-22 23:15:02 +02:00
}
else if ( code = = 192 )
{
/* ===== sys_event_flag_set_bit_impatient ===== */
2017-02-05 00:26:57 +01:00
u32 flag = value & 0xffffff ;
2019-05-18 22:47:35 +02:00
u32 data = 0 ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( data ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_event_flag_set_bit_impatient(value=0x%x (flag=%d)): Out_MBox is empty " HERE , value , flag ) ;
2014-08-22 23:15:02 +02:00
}
2020-02-01 09:36:09 +01:00
spu_log . trace ( " sys_event_flag_set_bit_impatient(id=%d, value=0x%x (flag=%d)) " , data , value , flag ) ;
2014-08-22 23:15:02 +02:00
2017-02-03 17:27:03 +01:00
// Use the syscall to set flag
sys_event_flag_set ( data , 1ull < < flag ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
else
{
2015-03-02 03:10:41 +01:00
if ( ch_out_mbox . get_count ( ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " SPU_WrOutIntrMbox: unknown data (value=0x%x); Out_MBox = 0x%x " HERE , value , ch_out_mbox . get_value ( ) ) ;
2014-08-22 23:15:02 +02:00
}
else
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " SPU_WrOutIntrMbox: unknown data (value=0x%x) " HERE , value ) ;
2014-08-22 23:15:02 +02:00
}
}
}
}
case SPU_WrOutMbox :
{
2018-04-03 21:42:47 +02:00
while ( ! ch_out_mbox . try_push ( value ) )
{
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2016-04-14 01:09:41 +02:00
{
2016-04-19 15:04:02 +02:00
return false ;
2016-04-14 01:09:41 +02:00
}
2015-07-03 18:07:36 +02:00
2016-07-27 23:43:22 +02:00
thread_ctrl : : wait ( ) ;
2014-12-23 00:31:11 +01:00
}
2015-03-02 03:10:41 +01:00
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_WrTagMask :
{
2015-03-02 03:10:41 +01:00
ch_tag_mask = value ;
2018-03-24 22:03:32 +01:00
if ( ch_tag_upd )
{
const u32 completed = get_mfc_completed ( ) ;
if ( completed & & ch_tag_upd = = 1 )
{
ch_tag_stat . set_value ( completed ) ;
ch_tag_upd = 0 ;
}
else if ( completed = = value & & ch_tag_upd = = 2 )
{
ch_tag_stat . set_value ( completed ) ;
ch_tag_upd = 0 ;
}
}
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_WrTagUpdate :
{
2017-02-17 20:35:57 +01:00
if ( value > 2 )
{
break ;
}
2018-03-24 22:03:32 +01:00
const u32 completed = get_mfc_completed ( ) ;
2017-02-17 20:35:57 +01:00
2018-03-24 22:03:32 +01:00
if ( ! value )
2017-05-15 20:54:12 +02:00
{
2018-04-21 22:11:08 +02:00
ch_tag_upd = 0 ;
2018-03-24 22:03:32 +01:00
ch_tag_stat . set_value ( completed ) ;
2017-05-15 20:54:12 +02:00
}
2018-03-24 22:03:32 +01:00
else if ( completed & & value = = 1 )
2017-02-17 20:35:57 +01:00
{
2018-04-21 22:11:08 +02:00
ch_tag_upd = 0 ;
2018-03-24 22:03:32 +01:00
ch_tag_stat . set_value ( completed ) ;
2017-02-17 20:35:57 +01:00
}
2018-03-24 22:03:32 +01:00
else if ( completed = = ch_tag_mask & & value = = 2 )
2017-02-17 20:35:57 +01:00
{
2018-04-21 22:11:08 +02:00
ch_tag_upd = 0 ;
2017-02-17 20:35:57 +01:00
ch_tag_stat . set_value ( completed ) ;
}
else
{
2018-03-24 22:03:32 +01:00
ch_tag_upd = value ;
ch_tag_stat . set_value ( 0 , false ) ;
2017-02-17 20:35:57 +01:00
}
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_LSA :
{
2017-02-17 20:35:57 +01:00
ch_mfc_cmd . lsa = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_EAH :
{
2017-02-17 20:35:57 +01:00
ch_mfc_cmd . eah = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_EAL :
{
2017-02-17 20:35:57 +01:00
ch_mfc_cmd . eal = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
case MFC_Size :
{
2017-11-29 13:28:41 +01:00
ch_mfc_cmd . size = value & 0x7fff ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_TagID :
2014-08-22 23:15:02 +02:00
{
2017-11-29 13:28:41 +01:00
ch_mfc_cmd . tag = value & 0x1f ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_Cmd :
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
ch_mfc_cmd . cmd = MFC ( value & 0xff ) ;
2019-01-15 16:31:21 +01:00
return process_mfc_cmd ( ) ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case MFC_WrListStallAck :
2014-08-22 23:15:02 +02:00
{
2017-02-17 20:35:57 +01:00
// Reset stall status for specified tag
2019-01-07 07:13:17 +01:00
const u32 tag_mask = utils : : rol32 ( 1 , value ) ;
2018-09-02 19:22:35 +02:00
if ( ch_stall_mask & tag_mask )
2014-10-02 12:29:20 +02:00
{
2018-09-02 19:22:35 +02:00
ch_stall_mask & = ~ tag_mask ;
2019-01-07 07:13:17 +01:00
for ( u32 i = 0 ; i < mfc_size ; i + + )
{
if ( mfc_queue [ i ] . tag = = ( value | 0x80 ) )
{
// Unset stall bit
mfc_queue [ i ] . tag & = 0x7f ;
}
}
2018-04-08 13:03:00 +02:00
do_mfc ( true ) ;
2014-10-02 12:29:20 +02:00
}
2015-03-02 03:10:41 +01:00
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrDec :
2014-08-22 23:15:02 +02:00
{
2015-07-06 01:21:15 +02:00
ch_dec_start_timestamp = get_timebased_time ( ) ;
2015-03-02 03:10:41 +01:00
ch_dec_value = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrEventMask :
2014-08-22 23:15:02 +02:00
{
2015-09-18 00:41:14 +02:00
ch_event_mask = value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
case SPU_WrEventAck :
2014-08-22 23:15:02 +02:00
{
2015-03-02 03:10:41 +01:00
ch_event_stat & = ~ value ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2017-03-11 19:55:50 +01:00
case 69 :
{
return true ;
}
2014-08-22 23:15:02 +02:00
}
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown/illegal channel (ch=%d [%s], value=0x%x) " HERE , ch , ch < 128 ? spu_ch_name [ ch ] : " ??? " , value ) ;
2015-03-02 03:10:41 +01:00
}
2018-10-11 00:17:19 +02:00
bool spu_thread : : stop_and_signal ( u32 code )
2015-03-02 03:10:41 +01:00
{
2020-02-01 09:36:09 +01:00
spu_log . trace ( " stop_and_signal(code=0x%x) " , code ) ;
2014-08-22 23:15:02 +02:00
2016-04-14 01:09:41 +02:00
if ( offset > = RAW_SPU_BASE_ADDR )
2014-08-22 23:15:02 +02:00
{
2020-01-21 13:51:55 +01:00
// Save next PC and current SPU Interrupt Status
2020-01-09 07:52:31 +01:00
state + = cpu_flag : : stop + cpu_flag : : wait ;
2020-01-21 19:05:45 +01:00
status_npc . atomic_op ( [ & ] ( status_npc_sync_var & state )
2015-03-02 03:10:41 +01:00
{
2020-01-21 19:05:45 +01:00
state . status = ( state . status & 0xffff ) | ( code < < 16 ) ;
state . status | = SPU_STATUS_STOPPED_BY_STOP ;
state . status & = ~ SPU_STATUS_RUNNING ;
state . npc = ( pc + 4 ) | + interrupts_enabled ;
2015-03-02 03:10:41 +01:00
} ) ;
2014-08-22 23:15:02 +02:00
2015-07-12 23:02:02 +02:00
int_ctrl [ 2 ] . set ( SPU_INT2_STAT_SPU_STOP_AND_SIGNAL_INT ) ;
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2018-11-05 12:24:08 +01:00
return true ;
2015-03-02 03:10:41 +01:00
}
2014-08-22 23:15:02 +02:00
switch ( code )
{
2017-02-13 16:18:05 +01:00
case 0x000 :
{
2020-02-01 09:36:09 +01:00
spu_log . warning ( " STOP 0x0 " ) ;
2017-02-24 14:28:05 +01:00
// HACK: find an ILA instruction
for ( u32 addr = pc ; addr < 0x40000 ; addr + = 4 )
{
const u32 instr = _ref < u32 > ( addr ) ;
if ( instr > > 25 = = 0x21 )
{
pc = addr ;
return false ;
}
if ( instr > 0x1fffff )
{
break ;
}
}
// HACK: wait for executable code
while ( ! _ref < u32 > ( pc ) )
{
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2017-02-24 14:28:05 +01:00
{
return false ;
}
thread_ctrl : : wait_for ( 1000 ) ;
}
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2017-02-24 14:28:05 +01:00
return false ;
2017-02-13 16:18:05 +01:00
}
2014-10-02 12:29:20 +02:00
case 0x001 :
{
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2017-02-05 15:06:03 +01:00
thread_ctrl : : wait_for ( 1000 ) ; // hack
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-10-02 12:29:20 +02:00
}
case 0x002 :
{
2016-08-09 16:14:41 +02:00
state + = cpu_flag : : ret ;
2016-04-19 15:04:02 +02:00
return true ;
2014-10-02 12:29:20 +02:00
}
2014-08-27 23:04:55 +02:00
case 0x110 :
2014-08-22 23:15:02 +02:00
{
2014-08-27 23:04:55 +02:00
/* ===== sys_spu_thread_receive_event ===== */
2019-05-18 22:47:35 +02:00
u32 spuq = 0 ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( spuq ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_receive_event(): Out_MBox is empty " HERE ) ;
2014-08-22 23:15:02 +02:00
}
2015-07-17 18:27:12 +02:00
if ( u32 count = ch_in_mbox . get_count ( ) )
2014-08-22 23:15:02 +02:00
{
2020-02-01 09:36:09 +01:00
spu_log . error ( " sys_spu_thread_receive_event(): In_MBox is not empty (%d) " , count ) ;
2016-04-19 15:04:02 +02:00
return ch_in_mbox . set_values ( 1 , CELL_EBUSY ) , true ;
2014-08-22 23:15:02 +02:00
}
2020-02-01 09:36:09 +01:00
spu_log . trace ( " sys_spu_thread_receive_event(spuq=0x%x) " , spuq ) ;
2014-08-22 23:15:02 +02:00
2020-02-05 21:46:05 +01:00
if ( ! group - > has_scheduler_context /*|| group->type & 0xf00*/ )
2015-07-03 18:07:36 +02:00
{
2020-02-05 21:46:05 +01:00
spu_log . error ( " sys_spu_thread_receive_event(): Incompatible group type = 0x%x " , group - > type ) ;
2016-04-19 15:04:02 +02:00
return ch_in_mbox . set_values ( 1 , CELL_EINVAL ) , true ;
2015-07-03 18:07:36 +02:00
}
2017-01-29 17:50:18 +01:00
std : : shared_ptr < lv2_event_queue > queue ;
2015-03-05 14:18:06 +01:00
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2017-02-05 00:26:57 +01:00
while ( true )
2015-03-05 14:18:06 +01:00
{
2017-02-05 00:26:57 +01:00
queue . reset ( ) ;
2015-03-05 14:18:06 +01:00
2017-02-05 00:26:57 +01:00
// Check group status, wait if necessary
while ( group - > run_state > = SPU_THREAD_GROUP_STATUS_WAITING & & group - > run_state < = SPU_THREAD_GROUP_STATUS_SUSPENDED )
{
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2015-03-05 14:18:06 +01:00
{
2017-02-05 00:26:57 +01:00
return false ;
2015-03-05 14:18:06 +01:00
}
2017-02-05 00:26:57 +01:00
thread_ctrl : : wait ( ) ;
2015-03-05 14:18:06 +01:00
}
2017-02-05 00:26:57 +01:00
reader_lock rlock ( id_manager : : g_mutex ) ;
2014-12-23 00:31:11 +01:00
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( group - > mutex ) ;
2015-07-03 18:07:36 +02:00
2017-02-05 00:26:57 +01:00
if ( group - > run_state > = SPU_THREAD_GROUP_STATUS_WAITING & & group - > run_state < = SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED )
2016-04-14 01:09:41 +02:00
{
2017-02-05 00:26:57 +01:00
// Try again
continue ;
2016-04-14 01:09:41 +02:00
}
2015-07-03 18:07:36 +02:00
2017-02-05 00:26:57 +01:00
for ( auto & v : this - > spuq )
2014-12-23 00:31:11 +01:00
{
2017-02-05 00:26:57 +01:00
if ( spuq = = v . first )
2016-04-14 01:09:41 +02:00
{
2017-02-05 00:26:57 +01:00
queue = v . second . lock ( ) ;
if ( queue )
{
break ;
}
2016-04-14 01:09:41 +02:00
}
2014-12-23 00:31:11 +01:00
}
2015-07-03 18:07:36 +02:00
2017-02-05 00:26:57 +01:00
if ( ! queue )
{
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2017-02-05 00:26:57 +01:00
return ch_in_mbox . set_values ( 1 , CELL_EINVAL ) , true ; // TODO: check error value
}
2018-09-03 21:28:33 +02:00
std : : lock_guard qlock ( queue - > mutex ) ;
2015-07-19 14:58:11 +02:00
2017-02-03 22:36:04 +01:00
if ( queue - > events . empty ( ) )
2015-07-19 14:58:11 +02:00
{
2017-02-03 22:36:04 +01:00
queue - > sq . emplace_back ( this ) ;
2017-02-05 00:26:57 +01:00
group - > run_state = SPU_THREAD_GROUP_STATUS_WAITING ;
for ( auto & thread : group - > threads )
{
if ( thread )
{
thread - > state + = cpu_flag : : suspend ;
}
}
// Wait
break ;
2017-02-03 22:36:04 +01:00
}
else
{
2017-02-05 00:26:57 +01:00
// Return the event immediately
2017-02-03 22:36:04 +01:00
const auto event = queue - > events . front ( ) ;
const auto data1 = static_cast < u32 > ( std : : get < 1 > ( event ) ) ;
const auto data2 = static_cast < u32 > ( std : : get < 2 > ( event ) ) ;
const auto data3 = static_cast < u32 > ( std : : get < 3 > ( event ) ) ;
ch_in_mbox . set_values ( 4 , CELL_OK , data1 , data2 , data3 ) ;
queue - > events . pop_front ( ) ;
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2017-02-05 00:26:57 +01:00
return true ;
2017-02-03 22:36:04 +01:00
}
}
2015-07-01 00:25:52 +02:00
2017-02-05 00:26:57 +01:00
while ( true )
2017-02-03 22:36:04 +01:00
{
2018-10-11 00:17:19 +02:00
if ( is_stopped ( ) )
2017-02-03 22:36:04 +01:00
{
return false ;
2015-07-19 14:58:11 +02:00
}
2015-03-04 05:42:04 +01:00
2017-02-05 00:26:57 +01:00
if ( ! state . test_and_reset ( cpu_flag : : signal ) )
{
thread_ctrl : : wait ( ) ;
}
else
{
break ;
}
2015-07-03 18:07:36 +02:00
}
2017-02-05 00:26:57 +01:00
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( group - > mutex ) ;
2017-12-09 15:57:43 +01:00
2017-02-05 00:26:57 +01:00
if ( group - > run_state = = SPU_THREAD_GROUP_STATUS_WAITING )
2015-07-03 18:07:36 +02:00
{
2017-02-05 00:26:57 +01:00
group - > run_state = SPU_THREAD_GROUP_STATUS_RUNNING ;
2015-07-03 18:07:36 +02:00
}
2017-02-05 00:26:57 +01:00
else if ( group - > run_state = = SPU_THREAD_GROUP_STATUS_WAITING_AND_SUSPENDED )
2015-07-03 18:07:36 +02:00
{
2017-02-05 00:26:57 +01:00
group - > run_state = SPU_THREAD_GROUP_STATUS_SUSPENDED ;
2015-07-03 18:07:36 +02:00
}
2015-03-05 22:29:05 +01:00
2015-07-19 14:58:11 +02:00
for ( auto & thread : group - > threads )
2015-03-05 22:29:05 +01:00
{
2017-02-05 00:26:57 +01:00
if ( thread )
2016-04-14 01:09:41 +02:00
{
2016-08-09 16:14:41 +02:00
thread - > state - = cpu_flag : : suspend ;
2017-02-05 00:26:57 +01:00
if ( thread . get ( ) ! = this )
{
2018-10-11 00:17:19 +02:00
thread_ctrl : : notify ( * thread ) ;
2017-02-05 00:26:57 +01:00
}
2016-04-14 01:09:41 +02:00
}
2015-03-05 22:29:05 +01:00
}
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-22 23:15:02 +02:00
}
2014-08-27 23:04:55 +02:00
2018-11-12 09:55:48 +01:00
case 0x111 :
{
/* ===== sys_spu_thread_tryreceive_event ===== */
2019-05-18 22:47:35 +02:00
u32 spuq = 0 ;
2018-11-12 09:55:48 +01:00
if ( ! ch_out_mbox . try_pop ( spuq ) )
{
fmt : : throw_exception ( " sys_spu_thread_tryreceive_event(): Out_MBox is empty " HERE ) ;
}
if ( u32 count = ch_in_mbox . get_count ( ) )
{
2020-02-01 09:36:09 +01:00
spu_log . error ( " sys_spu_thread_tryreceive_event(): In_MBox is not empty (%d) " , count ) ;
2018-11-12 09:55:48 +01:00
return ch_in_mbox . set_values ( 1 , CELL_EBUSY ) , true ;
}
2020-02-01 09:36:09 +01:00
spu_log . trace ( " sys_spu_thread_tryreceive_event(spuq=0x%x) " , spuq ) ;
2018-11-12 09:55:48 +01:00
std : : lock_guard lock ( group - > mutex ) ;
std : : shared_ptr < lv2_event_queue > queue ;
for ( auto & v : this - > spuq )
{
if ( spuq = = v . first )
{
2019-04-06 08:15:04 +02:00
if ( ( queue = v . second . lock ( ) ) )
2018-11-12 09:55:48 +01:00
{
break ;
}
}
}
if ( ! queue )
{
return ch_in_mbox . set_values ( 1 , CELL_EINVAL ) , true ;
}
std : : lock_guard qlock ( queue - > mutex ) ;
if ( queue - > events . empty ( ) )
{
return ch_in_mbox . set_values ( 1 , CELL_EBUSY ) , true ;
}
const auto event = queue - > events . front ( ) ;
const auto data1 = static_cast < u32 > ( std : : get < 1 > ( event ) ) ;
const auto data2 = static_cast < u32 > ( std : : get < 2 > ( event ) ) ;
const auto data3 = static_cast < u32 > ( std : : get < 3 > ( event ) ) ;
ch_in_mbox . set_values ( 4 , CELL_OK , data1 , data2 , data3 ) ;
queue - > events . pop_front ( ) ;
return true ;
}
2018-02-04 12:47:49 +01:00
case 0x100 :
{
2019-06-29 17:48:42 +02:00
// SPU thread group yield (TODO)
2018-02-04 12:47:49 +01:00
if ( ch_out_mbox . get_count ( ) )
{
fmt : : throw_exception ( " STOP code 0x100: Out_MBox is not empty " HERE ) ;
}
2019-06-29 17:48:42 +02:00
std : : atomic_thread_fence ( std : : memory_order_seq_cst ) ;
2018-02-04 12:47:49 +01:00
return true ;
}
2014-08-27 23:04:55 +02:00
case 0x101 :
{
/* ===== sys_spu_thread_group_exit ===== */
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2019-05-18 22:47:35 +02:00
u32 value = 0 ;
2015-07-17 18:27:12 +02:00
2017-02-05 00:26:57 +01:00
if ( ! ch_out_mbox . try_pop ( value ) )
2014-08-27 23:04:55 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_group_exit(): Out_MBox is empty " HERE ) ;
2014-08-27 23:04:55 +02:00
}
2015-03-02 03:10:41 +01:00
2020-02-01 09:36:09 +01:00
spu_log . trace ( " sys_spu_thread_group_exit(status=0x%x) " , value ) ;
2015-03-02 03:10:41 +01:00
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( group - > mutex ) ;
2015-03-02 03:10:41 +01:00
2015-09-04 01:23:31 +02:00
for ( auto & thread : group - > threads )
2014-08-27 23:04:55 +02:00
{
2015-07-19 14:58:11 +02:00
if ( thread & & thread . get ( ) ! = this )
2014-08-27 23:04:55 +02:00
{
2016-08-09 16:14:41 +02:00
thread - > state + = cpu_flag : : stop ;
2018-10-11 00:17:19 +02:00
thread_ctrl : : notify ( * thread ) ;
2014-08-27 23:04:55 +02:00
}
}
2015-03-04 22:51:14 +01:00
group - > exit_status = value ;
2019-02-08 18:23:49 +01:00
group - > join_state = SYS_SPU_THREAD_GROUP_JOIN_GROUP_EXIT ;
2015-07-01 00:25:52 +02:00
2016-08-09 16:14:41 +02:00
state + = cpu_flag : : stop ;
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-27 23:04:55 +02:00
}
2014-08-22 23:15:02 +02:00
case 0x102 :
2014-08-27 23:04:55 +02:00
{
/* ===== sys_spu_thread_exit ===== */
2019-06-06 20:32:35 +02:00
state + = cpu_flag : : wait ;
2015-03-02 03:10:41 +01:00
if ( ! ch_out_mbox . get_count ( ) )
2014-08-22 23:15:02 +02:00
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " sys_spu_thread_exit(): Out_MBox is empty " HERE ) ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2020-02-01 09:36:09 +01:00
spu_log . trace ( " sys_spu_thread_exit(status=0x%x) " , ch_out_mbox . get_value ( ) ) ;
2020-01-21 19:05:45 +01:00
status_npc = { SPU_STATUS_STOPPED_BY_STOP , 0 } ;
2016-08-09 16:14:41 +02:00
state + = cpu_flag : : stop ;
2019-06-06 20:32:35 +02:00
check_state ( ) ;
2016-04-19 15:04:02 +02:00
return true ;
2014-08-27 23:04:55 +02:00
}
2015-03-02 03:10:41 +01:00
}
2014-08-27 23:04:55 +02:00
2015-03-02 03:10:41 +01:00
if ( ! ch_out_mbox . get_count ( ) )
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown STOP code: 0x%x (Out_MBox is empty) " HERE , code ) ;
2015-03-02 03:10:41 +01:00
}
else
{
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Unknown STOP code: 0x%x (Out_MBox=0x%x) " HERE , code , ch_out_mbox . get_value ( ) ) ;
2015-03-02 03:10:41 +01:00
}
}
2018-10-11 00:17:19 +02:00
void spu_thread : : halt ( )
2015-03-02 03:10:41 +01:00
{
2020-02-01 09:36:09 +01:00
spu_log . trace ( " halt() " ) ;
2015-03-02 03:10:41 +01:00
2016-04-14 01:09:41 +02:00
if ( offset > = RAW_SPU_BASE_ADDR )
2015-03-02 03:10:41 +01:00
{
2020-01-09 07:52:31 +01:00
state + = cpu_flag : : stop + cpu_flag : : wait ;
2020-01-21 19:05:45 +01:00
status_npc . atomic_op ( [ this ] ( status_npc_sync_var & state )
2014-08-22 23:15:02 +02:00
{
2020-01-21 19:05:45 +01:00
state . status | = SPU_STATUS_STOPPED_BY_HALT ;
state . status & = ~ SPU_STATUS_RUNNING ;
state . npc = pc | + interrupts_enabled ;
2015-03-02 03:10:41 +01:00
} ) ;
2015-07-12 23:02:02 +02:00
int_ctrl [ 2 ] . set ( SPU_INT2_STAT_SPU_HALT_OR_STEP_INT ) ;
2015-07-01 00:25:52 +02:00
2019-11-07 23:36:19 +01:00
spu_runtime : : g_escape ( this ) ;
2014-08-22 23:15:02 +02:00
}
2015-03-02 03:10:41 +01:00
2016-08-08 18:01:06 +02:00
fmt : : throw_exception ( " Halt " HERE ) ;
2014-10-24 15:24:09 +02:00
}
2014-11-19 15:16:30 +01:00
2018-10-11 00:17:19 +02:00
void spu_thread : : fast_call ( u32 ls_addr )
2014-11-19 15:16:30 +01:00
{
2016-04-14 01:09:41 +02:00
// LS:0x0: this is originally the entry point of the interrupt handler, but interrupts are not implemented
_ref < u32 > ( 0 ) = 0x00000002 ; // STOP 2
2015-07-01 00:25:52 +02:00
2016-04-14 01:09:41 +02:00
auto old_pc = pc ;
auto old_lr = gpr [ 0 ] . _u32 [ 3 ] ;
auto old_stack = gpr [ 1 ] . _u32 [ 3 ] ; // only saved and restored (may be wrong)
pc = ls_addr ;
gpr [ 0 ] . _u32 [ 3 ] = 0x0 ;
2019-11-07 23:36:19 +01:00
cpu_task ( ) ;
2014-11-19 15:16:30 +01:00
2016-08-09 16:14:41 +02:00
state - = cpu_flag : : ret ;
2016-04-14 01:09:41 +02:00
pc = old_pc ;
gpr [ 0 ] . _u32 [ 3 ] = old_lr ;
gpr [ 1 ] . _u32 [ 3 ] = old_stack ;
2016-04-25 12:49:12 +02:00
}
2018-10-11 00:17:19 +02:00
DECLARE ( spu_thread : : g_raw_spu_ctr ) { } ;
DECLARE ( spu_thread : : g_raw_spu_id ) { } ;