1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 02:32:36 +01:00
This commit is contained in:
Nekotekina 2016-06-07 23:24:20 +03:00
parent 6fa5e2cc7c
commit a8bebcba55
31 changed files with 7346 additions and 213 deletions

View File

@ -20,10 +20,6 @@ env:
# Test mode is for testing if it's working with Coverity. Change to true if testing, to avoid reaching the quota.
- coverity_scan_script_test_mode=false
branches:
except:
- ppu_recompiler
matrix:
exclude:
- os: osx

View File

@ -102,6 +102,23 @@ enum x64_reg_t : u32
X64_IMM16,
X64_IMM32,
X64_BIT_O = 0x90,
X64_BIT_NO,
X64_BIT_C,
X64_BIT_NC,
X64_BIT_Z,
X64_BIT_NZ,
X64_BIT_BE,
X64_BIT_NBE,
X64_BIT_S,
X64_BIT_NS,
X64_BIT_P,
X64_BIT_NP,
X64_BIT_L,
X64_BIT_NL,
X64_BIT_LE,
X64_BIT_NLE,
X64R_ECX = X64R_CL,
};
@ -109,16 +126,22 @@ enum x64_op_t : u32
{
X64OP_NONE,
X64OP_LOAD, // obtain and put the value into x64 register
X64OP_LOAD_BE,
X64OP_STORE, // take the value from x64 register or an immediate and use it
X64OP_STORE_BE,
X64OP_MOVS,
X64OP_STOS,
X64OP_XCHG,
X64OP_CMPXCHG,
X64OP_LOAD_AND_STORE, // lock and [mem], reg
X64OP_LOAD_OR_STORE, // lock or [mem], reg (TODO)
X64OP_LOAD_XOR_STORE, // lock xor [mem], reg (TODO)
X64OP_INC, // lock inc [mem] (TODO)
X64OP_DEC, // lock dec [mem] (TODO)
X64OP_AND, // lock and [mem], ...
X64OP_OR, // lock or [mem], ...
X64OP_XOR, // lock xor [mem], ...
X64OP_INC, // lock inc [mem]
X64OP_DEC, // lock dec [mem]
X64OP_ADD, // lock add [mem], ...
X64OP_ADC, // lock adc [mem], ...
X64OP_SUB, // lock sub [mem], ...
X64OP_SBB, // lock sbb [mem], ...
};
void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, size_t& out_size, size_t& out_length)
@ -321,6 +344,56 @@ void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, siz
}
break;
}
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x98:
case 0x9a:
case 0x9b:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
{
if (!lock) // SETcc
{
out_op = X64OP_STORE;
out_reg = x64_reg_t(X64_BIT_O + op2 - 0x90); // 0x90 .. 0x9f
out_size = 1;
out_length += get_modRM_size(code);
return;
}
break;
}
case 0x38:
{
out_length++, code++;
switch (op3)
{
case 0xf0:
case 0xf1:
{
if (!repne) // MOVBE
{
out_op = op3 == 0xf0 ? X64OP_LOAD_BE : X64OP_STORE_BE;
out_reg = get_modRM_reg(code, rex);
out_size = get_op_size(rex, oso);
out_length += get_modRM_size(code);
return;
}
break;
}
}
break;
}
}
break;
@ -329,7 +402,7 @@ void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, siz
{
if (!oso)
{
out_op = X64OP_LOAD_AND_STORE;
out_op = X64OP_AND;
out_reg = rex & 8 ? get_modRM_reg(code, rex) : get_modRM_reg_lh(code);
out_size = 1;
out_length += get_modRM_size(code);
@ -341,7 +414,7 @@ void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, siz
{
if (true)
{
out_op = X64OP_LOAD_AND_STORE;
out_op = X64OP_AND;
out_reg = get_modRM_reg(code, rex);
out_size = get_op_size(rex, oso);
out_length += get_modRM_size(code);
@ -349,6 +422,63 @@ void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, siz
}
break;
}
case 0x80:
{
switch (auto mod_code = get_modRM_reg(code, 0))
{
//case 0: out_op = X64OP_ADD; break; // TODO: strange info in instruction manual
case 1: out_op = X64OP_OR; break;
case 2: out_op = X64OP_ADC; break;
case 3: out_op = X64OP_SBB; break;
case 4: out_op = X64OP_AND; break;
case 5: out_op = X64OP_SUB; break;
case 6: out_op = X64OP_XOR; break;
default: out_op = X64OP_NONE; break; // CMP
}
out_reg = X64_IMM8;
out_size = 1;
out_length += get_modRM_size(code) + 1;
return;
}
case 0x81:
{
switch (auto mod_code = get_modRM_reg(code, 0))
{
case 0: out_op = X64OP_ADD; break;
case 1: out_op = X64OP_OR; break;
case 2: out_op = X64OP_ADC; break;
case 3: out_op = X64OP_SBB; break;
case 4: out_op = X64OP_AND; break;
case 5: out_op = X64OP_SUB; break;
case 6: out_op = X64OP_XOR; break;
default: out_op = X64OP_NONE; break; // CMP
}
out_reg = oso ? X64_IMM16 : X64_IMM32;
out_size = get_op_size(rex, oso);
out_length += get_modRM_size(code) + (oso ? 2 : 4);
return;
}
case 0x83:
{
switch (auto mod_code = get_modRM_reg(code, 0))
{
case 0: out_op = X64OP_ADD; break;
case 1: out_op = X64OP_OR; break;
case 2: out_op = X64OP_ADC; break;
case 3: out_op = X64OP_SBB; break;
case 4: out_op = X64OP_AND; break;
case 5: out_op = X64OP_SUB; break;
case 6: out_op = X64OP_XOR; break;
default: out_op = X64OP_NONE; break; // CMP
}
out_reg = X64_IMM8;
out_size = get_op_size(rex, oso);
out_length += get_modRM_size(code) + 1;
return;
}
case 0x86:
{
if (!oso) // XCHG r8/m8, r8
@ -459,7 +589,7 @@ void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, siz
}
case 0xc6:
{
if (!lock && !oso && get_modRM_reg(code, 0) == X64R_RAX) // MOV r8/m8, imm8
if (!lock && !oso && get_modRM_reg(code, 0) == 0) // MOV r8/m8, imm8
{
out_op = X64OP_STORE;
out_reg = X64_IMM8;
@ -471,7 +601,7 @@ void decode_x64_reg_op(const u8* code, x64_op_t& out_op, x64_reg_t& out_reg, siz
}
case 0xc7:
{
if (!lock && get_modRM_reg(code, 0) == X64R_RAX) // MOV r/m, imm16/imm32 (16, 32, 64)
if (!lock && get_modRM_reg(code, 0) == 0) // MOV r/m, imm16/imm32 (16, 32, 64)
{
out_op = X64OP_STORE;
out_reg = oso ? X64_IMM16 : X64_IMM32;
@ -599,6 +729,9 @@ bool get_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, size_
switch (d_size)
{
case 1: out_value = (u8)imm_value; return true;
case 2: out_value = (u16)imm_value; return true; // sign-extended
case 4: out_value = (u32)imm_value; return true; // sign-extended
case 8: out_value = (u64)imm_value; return true; // sign-extended
}
}
else if (reg == X64_IMM16)
@ -625,6 +758,29 @@ bool get_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, size_
out_value = (u32)RCX(context);
return true;
}
else if (reg >= X64_BIT_O && reg <= X64_BIT_NLE)
{
const u32 _cf = EFLAGS(context) & 0x1;
const u32 _zf = EFLAGS(context) & 0x40;
const u32 _sf = EFLAGS(context) & 0x80;
const u32 _of = EFLAGS(context) & 0x800;
const u32 _pf = EFLAGS(context) & 0x4;
const u32 _l = (_sf << 4) ^ _of; // SF != OF
switch (reg & ~1)
{
case X64_BIT_O: out_value = !!_of ^ (reg & 1); break;
case X64_BIT_C: out_value = !!_cf ^ (reg & 1); break;
case X64_BIT_Z: out_value = !!_zf ^ (reg & 1); break;
case X64_BIT_BE: out_value = !!(_cf | _zf) ^ (reg & 1); break;
case X64_BIT_S: out_value = !!_sf ^ (reg & 1); break;
case X64_BIT_P: out_value = !!_pf ^ (reg & 1); break;
case X64_BIT_L: out_value = !!_l ^ (reg & 1); break;
case X64_BIT_LE: out_value = !!(_l | _zf) ^ (reg & 1); break;
}
return true;
}
LOG_ERROR(MEMORY, "get_x64_reg_value(): invalid arguments (reg=%d, d_size=%lld, i_size=%lld)", reg, d_size, i_size);
return false;
@ -649,7 +805,7 @@ bool put_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, u64 v
return false;
}
bool set_x64_cmp_flags(x64_context* context, size_t d_size, u64 x, u64 y)
bool set_x64_cmp_flags(x64_context* context, size_t d_size, u64 x, u64 y, bool carry = true)
{
switch (d_size)
{
@ -664,11 +820,11 @@ bool set_x64_cmp_flags(x64_context* context, size_t d_size, u64 x, u64 y)
const u64 diff = x - y;
const u64 summ = x + y;
if (((x & y) | ((x ^ y) & ~summ)) & sign)
if (carry && ((x & y) | ((x ^ y) & ~summ)) & sign)
{
EFLAGS(context) |= 0x1; // set CF
}
else
else if (carry)
{
EFLAGS(context) &= ~0x1; // clear CF
}
@ -833,9 +989,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
switch (op)
{
case X64OP_LOAD:
case X64OP_LOAD_BE:
{
u32 value;
if (is_writing || !thread->read_reg(addr, value) || !put_x64_reg_value(context, reg, d_size, se_storage<u32>::swap(value)))
if (is_writing || !thread->read_reg(addr, value) || !put_x64_reg_value(context, reg, d_size, op == X64OP_LOAD ? se_storage<u32>::swap(value) : value))
{
return false;
}
@ -843,9 +1000,10 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
break;
}
case X64OP_STORE:
case X64OP_STORE_BE:
{
u64 reg_value;
if (!is_writing || !get_x64_reg_value(context, reg, d_size, i_size, reg_value) || !thread->write_reg(addr, se_storage<u32>::swap((u32)reg_value)))
if (!is_writing || !get_x64_reg_value(context, reg, d_size, i_size, reg_value) || !thread->write_reg(addr, op == X64OP_STORE ? se_storage<u32>::swap((u32)reg_value) : (u32)reg_value))
{
return false;
}
@ -881,8 +1039,9 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
switch (op)
{
case X64OP_STORE:
case X64OP_STORE_BE:
{
if (d_size == 16)
if (d_size == 16 && op == X64OP_STORE)
{
if (reg - X64R_XMM0 >= 16)
{
@ -900,7 +1059,44 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
return false;
}
std::memcpy(vm::base_priv(addr), &reg_value, d_size);
if (op == X64OP_STORE_BE && d_size == 2)
{
reg_value = se_storage<u16>::swap((u16)reg_value);
}
else if (op == X64OP_STORE_BE && d_size == 4)
{
reg_value = se_storage<u32>::swap((u32)reg_value);
}
else if (op == X64OP_STORE_BE && d_size == 8)
{
reg_value = se_storage<u64>::swap(reg_value);
}
else if (op == X64OP_STORE_BE)
{
return false;
}
if (d_size == 1)
{
*(volatile u8*)vm::base_priv(addr) = (u8)reg_value;
}
else if (d_size == 2 && addr % 2 == 0)
{
*(volatile u16*)vm::base_priv(addr) = (u16)reg_value;
}
else if (d_size == 4 && addr % 4 == 0)
{
*(volatile u32*)vm::base_priv(addr) = (u32)reg_value;
}
else if (d_size == 8 && addr % 8 == 0)
{
*(volatile u64*)vm::base_priv(addr) = (u64)reg_value;
}
else
{
std::memcpy(vm::base_priv(addr), &reg_value, d_size);
}
break;
}
case X64OP_MOVS:
@ -1060,7 +1256,7 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
}
break;
}
case X64OP_LOAD_AND_STORE:
case X64OP_AND:
{
u64 value;
if (!get_x64_reg_value(context, reg, d_size, i_size, value))
@ -1083,6 +1279,182 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
}
break;
}
case X64OP_OR:
{
u64 value;
if (!get_x64_reg_value(context, reg, d_size, i_size, value))
{
return false;
}
switch (d_size)
{
case 1: value = *(atomic_t<u8>*)vm::base_priv(addr) |= (u8)value; break;
case 2: value = *(atomic_t<u16>*)vm::base_priv(addr) |= (u16)value; break;
case 4: value = *(atomic_t<u32>*)vm::base_priv(addr) |= (u32)value; break;
case 8: value = *(atomic_t<u64>*)vm::base_priv(addr) |= (u64)value; break;
default: return false;
}
if (!set_x64_cmp_flags(context, d_size, value, 0))
{
return false;
}
break;
}
case X64OP_XOR:
{
u64 value;
if (!get_x64_reg_value(context, reg, d_size, i_size, value))
{
return false;
}
switch (d_size)
{
case 1: value = *(atomic_t<u8>*)vm::base_priv(addr) ^= (u8)value; break;
case 2: value = *(atomic_t<u16>*)vm::base_priv(addr) ^= (u16)value; break;
case 4: value = *(atomic_t<u32>*)vm::base_priv(addr) ^= (u32)value; break;
case 8: value = *(atomic_t<u64>*)vm::base_priv(addr) ^= (u64)value; break;
default: return false;
}
if (!set_x64_cmp_flags(context, d_size, value, 0))
{
return false;
}
break;
}
case X64OP_INC:
{
u64 value;
switch (d_size)
{
case 1: value = ++*(atomic_t<u8>*)vm::base_priv(addr); break;
case 2: value = ++*(atomic_t<u16>*)vm::base_priv(addr); break;
case 4: value = ++*(atomic_t<u32>*)vm::base_priv(addr); break;
case 8: value = ++*(atomic_t<u64>*)vm::base_priv(addr); break;
default: return false;
}
if (!set_x64_cmp_flags(context, d_size, value, 1, false)) // ???
{
return false;
}
break;
}
case X64OP_DEC:
{
u64 value;
switch (d_size)
{
case 1: value = --*(atomic_t<u8>*)vm::base_priv(addr); break;
case 2: value = --*(atomic_t<u16>*)vm::base_priv(addr); break;
case 4: value = --*(atomic_t<u32>*)vm::base_priv(addr); break;
case 8: value = --*(atomic_t<u64>*)vm::base_priv(addr); break;
default: return false;
}
if (!set_x64_cmp_flags(context, d_size, value, -1, false)) // ???
{
return false;
}
break;
}
case X64OP_ADD:
{
u64 value, new_value;
if (!get_x64_reg_value(context, reg, d_size, i_size, value))
{
return false;
}
switch (d_size)
{
case 1: new_value = *(atomic_t<u8>*)vm::base_priv(addr) += (u8)value; break;
case 2: new_value = *(atomic_t<u16>*)vm::base_priv(addr) += (u16)value; break;
case 4: new_value = *(atomic_t<u32>*)vm::base_priv(addr) += (u32)value; break;
case 8: new_value = *(atomic_t<u64>*)vm::base_priv(addr) += (u64)value; break;
default: return false;
}
if (!set_x64_cmp_flags(context, d_size, new_value, value)) // ???
{
return false;
}
break;
}
case X64OP_ADC:
{
u64 value, new_value;
if (!get_x64_reg_value(context, reg, d_size, i_size, value))
{
return false;
}
switch (d_size)
{
case 1: new_value = *(atomic_t<u8>*)vm::base_priv(addr) += (u8)(value + (EFLAGS(context) & 1)); break;
case 2: new_value = *(atomic_t<u16>*)vm::base_priv(addr) += (u16)(value + (EFLAGS(context) & 1)); break;
case 4: new_value = *(atomic_t<u32>*)vm::base_priv(addr) += (u32)(value + (EFLAGS(context) & 1)); break;
case 8: new_value = *(atomic_t<u64>*)vm::base_priv(addr) += (u64)(value + (EFLAGS(context) & 1)); break;
default: return false;
}
if (!set_x64_cmp_flags(context, d_size, new_value, value + (EFLAGS(context) & 1))) // ???
{
return false;
}
break;
}
case X64OP_SUB:
{
u64 value, new_value;
if (!get_x64_reg_value(context, reg, d_size, i_size, value))
{
return false;
}
switch (d_size)
{
case 1: new_value = *(atomic_t<u8>*)vm::base_priv(addr) -= (u8)value; break;
case 2: new_value = *(atomic_t<u16>*)vm::base_priv(addr) -= (u16)value; break;
case 4: new_value = *(atomic_t<u32>*)vm::base_priv(addr) -= (u32)value; break;
case 8: new_value = *(atomic_t<u64>*)vm::base_priv(addr) -= (u64)value; break;
default: return false;
}
if (!set_x64_cmp_flags(context, d_size, new_value, 0 - value)) // ???
{
return false;
}
break;
}
case X64OP_SBB:
{
u64 value, new_value;
if (!get_x64_reg_value(context, reg, d_size, i_size, value))
{
return false;
}
switch (d_size)
{
case 1: new_value = *(atomic_t<u8>*)vm::base_priv(addr) -= (u8)(value + (EFLAGS(context) & 1)); break;
case 2: new_value = *(atomic_t<u16>*)vm::base_priv(addr) -= (u16)(value + (EFLAGS(context) & 1)); break;
case 4: new_value = *(atomic_t<u32>*)vm::base_priv(addr) -= (u32)(value + (EFLAGS(context) & 1)); break;
case 8: new_value = *(atomic_t<u64>*)vm::base_priv(addr) -= (u64)(value + (EFLAGS(context) & 1)); break;
default: return false;
}
if (!set_x64_cmp_flags(context, d_size, new_value, 0 - (value + (EFLAGS(context) & 1)))) // ???
{
return false;
}
break;
}
default:
{
LOG_ERROR(MEMORY, "Invalid or unsupported operation (op=%d, reg=%d, d_size=%lld, a_size=0x%llx, i_size=%lld)", op, reg, d_size, a_size, i_size);

View File

@ -1,10 +1,10 @@
if(APPLE OR WIN32 AND NOT MSVC)
if(APPLE)
else()
add_subdirectory( glslang )
set(BUILD_TESTS OFF CACHE BOOL "Build tests" FORCE)
set(BUILD_DEMOS OFF CACHE BOOL "Build demos" FORCE)
# TravisCI break build with layers and vkjson
set(BUILD_LAYERS OFF CACHE BOOL "Build demos" FORCE)
set(BUILD_VKJSON OFF CACHE BOOL "Build demos" FORCE)
add_subdirectory( Vulkan-LoaderAndValidationLayers )
add_subdirectory( glslang )
endif()

View File

@ -8,10 +8,6 @@ clone_folder: c:\rpcs3
clone_depth: 3
test: off
branches:
except:
- ppu_recompiler
before_build:
# until git for win 2.5 release with commit checkout
- git submodule update --init 3rdparty/ffmpeg 3rdparty/pugixml asmjit 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp
@ -25,7 +21,8 @@ build_script:
install:
- ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/wxWidgets.7z'
- ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/llvmlibs.7z'
# - ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/llvmlibs.7z'
- ps: Start-FileDownload 'https://drive.google.com/uc?export=download&id=0B-98fOyaZKJ5YWVnb29JZXFQWkU' -FileName llvmlibs.7z
- ps: Start-FileDownload 'https://402331b94f8e4b87ae2ef4677347f7956cf3861f.googledrive.com/host/0B6v_qtb9hkicfmt0NG0wTTRtUmF4X3VTQk5Oc2JidEVKVnUteDA1dXdrYlNsVW9kREpsSHc/zlib.7z'
- set WXWIN=C:\rpcs3\wxWidgets
- set OPENALDIR=C:\rpcs3\3rdparty\OpenAL

2
llvm

@ -1 +1 @@
Subproject commit 19ade095e8c3ea61f84b71074433309f0c7c7b3b
Subproject commit 051e787f26dbfdc26cf61a57bc82ca00dcb812e8

View File

@ -25,7 +25,6 @@ elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
endif()
if(WIN32)
add_compile_options(-pthread)
add_compile_options(-D__GXX_ABI_VERSION=1009)
endif()
endif()
@ -94,7 +93,7 @@ if(NOT MSVC)
endif()
find_package(OpenGL REQUIRED)
find_package(OpenAL REQUIRED)
#find_package(LLVM CONFIG) # TODO
find_package(LLVM 3.8 CONFIG)
include("${wxWidgets_USE_FILE}")
@ -136,16 +135,14 @@ if(WIN32)
endif()
if(NOT LLVM_FOUND)
Message("LLVM not found! LLVM 3.6 is required. RPCS3 will be compiled without LLVM support.")
elseif(${LLVM_PACKAGE_VERSION} VERSION_LESS "3.6" OR ${LLVM_PACKAGE_VERSION} VERSION_EQUAL "3.7" OR ${LLVM_PACKAGE_VERSION} VERSION_GREATER "3.7")
Message("LLVM ${LLVM_PACKAGE_VERSION} is not supported! LLVM 3.6 is required. RPCS3 will be compiled without LLVM support.")
Message("LLVM not found! LLVM 3.8 is required. RPCS3 will be compiled without LLVM support.")
else()
add_definitions(${LLVM_DEFINITIONS})
add_definitions(-DLLVM_AVAILABLE)
if (CMAKE_BUILD_TYPE STREQUAL "Release")
llvm_map_components_to_libnames(LLVM_LIBS mcjit vectorize x86codegen x86disassembler)
llvm_map_components_to_libnames(LLVM_LIBS mcjit vectorize ipo x86codegen x86disassembler)
else()
llvm_map_components_to_libnames(LLVM_LIBS mcjit vectorize x86codegen x86disassembler mcdisassembler)
llvm_map_components_to_libnames(LLVM_LIBS mcjit vectorize ipo x86codegen x86disassembler mcdisassembler)
endif()
endif()
@ -172,7 +169,7 @@ RPCS3_SRC
"${RPCS3_SRC_DIR}/../asmjit/src/asmjit/*.cpp"
)
if(APPLE OR WIN32 AND NOT MSVC)
if(APPLE)
set (EXCLUDE_DIR "/RSX/VK/")
foreach (TMP_PATH ${RPCS3_SRC})
string (FIND ${TMP_PATH} ${EXCLUDE_DIR} EXCLUDE_DIR_FOUND)
@ -186,14 +183,15 @@ add_executable(rpcs3 ${RPCS3_SRC})
if(MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib /NODEFAULTLIB:msvcrtd.lib")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /DYNAMICBASE:NO /BASE:0x10000 /FIXED")
endif()
if(WIN32)
target_link_libraries(rpcs3 ws2_32.lib Winmm.lib)
target_link_libraries(rpcs3 ws2_32.lib Winmm.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV)
if(NOT MSVC)
target_link_libraries(rpcs3 ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} opengl32.lib glu32.lib -lstdc++.dll -lpthread.dll)
target_link_libraries(rpcs3 ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} opengl32.lib glu32.lib libpthread)
else()
target_link_libraries(rpcs3 dxgi.lib d2d1.lib dwrite.lib VKstatic.1 glslang OSDependent OGLCompiler SPIRV)
target_link_libraries(rpcs3 dxgi.lib d2d1.lib dwrite.lib)
endif()
target_link_libraries(rpcs3 avformat.lib avcodec.lib avutil.lib swresample.lib swscale.lib png16_static ${wxWidgets_LIBRARIES} ${OPENAL_LIBRARY} ${ADDITIONAL_LIBS})
else()

View File

@ -1,7 +1,7 @@
#pragma once
#include "Utilities/Thread.h"
#include "Utilities/BitSet.h"
#include "../Utilities/Thread.h"
#include "../Utilities/BitSet.h"
// CPU Thread Type
enum class cpu_type : u32

View File

@ -0,0 +1 @@
#include "CPUTranslator.h"

View File

@ -0,0 +1 @@
#pragma once

View File

@ -74,7 +74,7 @@ void sys_initialize_tls(PPUThread& ppu, u64 main_thread_id, u32 tls_seg_addr, u3
// Allocate TLS for main thread
ppu.GPR[13] = ppu_alloc_tls() + 0x7000 + TLS_SYS;
sysPrxForUser.notice("TLS initialized (addr=0x%x, size=0x%x, max=0x%zu)", g_tls_addr - 0x30, g_tls_size, g_tls_max);
sysPrxForUser.notice("TLS initialized (addr=0x%x, size=0x%x, max=0x%x)", g_tls_addr - 0x30, g_tls_size, g_tls_max);
// TODO
g_spu_printf_agcb = vm::null;

View File

@ -1,8 +1,10 @@
#include "stdafx.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUModule.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/lv2/sys_ppu_thread.h"
#include "Emu/Cell/lv2/sys_event.h"
#include "sysPrxForUser.h"
extern logs::channel sysPrxForUser;
@ -29,8 +31,30 @@ s32 sys_ppu_thread_create(vm::ptr<u64> thread_id, u32 entry, u64 arg, s32 prio,
return res;
}
if (flags & SYS_PPU_THREAD_CREATE_INTERRUPT)
{
return CELL_OK;
}
// Run the thread
return flags & SYS_PPU_THREAD_CREATE_INTERRUPT ? CELL_OK : sys_ppu_thread_start(static_cast<u32>(*thread_id));
if (s32 res = sys_ppu_thread_start(static_cast<u32>(*thread_id)))
{
return res;
}
// Dirty hack for sound: confirm the creation of _mxr000 event queue
if (std::memcmp(threadname.get_ptr(), "_cellsurMixerMain", 18) == 0)
{
while (!idm::select<lv2_event_queue_t>([](u32, lv2_event_queue_t& eq)
{
return eq.name == "_mxr000\0"_u64;
}))
{
thread_ctrl::sleep(50000);
}
}
return CELL_OK;
}
s32 sys_ppu_thread_get_id(PPUThread& ppu, vm::ptr<u64> thread_id)

View File

@ -395,3 +395,401 @@ struct ppu_itype
return value;
}
};
struct ppu_iname
{
// Aggregate to store instruction name
struct type { const char* name; };
// Enable address-of operator for ppu_decoder<>
friend constexpr const char* operator &(type value)
{
return value.name;
}
#define NAME(x) static constexpr type x{#x};
NAME(UNK)
NAME(MFVSCR)
NAME(MTVSCR)
NAME(VADDCUW)
NAME(VADDFP)
NAME(VADDSBS)
NAME(VADDSHS)
NAME(VADDSWS)
NAME(VADDUBM)
NAME(VADDUBS)
NAME(VADDUHM)
NAME(VADDUHS)
NAME(VADDUWM)
NAME(VADDUWS)
NAME(VAND)
NAME(VANDC)
NAME(VAVGSB)
NAME(VAVGSH)
NAME(VAVGSW)
NAME(VAVGUB)
NAME(VAVGUH)
NAME(VAVGUW)
NAME(VCFSX)
NAME(VCFUX)
NAME(VCMPBFP)
NAME(VCMPEQFP)
NAME(VCMPEQUB)
NAME(VCMPEQUH)
NAME(VCMPEQUW)
NAME(VCMPGEFP)
NAME(VCMPGTFP)
NAME(VCMPGTSB)
NAME(VCMPGTSH)
NAME(VCMPGTSW)
NAME(VCMPGTUB)
NAME(VCMPGTUH)
NAME(VCMPGTUW)
NAME(VCTSXS)
NAME(VCTUXS)
NAME(VEXPTEFP)
NAME(VLOGEFP)
NAME(VMADDFP)
NAME(VMAXFP)
NAME(VMAXSB)
NAME(VMAXSH)
NAME(VMAXSW)
NAME(VMAXUB)
NAME(VMAXUH)
NAME(VMAXUW)
NAME(VMHADDSHS)
NAME(VMHRADDSHS)
NAME(VMINFP)
NAME(VMINSB)
NAME(VMINSH)
NAME(VMINSW)
NAME(VMINUB)
NAME(VMINUH)
NAME(VMINUW)
NAME(VMLADDUHM)
NAME(VMRGHB)
NAME(VMRGHH)
NAME(VMRGHW)
NAME(VMRGLB)
NAME(VMRGLH)
NAME(VMRGLW)
NAME(VMSUMMBM)
NAME(VMSUMSHM)
NAME(VMSUMSHS)
NAME(VMSUMUBM)
NAME(VMSUMUHM)
NAME(VMSUMUHS)
NAME(VMULESB)
NAME(VMULESH)
NAME(VMULEUB)
NAME(VMULEUH)
NAME(VMULOSB)
NAME(VMULOSH)
NAME(VMULOUB)
NAME(VMULOUH)
NAME(VNMSUBFP)
NAME(VNOR)
NAME(VOR)
NAME(VPERM)
NAME(VPKPX)
NAME(VPKSHSS)
NAME(VPKSHUS)
NAME(VPKSWSS)
NAME(VPKSWUS)
NAME(VPKUHUM)
NAME(VPKUHUS)
NAME(VPKUWUM)
NAME(VPKUWUS)
NAME(VREFP)
NAME(VRFIM)
NAME(VRFIN)
NAME(VRFIP)
NAME(VRFIZ)
NAME(VRLB)
NAME(VRLH)
NAME(VRLW)
NAME(VRSQRTEFP)
NAME(VSEL)
NAME(VSL)
NAME(VSLB)
NAME(VSLDOI)
NAME(VSLH)
NAME(VSLO)
NAME(VSLW)
NAME(VSPLTB)
NAME(VSPLTH)
NAME(VSPLTISB)
NAME(VSPLTISH)
NAME(VSPLTISW)
NAME(VSPLTW)
NAME(VSR)
NAME(VSRAB)
NAME(VSRAH)
NAME(VSRAW)
NAME(VSRB)
NAME(VSRH)
NAME(VSRO)
NAME(VSRW)
NAME(VSUBCUW)
NAME(VSUBFP)
NAME(VSUBSBS)
NAME(VSUBSHS)
NAME(VSUBSWS)
NAME(VSUBUBM)
NAME(VSUBUBS)
NAME(VSUBUHM)
NAME(VSUBUHS)
NAME(VSUBUWM)
NAME(VSUBUWS)
NAME(VSUMSWS)
NAME(VSUM2SWS)
NAME(VSUM4SBS)
NAME(VSUM4SHS)
NAME(VSUM4UBS)
NAME(VUPKHPX)
NAME(VUPKHSB)
NAME(VUPKHSH)
NAME(VUPKLPX)
NAME(VUPKLSB)
NAME(VUPKLSH)
NAME(VXOR)
NAME(TDI)
NAME(TWI)
NAME(MULLI)
NAME(SUBFIC)
NAME(CMPLI)
NAME(CMPI)
NAME(ADDIC)
NAME(ADDI)
NAME(ADDIS)
NAME(BC)
NAME(HACK)
NAME(SC)
NAME(B)
NAME(MCRF)
NAME(BCLR)
NAME(CRNOR)
NAME(CRANDC)
NAME(ISYNC)
NAME(CRXOR)
NAME(CRNAND)
NAME(CRAND)
NAME(CREQV)
NAME(CRORC)
NAME(CROR)
NAME(BCCTR)
NAME(RLWIMI)
NAME(RLWINM)
NAME(RLWNM)
NAME(ORI)
NAME(ORIS)
NAME(XORI)
NAME(XORIS)
NAME(ANDI)
NAME(ANDIS)
NAME(RLDICL)
NAME(RLDICR)
NAME(RLDIC)
NAME(RLDIMI)
NAME(RLDCL)
NAME(RLDCR)
NAME(CMP)
NAME(TW)
NAME(LVSL)
NAME(LVEBX)
NAME(SUBFC)
NAME(ADDC)
NAME(MULHDU)
NAME(MULHWU)
NAME(MFOCRF)
NAME(LWARX)
NAME(LDX)
NAME(LWZX)
NAME(SLW)
NAME(CNTLZW)
NAME(SLD)
NAME(AND)
NAME(CMPL)
NAME(LVSR)
NAME(LVEHX)
NAME(SUBF)
NAME(LDUX)
NAME(DCBST)
NAME(LWZUX)
NAME(CNTLZD)
NAME(ANDC)
NAME(TD)
NAME(LVEWX)
NAME(MULHD)
NAME(MULHW)
NAME(LDARX)
NAME(DCBF)
NAME(LBZX)
NAME(LVX)
NAME(NEG)
NAME(LBZUX)
NAME(NOR)
NAME(STVEBX)
NAME(SUBFE)
NAME(ADDE)
NAME(MTOCRF)
NAME(STDX)
NAME(STWCX)
NAME(STWX)
NAME(STVEHX)
NAME(STDUX)
NAME(STWUX)
NAME(STVEWX)
NAME(SUBFZE)
NAME(ADDZE)
NAME(STDCX)
NAME(STBX)
NAME(STVX)
NAME(SUBFME)
NAME(MULLD)
NAME(ADDME)
NAME(MULLW)
NAME(DCBTST)
NAME(STBUX)
NAME(ADD)
NAME(DCBT)
NAME(LHZX)
NAME(EQV)
NAME(ECIWX)
NAME(LHZUX)
NAME(XOR)
NAME(MFSPR)
NAME(LWAX)
NAME(DST)
NAME(LHAX)
NAME(LVXL)
NAME(MFTB)
NAME(LWAUX)
NAME(DSTST)
NAME(LHAUX)
NAME(STHX)
NAME(ORC)
NAME(ECOWX)
NAME(STHUX)
NAME(OR)
NAME(DIVDU)
NAME(DIVWU)
NAME(MTSPR)
NAME(DCBI)
NAME(NAND)
NAME(STVXL)
NAME(DIVD)
NAME(DIVW)
NAME(LVLX)
NAME(LDBRX)
NAME(LSWX)
NAME(LWBRX)
NAME(LFSX)
NAME(SRW)
NAME(SRD)
NAME(LVRX)
NAME(LSWI)
NAME(LFSUX)
NAME(SYNC)
NAME(LFDX)
NAME(LFDUX)
NAME(STVLX)
NAME(STDBRX)
NAME(STSWX)
NAME(STWBRX)
NAME(STFSX)
NAME(STVRX)
NAME(STFSUX)
NAME(STSWI)
NAME(STFDX)
NAME(STFDUX)
NAME(LVLXL)
NAME(LHBRX)
NAME(SRAW)
NAME(SRAD)
NAME(LVRXL)
NAME(DSS)
NAME(SRAWI)
NAME(SRADI)
NAME(EIEIO)
NAME(STVLXL)
NAME(STHBRX)
NAME(EXTSH)
NAME(STVRXL)
NAME(EXTSB)
NAME(STFIWX)
NAME(EXTSW)
NAME(ICBI)
NAME(DCBZ)
NAME(LWZ)
NAME(LWZU)
NAME(LBZ)
NAME(LBZU)
NAME(STW)
NAME(STWU)
NAME(STB)
NAME(STBU)
NAME(LHZ)
NAME(LHZU)
NAME(LHA)
NAME(LHAU)
NAME(STH)
NAME(STHU)
NAME(LMW)
NAME(STMW)
NAME(LFS)
NAME(LFSU)
NAME(LFD)
NAME(LFDU)
NAME(STFS)
NAME(STFSU)
NAME(STFD)
NAME(STFDU)
NAME(LD)
NAME(LDU)
NAME(LWA)
NAME(STD)
NAME(STDU)
NAME(FDIVS)
NAME(FSUBS)
NAME(FADDS)
NAME(FSQRTS)
NAME(FRES)
NAME(FMULS)
NAME(FMADDS)
NAME(FMSUBS)
NAME(FNMSUBS)
NAME(FNMADDS)
NAME(MTFSB1)
NAME(MCRFS)
NAME(MTFSB0)
NAME(MTFSFI)
NAME(MFFS)
NAME(MTFSF)
NAME(FCMPU)
NAME(FRSP)
NAME(FCTIW)
NAME(FCTIWZ)
NAME(FDIV)
NAME(FSUB)
NAME(FADD)
NAME(FSQRT)
NAME(FSEL)
NAME(FMUL)
NAME(FRSQRTE)
NAME(FMSUB)
NAME(FMADD)
NAME(FNMSUB)
NAME(FNMADD)
NAME(FCMPO)
NAME(FNEG)
NAME(FMR)
NAME(FNABS)
NAME(FABS)
NAME(FCTID)
NAME(FCTIDZ)
NAME(FCFID)
#undef NAME
};

View File

@ -53,7 +53,7 @@ inline __m128i sse_cmpgt_epu32(__m128i A, __m128i B)
return _mm_cmpgt_epi32(_mm_xor_si128(A, sign), _mm_xor_si128(B, sign));
}
inline __m128 sse_exp2_ps(__m128 A)
extern __m128 sse_exp2_ps(__m128 A)
{
const auto x0 = _mm_max_ps(_mm_min_ps(A, _mm_set1_ps(127.4999961f)), _mm_set1_ps(-127.4999961f));
const auto x1 = _mm_add_ps(x0, _mm_set1_ps(0.5f));
@ -65,7 +65,7 @@ inline __m128 sse_exp2_ps(__m128 A)
return _mm_mul_ps(_mm_add_ps(_mm_add_ps(x6, x6), _mm_set1_ps(1.0f)), _mm_castsi128_ps(_mm_slli_epi32(_mm_add_epi32(x2, _mm_set1_epi32(127)), 23)));
}
inline __m128 sse_log2_ps(__m128 A)
extern __m128 sse_log2_ps(__m128 A)
{
const auto _1 = _mm_set1_ps(1.0f);
const auto _c = _mm_set1_ps(1.442695040f);
@ -81,6 +81,125 @@ inline __m128 sse_log2_ps(__m128 A)
return _mm_add_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(_mm_mul_ps(x5, x6), x7), x4), _c), _mm_add_ps(_mm_mul_ps(x4, _c), x8));
}
extern __m128i sse_altivec_vperm(__m128i A, __m128i B, __m128i C)
{
const auto index = _mm_andnot_si128(C, _mm_set1_epi8(0x1f));
const auto mask = _mm_cmpgt_epi8(index, _mm_set1_epi8(0xf));
const auto sa = _mm_shuffle_epi8(A, index);
const auto sb = _mm_shuffle_epi8(B, index);
return _mm_or_si128(_mm_and_si128(mask, sa), _mm_andnot_si128(mask, sb));
}
extern __m128i sse_altivec_lvsl(u64 addr)
{
alignas(16) static const u64 lvsl_values[0x10][2] =
{
{ 0x08090A0B0C0D0E0F, 0x0001020304050607 },
{ 0x090A0B0C0D0E0F10, 0x0102030405060708 },
{ 0x0A0B0C0D0E0F1011, 0x0203040506070809 },
{ 0x0B0C0D0E0F101112, 0x030405060708090A },
{ 0x0C0D0E0F10111213, 0x0405060708090A0B },
{ 0x0D0E0F1011121314, 0x05060708090A0B0C },
{ 0x0E0F101112131415, 0x060708090A0B0C0D },
{ 0x0F10111213141516, 0x0708090A0B0C0D0E },
{ 0x1011121314151617, 0x08090A0B0C0D0E0F },
{ 0x1112131415161718, 0x090A0B0C0D0E0F10 },
{ 0x1213141516171819, 0x0A0B0C0D0E0F1011 },
{ 0x131415161718191A, 0x0B0C0D0E0F101112 },
{ 0x1415161718191A1B, 0x0C0D0E0F10111213 },
{ 0x15161718191A1B1C, 0x0D0E0F1011121314 },
{ 0x161718191A1B1C1D, 0x0E0F101112131415 },
{ 0x1718191A1B1C1D1E, 0x0F10111213141516 },
};
return _mm_load_si128((__m128i*)lvsl_values[addr & 0xf]);
}
extern __m128i sse_altivec_lvsr(u64 addr)
{
alignas(16) static const u64 lvsr_values[0x10][2] =
{
{ 0x18191A1B1C1D1E1F, 0x1011121314151617 },
{ 0x1718191A1B1C1D1E, 0x0F10111213141516 },
{ 0x161718191A1B1C1D, 0x0E0F101112131415 },
{ 0x15161718191A1B1C, 0x0D0E0F1011121314 },
{ 0x1415161718191A1B, 0x0C0D0E0F10111213 },
{ 0x131415161718191A, 0x0B0C0D0E0F101112 },
{ 0x1213141516171819, 0x0A0B0C0D0E0F1011 },
{ 0x1112131415161718, 0x090A0B0C0D0E0F10 },
{ 0x1011121314151617, 0x08090A0B0C0D0E0F },
{ 0x0F10111213141516, 0x0708090A0B0C0D0E },
{ 0x0E0F101112131415, 0x060708090A0B0C0D },
{ 0x0D0E0F1011121314, 0x05060708090A0B0C },
{ 0x0C0D0E0F10111213, 0x0405060708090A0B },
{ 0x0B0C0D0E0F101112, 0x030405060708090A },
{ 0x0A0B0C0D0E0F1011, 0x0203040506070809 },
{ 0x090A0B0C0D0E0F10, 0x0102030405060708 },
};
return _mm_load_si128((__m128i*)lvsr_values[addr & 0xf]);
}
static const __m128i lvlx_masks[0x10] =
{
_mm_set_epi8(0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf),
_mm_set_epi8(0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1),
_mm_set_epi8(0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1),
_mm_set_epi8(0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1),
_mm_set_epi8(0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1),
_mm_set_epi8(0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1),
_mm_set_epi8(0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
};
static const __m128i lvrx_masks[0x10] =
{
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x0),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x0, 0x1),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8),
_mm_set_epi8(-1, -1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9),
_mm_set_epi8(-1, -1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa),
_mm_set_epi8(-1, -1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb),
_mm_set_epi8(-1, -1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc),
_mm_set_epi8(-1, -1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd),
_mm_set_epi8(-1, 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe),
};
extern __m128i sse_cellbe_lvlx(u64 addr)
{
return _mm_shuffle_epi8(_mm_load_si128((__m128i*)vm::base(addr & ~0xf)), lvlx_masks[addr & 0xf]);
}
extern void sse_cellbe_stvlx(u64 addr, __m128i a)
{
_mm_maskmoveu_si128(_mm_shuffle_epi8(a, lvlx_masks[addr & 0xf]), lvrx_masks[addr & 0xf], (char*)vm::base(addr & ~0xf));
}
extern __m128i sse_cellbe_lvrx(u64 addr)
{
return _mm_shuffle_epi8(_mm_load_si128((__m128i*)vm::base(addr & ~0xf)), lvrx_masks[addr & 0xf]);
}
extern void sse_cellbe_stvrx(u64 addr, __m128i a)
{
_mm_maskmoveu_si128(_mm_shuffle_epi8(a, lvrx_masks[addr & 0xf]), lvlx_masks[addr & 0xf], (char*)vm::base(addr & ~0xf));
}
template<typename T>
struct add_flags_result_t
{
@ -807,11 +926,7 @@ bool ppu_interpreter::VOR(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::VPERM(PPUThread& ppu, ppu_opcode_t op)
{
const auto index = _mm_andnot_si128(ppu.VR[op.vc].vi, _mm_set1_epi8(0x1f));
const auto mask = _mm_cmpgt_epi8(index, _mm_set1_epi8(0xf));
const auto sa = _mm_shuffle_epi8(ppu.VR[op.va].vi, index);
const auto sb = _mm_shuffle_epi8(ppu.VR[op.vb].vi, index);
ppu.VR[op.vd].vi = _mm_or_si128(_mm_and_si128(mask, sa), _mm_andnot_si128(mask, sb));
ppu.VR[op.vd].vi = sse_altivec_vperm(ppu.VR[op.va].vi, ppu.VR[op.vb].vi, ppu.VR[op.vc].vi);
return true;
}
@ -2052,29 +2167,7 @@ bool ppu_interpreter::TW(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::LVSL(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
static const u64 lvsl_values[0x10][2] =
{
{ 0x08090A0B0C0D0E0F, 0x0001020304050607 },
{ 0x090A0B0C0D0E0F10, 0x0102030405060708 },
{ 0x0A0B0C0D0E0F1011, 0x0203040506070809 },
{ 0x0B0C0D0E0F101112, 0x030405060708090A },
{ 0x0C0D0E0F10111213, 0x0405060708090A0B },
{ 0x0D0E0F1011121314, 0x05060708090A0B0C },
{ 0x0E0F101112131415, 0x060708090A0B0C0D },
{ 0x0F10111213141516, 0x0708090A0B0C0D0E },
{ 0x1011121314151617, 0x08090A0B0C0D0E0F },
{ 0x1112131415161718, 0x090A0B0C0D0E0F10 },
{ 0x1213141516171819, 0x0A0B0C0D0E0F1011 },
{ 0x131415161718191A, 0x0B0C0D0E0F101112 },
{ 0x1415161718191A1B, 0x0C0D0E0F10111213 },
{ 0x15161718191A1B1C, 0x0D0E0F1011121314 },
{ 0x161718191A1B1C1D, 0x0E0F101112131415 },
{ 0x1718191A1B1C1D1E, 0x0F10111213141516 },
};
ppu.VR[op.vd]._u64[0] = lvsl_values[addr & 0xf][0];
ppu.VR[op.vd]._u64[1] = lvsl_values[addr & 0xf][1];
ppu.VR[op.vd].vi = sse_altivec_lvsl(addr);
return true;
}
@ -2218,29 +2311,7 @@ bool ppu_interpreter::CMPL(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::LVSR(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
static const u64 lvsr_values[0x10][2] =
{
{ 0x18191A1B1C1D1E1F, 0x1011121314151617 },
{ 0x1718191A1B1C1D1E, 0x0F10111213141516 },
{ 0x161718191A1B1C1D, 0x0E0F101112131415 },
{ 0x15161718191A1B1C, 0x0D0E0F1011121314 },
{ 0x1415161718191A1B, 0x0C0D0E0F10111213 },
{ 0x131415161718191A, 0x0B0C0D0E0F101112 },
{ 0x1213141516171819, 0x0A0B0C0D0E0F1011 },
{ 0x1112131415161718, 0x090A0B0C0D0E0F10 },
{ 0x1011121314151617, 0x08090A0B0C0D0E0F },
{ 0x0F10111213141516, 0x0708090A0B0C0D0E },
{ 0x0E0F101112131415, 0x060708090A0B0C0D },
{ 0x0D0E0F1011121314, 0x05060708090A0B0C },
{ 0x0C0D0E0F10111213, 0x0405060708090A0B },
{ 0x0B0C0D0E0F101112, 0x030405060708090A },
{ 0x0A0B0C0D0E0F1011, 0x0203040506070809 },
{ 0x090A0B0C0D0E0F10, 0x0102030405060708 },
};
ppu.VR[op.vd]._u64[0] = lvsr_values[addr & 0xf][0];
ppu.VR[op.vd]._u64[1] = lvsr_values[addr & 0xf][1];
ppu.VR[op.vd].vi = sse_altivec_lvsr(addr);
return true;
}
@ -2867,10 +2938,7 @@ bool ppu_interpreter::DIVW(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::LVLX(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
const u32 eb = addr & 0xf;
ppu.VR[op.vd].clear();
for (u32 i = 0; i < 16u - eb; ++i) ppu.VR[op.vd]._u8[15 - i] = vm::read8(vm::cast(addr + i, HERE));
ppu.VR[op.vd].vi = sse_cellbe_lvlx(addr);
return true;
}
@ -2934,10 +3002,7 @@ bool ppu_interpreter::SRD(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::LVRX(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
const u8 eb = addr & 0xf;
ppu.VR[op.vd].clear();
for (u32 i = 16 - eb; i < 16; ++i) ppu.VR[op.vd]._u8[15 - i] = vm::read8(vm::cast(addr + i - 16, HERE));
ppu.VR[op.vd].vi = sse_cellbe_lvrx(addr);
return true;
}
@ -3005,9 +3070,7 @@ bool ppu_interpreter::LFDUX(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::STVLX(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
const u32 eb = addr & 0xf;
for (u32 i = 0; i < 16u - eb; ++i) vm::write8(vm::cast(addr + i, HERE), ppu.VR[op.vs]._u8[15 - i]);
sse_cellbe_stvlx(addr, ppu.VR[op.vs].vi);
return true;
}
@ -3055,9 +3118,7 @@ bool ppu_interpreter::STFSX(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::STVRX(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
const u8 eb = addr & 0xf;
for (u32 i = 16 - eb; i < 16; ++i) vm::write8(vm::cast(addr + i - 16, HERE), ppu.VR[op.vs]._u8[15 - i]);
sse_cellbe_stvrx(addr, ppu.VR[op.vs].vi);
return true;
}
@ -3116,12 +3177,7 @@ bool ppu_interpreter::STFDUX(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::LVLXL(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
const u32 eb = addr & 0xf;
ppu.VR[op.vd].clear();
for (u32 i = 0; i < 16u - eb; ++i) ppu.VR[op.vd]._u8[15 - i] = vm::read8(vm::cast(addr + i, HERE));
return true;
return LVLX(ppu, op);
}
bool ppu_interpreter::LHBRX(PPUThread& ppu, ppu_opcode_t op)
@ -3171,12 +3227,7 @@ bool ppu_interpreter::SRAD(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::LVRXL(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
const u8 eb = addr & 0xf;
ppu.VR[op.vd].clear();
for (u32 i = 16 - eb; i < 16; ++i) ppu.VR[op.vd]._u8[15 - i] = vm::read8(vm::cast(addr + i - 16, HERE));
return true;
return LVRX(ppu, op);
}
bool ppu_interpreter::DSS(PPUThread& ppu, ppu_opcode_t op)
@ -3213,11 +3264,7 @@ bool ppu_interpreter::EIEIO(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::STVLXL(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
const u32 eb = addr & 0xf;
for (u32 i = 0; i < 16u - eb; ++i) vm::write8(vm::cast(addr + i, HERE), ppu.VR[op.vs]._u8[15 - i]);
return true;
return STVLX(ppu, op);
}
bool ppu_interpreter::STHBRX(PPUThread& ppu, ppu_opcode_t op)
@ -3236,11 +3283,7 @@ bool ppu_interpreter::EXTSH(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::STVRXL(PPUThread& ppu, ppu_opcode_t op)
{
const u64 addr = op.ra ? ppu.GPR[op.ra] + ppu.GPR[op.rb] : ppu.GPR[op.rb];
const u8 eb = addr & 0xf;
for (u32 i = 16 - eb; i < 16; ++i) vm::write8(vm::cast(addr + i - 16, HERE), ppu.VR[op.vs]._u8[15 - i]);
return true;
return STVRX(ppu, op);
}
bool ppu_interpreter::EXTSB(PPUThread& ppu, ppu_opcode_t op)
@ -3529,7 +3572,9 @@ bool ppu_interpreter::FSQRTS(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::FRES(PPUThread& ppu, ppu_opcode_t op)
{
ppu.FPR[op.frd] = f32(1.0 / ppu.FPR[op.frb]);
f32 value = f32(ppu.FPR[op.frb]);
_mm_store_ss(&value, _mm_rcp_ss(_mm_load_ss(&value)));
ppu.FPR[op.frd] = value;
VERIFY(!op.rc); //if (UNLIKELY(op.rc)) ppu.SetCR(1, ppu.FG, ppu.FL, ppu.FE, ppu.FU);
return true;
}
@ -3633,14 +3678,14 @@ bool ppu_interpreter::FRSP(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::FCTIW(PPUThread& ppu, ppu_opcode_t op)
{
(s32&)ppu.FPR[op.frd] = std::lrint(ppu.FPR[op.frb]);
(s32&)ppu.FPR[op.frd] = s32(ppu.FPR[op.frb]);
VERIFY(!op.rc); //if (UNLIKELY(op.rc)) ppu.SetCR(1, ppu.FG, ppu.FL, ppu.FE, ppu.FU);
return true;
}
bool ppu_interpreter::FCTIWZ(PPUThread& ppu, ppu_opcode_t op)
{
(s32&)ppu.FPR[op.frd] = static_cast<s32>(ppu.FPR[op.frb]);
(s32&)ppu.FPR[op.frd] = _mm_cvttsd_si32(_mm_load_sd(&ppu.FPR[op.frb]));
VERIFY(!op.rc); //if (UNLIKELY(op.rc)) ppu.SetCR(1, ppu.FG, ppu.FL, ppu.FE, ppu.FU);
return true;
}
@ -3689,7 +3734,9 @@ bool ppu_interpreter::FMUL(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::FRSQRTE(PPUThread& ppu, ppu_opcode_t op)
{
ppu.FPR[op.frd] = 1.0 / std::sqrt(ppu.FPR[op.frb]);
f32 value = f32(ppu.FPR[op.frb]);
_mm_store_ss(&value, _mm_rsqrt_ss(_mm_load_ss(&value)));
ppu.FPR[op.frd] = value;
VERIFY(!op.rc); //if (UNLIKELY(op.rc)) ppu.SetCR(1, ppu.FG, ppu.FL, ppu.FE, ppu.FU);
return true;
}
@ -3758,14 +3805,14 @@ bool ppu_interpreter::FABS(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::FCTID(PPUThread& ppu, ppu_opcode_t op)
{
(s64&)ppu.FPR[op.frd] = std::llrint(ppu.FPR[op.frb]);
(s64&)ppu.FPR[op.frd] = s64(ppu.FPR[op.frb]);
VERIFY(!op.rc); //if (UNLIKELY(op.rc)) ppu.SetCR(1, ppu.FG, ppu.FL, ppu.FE, ppu.FU);
return true;
}
bool ppu_interpreter::FCTIDZ(PPUThread& ppu, ppu_opcode_t op)
{
(s64&)ppu.FPR[op.frd] = static_cast<s64>(ppu.FPR[op.frb]);
(s64&)ppu.FPR[op.frd] = _mm_cvttsd_si64(_mm_load_sd(&ppu.FPR[op.frb]));
VERIFY(!op.rc); //if (UNLIKELY(op.rc)) ppu.SetCR(1, ppu.FG, ppu.FL, ppu.FE, ppu.FU);
return true;
}
@ -3779,5 +3826,5 @@ bool ppu_interpreter::FCFID(PPUThread& ppu, ppu_opcode_t op)
bool ppu_interpreter::UNK(PPUThread& ppu, ppu_opcode_t op)
{
throw fmt::exception("Unknown/Illegal opcode: 0x%08x" HERE, op.opcode);
throw fmt::exception("Unknown/Illegal opcode: 0x%08x (pc=0x%x)" HERE, op.opcode, ppu.pc);
}

View File

@ -13,8 +13,11 @@
#include "Emu/Cell/lv2/sys_prx.h"
#include <unordered_set>
#include <algorithm>
#include "yaml-cpp/yaml.h"
const ppu_decoder<ppu_itype> s_ppu_itype;
//const ppu_decoder<ppu_iname> s_ppu_iname;
LOG_CHANNEL(cellAdec);
LOG_CHANNEL(cellAtrac);
@ -117,6 +120,8 @@ extern std::string ppu_get_variable_name(const std::string& module, u32 vnid);
extern void sys_initialize_tls(PPUThread&, u64, u32, u32, u32);
extern void ppu_initialize(const std::string& name, const std::vector<std::pair<u32, u32>>& set, u32 entry);
// Function lookup table. Not supposed to grow after emulation start.
std::vector<ppu_function_t> g_ppu_function_cache;
@ -138,20 +143,14 @@ extern void ppu_execute_function(PPUThread& ppu, u32 index)
{
func(ppu);
}
catch (EmulationStopped)
{
LOG_WARNING(PPU, "Function '%s' aborted", ppu.last_function);
ppu.last_function = previous_function;
throw;
}
catch (...)
{
LOG_ERROR(PPU, "Function '%s' aborted", ppu.last_function);
logs::HLE.format(Emu.IsStopped() ? logs::level::warning : logs::level::error, "Function '%s' aborted", ppu.last_function);
ppu.last_function = previous_function;
throw;
}
LOG_TRACE(PPU, "Function '%s' finished, r3=0x%llx", ppu.last_function, ppu.GPR[3]);
LOG_TRACE(HLE, "Function '%s' finished, r3=0x%llx", ppu.last_function, ppu.GPR[3]);
ppu.last_function = previous_function;
return;
}
@ -657,12 +656,16 @@ static auto ppu_load_exports(const std::shared_ptr<ppu_linkage_info>& link, u32
return result;
}
static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32 imports_start, u32 imports_end)
static u32 ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32 imports_start, u32 imports_end)
{
u32 result = imports_start;
for (u32 addr = imports_start; addr < imports_end;)
{
const auto& lib = vm::_ref<const ppu_prx_module_info>(addr);
result = std::min<u32>(result, lib.name.addr());
const std::string module_name(lib.name.get_ptr());
LOG_NOTICE(LOADER, "** Imported module '%s' (0x%x, 0x%x)", module_name, lib.unk4, lib.unk5);
@ -720,12 +723,404 @@ static void ppu_load_imports(const std::shared_ptr<ppu_linkage_info>& link, u32
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
}
return result;
}
// Returns max branch address of jumptable
never_inline static u32 ppu_is_jumptable(vm::ptr<u32>& start_ptr, u32 start, u32 end)
{
u32 max_addr = 0;
if (end - start_ptr.addr() < 8)
{
return 0;
}
for (vm::ptr<u32> ptr = start_ptr; ptr.addr() < end; ptr++)
{
const u32 addr = start_ptr.addr() + *ptr;
if (addr % 4 || addr < start || addr >= end)
{
if (ptr - start_ptr < 2)
{
return 0;
}
start_ptr = ptr;
return max_addr;
}
max_addr = std::max<u32>(max_addr, addr);
}
start_ptr = vm::cast(end);
return max_addr;
}
// Guess whether the function cannot be divided at specific position `split`
static bool ppu_is_coherent(u32 start, u32 end, u32 split)
{
// Check if the block before `split` is directly connected (can fall through)
for (vm::ptr<u32> rptr = vm::cast(split - 4);; rptr--)
{
const u32 _last = *rptr;
// Skip NOPs
if (_last == ppu_instructions::NOP())
{
if (rptr.addr() == start) return true;
continue;
}
switch (const auto type = s_ppu_itype.decode(_last))
{
case ppu_itype::UNK:
case ppu_itype::TD:
case ppu_itype::TDI:
case ppu_itype::TW:
case ppu_itype::TWI:
{
break;
}
case ppu_itype::B:
{
if (ppu_opcode_t{_last}.lk) return true;
break;
}
case ppu_itype::BC:
case ppu_itype::BCLR:
{
if (ppu_opcode_t{_last}.lk || (ppu_opcode_t{_last}.bo & 0x14) != 0x14) return true;
break;
}
case ppu_itype::BCCTR:
{
if (ppu_opcode_t{_last}.lk || (ppu_opcode_t{_last}.bo & 0x10) == 0) return true;
break;
}
default:
{
return true;
}
}
break;
}
// Find branches from one part to another
for (vm::ptr<u32> ptr = vm::cast(start); ptr.addr() < split; ptr++)
{
const u32 value = *ptr;
const auto type = s_ppu_itype.decode(value);
const ppu_opcode_t op{value};
if (type == ppu_itype::B || type == ppu_itype::BC)
{
const u32 target = ppu_branch_target(op.aa ? 0 : ptr.addr(), type == ppu_itype::B ? +op.ll : +op.simm16);
if (target % 4 == 0 && target >= split && target < end)
{
return !op.lk;
}
}
if (type == ppu_itype::BCCTR && !op.lk)
{
const u32 max = ppu_is_jumptable(++ptr, start, end);
if (max && max >= split)
{
return true;
}
ptr--;
}
}
// TODO: ???
return false;
}
static std::vector<std::pair<u32, u32>> ppu_analyse(u32 start, u32 end, const std::vector<std::pair<u32, u32>>& segs, u32 rtoc)
{
// Function entries (except the last one)
std::set<u32> result
{
end,
};
// Instruction usage stats
//std::unordered_map<const char*, u64> stats;
// Jumptable entries (addr->size)
std::unordered_map<u32, u32> jts;
// Block entries
std::set<u32> blocks;
// First pass; Detect branch + link instructions
for (vm::ptr<u32> ptr = vm::cast(start); ptr.addr() < end; ptr++)
{
const u32 value = *ptr;
const auto type = s_ppu_itype.decode(value);
//const auto name = s_ppu_iname.decode(value);
const ppu_opcode_t op{value};
if (type == ppu_itype::B || type == ppu_itype::BC)
{
const u32 target = ppu_branch_target(op.aa ? 0 : ptr.addr(), type == ppu_itype::B ? +op.ll : +op.simm16);
if (op.lk && target % 4 == 0 && target >= start && target < end && target != ptr.addr())
{
LOG_NOTICE(PPU, "BCall: 0x%x -> 0x%x", ptr, target);
result.emplace(target);
}
if (!op.lk && target % 4 == 0 && target >= start && target < end)
{
blocks.emplace(target);
}
}
if (type == ppu_itype::BCCTR && !op.lk)
{
const auto jt = ++ptr;
if (ppu_is_jumptable(ptr, start, end))
{
LOG_NOTICE(PPU, "JTable: 0x%x .. 0x%x", jt, ptr);
jts.emplace(jt.addr(), ptr.addr() - jt.addr());
for (auto _ptr = jt; _ptr != ptr; _ptr++)
{
blocks.emplace(jt.addr() + *_ptr);
}
}
else
{
LOG_NOTICE(PPU, "BCCTR: 0x%x", ptr - 1);
}
ptr--;
}
//stats[name]++;
}
// Find OPD table
for (const auto& seg : segs)
{
for (vm::ptr<u32> ptr = vm::cast(seg.first); ptr.addr() < seg.first + seg.second; ptr++)
{
if (ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0 && ptr[1] == rtoc)
{
while (ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0 && !jts.count(ptr[0]) /*&& ptr[1] == rtoc*/)
{
LOG_NOTICE(PPU, "OPD: 0x%x -> 0x%x (rtoc=0x%x)", ptr, ptr[0], ptr[1]);
result.emplace(ptr[0]);
ptr += 2;
}
break;
}
}
}
// Find more block entries
for (const auto& seg : segs)
{
for (vm::ptr<u32> ptr = vm::cast(seg.first); ptr.addr() < seg.first + seg.second; ptr++)
{
const u32 value = *ptr;
if (value % 4 == 0 && value >= start && value < end)
{
blocks.emplace(value);
}
}
}
// Detect tail calls
std::deque<u32> task{result.begin(), result.end()};
while (!task.empty())
{
const u32 f_start = task.front();
const auto f_up = result.upper_bound(f_start);
if (f_up != result.end()) for (vm::ptr<u32> ptr = vm::cast(f_start); ptr.addr() < *f_up; ptr++)
{
const u32 value = *ptr;
const auto type = s_ppu_itype.decode(value);
const ppu_opcode_t op{value};
if (type == ppu_itype::B || type == ppu_itype::BC)
{
const u32 target = ppu_branch_target(op.aa ? 0 : ptr.addr(), type == ppu_itype::B ? +op.ll : +op.simm16);
if (!op.lk && target % 4 == 0 && target >= start && target < end && (target < f_start || target >= *f_up))
{
auto _lower = result.lower_bound(target);
if (*_lower == target || _lower == result.begin())
{
continue;
}
const u32 f2_end = *_lower;
const u32 f2_start = *--_lower;
if (ppu_is_coherent(f2_start, f2_end, target))
{
continue;
}
LOG_NOTICE(LOADER, "Tail call: 0x%x -> 0x%x", ptr, target);
result.emplace(target);
// Rescan two new functions if the insertion took place
task.push_back(target);
task.push_back(f2_start);
}
}
}
task.pop_front();
}
// Fill (addr, size) vector
std::vector<std::pair<u32, u32>> vr;
for (auto it = result.begin(), end = --result.end(); it != end; it++)
{
const u32 addr = *it;
const auto _up = result.upper_bound(addr);
// Set initial (addr, size)
vr.emplace_back(std::make_pair(addr, *_up - addr));
// Analyse function against its end
for (u32& size = vr.back().second; size;)
{
const auto next = result.upper_bound(addr + size);
if (next != result.end() && ppu_is_coherent(addr, *next, addr + size))
{
// Extend and check again
const u32 new_size = *next - addr;
LOG_NOTICE(LOADER, "Extended: 0x%x (0x%x --> 0x%x)", addr, size, new_size);
size = new_size;
continue;
}
break;
}
}
// Add blocks as (addr, 0) // TODO
for (auto value : blocks)
{
vr.emplace_back(std::make_pair(value, 0));
}
// Print some stats
//{
// std::multimap<u64, const char*, std::greater<u64>> sorted;
// for (const auto& pair : stats)
// sorted.emplace(pair.second, pair.first);
// for (const auto& stat : sorted)
// LOG_NOTICE(PPU, "Stats: (%llu) %s", stat.first, stat.second);
//}
return vr;
}
static void ppu_validate(const std::string& fname, const std::vector<std::pair<u32, u32>>& funcs, u32 reloc)
{
// Load custom PRX configuration if available
if (fs::file yml{fname + ".yml"})
{
const auto cfg = YAML::Load(yml.to_string());
u32 index = 0;
// Validate detected functions using information provided
for (const auto func : cfg["functions"])
{
const u32 addr = func["addr"].as<u32>(-1);
const u32 size = func["size"].as<u32>(0);
if (addr != -1 && index < funcs.size())
{
u32 found = funcs[index].first - reloc;
while (addr > found && index + 1 < funcs.size())
{
LOG_ERROR(LOADER, "%s.yml : validation failed at 0x%x (0x%x, 0x%x)", fname, found, addr, size);
index++;
found = funcs[index].first - reloc;
}
if (addr < found)
{
LOG_ERROR(LOADER, "%s.yml : function not found (0x%x, 0x%x)", fname, addr, size);
continue;
}
if (size && size < funcs[index].second)
{
LOG_WARNING(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", fname, found, funcs[index].second, addr, size);
}
if (size > funcs[index].second)
{
LOG_ERROR(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", fname, found, funcs[index].second, addr, size);
}
index++;
}
else
{
LOG_ERROR(LOADER, "%s.yml : function not found at the end (0x%x, 0x%x)", fname, addr, size);
break;
}
}
if (!index)
{
return; // ???
}
while (index < funcs.size())
{
if (funcs[index].second)
{
LOG_ERROR(LOADER, "%s.yml : function not covered at 0x%x (size=0x%x)", fname, funcs[index].first, funcs[index].second);
}
index++;
}
LOG_SUCCESS(LOADER, "%s.yml : validation completed", fname);
}
}
template<>
std::shared_ptr<lv2_prx_t> ppu_prx_loader::load() const
{
std::vector<u32> segments;
std::vector<std::pair<u32, u32>> segments;
for (const auto& prog : progs)
{
@ -753,7 +1148,7 @@ std::shared_ptr<lv2_prx_t> ppu_prx_loader::load() const
std::memcpy(vm::base(addr), prog.bin.data(), file_size);
LOG_WARNING(LOADER, "**** Loaded to 0x%x (size=0x%x)", addr, mem_size);
segments.push_back(addr);
segments.emplace_back(std::make_pair(addr, mem_size));
}
break;
@ -787,8 +1182,8 @@ std::shared_ptr<lv2_prx_t> ppu_prx_loader::load() const
{
const auto& rel = reinterpret_cast<const ppu_prx_relocation_info&>(prog.bin[i]);
const u32 raddr = vm::cast(segments.at(rel.index_addr) + rel.offset, HERE);
const u64 rdata = segments.at(rel.index_value) + rel.ptr.addr();
const u32 raddr = vm::cast(segments.at(rel.index_addr).first + rel.offset, HERE);
const u64 rdata = segments.at(rel.index_value).first + rel.ptr.addr();
switch (const u32 type = rel.type)
{
@ -853,14 +1248,24 @@ std::shared_ptr<lv2_prx_t> ppu_prx_loader::load() const
};
// Access library information (TODO)
const auto& lib_info = vm::_ref<const ppu_prx_library_info>(vm::cast(segments[0] + progs[0].p_paddr - progs[0].p_offset, HERE));
const auto& lib_name = std::string(lib_info.name);
const auto& lib_info = vm::cptr<ppu_prx_library_info>(vm::cast(segments[0].first + progs[0].p_paddr - progs[0].p_offset, HERE));
const auto& lib_name = std::string(lib_info->name);
LOG_WARNING(LOADER, "Library %s (toc=0x%x, rtoc=0x%x):", lib_name, lib_info.toc, lib_info.toc + segments[0]);
LOG_WARNING(LOADER, "Library %s (rtoc=0x%x):", lib_name, lib_info->toc);
prx->specials = ppu_load_exports(link, lib_info.exports_start, lib_info.exports_end);
prx->specials = ppu_load_exports(link, lib_info->exports_start, lib_info->exports_end);
ppu_load_imports(link, lib_info.imports_start, lib_info.imports_end);
const std::initializer_list<u32> addr_list
{
ppu_load_imports(link, lib_info->imports_start, lib_info->imports_end),
lib_info.addr(),
lib_info->imports_start,
lib_info->exports_start,
};
// Get functions
prx->func = ppu_analyse(segments[0].first, std::min(addr_list), segments, lib_info->toc);
}
else
{
@ -887,9 +1292,18 @@ void ppu_exec_loader::load() const
// Access linkage information object
const auto link = fxm::get_always<ppu_linkage_info>();
// Segment info
std::vector<std::pair<u32, u32>> segments;
// Functions
std::vector<std::pair<u32, u32>> exec_set;
u32 exec_end{};
// Allocate memory at fixed positions
for (const auto& prog : progs)
{
LOG_NOTICE(LOADER, "** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
const u32 addr = vm::cast(prog.p_vaddr, HERE);
const u32 size = fmt::narrow<u32>("Invalid p_memsz: 0x%llx" HERE, prog.p_memsz);
@ -902,6 +1316,11 @@ void ppu_exec_loader::load() const
throw fmt::exception("vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size);
std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
segments.emplace_back(std::make_pair(addr, size));
if (prog.p_flags & 1) // Test EXEC flag
exec_end = addr + size;
}
}
@ -987,13 +1406,19 @@ void ppu_exec_loader::load() const
const auto& proc_prx_param = vm::_ref<const ppu_proc_prx_param_t>(vm::cast(prog.p_vaddr, HERE));
LOG_NOTICE(LOADER, "* libent_start = *0x%x", proc_prx_param.libent_start);
LOG_NOTICE(LOADER, "* libstub_start = *0x%x", proc_prx_param.libstub_start);
if (proc_prx_param.magic != 0x1b434cec)
{
throw fmt::exception("Bad magic! (0x%x)", proc_prx_param.magic);
}
ppu_load_exports(link, proc_prx_param.libent_start, proc_prx_param.libent_end);
ppu_load_imports(link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
const u32 min_addr = ppu_load_imports(link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
exec_end = std::min<u32>(min_addr, exec_end);
}
break;
}
@ -1035,10 +1460,16 @@ void ppu_exec_loader::load() const
const auto prx = loader.load();
// Register start function
if (prx->start)
{
start_funcs.push_back(prx->start.addr());
}
// Add functions
exec_set.insert(exec_set.end(), prx->func.begin(), prx->func.end());
ppu_validate(lle_dir + '/' + name, prx->func, prx->func[0].first);
}
else
{
@ -1167,6 +1598,17 @@ void ppu_exec_loader::load() const
}
}
// Analyse executable
const u32 entry_rtoc = vm::read32(vm::cast(header.e_entry, HERE) + 4);
const auto funcs = ppu_analyse(segments[0].first, exec_end, segments, entry_rtoc);
ppu_validate(vfs::get(Emu.GetPath()), funcs, 0);
for (const auto& pair : funcs)
{
exec_set.emplace_back(pair);
}
// TODO: adjust for liblv2 loading option
using namespace ppu_instructions;
@ -1177,7 +1619,7 @@ void ppu_exec_loader::load() const
static const int branch_size = 10 * 4;
auto make_branch = [](vm::ptr<u32>& ptr, u32 addr)
auto make_branch = [](vm::ptr<u32>& ptr, u32 addr, bool last)
{
const u32 stub = vm::read32(addr);
const u32 rtoc = vm::read32(addr + 4);
@ -1189,7 +1631,7 @@ void ppu_exec_loader::load() const
*ptr++ = ORI(r2, r2, rtoc & 0xffff);
*ptr++ = ORIS(r2, r2, rtoc >> 16);
*ptr++ = MTCTR(r0);
*ptr++ = BCTRL();
*ptr++ = last ? BCTR() : BCTRL();
};
auto entry = vm::ptr<u32>::make(vm::alloc(48 + branch_size * (::size32(start_funcs) + 1), vm::main));
@ -1217,7 +1659,7 @@ void ppu_exec_loader::load() const
// Reset arguments (TODO)
*entry++ = LI(r3, 0);
*entry++ = LI(r4, 0);
make_branch(entry, f);
make_branch(entry, f, false);
}
// Restore initialization args
@ -1229,7 +1671,13 @@ void ppu_exec_loader::load() const
*entry++ = MR(r12, r19);
// Branch to initialization
make_branch(entry, vm::cast(header.e_entry, HERE));
make_branch(entry, static_cast<u32>(header.e_entry), true);
// Register entry function (addr, size)
exec_set.emplace_back(std::make_pair(entry.addr() & -0x1000, entry.addr() & 0xfff));
// Initialize recompiler
ppu_initialize("", exec_set, static_cast<u32>(header.e_entry));
auto ppu = idm::make_ptr<PPUThread>("main_thread");

View File

@ -5,6 +5,7 @@
#include "Emu/IdManager.h"
#include "PPUThread.h"
#include "PPUInterpreter.h"
#include "PPUAnalyser.h"
#include "PPUModule.h"
enum class ppu_decoder_type
@ -24,6 +25,18 @@ cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder(cfg::root.core, "PPU Decoder"
const ppu_decoder<ppu_interpreter_precise> s_ppu_interpreter_precise;
const ppu_decoder<ppu_interpreter_fast> s_ppu_interpreter_fast;
struct ppu_addr_hash
{
u32 operator()(u32 value) const
{
return value / sizeof(32);
}
};
static std::unordered_map<u32, void(*)(), ppu_addr_hash> s_ppu_compiled;
std::string PPUThread::get_name() const
{
return fmt::format("PPU[0x%x] Thread (%s)", id, name);
@ -90,6 +103,16 @@ void PPUThread::cpu_task()
return custom_task(*this);
}
if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm)
{
const auto found = s_ppu_compiled.find(pc);
if (found != s_ppu_compiled.end())
{
return found->second();
}
}
g_tls_log_prefix = []
{
const auto cpu = static_cast<PPUThread*>(get_current_cpu_thread());
@ -298,3 +321,733 @@ void PPUThread::fast_call(u32 addr, u32 rtoc)
// handle_interrupt();
//}
}
#ifdef LLVM_AVAILABLE
#ifdef _MSC_VER
#pragma warning(push, 0)
#endif
//#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
//#include "llvm/Support/Host.h"
#include "llvm/Support/FormattedStream.h"
//#include "llvm/Support/Debug.h"
//#include "llvm/CodeGen/CommandFlags.h"
//#include "llvm/CodeGen/Passes.h"
#include "llvm/IR/LLVMContext.h"
//#include "llvm/IR/Dominators.h"
#include "llvm/IR/Verifier.h"
//#include "llvm/IR/InstIterator.h"
#include "llvm/IR/LegacyPassManager.h"
//#include "llvm/IR/Module.h"
//#include "llvm/IR/Function.h"
//#include "llvm/Analysis/Passes.h"
//#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/MemoryDependenceAnalysis.h"
//#include "llvm/Analysis/LoopInfo.h"
//#include "llvm/Analysis/ScalarEvolution.h"
#include "llvm/Analysis/Lint.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/Vectorize.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
//#include "llvm/Object/ObjectFile.h"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "PPUTranslator.h"
#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#endif
const ppu_decoder<ppu_itype> s_ppu_itype;
extern u64 get_timebased_time();
extern void ppu_execute_syscall(PPUThread& ppu, u64 code);
extern void ppu_execute_function(PPUThread& ppu, u32 index);
extern __m128 sse_exp2_ps(__m128 A);
extern __m128 sse_log2_ps(__m128 A);
extern __m128i sse_altivec_vperm(__m128i A, __m128i B, __m128i C);
extern __m128i sse_altivec_lvsl(u64 addr);
extern __m128i sse_altivec_lvsr(u64 addr);
extern __m128i sse_cellbe_lvlx(u64 addr);
extern __m128i sse_cellbe_lvrx(u64 addr);
extern void sse_cellbe_stvlx(u64 addr, __m128i a);
extern void sse_cellbe_stvrx(u64 addr, __m128i a);
struct Listener final : llvm::JITEventListener
{
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
{
const llvm::StringRef elf = obj.getData();
fs::file(fs::get_config_dir() + "LLVM.obj", fs::rewrite)
.write(elf.data(), elf.size());
}
};
static Listener s_listener;
// Memory size: 512 MB
static const u64 s_memory_size = 0x20000000;
// Try to reserve a portion of virtual memory in the first 2 GB address space, if possible.
static void* const s_memory = []() -> void*
{
#ifdef _WIN32
for (u64 addr = 0x1000000; addr <= 0x60000000; addr += 0x1000000)
{
if (VirtualAlloc((void*)addr, s_memory_size, MEM_RESERVE, PAGE_NOACCESS))
{
return (void*)addr;
}
}
return VirtualAlloc(NULL, s_memory_size, MEM_RESERVE, PAGE_NOACCESS);
#else
return ::mmap((void*)0x10000000, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
#endif
}();
// EH frames
static u8* s_unwind_info;
static u64 s_unwind_size;
#ifdef _WIN32
// Custom .pdata section replacement
static std::vector<RUNTIME_FUNCTION> s_unwind;
#endif
struct MemoryManager final : llvm::RTDyldMemoryManager
{
static PPUThread* context(u64 addr)
{
//trace(addr);
return static_cast<PPUThread*>(get_current_cpu_thread());
}
[[noreturn]] static void trap(u64 addr)
{
LOG_ERROR(PPU, "Trap! (0x%llx)", addr);
throw fmt::exception("Trap! (0x%llx)", addr);
}
static void trace(u64 addr)
{
LOG_NOTICE(PPU, "Trace: 0x%llx", addr);
}
static void hack(u32 index)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
ppu_execute_function(ppu, index);
if (ppu.state.load() && ppu.check_status()) throw cpu_state::ret; // Temporarily
}
static void syscall(u64 code)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
ppu_execute_syscall(ppu, code);
if (ppu.state.load() && ppu.check_status()) throw cpu_state::ret; // Temporarily
}
static u32 tbl()
{
return (u32)get_timebased_time();
}
static void call(u32 addr)
{
const auto found = s_ppu_compiled.find(addr);
if (found != s_ppu_compiled.end())
{
return found->second();
}
const auto op = vm::read32(addr).value();
const auto itype = s_ppu_itype.decode(op);
// Allow HLE callbacks without compiling them
if (itype == ppu_itype::HACK && vm::read32(addr + 4) == ppu_instructions::BLR())
{
return hack(op & 0x3ffffff);
}
trap(addr);
}
static __m128 sse_rcp_ps(__m128 A)
{
return _mm_rcp_ps(A);
}
static __m128 sse_rsqrt_ps(__m128 A)
{
return _mm_rsqrt_ps(A);
}
static float sse_rcp_ss(float A)
{
_mm_store_ss(&A, _mm_rcp_ss(_mm_load_ss(&A)));
return A;
}
static float sse_rsqrt_ss(float A)
{
_mm_store_ss(&A, _mm_rsqrt_ss(_mm_load_ss(&A)));
return A;
}
static u32 lwarx(u32 addr)
{
be_t<u32> reg_value;
vm::reservation_acquire(&reg_value, addr, sizeof(reg_value));
return reg_value;
}
static u64 ldarx(u32 addr)
{
be_t<u64> reg_value;
vm::reservation_acquire(&reg_value, addr, sizeof(reg_value));
return reg_value;
}
static bool stwcx(u32 addr, u32 reg_value)
{
const be_t<u32> data = reg_value;
return vm::reservation_update(addr, &data, sizeof(data));
}
static bool stdcx(u32 addr, u64 reg_value)
{
const be_t<u64> data = reg_value;
return vm::reservation_update(addr, &data, sizeof(data));
}
static bool sraw_carry(s32 arg, u64 shift)
{
return (arg < 0) && (shift > 31 || (arg >> shift << shift) != arg);
}
static bool srad_carry(s64 arg, u64 shift)
{
return (arg < 0) && (shift > 63 || (arg >> shift << shift) != arg);
}
static bool adde_carry(u64 a, u64 b, bool c)
{
return _addcarry_u64(c, a, b, nullptr) != 0;
}
// Interpreter call for simple vector instructions
static __m128i vec3op(decltype(&ppu_interpreter::UNK) func, __m128i _a, __m128i _b, __m128i _c)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
ppu.VR[21].vi = _a;
ppu.VR[22].vi = _b;
ppu.VR[23].vi = _c;
ppu_opcode_t op{};
op.vd = 20;
op.va = 21;
op.vb = 22;
op.vc = 23;
func(ppu, op);
return ppu.VR[20].vi;
}
// Interpreter call for simple vector instructions with immediate
static __m128i veciop(decltype(&ppu_interpreter::UNK) func, ppu_opcode_t op, __m128i _b)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
ppu.VR[22].vi = _b;
op.vd = 20;
op.vb = 22;
func(ppu, op);
return ppu.VR[20].vi;
}
// Interpreter call for FP instructions
static f64 fpop(decltype(&ppu_interpreter::UNK) func, f64 _a, f64 _b, f64 _c)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
ppu.FPR[21] = _a;
ppu.FPR[22] = _b;
ppu.FPR[23] = _c;
ppu_opcode_t op{};
op.frd = 20;
op.fra = 21;
op.frb = 22;
op.frc = 23;
func(ppu, op);
return ppu.FPR[20];
}
// Interpreter call for GPR instructions writing result to RA
static u64 aimmop(decltype(&ppu_interpreter::UNK) func, ppu_opcode_t op, u64 _s)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
const u64 a = ppu.GPR[op.ra];
const u64 s = ppu.GPR[op.rs];
ppu.GPR[op.rs] = _s;
func(ppu, op);
const u64 r = ppu.GPR[op.ra];
ppu.GPR[op.ra] = a;
ppu.GPR[op.rs] = s;
return r;
}
// Interpreter call for GPR instructions writing result to RA
static u64 aimmbop(decltype(&ppu_interpreter::UNK) func, ppu_opcode_t op, u64 _s, u64 _b)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
const u64 a = ppu.GPR[op.ra];
const u64 s = ppu.GPR[op.rs];
const u64 b = ppu.GPR[op.rb];
ppu.GPR[op.rs] = _s;
ppu.GPR[op.rb] = _b;
func(ppu, op);
const u64 r = ppu.GPR[op.ra];
ppu.GPR[op.ra] = a;
ppu.GPR[op.rs] = s;
ppu.GPR[op.rb] = b;
return r;
}
// Interpreter call for GPR instructions writing result to RA (destructive)
static u64 aaimmop(decltype(&ppu_interpreter::UNK) func, ppu_opcode_t op, u64 _s, u64 _a)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
const u64 s = ppu.GPR[op.rs];
const u64 a = ppu.GPR[op.ra];
ppu.GPR[op.rs] = _s;
ppu.GPR[op.ra] = _a;
func(ppu, op);
const u64 r = ppu.GPR[op.ra];
ppu.GPR[op.rs] = s;
ppu.GPR[op.ra] = a;
return r;
}
static u64 immaop(decltype(&ppu_interpreter::UNK) func, ppu_opcode_t op, u64 _a)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
const u64 a = ppu.GPR[op.ra];
const u64 d = ppu.GPR[op.rd];
ppu.GPR[op.ra] = _a;
func(ppu, op);
const u64 r = ppu.GPR[op.rd];
ppu.GPR[op.ra] = a;
ppu.GPR[op.rd] = d;
return r;
}
static u64 immabop(decltype(&ppu_interpreter::UNK) func, ppu_opcode_t op, u64 _a, u64 _b)
{
PPUThread& ppu = static_cast<PPUThread&>(*get_current_cpu_thread());
const u64 a = ppu.GPR[op.ra];
const u64 b = ppu.GPR[op.rb];
const u64 d = ppu.GPR[op.rd];
ppu.GPR[op.ra] = _a;
ppu.GPR[op.rb] = _b;
func(ppu, op);
const u64 r = ppu.GPR[op.rd];
ppu.GPR[op.ra] = a;
ppu.GPR[op.rb] = b;
ppu.GPR[op.rd] = d;
return r;
}
// No operation on specific u64 value (silly optimization barrier)
static u64 nop64(u64 value)
{
return value;
}
std::unordered_map<std::string, u64> table
{
{ "__memory", (u64)vm::base(0) },
{ "__context", (u64)&context },
{ "__trap", (u64)&trap },
{ "__trace", (u64)&trace },
{ "__hlecall", (u64)&hack },
{ "__syscall", (u64)&syscall },
{ "__get_tbl", (u64)&tbl },
{ "__call", (u64)&call },
{ "__lwarx", (u64)&lwarx },
{ "__ldarx", (u64)&ldarx },
{ "__stwcx", (u64)&stwcx },
{ "__stdcx", (u64)&stdcx },
{ "__sraw_get_ca", (u64)&sraw_carry },
{ "__srad_get_ca", (u64)&srad_carry },
{ "__adde_get_ca", (u64)&adde_carry },
{ "__vexptefp", (u64)&sse_exp2_ps },
{ "__vlogefp", (u64)&sse_log2_ps },
{ "__vperm", (u64)&sse_altivec_vperm },
{ "__vrefp", (u64)&sse_rcp_ps },
{ "__vrsqrtefp", (u64)&sse_rsqrt_ps },
{ "__vec3op", (u64)&vec3op },
{ "__veciop", (u64)&veciop },
{ "__aimmop", (u64)&aimmop },
{ "__aimmbop", (u64)&aimmbop },
{ "__aaimmop", (u64)&aaimmop },
{ "__immaop", (u64)&immaop },
{ "__immabop", (u64)&immabop },
{ "__fpop", (u64)&fpop },
{ "__nop64", (u64)&nop64 },
{ "__lvsl", (u64)&sse_altivec_lvsl },
{ "__lvsr", (u64)&sse_altivec_lvsr },
{ "__lvlx", (u64)&sse_cellbe_lvlx },
{ "__lvrx", (u64)&sse_cellbe_lvrx },
{ "__stvlx", (u64)&sse_cellbe_stvlx },
{ "__stvrx", (u64)&sse_cellbe_stvrx },
{ "__fre", (u64)&sse_rcp_ss },
{ "__frsqrte", (u64)&sse_rsqrt_ss },
};
virtual u64 getSymbolAddress(const std::string& name) override
{
if (u64 addr = RTDyldMemoryManager::getSymbolAddress(name))
{
LOG_ERROR(GENERAL, "LLVM: Linkage requested %s -> 0x%016llx", name, addr);
return addr;
}
const auto found = table.find(name);
if (found != table.end())
{
return found->second;
}
LOG_FATAL(GENERAL, "LLVM: Linkage failed for %s", name);
return (u64)trap;
}
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
{
// Simple allocation (TODO)
const auto ptr = m_next; m_next = (void*)::align((u64)m_next + size, 4096);
#ifdef _WIN32
if (!VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
#else
if (::mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC))
#endif
{
LOG_FATAL(GENERAL, "LLVM: Failed to allocate code section '%s', error %u", sec_name.data(), GetLastError());
return nullptr;
}
LOG_SUCCESS(GENERAL, "LLVM: Code section '%s' allocated -> 0x%p", sec_name.data(), ptr);
return (u8*)ptr;
}
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
{
// Simple allocation (TODO)
const auto ptr = m_next; m_next = (void*)::align((u64)m_next + size, 4096);
#ifdef _WIN32
if (!VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE))
#else
if (::mprotect(ptr, size, PROT_READ | PROT_WRITE))
#endif
{
LOG_FATAL(GENERAL, "LLVM: Failed to allocate data section '%s', error %u", sec_name.data(), GetLastError());
return nullptr;
}
LOG_SUCCESS(GENERAL, "LLVM: Data section '%s' allocated -> 0x%p", sec_name.data(), ptr);
return (u8*)ptr;
}
virtual bool finalizeMemory(std::string* = nullptr) override
{
// TODO: make sections read-only when necessary
return false;
}
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
{
s_unwind_info = addr;
s_unwind_size = size;
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
}
virtual void deregisterEHFrames(u8* addr, u64 load_addr, std::size_t size) override
{
LOG_ERROR(GENERAL, "deregisterEHFrames() called"); // Not expected
return RTDyldMemoryManager::deregisterEHFrames(addr, load_addr, size);
}
~MemoryManager()
{
#ifdef _WIN32
if (!RtlDeleteFunctionTable(s_unwind.data()))
{
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable(addr=0x%p) failed! Error %u", s_unwind_info, GetLastError());
}
if (!VirtualFree(s_memory, 0, MEM_DECOMMIT))
{
LOG_FATAL(GENERAL, "VirtualFree(0x%p) failed! Error %u", s_memory, GetLastError());
}
#else
if (::mprotect(s_memory, s_memory_size, PROT_NONE))
{
LOG_FATAL(GENERAL, "mprotect(0x%p) failed! Error %d", s_memory, errno);
}
// TODO: unregister EH frames if necessary
#endif
}
private:
void* m_next = s_memory;
};
llvm::LLVMContext g_context;
extern void ppu_initialize(const std::string& name, const std::vector<std::pair<u32, u32>>& funcs, u32 entry)
{
if (!s_memory)
{
throw std::runtime_error("LLVM: Memory not allocated, report to the developers." HERE);
}
if (g_cfg_ppu_decoder.get() != ppu_decoder_type::llvm || funcs.empty())
{
return;
}
using namespace llvm;
InitializeNativeTarget();
InitializeNativeTargetAsmPrinter();
LLVMLinkInMCJIT();
// Initialization
const auto _pi8 = Type::getInt8PtrTy(g_context);
const auto _void = Type::getVoidTy(g_context);
const auto _func = FunctionType::get(Type::getVoidTy(g_context), false);
// Create LLVM module
std::unique_ptr<Module> module = std::make_unique<Module>(name, g_context);
// Initialize target
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
// Initialize translator
std::unique_ptr<PPUTranslator> translator = std::make_unique<PPUTranslator>(g_context, module.get(), 0, entry);
// Initialize function list
for (const auto& info : funcs)
{
if (info.second)
{
translator->AddFunction(info.first, cast<Function>(module->getOrInsertFunction(fmt::format("__sub_%x", info.first), _func)));
}
translator->AddBlockInfo(info.first);
}
legacy::FunctionPassManager pm(module.get());
// Basic optimizations
pm.add(createCFGSimplificationPass());
pm.add(createPromoteMemoryToRegisterPass());
pm.add(createEarlyCSEPass());
pm.add(createTailCallEliminationPass());
pm.add(createReassociatePass());
pm.add(createInstructionCombiningPass());
//pm.add(new DominatorTreeWrapperPass());
//pm.add(createInstructionCombiningPass());
//pm.add(new MemoryDependenceAnalysis());
pm.add(createDeadStoreEliminationPass());
//pm.add(createGVNPass());
//pm.add(createBBVectorizePass());
//pm.add(new LoopInfo());
//pm.add(new ScalarEvolution());
pm.add(createSCCPPass());
//pm.addPass(new SyscallAnalysisPass()); // Requires constant propagation
pm.add(createInstructionCombiningPass());
pm.add(createAggressiveDCEPass());
pm.add(createCFGSimplificationPass());
//pm.add(createLintPass()); // Check
// Translate functions
for (const auto& info : funcs)
{
if (info.second)
{
pm.run(*translator->TranslateToIR(info.first, info.first + info.second, vm::_ptr<u32>(info.first)));
}
}
//static auto s_current = &PPUTranslator::UNK;
//for (const auto& info : s_test)
//{
// const u64 pseudo_addr = (u64)&info.first + INT64_MIN;
// s_current = info.second;
// const auto func = translator->TranslateToIR(pseudo_addr, pseudo_addr, nullptr, [](PPUTranslator* _this)
// {
// (_this->*s_current)(op);
// _this->ReturnFromFunction();
// });
// pm.run(*func);
//}
legacy::PassManager mpm;
// Remove unused functions, structs, global variables, etc
mpm.add(createStripDeadPrototypesPass());
mpm.run(*module);
std::string result;
raw_string_ostream out(result);
out << *module; // print IR
fs::file(fs::get_config_dir() + "LLVM.log", fs::rewrite)
.write(out.str());
result.clear();
if (verifyModule(*module, &out))
{
out.flush();
LOG_ERROR(PPU, "{%s} LLVM: Translation failed:\n%s", name, result);
return;
}
LOG_SUCCESS(PPU, "LLVM: %zu functions generated", module->getFunctionList().size());
Module* module_ptr = module.get();
std::shared_ptr<ExecutionEngine> engine(EngineBuilder(std::move(module))
.setErrorStr(&result)
.setMCJITMemoryManager(std::make_unique<MemoryManager>())
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setRelocationModel(Reloc::PIC_)
.setCodeModel((u64)s_memory <= 0x60000000 ? CodeModel::Medium : CodeModel::Large)
.setMCPU(sys::getHostCPUName())
.create());
if (!engine)
{
throw fmt::exception("LLVM: Failed to create ExecutionEngine: %s", result);
}
engine->setProcessAllSections(true);
//engine->setVerifyModules(true);
engine->RegisterJITEventListener(&s_listener);
engine->finalizeObject();
s_ppu_compiled.clear();
// Get function addresses
for (const auto& info : funcs)
{
if (info.second)
{
const std::uintptr_t link = engine->getFunctionAddress(fmt::format("__sub_%x", info.first));
s_ppu_compiled.emplace(info.first, (void(*)())link);
LOG_NOTICE(PPU, "** Function __sub_%x -> 0x%llx (addr=0x%x, size=0x%x)", info.first, link, info.first, info.second);
}
}
// Delete IR to lower memory consumption
for (auto& func : module_ptr->functions())
{
func.deleteBody();
}
#ifdef _WIN32
// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
std::set<u64> func_set;
for (const auto& pair : s_ppu_compiled)
{
// Get addresses
func_set.emplace((u64)pair.second);
}
func_set.emplace(::align(*--func_set.end() + 4096, 4096));
const u64 base = (u64)s_memory;
const u8* bits = s_unwind_info;
s_unwind.clear();
s_unwind.reserve(s_ppu_compiled.size());
for (auto it = func_set.begin(), end = --func_set.end(); it != end; it++)
{
const u64 addr = *it;
const u64 next = *func_set.upper_bound(addr);
// Generate RUNTIME_FUNCTION record
RUNTIME_FUNCTION uw;
uw.BeginAddress = static_cast<u32>(addr - base);
uw.EndAddress = static_cast<u32>(next - base);
uw.UnwindData = static_cast<u32>((u64)bits - base);
s_unwind.emplace_back(uw);
// Parse .xdata record
VERIFY(*bits++ == 1); // Version and flags
bits++; // Size of prolog
const u8 count = *bits++; // Count of unwind codes
bits++; // Frame Reg + Off
bits += ::align(count, 2) * sizeof(u16); // UNWIND_CODE array
while (!*bits && bits < s_unwind_info + s_unwind_size) bits++; // Skip strange zero padding (???)
}
VERIFY(bits == s_unwind_info + s_unwind_size);
VERIFY(RtlAddFunctionTable(s_unwind.data(), (DWORD)s_unwind.size(), base));
LOG_SUCCESS(GENERAL, "LLVM: UNWIND_INFO registered (addr=0x%p, size=0x%llx)", s_unwind_info, s_unwind_size);
#endif
fxm::import<ExecutionEngine>(WRAP_EXPR(engine));
LOG_SUCCESS(PPU, "LLVM: Compilation finished (%s)", sys::getHostCPUName().data());
}
#else
extern void ppu_initialize(const std::string& name, const std::vector<std::pair<u32, u32>>& funcs, u32 entry)
{
}
#endif

View File

@ -1,8 +1,8 @@
#pragma once
#include "Emu/Cell/Common.h"
#include "Emu/CPU/CPUThread.h"
#include "Emu/Memory/vm.h"
#include "Common.h"
#include "../CPU/CPUThread.h"
#include "../Memory/vm.h"
class PPUThread final : public cpu_thread
{

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,830 @@
#pragma once
#include <unordered_map>
#include <map>
#include <unordered_set>
#include <set>
#include <array>
#include "../rpcs3/Emu/Cell/PPUOpcodes.h"
#ifdef _MSC_VER
#pragma warning(push, 0)
#endif
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Module.h"
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "../Utilities/types.h"
#include "../Utilities/StrFmt.h"
#include "../Utilities/Macro.h"
#include "../Utilities/BEType.h"
template<typename T, typename = void>
struct TypeGen
{
static_assert(!sizeof(T), "GetType<>() error: unknown type");
};
template<typename T>
struct TypeGen<T, std::enable_if_t<std::is_void<T>::value>>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getVoidTy(context); }
};
template<typename T>
struct TypeGen<T, std::enable_if_t<std::is_same<T, s64>::value || std::is_same<T, u64>::value>>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt64Ty(context); }
};
template<typename T>
struct TypeGen<T, std::enable_if_t<std::is_same<T, s32>::value || std::is_same<T, u32>::value>>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt32Ty(context); }
};
template<typename T>
struct TypeGen<T, std::enable_if_t<std::is_same<T, s16>::value || std::is_same<T, u16>::value>>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt16Ty(context); }
};
template<typename T>
struct TypeGen<T, std::enable_if_t<std::is_same<T, s8>::value || std::is_same<T, u8>::value || std::is_same<T, char>::value>>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt8Ty(context); }
};
template<>
struct TypeGen<f32, void>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getFloatTy(context); }
};
template<>
struct TypeGen<f64, void>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getDoubleTy(context); }
};
template<>
struct TypeGen<bool, void>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getInt1Ty(context); }
};
template<>
struct TypeGen<u128, void>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::Type::getIntNTy(context, 128); }
};
// Pointer type
template<typename T>
struct TypeGen<T*, void>
{
static llvm::Type* get(llvm::LLVMContext& context) { return TypeGen<T>::get(context)->getPointerTo(); }
};
// Vector type
template<typename T, int N>
struct TypeGen<T[N], void>
{
static llvm::Type* get(llvm::LLVMContext& context) { return llvm::VectorType::get(TypeGen<T>::get(context), N); }
};
class PPUTranslator final //: public CPUTranslator
{
// LLVM context
llvm::LLVMContext& m_context;
// Module to which all generated code is output to
llvm::Module* const m_module;
// Base address (TODO)
const u64 m_base_addr;
// Endianness, affects vector element numbering (TODO)
const bool m_is_be;
// Attributes for function calls which are "pure" and may be optimized away if their results are unused
const llvm::AttributeSet m_pure_attr;
// Available functions: types (not set or nullptr for untyped)
std::unordered_map<u64, llvm::FunctionType*> m_func_types;
// Available functions
std::unordered_map<u64, llvm::Function*> m_func_list;
// LLVM IR builder
llvm::IRBuilder<>* m_ir;
// LLVM function
llvm::Function* m_function;
// LLVM function type (may be null)
llvm::FunctionType* m_function_type;
// Function range
u64 m_start_addr, m_end_addr, m_current_addr;
// Basic blocks for current function
std::unordered_map<u64, llvm::BasicBlock*> m_blocks;
// Supplementary block info for all functions
std::set<u64> m_block_info;
// JT resolver block
llvm::BasicBlock* m_jtr;
// Current binary data
be_t<u32>* m_bin{};
/* Variables */
// Explicit register usage counter
std::unordered_map<llvm::Value*, u64> m_value_usage;
// Memory base
llvm::Value* m_base;
// Thread context (obtained by __context)
llvm::Value* m_thread;
// Thread context struct
llvm::StructType* m_thread_type;
llvm::Value* m_globals[96]{};
llvm::Value** const m_g_gpr = m_globals + 0;
llvm::Value** const m_g_fpr = m_globals + 32;
llvm::Value** const m_g_vr = m_globals + 64;
llvm::Value* m_locals[96]{};
llvm::Value** const m_gpr = m_locals + 0;
llvm::Value** const m_fpr = m_locals + 32;
llvm::Value** const m_vr = m_locals + 64;
llvm::Value* m_cr[32]{};
llvm::Value* m_reg_lr;
llvm::Value* m_reg_ctr; // CTR register (counter)
llvm::Value* m_reg_vrsave;
llvm::Value* m_xer_so; // XER.SO bit, summary overflow
llvm::Value* m_xer_ov; // XER.OV bit, overflow flag
llvm::Value* m_xer_ca; // XER.CA bit, carry flag
llvm::Value* m_xer_count;
llvm::Value* m_vscr_nj; // VSCR.NJ bit, non-Java mode
llvm::Value* m_vscr_sat; // VSCR.SAT bit, sticky saturation flag
llvm::Value* m_fpscr[32]{};
llvm::Value* m_fpscr_fx; // bit 32 (first)
llvm::Value* m_fpscr_ox; // bit 35 (4th)
llvm::Value* m_fpscr_ux;
llvm::Value* m_fpscr_zx;
llvm::Value* m_fpscr_xx;
llvm::Value* m_fpscr_vxsnan;
llvm::Value* m_fpscr_vxisi;
llvm::Value* m_fpscr_vxidi;
llvm::Value* m_fpscr_vxzdz;
llvm::Value* m_fpscr_vximz;
llvm::Value* m_fpscr_vxvc;
llvm::Value* m_fpscr_fr;
llvm::Value* m_fpscr_fi;
llvm::Value* m_fpscr_c;
llvm::Value* m_fpscr_lt;
llvm::Value* m_fpscr_gt;
llvm::Value* m_fpscr_eq;
llvm::Value* m_fpscr_un;
llvm::Value* m_fpscr_reserved;
llvm::Value* m_fpscr_vxsoft;
llvm::Value* m_fpscr_vxsqrt;
llvm::Value* m_fpscr_vxcvi;
llvm::Value* m_fpscr_ve;
llvm::Value* m_fpscr_oe;
llvm::Value* m_fpscr_ue;
llvm::Value* m_fpscr_ze;
llvm::Value* m_fpscr_xe;
llvm::Value* m_fpscr_ni;
llvm::Value* m_fpscr_rnh; // RN high bit
llvm::Value* m_fpscr_rnl; // RN low bit
public:
// Change integer size for integer or integer vector type (by 2^degree)
llvm::Type* ScaleType(llvm::Type*, s32 pow2 = 0);
// Extend arg to double width with its copy
llvm::Value* DuplicateExt(llvm::Value* arg);
// Rotate arg left by n (n must be < bitwidth)
llvm::Value* RotateLeft(llvm::Value* arg, u64 n);
// Rotate arg left by n (n will be masked)
llvm::Value* RotateLeft(llvm::Value* arg, llvm::Value* n);
// Emit function call
void CallFunction(u64 target, bool tail, llvm::Value* indirect = nullptr);
// Set some registers to undef (after function call)
void UndefineVolatileRegisters();
// Get the basic block for the specified address
llvm::BasicBlock* GetBasicBlock(u64 addr);
// Load GPR
llvm::Value* GetGpr(u32 r, u32 num_bits = 64);
// Set GPR
void SetGpr(u32 r, llvm::Value* value);
// Get FPR
llvm::Value* GetFpr(u32 r, u32 bits = 64, bool as_int = false);
// Set FPR
void SetFpr(u32 r, llvm::Value* val);
// Vector register type
enum class VrType
{
vi8, // i8 vector
vi16, // i16 vector
vi32, // i32 vector
vf, // f32 vector
i128, // Solid 128-bit integer
};
// Load VR
llvm::Value* GetVr(u32 vr, VrType);
// Load VRs
template<typename... Vrs>
std::array<llvm::Value*, sizeof...(Vrs)> GetVrs(VrType type, Vrs... regs)
{
static_assert(sizeof...(Vrs), "Empty VR list");
return{ GetVr(regs, type)... };
}
// Set VR to the specified value
void SetVr(u32 vr, llvm::Value*);
// Bitcast to scalar integer value
llvm::Value* Solid(llvm::Value*);
// Compare value with zero constant of appropriate size
llvm::Value* IsZero(llvm::Value*); llvm::Value* IsNotZero(llvm::Value*);
// Compare value with all-ones constant of appropriate size
llvm::Value* IsOnes(llvm::Value*); llvm::Value* IsNotOnes(llvm::Value*);
// Broadcast specified value
llvm::Value* Broadcast(llvm::Value* value, u32 count);
// Saturate scalar or vector given the comparison operand and the extreme value to compare with (second result is the comparison result)
std::pair<llvm::Value*, llvm::Value*> Saturate(llvm::Value* value, llvm::CmpInst::Predicate inst, llvm::Value* extreme);
// Saturate signed value (second result is the disjunction of comparison results)
std::pair<llvm::Value*, llvm::Value*> SaturateSigned(llvm::Value* value, u64 min, u64 max);
// Multiply FP value or vector by the pow(2, scale)
llvm::Value* Scale(llvm::Value* value, s32 scale);
// Create shuffle instruction with constant args
llvm::Value* Shuffle(llvm::Value* left, llvm::Value* right, std::initializer_list<u32> indices);
template<std::size_t N>
std::array<llvm::Value*, N> Shuffle(std::array<llvm::Value*, N> left, std::array<llvm::Value*, N> right, std::initializer_list<u32> indices)
{
for (std::size_t i = 0; i < N; i++) left[i] = Shuffle(left[i], right[i], indices);
return left;
}
// Create sign extension (with double size if type is nullptr)
llvm::Value* SExt(llvm::Value* value, llvm::Type* = nullptr);
template<std::size_t N>
std::array<llvm::Value*, N> SExt(std::array<llvm::Value*, N> values, llvm::Type* type = nullptr)
{
for (std::size_t i = 0; i < N; i++) values[i] = SExt(values[i], type);
return values;
}
// Create zero extension (with double size if type is nullptr)
llvm::Value* ZExt(llvm::Value*, llvm::Type* = nullptr);
template<std::size_t N>
std::array<llvm::Value*, N> ZExt(std::array<llvm::Value*, N> values, llvm::Type* type = nullptr)
{
for (std::size_t i = 0; i < N; i++) values[i] = ZExt(values[i], type);
return values;
}
// Add multiple elements
llvm::Value* Add(std::initializer_list<llvm::Value*>);
// Create tuncation (with half size if type is nullptr)
llvm::Value* Trunc(llvm::Value*, llvm::Type* = nullptr);
// Get specified CR bit
llvm::Value* GetCrb(u32 crb);
// Set specified CR bit
void SetCrb(u32 crb, llvm::Value* value);
// Set CR field, if `so` value (5th arg) is nullptr, loaded from XER.SO
void SetCrField(u32 group, llvm::Value* lt, llvm::Value* gt, llvm::Value* eq, llvm::Value* so = nullptr);
// Set CR field based on signed comparison
void SetCrFieldSignedCmp(u32 n, llvm::Value* a, llvm::Value* b);
// Set CR field based on unsigned comparison
void SetCrFieldUnsignedCmp(u32 n, llvm::Value* a, llvm::Value* b);
// Set FPSCR CC fields provided, optionally updating CR1
void SetFPCC(llvm::Value* lt, llvm::Value* gt, llvm::Value* eq, llvm::Value* un, bool set_cr = false);
// Update FPRF fields for the value, optionally updating CR1
void SetFPRF(llvm::Value* value, bool set_cr);
// Update FR bit
void SetFPSCR_FR(llvm::Value* value);
// Update FI bit (and set XX exception)
void SetFPSCR_FI(llvm::Value* value);
// Update sticky FPSCR exception bit, update FPSCR.FX
void SetFPSCRException(llvm::Value* ptr, llvm::Value* value);
// Get FPSCR bit (exception bits are cleared)
llvm::Value* GetFPSCRBit(u32 n);
// Set FPSCR bit
void SetFPSCRBit(u32 n, llvm::Value*, bool update_fx);
// Get XER.CA bit
llvm::Value* GetCarry();
// Set XER.CA bit
void SetCarry(llvm::Value*);
// Set XER.OV bit, and update XER.SO bit (|=)
void SetOverflow(llvm::Value*);
// Update sticky VSCR.SAT bit (|=)
void SetSat(llvm::Value*);
// Check condition for trap instructions
llvm::Value* CheckTrapCondition(u32 to, llvm::Value* left, llvm::Value* right);
// Emit trap
llvm::Value* Trap(u64 addr);
// Check condition for branch instructions
llvm::Value* CheckBranchCondition(u32 bo, u32 bi);
// Branch to next instruction if condition failed, never branch on nullptr
void UseCondition(llvm::Value* = nullptr);
// Check whether the address is stack
bool IsStackAddr(llvm::Value* addr);
// Get memory pointer
llvm::Value* GetMemory(llvm::Value* addr, llvm::Type* type);
// Read from memory
llvm::Value* ReadMemory(llvm::Value* addr, llvm::Type* type, bool is_be = true, u32 align = 1);
// Write to memory
void WriteMemory(llvm::Value* addr, llvm::Value* value, bool is_be = true, u32 align = 1);
// Convert a C++ type to an LLVM type
template<typename T>
llvm::Type* GetType()
{
return TypeGen<T>::get(m_context);
}
// Get an undefined value with specified type
template<typename T>
llvm::Value* GetUndef()
{
return llvm::UndefValue::get(GetType<T>());
}
// Call a function with attribute list
template<typename... Args>
llvm::Value* Call(llvm::Type* ret, llvm::AttributeSet attr, llvm::StringRef name, Args... args)
{
// Call the function
return m_ir->CreateCall(m_module->getOrInsertFunction(name, llvm::FunctionType::get(ret, {args->getType()...}, false), attr), {args...});
}
// Call a function
template<typename... Args>
llvm::Value* Call(llvm::Type* ret, llvm::StringRef name, Args... args)
{
return Call(ret, llvm::AttributeSet{}, name, args...);
}
// Handle compilation errors
void CompilationError(const std::string& error);
PPUTranslator(llvm::LLVMContext& context, llvm::Module* module, u64 base, u64 entry);
~PPUTranslator();
// Add function
void AddFunction(u64 addr, llvm::Function* func, llvm::FunctionType* type = nullptr);
// Add block entry hint (not essential)
void AddBlockInfo(u64 addr);
// Parses PPU opcodes and translate them into LLVM IR
llvm::Function* TranslateToIR(u64 start_addr, u64 end_addr, be_t<u32>* bin, void(*custom)(PPUTranslator*) = nullptr);
void MFVSCR(ppu_opcode_t op);
void MTVSCR(ppu_opcode_t op);
void VADDCUW(ppu_opcode_t op);
void VADDFP(ppu_opcode_t op);
void VADDSBS(ppu_opcode_t op);
void VADDSHS(ppu_opcode_t op);
void VADDSWS(ppu_opcode_t op);
void VADDUBM(ppu_opcode_t op);
void VADDUBS(ppu_opcode_t op);
void VADDUHM(ppu_opcode_t op);
void VADDUHS(ppu_opcode_t op);
void VADDUWM(ppu_opcode_t op);
void VADDUWS(ppu_opcode_t op);
void VAND(ppu_opcode_t op);
void VANDC(ppu_opcode_t op);
void VAVGSB(ppu_opcode_t op);
void VAVGSH(ppu_opcode_t op);
void VAVGSW(ppu_opcode_t op);
void VAVGUB(ppu_opcode_t op);
void VAVGUH(ppu_opcode_t op);
void VAVGUW(ppu_opcode_t op);
void VCFSX(ppu_opcode_t op);
void VCFUX(ppu_opcode_t op);
void VCMPBFP(ppu_opcode_t op);
void VCMPEQFP(ppu_opcode_t op);
void VCMPEQUB(ppu_opcode_t op);
void VCMPEQUH(ppu_opcode_t op);
void VCMPEQUW(ppu_opcode_t op);
void VCMPGEFP(ppu_opcode_t op);
void VCMPGTFP(ppu_opcode_t op);
void VCMPGTSB(ppu_opcode_t op);
void VCMPGTSH(ppu_opcode_t op);
void VCMPGTSW(ppu_opcode_t op);
void VCMPGTUB(ppu_opcode_t op);
void VCMPGTUH(ppu_opcode_t op);
void VCMPGTUW(ppu_opcode_t op);
void VCTSXS(ppu_opcode_t op);
void VCTUXS(ppu_opcode_t op);
void VEXPTEFP(ppu_opcode_t op);
void VLOGEFP(ppu_opcode_t op);
void VMADDFP(ppu_opcode_t op);
void VMAXFP(ppu_opcode_t op);
void VMAXSB(ppu_opcode_t op);
void VMAXSH(ppu_opcode_t op);
void VMAXSW(ppu_opcode_t op);
void VMAXUB(ppu_opcode_t op);
void VMAXUH(ppu_opcode_t op);
void VMAXUW(ppu_opcode_t op);
void VMHADDSHS(ppu_opcode_t op);
void VMHRADDSHS(ppu_opcode_t op);
void VMINFP(ppu_opcode_t op);
void VMINSB(ppu_opcode_t op);
void VMINSH(ppu_opcode_t op);
void VMINSW(ppu_opcode_t op);
void VMINUB(ppu_opcode_t op);
void VMINUH(ppu_opcode_t op);
void VMINUW(ppu_opcode_t op);
void VMLADDUHM(ppu_opcode_t op);
void VMRGHB(ppu_opcode_t op);
void VMRGHH(ppu_opcode_t op);
void VMRGHW(ppu_opcode_t op);
void VMRGLB(ppu_opcode_t op);
void VMRGLH(ppu_opcode_t op);
void VMRGLW(ppu_opcode_t op);
void VMSUMMBM(ppu_opcode_t op);
void VMSUMSHM(ppu_opcode_t op);
void VMSUMSHS(ppu_opcode_t op);
void VMSUMUBM(ppu_opcode_t op);
void VMSUMUHM(ppu_opcode_t op);
void VMSUMUHS(ppu_opcode_t op);
void VMULESB(ppu_opcode_t op);
void VMULESH(ppu_opcode_t op);
void VMULEUB(ppu_opcode_t op);
void VMULEUH(ppu_opcode_t op);
void VMULOSB(ppu_opcode_t op);
void VMULOSH(ppu_opcode_t op);
void VMULOUB(ppu_opcode_t op);
void VMULOUH(ppu_opcode_t op);
void VNMSUBFP(ppu_opcode_t op);
void VNOR(ppu_opcode_t op);
void VOR(ppu_opcode_t op);
void VPERM(ppu_opcode_t op);
void VPKPX(ppu_opcode_t op);
void VPKSHSS(ppu_opcode_t op);
void VPKSHUS(ppu_opcode_t op);
void VPKSWSS(ppu_opcode_t op);
void VPKSWUS(ppu_opcode_t op);
void VPKUHUM(ppu_opcode_t op);
void VPKUHUS(ppu_opcode_t op);
void VPKUWUM(ppu_opcode_t op);
void VPKUWUS(ppu_opcode_t op);
void VREFP(ppu_opcode_t op);
void VRFIM(ppu_opcode_t op);
void VRFIN(ppu_opcode_t op);
void VRFIP(ppu_opcode_t op);
void VRFIZ(ppu_opcode_t op);
void VRLB(ppu_opcode_t op);
void VRLH(ppu_opcode_t op);
void VRLW(ppu_opcode_t op);
void VRSQRTEFP(ppu_opcode_t op);
void VSEL(ppu_opcode_t op);
void VSL(ppu_opcode_t op);
void VSLB(ppu_opcode_t op);
void VSLDOI(ppu_opcode_t op);
void VSLH(ppu_opcode_t op);
void VSLO(ppu_opcode_t op);
void VSLW(ppu_opcode_t op);
void VSPLTB(ppu_opcode_t op);
void VSPLTH(ppu_opcode_t op);
void VSPLTISB(ppu_opcode_t op);
void VSPLTISH(ppu_opcode_t op);
void VSPLTISW(ppu_opcode_t op);
void VSPLTW(ppu_opcode_t op);
void VSR(ppu_opcode_t op);
void VSRAB(ppu_opcode_t op);
void VSRAH(ppu_opcode_t op);
void VSRAW(ppu_opcode_t op);
void VSRB(ppu_opcode_t op);
void VSRH(ppu_opcode_t op);
void VSRO(ppu_opcode_t op);
void VSRW(ppu_opcode_t op);
void VSUBCUW(ppu_opcode_t op);
void VSUBFP(ppu_opcode_t op);
void VSUBSBS(ppu_opcode_t op);
void VSUBSHS(ppu_opcode_t op);
void VSUBSWS(ppu_opcode_t op);
void VSUBUBM(ppu_opcode_t op);
void VSUBUBS(ppu_opcode_t op);
void VSUBUHM(ppu_opcode_t op);
void VSUBUHS(ppu_opcode_t op);
void VSUBUWM(ppu_opcode_t op);
void VSUBUWS(ppu_opcode_t op);
void VSUMSWS(ppu_opcode_t op);
void VSUM2SWS(ppu_opcode_t op);
void VSUM4SBS(ppu_opcode_t op);
void VSUM4SHS(ppu_opcode_t op);
void VSUM4UBS(ppu_opcode_t op);
void VUPKHPX(ppu_opcode_t op);
void VUPKHSB(ppu_opcode_t op);
void VUPKHSH(ppu_opcode_t op);
void VUPKLPX(ppu_opcode_t op);
void VUPKLSB(ppu_opcode_t op);
void VUPKLSH(ppu_opcode_t op);
void VXOR(ppu_opcode_t op);
void TDI(ppu_opcode_t op);
void TWI(ppu_opcode_t op);
void MULLI(ppu_opcode_t op);
void SUBFIC(ppu_opcode_t op);
void CMPLI(ppu_opcode_t op);
void CMPI(ppu_opcode_t op);
void ADDIC(ppu_opcode_t op);
void ADDI(ppu_opcode_t op);
void ADDIS(ppu_opcode_t op);
void BC(ppu_opcode_t op);
void HACK(ppu_opcode_t op);
void SC(ppu_opcode_t op);
void B(ppu_opcode_t op);
void MCRF(ppu_opcode_t op);
void BCLR(ppu_opcode_t op);
void CRNOR(ppu_opcode_t op);
void CRANDC(ppu_opcode_t op);
void ISYNC(ppu_opcode_t op);
void CRXOR(ppu_opcode_t op);
void CRNAND(ppu_opcode_t op);
void CRAND(ppu_opcode_t op);
void CREQV(ppu_opcode_t op);
void CRORC(ppu_opcode_t op);
void CROR(ppu_opcode_t op);
void BCCTR(ppu_opcode_t op);
void RLWIMI(ppu_opcode_t op);
void RLWINM(ppu_opcode_t op);
void RLWNM(ppu_opcode_t op);
void ORI(ppu_opcode_t op);
void ORIS(ppu_opcode_t op);
void XORI(ppu_opcode_t op);
void XORIS(ppu_opcode_t op);
void ANDI(ppu_opcode_t op);
void ANDIS(ppu_opcode_t op);
void RLDICL(ppu_opcode_t op);
void RLDICR(ppu_opcode_t op);
void RLDIC(ppu_opcode_t op);
void RLDIMI(ppu_opcode_t op);
void RLDCL(ppu_opcode_t op);
void RLDCR(ppu_opcode_t op);
void CMP(ppu_opcode_t op);
void TW(ppu_opcode_t op);
void LVSL(ppu_opcode_t op);
void LVEBX(ppu_opcode_t op);
void SUBFC(ppu_opcode_t op);
void MULHDU(ppu_opcode_t op);
void ADDC(ppu_opcode_t op);
void MULHWU(ppu_opcode_t op);
void MFOCRF(ppu_opcode_t op);
void LWARX(ppu_opcode_t op);
void LDX(ppu_opcode_t op);
void LWZX(ppu_opcode_t op);
void SLW(ppu_opcode_t op);
void CNTLZW(ppu_opcode_t op);
void SLD(ppu_opcode_t op);
void AND(ppu_opcode_t op);
void CMPL(ppu_opcode_t op);
void LVSR(ppu_opcode_t op);
void LVEHX(ppu_opcode_t op);
void SUBF(ppu_opcode_t op);
void LDUX(ppu_opcode_t op);
void DCBST(ppu_opcode_t op);
void LWZUX(ppu_opcode_t op);
void CNTLZD(ppu_opcode_t op);
void ANDC(ppu_opcode_t op);
void TD(ppu_opcode_t op);
void LVEWX(ppu_opcode_t op);
void MULHD(ppu_opcode_t op);
void MULHW(ppu_opcode_t op);
void LDARX(ppu_opcode_t op);
void DCBF(ppu_opcode_t op);
void LBZX(ppu_opcode_t op);
void LVX(ppu_opcode_t op);
void NEG(ppu_opcode_t op);
void LBZUX(ppu_opcode_t op);
void NOR(ppu_opcode_t op);
void STVEBX(ppu_opcode_t op);
void SUBFE(ppu_opcode_t op);
void ADDE(ppu_opcode_t op);
void MTOCRF(ppu_opcode_t op);
void STDX(ppu_opcode_t op);
void STWCX(ppu_opcode_t op);
void STWX(ppu_opcode_t op);
void STVEHX(ppu_opcode_t op);
void STDUX(ppu_opcode_t op);
void STWUX(ppu_opcode_t op);
void STVEWX(ppu_opcode_t op);
void SUBFZE(ppu_opcode_t op);
void ADDZE(ppu_opcode_t op);
void STDCX(ppu_opcode_t op);
void STBX(ppu_opcode_t op);
void STVX(ppu_opcode_t op);
void MULLD(ppu_opcode_t op);
void SUBFME(ppu_opcode_t op);
void ADDME(ppu_opcode_t op);
void MULLW(ppu_opcode_t op);
void DCBTST(ppu_opcode_t op);
void STBUX(ppu_opcode_t op);
void ADD(ppu_opcode_t op);
void DCBT(ppu_opcode_t op);
void LHZX(ppu_opcode_t op);
void EQV(ppu_opcode_t op);
void ECIWX(ppu_opcode_t op);
void LHZUX(ppu_opcode_t op);
void XOR(ppu_opcode_t op);
void MFSPR(ppu_opcode_t op);
void LWAX(ppu_opcode_t op);
void DST(ppu_opcode_t op);
void LHAX(ppu_opcode_t op);
void LVXL(ppu_opcode_t op);
void MFTB(ppu_opcode_t op);
void LWAUX(ppu_opcode_t op);
void DSTST(ppu_opcode_t op);
void LHAUX(ppu_opcode_t op);
void STHX(ppu_opcode_t op);
void ORC(ppu_opcode_t op);
void ECOWX(ppu_opcode_t op);
void STHUX(ppu_opcode_t op);
void OR(ppu_opcode_t op);
void DIVDU(ppu_opcode_t op);
void DIVWU(ppu_opcode_t op);
void MTSPR(ppu_opcode_t op);
void DCBI(ppu_opcode_t op);
void NAND(ppu_opcode_t op);
void STVXL(ppu_opcode_t op);
void DIVD(ppu_opcode_t op);
void DIVW(ppu_opcode_t op);
void LVLX(ppu_opcode_t op);
void LDBRX(ppu_opcode_t op);
void LSWX(ppu_opcode_t op);
void LWBRX(ppu_opcode_t op);
void LFSX(ppu_opcode_t op);
void SRW(ppu_opcode_t op);
void SRD(ppu_opcode_t op);
void LVRX(ppu_opcode_t op);
void LSWI(ppu_opcode_t op);
void LFSUX(ppu_opcode_t op);
void SYNC(ppu_opcode_t op);
void LFDX(ppu_opcode_t op);
void LFDUX(ppu_opcode_t op);
void STVLX(ppu_opcode_t op);
void STDBRX(ppu_opcode_t op);
void STSWX(ppu_opcode_t op);
void STWBRX(ppu_opcode_t op);
void STFSX(ppu_opcode_t op);
void STVRX(ppu_opcode_t op);
void STFSUX(ppu_opcode_t op);
void STSWI(ppu_opcode_t op);
void STFDX(ppu_opcode_t op);
void STFDUX(ppu_opcode_t op);
void LVLXL(ppu_opcode_t op);
void LHBRX(ppu_opcode_t op);
void SRAW(ppu_opcode_t op);
void SRAD(ppu_opcode_t op);
void LVRXL(ppu_opcode_t op);
void DSS(ppu_opcode_t op);
void SRAWI(ppu_opcode_t op);
void SRADI(ppu_opcode_t op);
void EIEIO(ppu_opcode_t op);
void STVLXL(ppu_opcode_t op);
void STHBRX(ppu_opcode_t op);
void EXTSH(ppu_opcode_t op);
void STVRXL(ppu_opcode_t op);
void EXTSB(ppu_opcode_t op);
void STFIWX(ppu_opcode_t op);
void EXTSW(ppu_opcode_t op);
void ICBI(ppu_opcode_t op);
void DCBZ(ppu_opcode_t op);
void LWZ(ppu_opcode_t op);
void LWZU(ppu_opcode_t op);
void LBZ(ppu_opcode_t op);
void LBZU(ppu_opcode_t op);
void STW(ppu_opcode_t op);
void STWU(ppu_opcode_t op);
void STB(ppu_opcode_t op);
void STBU(ppu_opcode_t op);
void LHZ(ppu_opcode_t op);
void LHZU(ppu_opcode_t op);
void LHA(ppu_opcode_t op);
void LHAU(ppu_opcode_t op);
void STH(ppu_opcode_t op);
void STHU(ppu_opcode_t op);
void LMW(ppu_opcode_t op);
void STMW(ppu_opcode_t op);
void LFS(ppu_opcode_t op);
void LFSU(ppu_opcode_t op);
void LFD(ppu_opcode_t op);
void LFDU(ppu_opcode_t op);
void STFS(ppu_opcode_t op);
void STFSU(ppu_opcode_t op);
void STFD(ppu_opcode_t op);
void STFDU(ppu_opcode_t op);
void LD(ppu_opcode_t op);
void LDU(ppu_opcode_t op);
void LWA(ppu_opcode_t op);
void STD(ppu_opcode_t op);
void STDU(ppu_opcode_t op);
void FDIVS(ppu_opcode_t op);
void FSUBS(ppu_opcode_t op);
void FADDS(ppu_opcode_t op);
void FSQRTS(ppu_opcode_t op);
void FRES(ppu_opcode_t op);
void FMULS(ppu_opcode_t op);
void FMADDS(ppu_opcode_t op);
void FMSUBS(ppu_opcode_t op);
void FNMSUBS(ppu_opcode_t op);
void FNMADDS(ppu_opcode_t op);
void MTFSB1(ppu_opcode_t op);
void MCRFS(ppu_opcode_t op);
void MTFSB0(ppu_opcode_t op);
void MTFSFI(ppu_opcode_t op);
void MFFS(ppu_opcode_t op);
void MTFSF(ppu_opcode_t op);
void FCMPU(ppu_opcode_t op);
void FRSP(ppu_opcode_t op);
void FCTIW(ppu_opcode_t op);
void FCTIWZ(ppu_opcode_t op);
void FDIV(ppu_opcode_t op);
void FSUB(ppu_opcode_t op);
void FADD(ppu_opcode_t op);
void FSQRT(ppu_opcode_t op);
void FSEL(ppu_opcode_t op);
void FMUL(ppu_opcode_t op);
void FRSQRTE(ppu_opcode_t op);
void FMSUB(ppu_opcode_t op);
void FMADD(ppu_opcode_t op);
void FNMSUB(ppu_opcode_t op);
void FNMADD(ppu_opcode_t op);
void FCMPO(ppu_opcode_t op);
void FNEG(ppu_opcode_t op);
void FMR(ppu_opcode_t op);
void FNABS(ppu_opcode_t op);
void FABS(ppu_opcode_t op);
void FCTID(ppu_opcode_t op);
void FCTIDZ(ppu_opcode_t op);
void FCFID(ppu_opcode_t op);
void UNK(ppu_opcode_t op);
};

View File

@ -192,6 +192,12 @@ void SPUThread::cpu_task()
return custom_task(*this);
}
if (g_cfg_spu_decoder.get() == spu_decoder_type::asmjit)
{
if (!spu_db) spu_db = fxm::get_always<SPUDatabase>();
return spu_recompiler_base::enter(*this);
}
g_tls_log_prefix = []
{
const auto cpu = static_cast<SPUThread*>(get_current_cpu_thread());
@ -199,12 +205,6 @@ void SPUThread::cpu_task()
return fmt::format("%s [0x%05x]", cpu->get_name(), cpu->pc);
};
if (g_cfg_spu_decoder.get() == spu_decoder_type::asmjit)
{
if (!spu_db) spu_db = fxm::get_always<SPUDatabase>();
return spu_recompiler_base::enter(*this);
}
// Select opcode table
const auto& table = *(
g_cfg_spu_decoder.get() == spu_decoder_type::precise ? &s_spu_interpreter_precise.get_table() :

View File

@ -56,11 +56,7 @@ LOG_CHANNEL(sys_vm);
extern std::string ppu_get_syscall_name(u64 code);
static void null_func(PPUThread& ppu)
{
LOG_TODO(HLE, "Unimplemented syscall %s -> CELL_OK", ppu_get_syscall_name(ppu.GPR[11]));
ppu.GPR[3] = 0;
}
static constexpr ppu_function_t null_func = nullptr;
// UNS = Unused
// ROOT = Root
@ -920,23 +916,28 @@ extern void ppu_execute_syscall(PPUThread& ppu, u64 code)
}
// If autopause occures, check_status() will hold the thread till unpaused.
if (debug::autopause::pause_syscall(code) && ppu.check_status()) throw cpu_state::ret;
if (debug::autopause::pause_syscall(code) && ppu.check_status())
{
throw cpu_state::ret;
}
const auto previous_function = ppu.last_function; // TODO: use gsl::finally or something
try
{
g_ppu_syscall_table[code](ppu);
}
catch (EmulationStopped)
{
LOG_WARNING(PPU, "Syscall '%s' (%llu) aborted", ppu_get_syscall_name(code), code);
ppu.last_function = previous_function;
throw;
if (auto func = g_ppu_syscall_table[code])
{
func(ppu);
}
else
{
LOG_TODO(HLE, "Unimplemented syscall %s -> CELL_OK", ppu_get_syscall_name(code));
ppu.GPR[3] = 0;
}
}
catch (...)
{
LOG_ERROR(PPU, "Syscall '%s' (%llu) aborted", ppu_get_syscall_name(code), code);
logs::PPU.format(Emu.IsStopped() ? logs::level::warning : logs::level::error, "Syscall '%s' (%llu) aborted", ppu_get_syscall_name(code), code);
ppu.last_function = previous_function;
throw;
}

View File

@ -13,9 +13,7 @@ logs::channel sys_ppu_thread("sys_ppu_thread", logs::level::notice);
void _sys_ppu_thread_exit(PPUThread& ppu, u64 errorcode)
{
sys_ppu_thread.trace("_sys_ppu_thread_exit(errorcode=0x%llx)", errorcode);
LV2_LOCK;
sys_ppu_thread.warning("_sys_ppu_thread_exit(errorcode=0x%llx)", errorcode);
// TODO: Should we really unlock mutexes?
@ -29,13 +27,17 @@ void _sys_ppu_thread_exit(PPUThread& ppu, u64 errorcode)
// }
//}
ppu.state += cpu_state::exit;
//ppu.handle_interrupt();
// Delete detached thread
if (!ppu.is_joinable)
{
idm::remove<PPUThread>(ppu.id);
LV2_LOCK;
ppu.state += cpu_state::exit;
//ppu.handle_interrupt();
// Delete detached thread
if (!ppu.is_joinable)
{
idm::remove<PPUThread>(ppu.id);
}
}
// Throw if this syscall was not called directly by the SC instruction (hack)

View File

@ -79,6 +79,7 @@ struct lv2_prx_t
bool is_started = false;
std::unordered_map<u32, u32> specials;
std::vector<std::pair<u32, u32>> func;
vm::ptr<s32(int argc, vm::ptr<void> argv)> start = vm::null;
vm::ptr<s32(int argc, vm::ptr<void> argv)> stop = vm::null;

View File

@ -178,7 +178,7 @@ namespace vm
_ptr_base operator --(int)
{
_ptr_base result = m_addr;
_ptr_base result = *this;
m_addr = vm::cast(m_addr, HERE) - SIZE_32(T);
return result;
}

View File

@ -91,15 +91,9 @@ extern void arm_execute_function(ARMv7Thread& cpu, u32 index)
{
func(cpu);
}
catch (EmulationStopped)
{
LOG_WARNING(ARMv7, "Function '%s' aborted", cpu.last_function);
cpu.last_function = previous_function;
throw;
}
catch (...)
{
LOG_ERROR(ARMv7, "Function '%s' aborted", cpu.last_function);
logs::ARMv7.format(Emu.IsStopped() ? logs::level::warning : logs::level::error, "Function '%s' aborted", cpu.last_function);
cpu.last_function = previous_function;
throw;
}

View File

@ -395,7 +395,10 @@ void Emulator::Stop()
idm::select<PPUThread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
{
cpu.state += cpu_state::dbg_global_stop;
cpu->lock_notify();
cpu->lock();
cpu->set_exception(std::make_exception_ptr(EmulationStopped()));
cpu->unlock();
cpu->notify();
});
}

View File

@ -340,7 +340,6 @@ SettingsDialog::SettingsDialog(wxWindow* parent)
radiobox_pad_helper ppu_decoder_modes({ "Core", "PPU Decoder" });
rbox_ppu_decoder = new wxRadioBox(p_core, wxID_ANY, "PPU Decoder", wxDefaultPosition, wxSize(-1, -1), ppu_decoder_modes, 1);
pads.emplace_back(std::make_unique<radiobox_pad>(std::move(ppu_decoder_modes), rbox_ppu_decoder));
rbox_ppu_decoder->Enable(2, false); // TODO
radiobox_pad_helper spu_decoder_modes({ "Core", "SPU Decoder" });
rbox_spu_decoder = new wxRadioBox(p_core, wxID_ANY, "SPU Decoder", wxDefaultPosition, wxSize(-1, -1), spu_decoder_modes, 1);

View File

@ -114,6 +114,12 @@
<ClCompile Include="..\Utilities\Thread.cpp" />
<ClCompile Include="..\Utilities\version.cpp" />
<ClCompile Include="..\Utilities\VirtualMemory.cpp" />
<ClCompile Include="Emu\Cell\PPUTranslator.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Emu\CPU\CPUTranslator.cpp">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Emu\PSP2\ARMv7Module.cpp" />
<ClCompile Include="Emu\Cell\lv2\lv2.cpp" />
<ClCompile Include="Emu\Cell\lv2\sys_cond.cpp" />
@ -425,6 +431,8 @@
<ClInclude Include="Crypto\utils.h" />
<ClInclude Include="define_new_memleakdetect.h" />
<ClInclude Include="Emu\Cell\PPUAnalyser.h" />
<ClInclude Include="Emu\Cell\PPUTranslator.h" />
<ClInclude Include="Emu\CPU\CPUTranslator.h" />
<ClInclude Include="Emu\IPC.h" />
<ClInclude Include="Emu\PSP2\ARMv7Callback.h" />
<ClInclude Include="Emu\PSP2\ARMv7DisAsm.h" />

View File

@ -641,6 +641,12 @@
<ClCompile Include="Emu\Cell\PPUModule.cpp">
<Filter>Emu\Cell</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\PPUTranslator.cpp">
<Filter>Emu\Cell</Filter>
</ClCompile>
<ClCompile Include="Emu\CPU\CPUTranslator.cpp">
<Filter>Emu\CPU</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\Modules\sys_libc_.cpp">
<Filter>Emu\Cell\Modules</Filter>
</ClCompile>
@ -1399,6 +1405,12 @@
<ClInclude Include="Emu\Cell\PPUAnalyser.h">
<Filter>Emu\Cell</Filter>
</ClInclude>
<ClInclude Include="Emu\Cell\PPUTranslator.h">
<Filter>Emu\Cell</Filter>
</ClInclude>
<ClInclude Include="Emu\CPU\CPUTranslator.h">
<Filter>Emu\CPU</Filter>
</ClInclude>
<ClInclude Include="..\3rdparty\stblib\stb_image.h">
<Filter>Header Files</Filter>
</ClInclude>

View File

@ -34,11 +34,10 @@
#include "Emu/Audio/Null/NullAudioThread.h"
#include "Emu/Audio/AL/OpenALThread.h"
#ifdef _MSC_VER
#include "Emu/RSX/VK/VKGSRender.h"
#include "Emu/RSX/D3D12/D3D12GSRender.h"
#endif
#ifdef _WIN32
#include "Emu/RSX/VK/VKGSRender.h"
#include "Emu/Audio/XAudio2/XAudio2Thread.h"
#include <wx/msw/wrapwin.h>
#endif
@ -97,6 +96,8 @@ cfg::map_entry<std::function<std::shared_ptr<GSRender>()>> g_cfg_gs_render(cfg::
{ "OpenGL", PURE_EXPR(std::make_shared<GLGSRender>()) },
#ifdef _MSC_VER
{ "D3D12", PURE_EXPR(std::make_shared<D3D12GSRender>()) },
#endif
#ifdef _WIN32
{ "Vulkan", PURE_EXPR(std::make_shared<VKGSRender>()) },
#endif
});

View File

@ -8,9 +8,9 @@
#define NOMINMAX
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif
//#ifndef __STDC_CONSTANT_MACROS
//#define __STDC_CONSTANT_MACROS
//#endif
#if defined(MSVC_CRT_MEMLEAK_DETECTION) && defined(_DEBUG) && !defined(DBG_NEW)
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )

View File

@ -10,7 +10,7 @@
<Lib>
<AdditionalLibraryDirectories Condition="'$(Configuration)'=='Debug - LLVM'">..\llvm_build\Debug\lib</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories Condition="'$(Configuration)'=='Release - LLVM'">..\llvm_build\Release\lib</AdditionalLibraryDirectories>
<AdditionalDependencies>LLVMMCJIT.lib;LLVMRuntimeDyld.lib;LLVMVectorize.lib;LLVMX86CodeGen.lib;LLVMX86Disassembler.lib;LLVMExecutionEngine.lib;LLVMAsmPrinter.lib;LLVMSelectionDAG.lib;LLVMCodeGen.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMTransformUtils.lib;LLVMipa.lib;LLVMAnalysis.lib;LLVMTarget.lib;LLVMX86Desc.lib;LLVMX86AsmPrinter.lib;LLVMObject.lib;LLVMMCParser.lib;LLVMBitReader.lib;LLVMCore.lib;LLVMX86Utils.lib;LLVMMC.lib;LLVMX86Info.lib;LLVMSupport.lib;LLVMMCDisassembler.lib</AdditionalDependencies>
<AdditionalDependencies>LLVMProfileData.lib;LLVMDebugInfoCodeView.lib;LLVMInstrumentation.lib;LLVMMCJIT.lib;LLVMRuntimeDyld.lib;LLVMVectorize.lib;LLVMX86CodeGen.lib;LLVMX86Disassembler.lib;LLVMExecutionEngine.lib;LLVMAsmPrinter.lib;LLVMSelectionDAG.lib;LLVMCodeGen.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMTransformUtils.lib;LLVMAnalysis.lib;LLVMTarget.lib;LLVMX86Desc.lib;LLVMX86AsmPrinter.lib;LLVMObject.lib;LLVMMCParser.lib;LLVMBitReader.lib;LLVMCore.lib;LLVMX86Utils.lib;LLVMMC.lib;LLVMX86Info.lib;LLVMSupport.lib;LLVMMCDisassembler.lib;LLVMipo.lib</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemGroup />