SPU LLVM: make savestates unsavable inside the code

This commit is contained in:
Ivan Chikish 2023-04-16 19:36:54 +03:00 committed by Ivan
parent 8153e5338f
commit 39d17a94c6

View File

@ -3982,9 +3982,6 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
llvm::GlobalVariable* m_scale_float_to{}; llvm::GlobalVariable* m_scale_float_to{};
llvm::GlobalVariable* m_scale_to_float{}; llvm::GlobalVariable* m_scale_to_float{};
// Helper for check_state
llvm::GlobalVariable* m_fake_global1{};
// Function for check_state execution // Function for check_state execution
llvm::Function* m_test_state{}; llvm::Function* m_test_state{};
@ -4880,7 +4877,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
} }
// Call cpu_thread::check_state if necessary and return or continue (full check) // Call cpu_thread::check_state if necessary and return or continue (full check)
void check_state(u32 addr, bool may_be_unsafe_for_savestate = true) void check_state(u32 addr)
{ {
const auto pstate = spu_ptr<u32>(&spu_thread::state); const auto pstate = spu_ptr<u32>(&spu_thread::state);
const auto _body = llvm::BasicBlock::Create(m_context, "", m_function); const auto _body = llvm::BasicBlock::Create(m_context, "", m_function);
@ -4888,19 +4885,7 @@ class spu_llvm_recompiler : public spu_recompiler_base, public cpu_translator
m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_ir->CreateLoad(get_type<u32>(), pstate, true), m_ir->getInt32(0)), _body, check, m_md_likely); m_ir->CreateCondBr(m_ir->CreateICmpEQ(m_ir->CreateLoad(get_type<u32>(), pstate, true), m_ir->getInt32(0)), _body, check, m_md_likely);
m_ir->SetInsertPoint(check); m_ir->SetInsertPoint(check);
update_pc(addr); update_pc(addr);
m_ir->CreateCall(m_test_state, {m_thread});
if (may_be_unsafe_for_savestate && std::none_of(std::begin(m_block->phi), std::end(m_block->phi), FN(!!x)))
{
may_be_unsafe_for_savestate = false;
}
m_ir->CreateStore(m_ir->getInt1(may_be_unsafe_for_savestate), m_fake_global1);
if (may_be_unsafe_for_savestate)
{
m_ir->CreateStore(m_ir->getInt8(0), spu_ptr<u8>(&spu_thread::unsavable));
}
m_ir->CreateBr(_body); m_ir->CreateBr(_body);
m_ir->SetInsertPoint(_body); m_ir->SetInsertPoint(_body);
} }
@ -5013,9 +4998,6 @@ public:
IRBuilder<> irb(m_context); IRBuilder<> irb(m_context);
m_ir = &irb; m_ir = &irb;
// Helper for check_state. Used to not interfere with LICM pass.
m_fake_global1 = new llvm::GlobalVariable(*m_module, get_type<bool>(), false, llvm::GlobalValue::InternalLinkage, m_ir->getFalse());
// Add entry function (contains only state/code check) // Add entry function (contains only state/code check)
const auto main_func = llvm::cast<llvm::Function>(m_module->getOrInsertFunction(m_hash, get_ftype<void, u8*, u8*, u64>()).getCallee()); const auto main_func = llvm::cast<llvm::Function>(m_module->getOrInsertFunction(m_hash, get_ftype<void, u8*, u8*, u64>()).getCallee());
const auto main_arg2 = main_func->getArg(2); const auto main_arg2 = main_func->getArg(2);
@ -5033,6 +5015,7 @@ public:
// Emit state check // Emit state check
const auto pstate = spu_ptr<u32>(&spu_thread::state); const auto pstate = spu_ptr<u32>(&spu_thread::state);
m_ir->CreateStore(m_ir->getInt8(false), spu_ptr<u8>(&spu_thread::unsavable));
m_ir->CreateCondBr(m_ir->CreateICmpNE(m_ir->CreateLoad(get_type<u32>(), pstate), m_ir->getInt32(0)), label_stop, label_test, m_md_unlikely); m_ir->CreateCondBr(m_ir->CreateICmpNE(m_ir->CreateLoad(get_type<u32>(), pstate), m_ir->getInt32(0)), label_stop, label_test, m_md_unlikely);
// Emit code check // Emit code check
@ -5200,6 +5183,7 @@ public:
m_ir->SetInsertPoint(label_body); m_ir->SetInsertPoint(label_body);
const auto pbcount = spu_ptr<u64>(&spu_thread::block_counter); const auto pbcount = spu_ptr<u64>(&spu_thread::block_counter);
m_ir->CreateStore(m_ir->CreateAdd(m_ir->CreateLoad(get_type<u64>(), pbcount), m_ir->getInt64(check_iterations)), pbcount); m_ir->CreateStore(m_ir->CreateAdd(m_ir->CreateLoad(get_type<u64>(), pbcount), m_ir->getInt64(check_iterations)), pbcount);
m_ir->CreateStore(m_ir->getInt8(true), spu_ptr<u8>(&spu_thread::unsavable));
// Call the entry function chunk // Call the entry function chunk
const auto entry_chunk = add_function(m_pos); const auto entry_chunk = add_function(m_pos);
@ -5634,38 +5618,6 @@ public:
{ {
const auto f = func.second.fn ? func.second.fn : func.second.chunk; const auto f = func.second.fn ? func.second.fn : func.second.chunk;
pm.run(*f); pm.run(*f);
for (auto& bb : *f)
{
for (auto& i : bb)
{
// Replace fake store with spu_test_state call
if (auto si = dyn_cast<StoreInst>(&i); si && si->getOperand(1) == m_fake_global1)
{
m_ir->SetInsertPoint(si);
if (si->getOperand(0) == m_ir->getTrue())
{
m_ir->CreateStore(m_ir->getInt8(1), _ptr<u8>(f->getArg(0), ::offset32(&spu_thread::unsavable)));
}
CallInst* ci{};
if (si->getOperand(0) == m_ir->getTrue() || si->getOperand(0) == m_ir->getFalse())
{
ci = m_ir->CreateCall(m_test_state, {f->getArg(0)});
ci->setCallingConv(m_test_state->getCallingConv());
}
else
{
continue;
}
si->replaceAllUsesWith(ci);
si->eraseFromParent();
break;
}
}
}
} }
// Clear context (TODO) // Clear context (TODO)
@ -6321,11 +6273,6 @@ public:
return exec_rdch(_spu, SPU_RdEventStat); return exec_rdch(_spu, SPU_RdEventStat);
} }
void ensure_gpr_stores()
{
m_block->store.fill(nullptr);
}
llvm::Value* get_rdch(spu_opcode_t op, u32 off, bool atomic) llvm::Value* get_rdch(spu_opcode_t op, u32 off, bool atomic)
{ {
const auto ptr = _ptr<u64>(m_thread, off); const auto ptr = _ptr<u64>(m_thread, off);
@ -6382,7 +6329,6 @@ public:
case SPU_RdInMbox: case SPU_RdInMbox:
{ {
update_pc(); update_pc();
ensure_gpr_stores();
res.value = call("spu_read_in_mbox", &exec_read_in_mbox, m_thread); res.value = call("spu_read_in_mbox", &exec_read_in_mbox, m_thread);
break; break;
} }
@ -6398,13 +6344,11 @@ public:
} }
case SPU_RdSigNotify1: case SPU_RdSigNotify1:
{ {
ensure_gpr_stores();
res.value = get_rdch(op, ::offset32(&spu_thread::ch_snr1), true); res.value = get_rdch(op, ::offset32(&spu_thread::ch_snr1), true);
break; break;
} }
case SPU_RdSigNotify2: case SPU_RdSigNotify2:
{ {
ensure_gpr_stores();
res.value = get_rdch(op, ::offset32(&spu_thread::ch_snr2), true); res.value = get_rdch(op, ::offset32(&spu_thread::ch_snr2), true);
break; break;
} }
@ -6446,7 +6390,6 @@ public:
default: default:
{ {
update_pc(); update_pc();
ensure_gpr_stores();
res.value = call("spu_read_channel", &exec_rdch, m_thread, m_ir->getInt32(op.ra)); res.value = call("spu_read_channel", &exec_rdch, m_thread, m_ir->getInt32(op.ra));
break; break;
} }
@ -6792,7 +6735,6 @@ public:
case MFC_GETLB_CMD: case MFC_GETLB_CMD:
case MFC_GETLF_CMD: case MFC_GETLF_CMD:
{ {
ensure_gpr_stores();
[[fallthrough]]; [[fallthrough]];
} }
case MFC_SDCRZ_CMD: case MFC_SDCRZ_CMD:
@ -7081,7 +7023,6 @@ public:
m_ir->CreateCondBr(m_ir->CreateICmpNE(_old, _new), _mfc, next); m_ir->CreateCondBr(m_ir->CreateICmpNE(_old, _new), _mfc, next);
m_ir->SetInsertPoint(_mfc); m_ir->SetInsertPoint(_mfc);
update_pc(); update_pc();
ensure_gpr_stores();
call("spu_list_unstall", &exec_list_unstall, m_thread, eval(val & 0x1f).value); call("spu_list_unstall", &exec_list_unstall, m_thread, eval(val & 0x1f).value);
m_ir->CreateBr(next); m_ir->CreateBr(next);
m_ir->SetInsertPoint(next); m_ir->SetInsertPoint(next);
@ -7105,7 +7046,6 @@ public:
} }
update_pc(); update_pc();
ensure_gpr_stores();
call("spu_write_channel", &exec_wrch, m_thread, m_ir->getInt32(op.ra), val.value); call("spu_write_channel", &exec_wrch, m_thread, m_ir->getInt32(op.ra), val.value);
} }
@ -7126,7 +7066,6 @@ public:
{ {
m_block->block_end = m_ir->GetInsertBlock(); m_block->block_end = m_ir->GetInsertBlock();
update_pc(m_pos + 4); update_pc(m_pos + 4);
ensure_gpr_stores();
tail_chunk(m_dispatch); tail_chunk(m_dispatch);
} }
} }
@ -10444,7 +10383,7 @@ public:
{ {
// Can't afford external tail call in true functions // Can't afford external tail call in true functions
m_ir->CreateStore(m_ir->getInt32("BIJT"_u32), _ptr<u32>(m_memptr, 0xffdead20)); m_ir->CreateStore(m_ir->getInt32("BIJT"_u32), _ptr<u32>(m_memptr, 0xffdead20));
m_ir->CreateStore(m_ir->getFalse(), m_fake_global1); m_ir->CreateCall(m_test_state, {m_thread});
m_ir->CreateBr(sw->getDefaultDest()); m_ir->CreateBr(sw->getDefaultDest());
} }
else else