mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-08 16:01:42 +12:00
PPU/LLVM: Propagate exceptions
It fixes a crash in Retro City Rampage with low (ie 10) llvm threshold.
This commit is contained in:
parent
8204737efa
commit
9d18bba1df
4 changed files with 96 additions and 28 deletions
|
@ -172,7 +172,7 @@ std::pair<Executable, llvm::ExecutionEngine *> Compiler::Compile(const std::stri
|
||||||
|
|
||||||
SetPc(m_ir_builder->getInt32(m_state.current_instruction_address));
|
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
|
// If the function has a default exit block then generate code for it
|
||||||
|
@ -182,8 +182,7 @@ std::pair<Executable, llvm::ExecutionEngine *> Compiler::Compile(const std::stri
|
||||||
PHINode *exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0);
|
PHINode *exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0);
|
||||||
exit_instr_list.push_back(exit_instr_i32);
|
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
|
// 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) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteFunction(PPUThread * ppu_state, u64 context) {
|
u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteFunction(PPUThread * ppu_state, u64 context) {
|
||||||
auto execution_engine = (CPUHybridDecoderRecompiler *)ppu_state->GetDecoder();
|
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
|
/// Get the branch type from a branch instruction
|
||||||
|
@ -552,14 +559,24 @@ u32 ppu_recompiler_llvm::CPUHybridDecoderRecompiler::ExecuteTillReturn(PPUThread
|
||||||
{
|
{
|
||||||
auto entry = ppu_state->PC;
|
auto entry = ppu_state->PC;
|
||||||
u32 exit = (u32)executable(ppu_state, 0);
|
u32 exit = (u32)executable(ppu_state, 0);
|
||||||
if (exit == 0)
|
if (exit == ExecutionStatus::ExecutionStatusReturn)
|
||||||
return 0;
|
return ExecutionStatus::ExecutionStatusReturn;
|
||||||
|
if (exit == ExecutionStatus::ExecutionStatusPropagateException)
|
||||||
|
return ExecutionStatus::ExecutionStatusPropagateException;
|
||||||
execution_engine->m_recompilation_engine->NotifyBlockStart(ppu_state->PC);
|
execution_engine->m_recompilation_engine->NotifyBlockStart(ppu_state->PC);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
u32 instruction = vm::ps3::read32(ppu_state->PC);
|
u32 instruction = vm::ps3::read32(ppu_state->PC);
|
||||||
u32 oldPC = ppu_state->PC;
|
u32 oldPC = ppu_state->PC;
|
||||||
|
try
|
||||||
|
{
|
||||||
execution_engine->m_decoder.Decode(instruction);
|
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;
|
auto branch_type = ppu_state->PC != oldPC ? GetBranchTypeFromInstruction(instruction) : BranchType::NonBranch;
|
||||||
ppu_state->PC += 4;
|
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();
|
if (Emu.GetCPUThreadStop() == ppu_state->PC) ppu_state->fast_stop();
|
||||||
return 0;
|
return 0;
|
||||||
case BranchType::FunctionCall: {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case BranchType::LocalBranch:
|
case BranchType::LocalBranch:
|
||||||
|
|
|
@ -24,6 +24,13 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace ppu_recompiler_llvm {
|
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 Compiler;
|
||||||
class RecompilationEngine;
|
class RecompilationEngine;
|
||||||
class ExecutionEngine;
|
class ExecutionEngine;
|
||||||
|
|
|
@ -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);
|
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) {
|
void Compiler::HACK(u32 index) {
|
||||||
Call<void>("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<u32>("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) {
|
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());
|
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);
|
CreateBranch(nullptr, lr_i32, false, (index & EIF_USE_BRANCH) == 0);
|
||||||
}
|
}
|
||||||
// copied from Compiler::SC()
|
|
||||||
//auto ret_i1 = Call<bool>("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) {
|
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");
|
auto merge_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "merge_true");
|
||||||
m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb);
|
m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb);
|
||||||
m_ir_builder->SetInsertPoint(then_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);
|
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);
|
SetPc(target_i32);
|
||||||
Function *fn = m_module->getFunction(fmt::format("function_0x%08X", target_address));
|
// Function *fn = m_module->getFunction(fmt::format("function_0x%08X", target_address));
|
||||||
if (fn)
|
llvm::Value *execStatus;
|
||||||
m_ir_builder->CreateCall2(fn, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0));
|
// if (fn)
|
||||||
else
|
// execStatus = m_ir_builder->CreateCall2(fn, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0));
|
||||||
Call<u32>("execute_unknown_function", nullptr, m_state.args[CompileTaskState::Args::State], m_ir_builder->getInt64(0));
|
// else
|
||||||
|
execStatus = Call<u32>("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));
|
m_ir_builder->CreateBr(GetBasicBlockFromAddress(m_state.current_instruction_address + 4));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -5221,15 +5247,25 @@ void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i32, bool
|
||||||
SetPc(target_i32);
|
SetPc(target_i32);
|
||||||
if (target_is_lr && !lk) {
|
if (target_is_lr && !lk) {
|
||||||
// Return from this function
|
// 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) {
|
else if (lk) {
|
||||||
BasicBlock *next_block = GetBasicBlockFromAddress(m_state.current_instruction_address + 4);
|
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);
|
m_ir_builder->CreateBr(next_block);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_ir_builder->CreateRet(m_ir_builder->getInt32(-1));
|
m_ir_builder->CreateRet(m_ir_builder->getInt32(ExecutionStatus::ExecutionStatusBlockEnded));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -536,6 +536,11 @@ public:
|
||||||
|
|
||||||
std::function<void(PPUThread& CPU)> custom_task;
|
std::function<void(PPUThread& CPU)> 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:
|
public:
|
||||||
PPUThread(const std::string& name);
|
PPUThread(const std::string& name);
|
||||||
virtual ~PPUThread() override;
|
virtual ~PPUThread() override;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue