Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and improve savestates #14953

Merged
merged 9 commits into from
Dec 29, 2023
3 changes: 2 additions & 1 deletion Utilities/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2641,9 +2641,10 @@ void thread_base::exec()

sig_log.fatal("Thread terminated due to fatal error: %s", reason);

logs::listener::sync_all();

if (IsDebuggerPresent())
{
logs::listener::sync_all();
utils::trap();
}

Expand Down
13 changes: 3 additions & 10 deletions rpcs3/Emu/Cell/Modules/cellAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -678,9 +678,10 @@ void cell_audio_thread::reset_counters()

cell_audio_thread::cell_audio_thread()
{
// Initialize dependencies
g_fxo->need<audio_out_configuration>();
}

void cell_audio_thread::operator()()
{
// Initialize loop variables (regardless of provider in order to initialize timestamps)
reset_counters();

Expand All @@ -694,14 +695,6 @@ cell_audio_thread::cell_audio_thread()

// Allocate ringbuffer
ringbuffer.reset(new audio_ringbuffer(cfg));
}

void cell_audio_thread::operator()()
{
if (cfg.raw.provider != audio_provider::cell_audio)
{
return;
}

thread_ctrl::scoped_priority high_prio(+1);

Expand Down
2 changes: 1 addition & 1 deletion rpcs3/Emu/Cell/PPUModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3019,7 +3019,7 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex

if (!ar && !virtual_load)
{
idm::import_existing<lv2_obj, lv2_overlay>(ovlm);
ensure(idm::import_existing<lv2_obj, lv2_overlay>(ovlm));
try_spawn_ppu_if_exclusive_program(*ovlm);
}

Expand Down
61 changes: 45 additions & 16 deletions rpcs3/Emu/Cell/SPURecompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4521,6 +4521,18 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
std::array<usz, s_reg_max> store_context_last_id = fill_array<usz>(0); // Protects against illegal forward ordering
std::array<usz, s_reg_max> store_context_first_id = fill_array<usz>(usz{umax}); // Protects against illegal past store elimination (backwards ordering is not implemented)
std::array<usz, s_reg_max> store_context_ctr = fill_array<usz>(1); // Store barrier cointer

bool does_gpr_barrier_proceed_last_store(u32 i) const noexcept
{
const usz counter = store_context_ctr[i];
return counter != 1 && counter > store_context_last_id[i];
}

bool does_gpr_barrier_preceed_first_store(u32 i) const noexcept
{
const usz counter = store_context_ctr[i];
return counter != 1 && counter < store_context_first_id[i];
}
};

struct function_info
Expand Down Expand Up @@ -6019,7 +6031,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
for (u32 i = 0; i < 128; i++)
{
// Check if the store is beyond the last barrier
if (auto& bs = bqbi->store[i]; bs && bqbi->store_context_last_id[i] == bqbi->store_context_ctr[i])
if (auto& bs = bqbi->store[i]; bs && !bqbi->does_gpr_barrier_proceed_last_store(i))
{
for (auto& [a, b] : m_blocks)
{
Expand Down Expand Up @@ -6080,7 +6092,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
llvm::SetVector<llvm::BasicBlock*> work_list;
std::unordered_map<llvm::BasicBlock*, bool> worked_on;

if (std::count(killers.begin(), killers.end(), common_pdom) == 0)
if (!common_pdom || std::count(killers.begin(), killers.end(), common_pdom) == 0)
{
if (common_pdom)
{
Expand All @@ -6099,14 +6111,15 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
}
}

// bool flag indicates the presence of a memory barrier before the killer store
std::vector<std::pair<llvm::BasicBlock*, bool>> work2_list;

for (usz wi = 0; wi < work_list.size(); wi++)
{
auto* cur = work_list[wi];
if (std::count(killers.begin(), killers.end(), cur))
{
work2_list.emplace_back(cur, bb_to_info[cur] && bb_to_info[cur]->store_context_first_id[i] > 1);
work2_list.emplace_back(cur, bb_to_info[cur] && bb_to_info[cur]->does_gpr_barrier_preceed_first_store(i));
continue;
}

Expand Down Expand Up @@ -6137,29 +6150,40 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
worked_on[work2_list[wi].first] = true;
}

// Need to treat tails differently: do not require checking barrier (checked before in a suitable manner)
const usz work_list_tail_blocks_max_index = work2_list.size();

for (usz wi = 0; wi < work2_list.size(); wi++)
{
auto [cur, found_user] = work2_list[wi];

if (cur == bs->getParent())
ensure(cur != bs->getParent());

if (!found_user && wi >= work_list_tail_blocks_max_index)
{
if (found_user)
if (auto info = bb_to_info[cur])
{
// Reset: store is being used and preserved by ensure_gpr_stores()
killers.clear();
break;
if (info->store_context_ctr[i] != 1)
{
found_user = true;
}
}

continue;
}

if (!found_user && bb_to_info[cur] && bb_to_info[cur]->store_context_last_id[i])
{
found_user = true;
}

for (auto* p : llvm::predecessors(cur))
{
if (p == bs->getParent())
{
if (found_user)
{
// Reset: store is being used and preserved by ensure_gpr_stores()
killers.clear();
break;
}

continue;
}

if (!worked_on[p])
{
worked_on[p] = true;
Expand All @@ -6171,6 +6195,11 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
work2_list.push_back(std::make_pair(p, true));
}
}

if (killers.empty())
{
break;
}
}

// Finally erase the dead store
Expand All @@ -6197,7 +6226,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
for (u32 i = 0; i < 128; i++)
{
// If store isn't erased, try to sink it
if (auto& bs = block_q[bi]->store[i]; bs && block_q[bi]->bb->targets.size() > 1 && block_q[bi]->store_context_last_id[i] == block_q[bi]->store_context_ctr[i])
if (auto& bs = block_q[bi]->store[i]; bs && block_q[bi]->bb->targets.size() > 1 && !block_q[bi]->does_gpr_barrier_proceed_last_store(i))
{
std::map<u32, block_info*, std::greater<>> sucs;

Expand Down
21 changes: 19 additions & 2 deletions rpcs3/Emu/Cell/SPUThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2022,6 +2022,20 @@ spu_thread::spu_thread(utils::serial& ar, lv2_spu_group* group)

serialize_common(ar);

raddr = ::narrow<u32>(ar.pop<u64>());

if (raddr)
{
// Acquire reservation
if (!vm::check_addr(raddr))
{
fmt::throw_exception("SPU Serialization: Reservation address is not accessible! (addr=0x%x)", raddr);
}

rtime = vm::reservation_acquire(raddr);
mov_rdata(rdata, *vm::get_super_ptr<spu_rdata_t>(raddr));
}

status_npc.raw().npc = pc | u8{interrupts_enabled};

if (get_type() == spu_type::threaded)
Expand Down Expand Up @@ -2058,8 +2072,8 @@ void spu_thread::save(utils::serial& ar)

if (raddr)
{
// Lose reservation at savestate load with an event if one existed at savestate save
set_events(SPU_EVENT_LR);
// Last check for reservation-lost event
get_events(SPU_EVENT_LR);
}

ar(index);
Expand All @@ -2073,6 +2087,9 @@ void spu_thread::save(utils::serial& ar)

serialize_common(ar);

// Let's save it as u64 for future proofing
ar(u64{raddr});

if (get_type() == spu_type::threaded)
{
for (const auto& [key, q] : spuq)
Expand Down
3 changes: 0 additions & 3 deletions rpcs3/Emu/Cell/lv2/sys_rsxaudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1306,9 +1306,6 @@ namespace audio

rsxaudio_backend_thread::rsxaudio_backend_thread()
{
// Initialize dependencies
g_fxo->need<audio_out_configuration>();

new_emu_cfg = get_emu_cfg();

const u64 new_vol = g_cfg.audio.volume;
Expand Down
2 changes: 2 additions & 0 deletions rpcs3/Emu/Cell/lv2/sys_rsxaudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ class rsxaudio_backend_thread

static constexpr auto thread_name = "RsxAudio Backend Thread"sv;

SAVESTATE_INIT_POS(8.91); // Depends on audio_out_configuration

private:

struct emu_audio_cfg
Expand Down
4 changes: 2 additions & 2 deletions rpcs3/Emu/Cell/lv2/sys_spu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ lv2_spu_group::lv2_spu_group(utils::serial& ar) noexcept
if (ar.pop<bool>())
{
ar(id_manager::g_id);
thread = std::make_shared<named_thread<spu_thread>>(ar, this);
idm::import_existing<named_thread<spu_thread>>(thread, idm::last_id());
thread = std::make_shared<named_thread<spu_thread>>(stx::launch_retainer{}, ar, this);
ensure(idm::import_existing<named_thread<spu_thread>>(thread, idm::last_id()));
running += !thread->stop_flag_removal_protection;
}
}
Expand Down
7 changes: 5 additions & 2 deletions rpcs3/Emu/IdManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ std::vector<std::pair<u128, id_manager::typeinfo>>& id_manager::get_typeinfo_map

idm::map_data* idm::allocate_id(std::vector<map_data>& vec, u32 type_id, u32 dst_id, u32 base, u32 step, u32 count, bool uses_lowest_id, std::pair<u32, u32> invl_range)
{
if (const u32 index = id_manager::get_index(dst_id, base, step, count, invl_range); index < count)
if (dst_id != (base ? 0 : u32{umax}))
{
// Fixed position construction
ensure(index < vec.size());
const u32 index = id_manager::get_index(dst_id, base, step, count, invl_range);
ensure(index < count);

vec.resize(std::max<usz>(vec.size(), index + 1));

if (vec[index].second)
{
Expand Down
62 changes: 43 additions & 19 deletions rpcs3/Emu/IdManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ constexpr auto* g_fxo = &g_fixed_typemap;

enum class thread_state : u32;

extern u16 serial_breathe_and_tag(utils::serial& ar, std::string_view name, bool tag_bit);

// Helper namespace
namespace id_manager
{
Expand Down Expand Up @@ -100,13 +102,26 @@ namespace id_manager
template <typename T, typename = void>
struct id_traits_load_func
{
static constexpr std::shared_ptr<void>(*load)(utils::serial&) = [](utils::serial& ar) -> std::shared_ptr<void> { return std::make_shared<T>(stx::exact_t<utils::serial&>(ar)); };
static constexpr std::shared_ptr<void>(*load)(utils::serial&) = [](utils::serial& ar) -> std::shared_ptr<void>
{
if constexpr (std::is_constructible_v<T, stx::exact_t<const stx::launch_retainer&>, stx::exact_t<utils::serial&>>)
{
return std::make_shared<T>(stx::launch_retainer{}, stx::exact_t<utils::serial&>(ar));
}
else
{
return std::make_shared<T>(stx::exact_t<utils::serial&>(ar));
}
};
};

template <typename T>
struct id_traits_load_func<T, std::void_t<decltype(&T::load)>>
{
static constexpr std::shared_ptr<void>(*load)(utils::serial&) = [](utils::serial& ar) -> std::shared_ptr<void> { return T::load(stx::exact_t<utils::serial&>(ar)); };
static constexpr std::shared_ptr<void>(*load)(utils::serial&) = [](utils::serial& ar) -> std::shared_ptr<void>
{
return T::load(stx::exact_t<utils::serial&>(ar));
};
};

template <typename T, typename = void>
Expand Down Expand Up @@ -270,16 +285,22 @@ namespace id_manager
{
vec.resize(T::id_count);

u32 i = ar.pop<u32>();
usz highest = 0;

ensure(i <= T::id_count);

while (--i != umax)
while (true)
{
const u16 tag = serial_breathe_and_tag(ar, g_fxo->get_name<id_map<T>>(), false);

if (tag >> 15)
{
// End
break;
}

// ID, type hash
const u32 id = ar;
const u32 id = ar.pop<u32>();

const u128 type_init_pos = u128{u32{ar}} << 64 | std::bit_cast<u64>(T::savestate_init_pos);
const u128 type_init_pos = u128{ar.pop<u32>()} << 64 | std::bit_cast<u64>(T::savestate_init_pos);
const typeinfo* info = nullptr;

// Search load functions for the one of this type (see make_typeinfo() for explenation about key composition reasoning)
Expand All @@ -298,22 +319,21 @@ namespace id_manager
// Simulate construction semantics (idm::last_id() value)
g_id = id;

auto& obj = vec[get_index(id, info->base, info->step, info->count, info->invl_range)];
const usz object_index = get_index(id, info->base, info->step, info->count, info->invl_range);
auto& obj = ::at32(vec, object_index);
ensure(!obj.second);

highest = std::max<usz>(highest, object_index + 1);

obj.first = id_key(id, static_cast<u32>(static_cast<u64>(type_init_pos >> 64)));
obj.second = info->load(ar);
}

vec.resize(highest);
}

void save(utils::serial& ar) requires IdmSavable<T>
{
u32 obj_count = 0;
usz obj_count_offs = ar.pos;

// To be patched at the end of the function
ar(obj_count);

for (const auto& p : vec)
{
if (!p.second) continue;
Expand All @@ -333,19 +353,23 @@ namespace id_manager
// Save each object with needed information
if (info && info->savable(p.second.get()))
{
// Create a tag for each object
serial_breathe_and_tag(ar, g_fxo->get_name<id_map<T>>(), false);

ar(p.first.value(), p.first.type());
info->save(ar, p.second.get());
obj_count++;
}
}

// Patch object count
ar.patch_raw_data(obj_count_offs, &obj_count, sizeof(obj_count));
// End sequence with tag bit set
serial_breathe_and_tag(ar, g_fxo->get_name<id_map<T>>(), true);
}

id_map& operator=(thread_state state) noexcept requires (std::is_assignable_v<T&, thread_state>)
{
if (private_copy.empty())
private_copy.clear();

if (!vec.empty() || !private_copy.empty())
{
reader_lock lock(g_mutex);

Expand Down
Loading