This commit is contained in:
Elad 2026-07-09 11:22:35 +00:00 committed by GitHub
commit 2c39c6409d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 186 additions and 60 deletions

View File

@ -2337,6 +2337,7 @@ void thread_base::start()
ensure(::ResumeThread(reinterpret_cast<HANDLE>(+m_thread)) != static_cast<DWORD>(-1));
#elif defined(__APPLE__)
pthread_attr_t attrs;
pthread_t thread_id{};
struct sched_param sp;
memset(&sp, 0, sizeof(struct sched_param));
sp.sched_priority=99;
@ -2346,20 +2347,43 @@ void thread_base::start()
pthread_attr_set_qos_class_np(&attrs, QOS_CLASS_USER_INTERACTIVE, 0);
pthread_attr_setschedpolicy(&attrs, SCHED_RR);
pthread_attr_setschedparam(&attrs, &sp);
ensure(pthread_create(reinterpret_cast<pthread_t*>(&m_thread.raw()), &attrs, entry_point, this) == 0);
ensure(pthread_create(&thread_id, &attrs, entry_point, this) == 0);
#else
ensure(pthread_create(reinterpret_cast<pthread_t*>(&m_thread.raw()), nullptr, entry_point, this) == 0);
pthread_t thread_id{};
ensure(pthread_create(&thread_id, nullptr, entry_point, this) == 0);
#endif
#ifndef _WIN32
// Update m_thread atomically
u64 dest_id = 0;
std::memcpy(&dest_id, &thread_id, sizeof(thread_id));
if (!m_thread && !m_thread.compare_and_swap_test(0, dest_id))
{
ensure(m_thread == dest_id);
}
#endif
}
void thread_base::initialize(void (*error_cb)())
{
#ifndef _WIN32
#ifdef ANDROID
m_thread.release(pthread_self());
#ifdef __APPLE__
while (!m_thread)
{
busy_wait();
}
[[maybe_unused]] u64 new_tid = 0;
#elif defined(ANDROID)
const u64 new_tid = pthread_self();
#else
m_thread.release(reinterpret_cast<u64>(pthread_self()));
const u64 new_tid = reinterpret_cast<u64>(pthread_self());
#endif
if (!m_thread && !m_thread.compare_and_swap_test(0, new_tid))
{
ensure(m_thread == new_tid);
}
#endif
// Initialize TLS variables
@ -3047,7 +3071,9 @@ void thread_ctrl::set_name(std::string name)
return false;
}).second)
{
#ifndef __APPLE__
utils::trap();
#endif
}
}
@ -3558,15 +3584,26 @@ std::pair<void*, usz> thread_ctrl::get_thread_stack()
u64 thread_ctrl::get_tid()
{
#ifdef _WIN32
return GetCurrentThreadId();
#elif defined(ANDROID)
return static_cast<u64>(pthread_self());
#elif defined(__linux__)
return syscall(SYS_gettid);
#else
return reinterpret_cast<u64>(pthread_self());
#endif
static thread_local u64 s_tls_tid = []() -> u64
{
#ifdef _WIN32
return GetCurrentThreadId();
#elif defined(ANDROID)
return pthread_gettid_np(pthread_self());
#elif defined(__linux__)
return syscall(SYS_gettid);
#elif defined(__APPLE__)
u64 tid{};
pthread_threadid_np(nullptr, &tid);
return tid;
#elif defined(__FreeBSD__)
return pthread_getthreadid_np();
#else
return static_cast<u64>(pthread_self());
#endif
}();
return s_tls_tid;
}
bool thread_ctrl::is_main()

View File

@ -1185,9 +1185,11 @@ static void ppu_check_patch_spu_images(const ppu_module<lv2_obj>& mod, const ppu
ensure(data.size() <= pos && index <= data.size());
const auto data_to_search = data.substr(index, pos - index);
for (std::string_view value : values)
{
if (usz pos0 = data.substr(index, pos - index).find(value); pos0 != umax && pos0 + index < pos)
if (usz pos0 = data_to_search.find(value); pos0 != umax && pos0 + index < pos)
{
pos = static_cast<u32>(pos0 + index);
}
@ -1211,12 +1213,22 @@ static void ppu_check_patch_spu_images(const ppu_module<lv2_obj>& mod, const ppu
{
const u32 old_prefix_addr = prefix_addr;
auto search_guid_pattern = [&](u32 index, std::string_view data_span, s32 advance_index, u32 lower_bound, u32 uppper_bound) -> u32
auto search_guid_pattern = [&](u32 index, std::string_view data_span, s32 advance_index, u32 lower_bound, u32 upper_bound) -> u32
{
for (u32 search = index & -16, tries = 16 * 64; tries && search >= lower_bound && search < uppper_bound; tries = tries - 1, search = advance_index < 0 ? utils::sub_saturate<u32>(search, 0 - advance_index) : search + advance_index)
ensure(upper_bound <= data_span.size());
ensure(lower_bound <= data_span.size());
ensure(lower_bound <= upper_bound);
for (u32 search = index & -16, tries = 16 * 64; tries && search >= lower_bound && search < (upper_bound & -16); tries = tries - 1, search = advance_index < 0 ? utils::sub_saturate<u32>(search, 0 - advance_index) : search + advance_index)
{
if (seg_view[search] != 0x42 && seg_view[search] != 0x43)
{
if (search == 0 && advance_index <= 0)
{
// Fast early-out
break;
}
continue;
}
@ -1260,7 +1272,7 @@ static void ppu_check_patch_spu_images(const ppu_module<lv2_obj>& mod, const ppu
{
const u32 instruction = std::min<u32>(search_guid_pattern(addr_last, ls_segment, +16, 0, ::size32(ls_segment)), find_first_of_multiple(ls_segment, prefixes, addr_last));
if (instruction != umax && std::memcmp(ls_segment.data() + instruction, "\x24\0\x40\x80", 4) == 0)
if (instruction < ls_segment.size() && std::memcmp(ls_segment.data() + instruction, "\x24\0\x40\x80", 4) == 0)
{
if (instruction % 4 != prefix_addr % 4)
{

View File

@ -9987,6 +9987,11 @@ std::array<reg_state_t, s_reg_max>& block_reg_info::evaluate_start_state(const s
void spu_recompiler_base::add_pattern(inst_attr attr, u32 start, u64 info, std::shared_ptr<void> info_ptr)
{
if (m_inst_attrs[start / 4] != inst_attr::none)
{
fmt::throw_exception("SPU Pattern is occupied! (start=0x%x)", start);
}
m_patterns[start] = pattern_info{info, info_ptr};
m_inst_attrs[start / 4] = attr;
}

View File

@ -210,7 +210,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
function_info* m_finfo = nullptr;
// Reduced Loop Pattern information (if available)
reduced_loop_t* m_reduced_loop_info = nullptr;
std::shared_ptr<reduced_loop_t> m_reduced_loop_info;
// All blocks in the current function chunk
std::unordered_map<u32, block_info, value_hash<u32, 2>> m_blocks;
@ -2689,7 +2689,7 @@ public:
}
const bool is_reduced_loop = m_inst_attrs[(baddr - start) / 4] == inst_attr::reduced_loop;
m_reduced_loop_info = is_reduced_loop ? std::static_pointer_cast<reduced_loop_t>(ensure(m_patterns.at(baddr - start).info_ptr)).get() : nullptr;
m_reduced_loop_info = is_reduced_loop ? std::static_pointer_cast<reduced_loop_t>(ensure(m_patterns.at(baddr - start).info_ptr)) : nullptr;
BasicBlock* block_optimization_phi_parent = nullptr;
const auto block_optimization_inner = is_reduced_loop ? BasicBlock::Create(m_context, fmt::format("b-loop-it-0x%x", m_pos), m_function) : nullptr;
@ -2981,6 +2981,11 @@ public:
condition = m_ir->CreateAnd(cond_verify, condition);
}
}
else
{
// Check spu_thread::state
condition = m_ir->CreateAnd(m_ir->CreateICmpEQ(spu_context_attr(m_ir->CreateLoad(get_type<u32>(), spu_ptr(&spu_thread::state), true)), m_ir->getInt32(0)), condition);
}
m_ir->CreateCondBr(condition, optimization_block, block_optimization_next);
};
@ -7808,7 +7813,7 @@ public:
value_t<f32[4]> clamp_smax(value_t<f32[4]> v, u32 gpr = s_reg_max)
{
if (m_reduced_loop_info && gpr < s_reg_max && m_reduced_loop_info->is_gpr_not_NaN_hint(gpr))
if (gpr < s_reg_max && m_reduced_loop_info && m_reduced_loop_info->is_gpr_not_NaN_hint(gpr))
{
return v;
}
@ -9288,6 +9293,11 @@ public:
return byteswap(data);
}
static constexpr u64 make_negative_LS_offset(u32 original)
{
return original | ~u64{SPU_LS_SIZE - 1};
}
void STQX(spu_opcode_t op)
{
const auto a = get_vr(op.ra);
@ -9297,17 +9307,20 @@ public:
{
if (auto [ok, data] = get_const_vector(pair.first.value, m_pos); ok)
{
data._u32[3] %= SPU_LS_SIZE;
// "sign extend" offset addend
// Discourage the use of multiple addresses to refer to the same block of memory (due to memory mirrors use)
// Which may confuse LLVM's optimization
const u64 addend = (data._u32[3] >= SPU_LS_SIZE) ? make_negative_LS_offset(data._u32[3]) : data._u32[3];
if (const u32 remainder = data._u32[3] % 0x10; remainder == 0)
{
value_t<u64> addr = eval(splat<u64>(data._u32[3]) + zext<u64>(extract(pair.second, 3) & 0x3fff0));
value_t<u64> addr = eval(splat<u64>(addend) + zext<u64>(extract(pair.second, 3) & 0x3fff0));
make_store_ls(addr, get_vr<u8[16]>(op.rt));
return;
}
else
{
value_t<u64> addr = eval(splat<u64>(data._u32[3] - remainder) + zext<u64>((extract(pair.second, 3) + remainder) & 0x3fff0));
value_t<u64> addr = eval(splat<u64>(addend - remainder) + zext<u64>((extract(pair.second, 3) + remainder) & 0x3fff0));
make_store_ls(addr, get_vr<u8[16]>(op.rt));
return;
}
@ -9327,17 +9340,20 @@ public:
{
if (auto [ok, data] = get_const_vector(pair.first.value, m_pos); ok)
{
data._u32[3] %= SPU_LS_SIZE;
// "sign extend" offset addend
// Discourage the use of multiple addresses to refer to the same block of memory
// Which may confuse LLVM's optimization
const u64 addend = (data._u32[3] >= SPU_LS_SIZE) ? make_negative_LS_offset(data._u32[3]) : data._u32[3];
if (const u32 remainder = data._u32[3] % 0x10; remainder == 0)
{
value_t<u64> addr = eval(splat<u64>(data._u32[3]) + zext<u64>(extract(pair.second, 3) & 0x3fff0));
value_t<u64> addr = eval(splat<u64>(addend) + zext<u64>(extract(pair.second, 3) & 0x3fff0));
set_vr(op.rt, make_load_ls(addr));
return;
}
else
{
value_t<u64> addr = eval(splat<u64>(data._u32[3] - remainder) + zext<u64>((extract(pair.second, 3) + remainder) & 0x3fff0));
value_t<u64> addr = eval(splat<u64>(addend - remainder) + zext<u64>((extract(pair.second, 3) + remainder) & 0x3fff0));
set_vr(op.rt, make_load_ls(addr));
return;
}
@ -9398,18 +9414,22 @@ public:
if (auto [ok, x, y] = match_expr(a, match<u32[4]>() + match<u32[4]>()); ok)
{
if (auto [ok1, data] = get_const_vector(x.value, m_pos + 1); ok1 && data._u32[3] % 16 == 0)
for (auto pair : std::initializer_list<std::pair<llvm_match_t<u32[4]>, llvm_match_t<u32[4]>>>{{x, y}, {y, x}})
{
value_t<u64> addr = eval(zext<u64>(extract(y, 3) & 0x3fff0) + ((get_imm<u64>(op.si10) << 4) + splat<u64>(data._u32[3] & 0x3fff0)));
make_store_ls(addr, get_vr<u8[16]>(op.rt));
return;
}
if (auto [ok, data] = get_const_vector(pair.first.value, m_pos); ok)
{
// "sign extend" offset addend
// Discourage the use of multiple addresses to refer to the same block of memory
// Which may confuse LLVM's optimization
const u64 addend = (data._u32[3] >= SPU_LS_SIZE) ? make_negative_LS_offset(data._u32[3]) : data._u32[3];
if (auto [ok2, data] = get_const_vector(y.value, m_pos + 2); ok2 && data._u32[3] % 16 == 0)
{
value_t<u64> addr = eval(zext<u64>(extract(x, 3) & 0x3fff0) + ((get_imm<u64>(op.si10) << 4) + splat<u64>(data._u32[3] & 0x3fff0)));
make_store_ls(addr, get_vr<u8[16]>(op.rt));
return;
if (const u32 remainder = data._u32[3] % 0x10; remainder == 0)
{
value_t<u64> addr = eval(zext<u64>(extract(pair.second, 3) & 0x3fff0) + ((get_imm<u64>(op.si10) << 4) + splat<u64>(addend)));
make_store_ls(addr, get_vr<u8[16]>(op.rt));
return;
}
}
}
}
@ -9421,20 +9441,22 @@ public:
{
const auto a = get_vr(op.ra);
if (auto [ok, x1, y1] = match_expr(a, match<u32[4]>() + match<u32[4]>()); ok)
if (auto [ok, x, y] = match_expr(a, match<u32[4]>() + match<u32[4]>()); ok)
{
if (auto [ok1, data] = get_const_vector(x1.value, m_pos + 1); ok1 && data._u32[3] % 16 == 0)
for (auto pair : std::initializer_list<std::pair<llvm_match_t<u32[4]>, llvm_match_t<u32[4]>>>{{x, y}, {y, x}})
{
value_t<u64> addr = eval(zext<u64>(extract(y1, 3) & 0x3fff0) + ((get_imm<u64>(op.si10) << 4) + splat<u64>(data._u32[3] & 0x3fff0)));
set_vr(op.rt, make_load_ls(addr));
return;
}
if (auto [ok, data] = get_const_vector(pair.first.value, m_pos); ok)
{
// "sign extend" offset addend
const u64 addend = (data._u32[3] >= SPU_LS_SIZE) ? data._u32[3] | ~u64{SPU_LS_SIZE - 1} : data._u32[3];
if (auto [ok2, data] = get_const_vector(y1.value, m_pos + 2); ok2 && data._u32[3] % 16 == 0)
{
value_t<u64> addr = eval(zext<u64>(extract(x1, 3) & 0x3fff0) + ((get_imm<u64>(op.si10) << 4) + splat<u64>(data._u32[3] & 0x3fff0)));
set_vr(op.rt, make_load_ls(addr));
return;
if (const u32 remainder = data._u32[3] % 0x10; remainder == 0)
{
value_t<u64> addr = eval(zext<u64>(extract(pair.second, 3) & 0x3fff0) + ((get_imm<u64>(op.si10) << 4) + splat<u64>(addend)));
set_vr(op.rt, make_load_ls(addr));
return;
}
}
}
}

View File

@ -685,7 +685,11 @@ public:
bool is_gpr_not_NaN_hint(u32 i) const noexcept
{
#ifdef ARCH_X64
return gpr_not_nans.test(i);
#else
return false;
#endif
}
origin_t get_reg(u32 reg_val) noexcept

View File

@ -1733,21 +1733,48 @@ void spu_thread::cleanup()
static_cast<named_thread<spu_thread>&>(*this) = thread_state::finished;
}
enum : s64
{
SIGNED_LS_SIZE = SPU_LS_SIZE
};
spu_thread::~spu_thread()
{
// Unmap LS and its mirrors
shm->unmap(ls + SPU_LS_SIZE);
shm->unmap(ls);
shm->unmap(ls - SPU_LS_SIZE);
utils::memory_release(ls - SPU_LS_SIZE * 2, SPU_LS_SIZE * 5);
for (s64 ls_offs = 0 - SIGNED_LS_SIZE * 2; ls_offs <= SIGNED_LS_SIZE * 2; ls_offs += SIGNED_LS_SIZE)
{
shm->unmap(ls + ls_offs);
}
utils::memory_release(ls - SIGNED_LS_SIZE * 3, SIGNED_LS_SIZE * 7);
}
u8* spu_thread::map_ls(utils::shm& shm, void* ptr)
{
const auto ls = ptr ? static_cast<u8*>(ptr) : static_cast<u8*>(ensure(utils::memory_reserve(SIGNED_LS_SIZE * 7, nullptr, true))) + SIGNED_LS_SIZE * 3;
vm::writer_lock mlock;
const auto ls = ptr ? static_cast<u8*>(ptr) : static_cast<u8*>(ensure(utils::memory_reserve(SPU_LS_SIZE * 5, nullptr, true))) + SPU_LS_SIZE * 2;
ensure(shm.map_critical(ls - SPU_LS_SIZE).first && shm.map_critical(ls).first && shm.map_critical(ls + SPU_LS_SIZE).first);
for (s64 ls_offs = 0 - SIGNED_LS_SIZE * 2; ls_offs <= SIGNED_LS_SIZE * 2; ls_offs += SIGNED_LS_SIZE)
{
const auto [ptr_ret, str] = shm.map_critical(ls + ls_offs);
if (!ptr_ret)
{
fmt::throw_exception("spu_thread::map_ls() failed: map_critical returned error (error=%s) [ls_offs=0x%x]", str, ls_offs);
}
if (ptr_ret != ls + ls_offs)
{
fmt::throw_exception("spu_thread::map_ls() failed: map_critical returned a different address: 0x%llx vs 0x%llx (error=%s) [ls_offs=0x%x]", ptr_ret, ls + ls_offs, str, ls_offs);
}
if (!str.empty())
{
fmt::throw_exception("spu_thread::map_ls() failed: map_critical returned unexpected error (error=%s) [ls_offs=0x%x]", str, ls_offs);
}
}
return ls;
}
@ -1792,7 +1819,7 @@ spu_thread::spu_thread(lv2_spu_group* group, u32 index, std::string_view name, u
, index(index)
, thread_type(group ? spu_type::threaded : is_isolated ? spu_type::isolated : spu_type::raw)
, shm(std::make_shared<utils::shm>(SPU_LS_SIZE))
, ls(static_cast<u8*>(utils::memory_reserve(SPU_LS_SIZE * 5, nullptr, true)) + SPU_LS_SIZE * 2)
, ls(static_cast<u8*>(utils::memory_reserve(SPU_LS_SIZE * 7, nullptr, true)) + SPU_LS_SIZE * 3)
, option(option)
, lv2_id(lv2_id)
, spu_tname(make_single<std::string>(name))

View File

@ -186,9 +186,17 @@ namespace
#ifdef _WIN32
tid = GetCurrentThreadId();
#elif defined(ANDROID)
tid = pthread_self();
tid = pthread_gettid_np(pthread_self());
#elif defined(__linux__)
tid = syscall(SYS_gettid);
#elif defined(__APPLE__)
u64 tid_temp{};
pthread_threadid_np(nullptr, &tid_temp);
tid = tid_temp; // Use a temporary for extra safety
#elif defined(__FreeBSD__)
tid = pthread_getthreadid_np();
#else
tid = reinterpret_cast<u64>(pthread_self());
tid = pthread_self();
#endif
#ifdef USE_STD

View File

@ -909,9 +909,14 @@ namespace utils
#ifdef _WIN32
::MEMORY_BASIC_INFORMATION mem{};
if (!::VirtualQuery(target, &mem, sizeof(mem)) || mem.State != MEM_RESERVE)
if (!::VirtualQuery(target, &mem, sizeof(mem)))
{
return {nullptr, fmt::format("VirtualQuery() Unexpceted memory info: state=0x%x, %s", mem.State, std::as_bytes(std::span(&mem, 1)))};
return {nullptr, fmt::format("VirtualQuery() Failed with %s", fmt::win_error{GetLastError(), nullptr})};
}
if (mem.State != MEM_RESERVE)
{
return {nullptr, fmt::format("VirtualQuery() reported unexpected memory info: state=0x%x, %s", mem.State, std::as_bytes(std::span(&mem, 1)))};
}
const auto base = static_cast<u8*>(mem.AllocationBase);
@ -977,8 +982,14 @@ namespace utils
return {nullptr, "VirtualAlloc() failed to reserve allocation end"};
}
#endif
const auto mapped_addr = this->map(target, prot, cow);
return {this->map(target, prot, cow), "Failed to map"};
if (!mapped_addr)
{
return {nullptr, "Failed to map"};
}
return {mapped_addr, {}};
}
u8* shm::map_self(protection prot)