From 9d18bba1dfa0893aa34ec787da72504cb27b48f9 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Sun, 30 Aug 2015 18:16:38 +0200 Subject: [PATCH] PPU/LLVM: Propagate exceptions It fixes a crash in Retro City Rampage with low (ie 10) llvm threshold. --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 38 +++++++++--- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 7 +++ rpcs3/Emu/Cell/PPULLVMRecompilerCore.cpp | 74 ++++++++++++++++++------ rpcs3/Emu/Cell/PPUThread.h | 5 ++ 4 files changed, 96 insertions(+), 28 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index f8bd5a75e4..e372cf8228 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -172,7 +172,7 @@ std::pair Compiler::Compile(const std::stri SetPc(m_ir_builder->getInt32(m_state.current_instruction_address)); - m_ir_builder->CreateRet(exit_instr_i32); + m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusBlockEnded)); } // If the function has a default exit block then generate code for it @@ -182,8 +182,7 @@ std::pair Compiler::Compile(const std::stri PHINode *exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0); exit_instr_list.push_back(exit_instr_i32); - m_ir_builder->CreateRet(exit_instr_i32); - + m_ir_builder->CreateRet(m_ir_builder->getInt32(0)); } // Add incoming values for all exit instr PHI nodes @@ -508,13 +507,21 @@ ppu_recompiler_llvm::CPUHybridDecoderRecompiler::~CPUHybridDecoderRecompiler() { } u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::DecodeMemory(const u32 address) { - ExecuteFunction(&m_ppu, 0); + // TODO: exception_ptr doesnt work, should add every possible exception + if (ExecuteFunction(&m_ppu, 0) == ExecutionStatus::ExecutionStatusPropagateException) + { + std::exception_ptr exn = m_ppu.pending_exception; + m_ppu.pending_exception = nullptr; + std::rethrow_exception(exn); + } return 0; } u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteFunction(PPUThread * ppu_state, u64 context) { auto execution_engine = (CPUHybridDecoderRecompiler *)ppu_state->GetDecoder(); - return ExecuteTillReturn(ppu_state, 0); + if (ExecuteTillReturn(ppu_state, 0) == ExecutionStatus::ExecutionStatusPropagateException) + return ExecutionStatus::ExecutionStatusPropagateException; + return ExecutionStatus::ExecutionStatusReturn; } /// Get the branch type from a branch instruction @@ -552,14 +559,24 @@ u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteTillReturn(PPUThread { auto entry = ppu_state->PC; u32 exit = (u32)executable(ppu_state, 0); - if (exit == 0) - return 0; + if (exit == ExecutionStatus::ExecutionStatusReturn) + return ExecutionStatus::ExecutionStatusReturn; + if (exit == ExecutionStatus::ExecutionStatusPropagateException) + return ExecutionStatus::ExecutionStatusPropagateException; execution_engine->m_recompilation_engine->NotifyBlockStart(ppu_state->PC); continue; } u32 instruction = vm::ps3::read32(ppu_state->PC); u32 oldPC = ppu_state->PC; - execution_engine->m_decoder.Decode(instruction); + try + { + execution_engine->m_decoder.Decode(instruction); + } + catch (...) + { + ppu_state->pending_exception = std::current_exception(); + return ExecutionStatus::ExecutionStatusPropagateException; + } auto branch_type = ppu_state->PC != oldPC ? GetBranchTypeFromInstruction(instruction) : BranchType::NonBranch; ppu_state->PC += 4; @@ -568,7 +585,10 @@ u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteTillReturn(PPUThread if (Emu.GetCPUThreadStop() == ppu_state->PC) ppu_state->fast_stop(); return 0; case BranchType::FunctionCall: { - ExecuteFunction(ppu_state, 0); + u32 status = ExecuteFunction(ppu_state, 0); + // TODO: exception_ptr doesnt work, should add every possible exception + if (status == ExecutionStatus::ExecutionStatusPropagateException) + return ExecutionStatus::ExecutionStatusPropagateException; break; } case BranchType::LocalBranch: diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index 1f9c5f127c..6668de5571 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -24,6 +24,13 @@ #endif namespace ppu_recompiler_llvm { + enum ExecutionStatus + { + ExecutionStatusReturn = 0, ///< Block has hit a return, caller can continue execution + ExecutionStatusBlockEnded, ///< Block has been executed but no return was hit, at least another block must be executed before caller can continue + ExecutionStatusPropagateException, ///< an exception was thrown + }; + class Compiler; class RecompilationEngine; class ExecutionEngine; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompilerCore.cpp b/rpcs3/Emu/Cell/PPULLVMRecompilerCore.cpp index 3c66119ab0..b688d6006c 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompilerCore.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompilerCore.cpp @@ -1752,21 +1752,37 @@ void Compiler::BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) { CreateBranch(CheckBranchCondition(bo, bi), target_i32, lk ? true : false); } + + +static u32 +wrappedExecutePPUFuncByIndex(PPUThread &CPU, u32 index) +{ + try + { + execute_ppu_func_by_index(CPU, index); + return ExecutionStatus::ExecutionStatusBlockEnded; + } + catch (...) + { + CPU.pending_exception = std::current_exception(); + return ExecutionStatus::ExecutionStatusPropagateException; + } +} + void Compiler::HACK(u32 index) { - Call("execute_ppu_func_by_index", &execute_ppu_func_by_index, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt32(index & EIF_USE_BRANCH ? index : index & ~EIF_PERFORM_BLR)); + llvm::Value *status = Call("wrappedExecutePPUFuncByIndex", &wrappedExecutePPUFuncByIndex, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt32(index & EIF_USE_BRANCH ? index : index & ~EIF_PERFORM_BLR)); + llvm::BasicBlock *cputhreadexitblock = GetBasicBlockFromAddress(m_state.current_instruction_address, "early_exit"); + llvm::Value *isCPUThreadExit = m_ir_builder->CreateICmpEQ(status, m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException)); + llvm::BasicBlock *normal_execution = GetBasicBlockFromAddress(m_state.current_instruction_address, "normal_execution"); + m_ir_builder->CreateCondBr(isCPUThreadExit, cputhreadexitblock, normal_execution); + m_ir_builder->SetInsertPoint(cputhreadexitblock); + m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException)); + + m_ir_builder->SetInsertPoint(normal_execution); if (index & EIF_PERFORM_BLR || index & EIF_USE_BRANCH) { auto lr_i32 = index & EIF_USE_BRANCH ? GetPc() : m_ir_builder->CreateTrunc(m_ir_builder->CreateAnd(GetLr(), ~0x3ULL), m_ir_builder->getInt32Ty()); CreateBranch(nullptr, lr_i32, false, (index & EIF_USE_BRANCH) == 0); } - // copied from Compiler::SC() - //auto ret_i1 = Call("PollStatus", m_poll_status_function, m_state.args[CompileTaskState::Args::State]); - //auto cmp_i1 = m_ir_builder->CreateICmpEQ(ret_i1, m_ir_builder->getInt1(true)); - //auto then_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "then_true"); - //auto merge_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "merge_true"); - //m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb); - //m_ir_builder->SetInsertPoint(then_bb); - //m_ir_builder->CreateRet(m_ir_builder->getInt32(0xFFFFFFFF)); - //m_ir_builder->SetInsertPoint(merge_bb); } void Compiler::SC(u32 lev) { @@ -1788,7 +1804,7 @@ void Compiler::SC(u32 lev) { auto merge_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "merge_true"); m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb); m_ir_builder->SetInsertPoint(then_bb); - m_ir_builder->CreateRet(m_ir_builder->getInt32(0xFFFFFFFF)); + m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusBlockEnded)); m_ir_builder->SetInsertPoint(merge_bb); } @@ -5199,11 +5215,21 @@ void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i32, bool } SetPc(target_i32); - Function *fn = m_module->getFunction(fmt::format("function_0x%08X", target_address)); - if (fn) - m_ir_builder->CreateCall2(fn, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0)); - else - Call("execute_unknown_function", nullptr, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0)); +// Function *fn = m_module->getFunction(fmt::format("function_0x%08X", target_address)); + llvm::Value *execStatus; +// if (fn) +// execStatus = m_ir_builder->CreateCall2(fn, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0)); +// else + execStatus = Call("execute_unknown_function", nullptr, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0)); + + llvm::BasicBlock *cputhreadexitblock = GetBasicBlockFromAddress(m_state.current_instruction_address, "early_exit"); + llvm::Value *isCPUThreadExit = m_ir_builder->CreateICmpEQ(execStatus, m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException)); + llvm::BasicBlock *normal_execution = GetBasicBlockFromAddress(m_state.current_instruction_address, "normal_execution"); + m_ir_builder->CreateCondBr(isCPUThreadExit, cputhreadexitblock, normal_execution); + m_ir_builder->SetInsertPoint(cputhreadexitblock); + m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException)); + m_ir_builder->SetInsertPoint(normal_execution); + m_ir_builder->CreateBr(GetBasicBlockFromAddress(m_state.current_instruction_address + 4)); } else { @@ -5221,15 +5247,25 @@ void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i32, bool SetPc(target_i32); if (target_is_lr && !lk) { // Return from this function - m_ir_builder->CreateRet(m_ir_builder->getInt32(0)); + m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusReturn)); } else if (lk) { BasicBlock *next_block = GetBasicBlockFromAddress(m_state.current_instruction_address + 4); - m_ir_builder->CreateCall2(m_execute_unknown_function, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0)); + + llvm::Value *execStatus = m_ir_builder->CreateCall2(m_execute_unknown_function, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0)); + + llvm::BasicBlock *cputhreadexitblock = GetBasicBlockFromAddress(m_state.current_instruction_address, "early_exit"); + llvm::Value *isCPUThreadExit = m_ir_builder->CreateICmpEQ(execStatus, m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException)); + llvm::BasicBlock *normal_execution = GetBasicBlockFromAddress(m_state.current_instruction_address, "normal_execution"); + m_ir_builder->CreateCondBr(isCPUThreadExit, cputhreadexitblock, normal_execution); + m_ir_builder->SetInsertPoint(cputhreadexitblock); + m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusPropagateException)); + m_ir_builder->SetInsertPoint(normal_execution); + m_ir_builder->CreateBr(next_block); } else { - m_ir_builder->CreateRet(m_ir_builder->getInt32(-1)); + m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusBlockEnded)); } } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 50991b5e94..11a0e1047b 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -536,6 +536,11 @@ public: std::function custom_task; + /// When a thread has met an exception, this variable is used to retro propagate it through stack call. + /// Note that exception_ptr is similar to shared_ptr and doesn't need to be freed, but need a nullptr + /// to be assigned. + std::exception_ptr pending_exception; + public: PPUThread(const std::string& name); virtual ~PPUThread() override;