diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 2b40c7954f..36d4295320 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -22,6 +22,8 @@ extern atomic_t g_progr_pdone; const spu_decoder s_spu_itype; const spu_decoder s_spu_iname; +extern u64 get_timebased_time(); + spu_cache::spu_cache(const std::string& loc) : m_file(loc, fs::read + fs::write + fs::create) { @@ -2684,18 +2686,168 @@ public: return _spu->get_ch_value(ch); } - void RDCH(spu_opcode_t op) // + static s64 exec_read_in_mbox(SPUThread* _spu) { - update_pc(); - value_t res; - res.value = call(&exec_rdch, m_thread, m_ir->getInt32(op.ra)); - const auto next = llvm::BasicBlock::Create(m_context, "", m_function); + // TODO + return _spu->get_ch_value(SPU_RdInMbox); + } + + static u32 exec_read_dec(SPUThread* _spu) + { + const u32 res = _spu->ch_dec_value - static_cast(get_timebased_time() - _spu->ch_dec_start_timestamp); + + if (res > 1500 && g_cfg.core.spu_loop_detection) + { + std::this_thread::yield(); + } + + return res; + } + + static s64 exec_read_events(SPUThread* _spu) + { + if (const u32 events = _spu->get_events()) + { + return events; + } + + // TODO + return _spu->get_ch_value(SPU_RdEventStat); + } + + llvm::Value* get_rdch(spu_opcode_t op, u32 off, bool atomic) + { + const auto ptr = _ptr(m_thread, off); + llvm::Value* val0; + + if (atomic) + { + const auto val = m_ir->CreateAtomicRMW(llvm::AtomicRMWInst::Xchg, ptr, m_ir->getInt64(0), llvm::AtomicOrdering::Acquire); + val0 = val; + } + else + { + const auto val = m_ir->CreateLoad(ptr); + m_ir->CreateStore(m_ir->getInt64(0), ptr); + val0 = val; + } + + const auto _cur = m_ir->GetInsertBlock(); + const auto done = llvm::BasicBlock::Create(m_context, "", m_function); + const auto wait = llvm::BasicBlock::Create(m_context, "", m_function); const auto stop = llvm::BasicBlock::Create(m_context, "", m_function); - m_ir->CreateCondBr(m_ir->CreateICmpSLT(res.value, m_ir->getInt64(0)), stop, next); + m_ir->CreateCondBr(m_ir->CreateICmpSLT(val0, m_ir->getInt64(0)), done, wait); + m_ir->SetInsertPoint(wait); + const auto val1 = call(&exec_rdch, m_thread, m_ir->getInt32(op.ra)); + m_ir->CreateCondBr(m_ir->CreateICmpSLT(val1, m_ir->getInt64(0)), stop, done); m_ir->SetInsertPoint(stop); m_ir->CreateRetVoid(); - m_ir->SetInsertPoint(next); - set_vr(op.rt, insert(splat(0), 3, trunc(res))); + m_ir->SetInsertPoint(done); + const auto rval = m_ir->CreatePHI(get_type(), 2); + rval->addIncoming(val0, _cur); + rval->addIncoming(val1, wait); + return m_ir->CreateTrunc(rval, get_type()); + } + + void RDCH(spu_opcode_t op) // + { + value_t res; + + switch (op.ra) + { + case SPU_RdSRR0: + { + res.value = m_ir->CreateLoad(spu_ptr(&SPUThread::srr0)); + break; + } + case SPU_RdInMbox: + { + update_pc(); + res.value = call(&exec_read_in_mbox, m_thread); + const auto next = llvm::BasicBlock::Create(m_context, "", m_function); + const auto stop = llvm::BasicBlock::Create(m_context, "", m_function); + m_ir->CreateCondBr(m_ir->CreateICmpSLT(res.value, m_ir->getInt64(0)), stop, next); + m_ir->SetInsertPoint(stop); + m_ir->CreateRetVoid(); + m_ir->SetInsertPoint(next); + res.value = m_ir->CreateTrunc(res.value, get_type()); + break; + } + case MFC_RdTagStat: + { + res.value = get_rdch(op, ::offset32(&SPUThread::ch_tag_stat), false); + break; + } + case MFC_RdTagMask: + { + res.value = m_ir->CreateLoad(spu_ptr(&SPUThread::ch_tag_mask)); + break; + } + case SPU_RdSigNotify1: + { + res.value = get_rdch(op, ::offset32(&SPUThread::ch_snr1), true); + break; + } + case SPU_RdSigNotify2: + { + res.value = get_rdch(op, ::offset32(&SPUThread::ch_snr2), true); + break; + } + case MFC_RdAtomicStat: + { + res.value = get_rdch(op, ::offset32(&SPUThread::ch_atomic_stat), false); + break; + } + case MFC_RdListStallStat: + { + res.value = get_rdch(op, ::offset32(&SPUThread::ch_stall_stat), false); + break; + } + case SPU_RdDec: + { + res.value = call(&exec_read_dec, m_thread); + break; + } + case SPU_RdEventMask: + { + res.value = m_ir->CreateLoad(spu_ptr(&SPUThread::ch_event_mask)); + break; + } + case SPU_RdEventStat: + { + update_pc(); + res.value = call(&exec_read_events, m_thread); + const auto next = llvm::BasicBlock::Create(m_context, "", m_function); + const auto stop = llvm::BasicBlock::Create(m_context, "", m_function); + m_ir->CreateCondBr(m_ir->CreateICmpSLT(res.value, m_ir->getInt64(0)), stop, next); + m_ir->SetInsertPoint(stop); + m_ir->CreateRetVoid(); + m_ir->SetInsertPoint(next); + res.value = m_ir->CreateTrunc(res.value, get_type()); + break; + } + case SPU_RdMachStat: + { + res.value = m_ir->CreateZExt(m_ir->CreateLoad(spu_ptr(&SPUThread::interrupts_enabled)), get_type()); + break; + } + + default: + { + update_pc(); + res.value = call(&exec_rdch, m_thread, m_ir->getInt32(op.ra)); + const auto next = llvm::BasicBlock::Create(m_context, "", m_function); + const auto stop = llvm::BasicBlock::Create(m_context, "", m_function); + m_ir->CreateCondBr(m_ir->CreateICmpSLT(res.value, m_ir->getInt64(0)), stop, next); + m_ir->SetInsertPoint(stop); + m_ir->CreateRetVoid(); + m_ir->SetInsertPoint(next); + res.value = m_ir->CreateTrunc(res.value, get_type()); + break; + } + } + + set_vr(op.rt, insert(splat(0), 3, res)); } static u32 exec_rchcnt(SPUThread* _spu, u32 ch)