From 6bc0ce8046a8a2fc3c8842f398118c417c513ad8 Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Sat, 25 Oct 2014 06:38:47 +0530 Subject: [PATCH 01/12] Intial commit for advanced tracer --- rpcs3/Emu/Cell/PPUInterpreter.h | 6 +- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 1638 +++++++++++++------------- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 1559 ++++++++++++------------ rpcs3/emucore.vcxproj.filters | 2 +- 4 files changed, 1596 insertions(+), 1609 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index cdb7a2d2ce..da61d3f1b7 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -55,9 +55,13 @@ u64 rotr64(const u64 x, const u8 n) { return (x >> n) | (x << (64 - n)); } #define rotl64 _rotl64 #define rotr64 _rotr64 +namespace ppu_recompiler_llvm { + class Compiler; +} + class PPUInterpreter : public PPUOpcodes { - friend class PPULLVMRecompiler; + friend class ppu_recompiler_llvm::Compiler; private: PPUThread& CPU; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 5d66f575a9..4dda28cb55 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -21,13 +21,12 @@ #include "llvm/MC/MCDisassembler.h" using namespace llvm; +using namespace ppu_recompiler_llvm; -u64 PPULLVMRecompiler::s_rotate_mask[64][64]; -bool PPULLVMRecompiler::s_rotate_mask_inited = false; +u64 Compiler::s_rotate_mask[64][64]; +bool Compiler::s_rotate_mask_inited = false; -PPULLVMRecompiler::PPULLVMRecompiler() - : ThreadBase("PPULLVMRecompiler") - , m_revision(0) { +Compiler::Compiler() { InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); InitializeNativeTargetDisassembler(); @@ -70,181 +69,164 @@ PPULLVMRecompiler::PPULLVMRecompiler() } } -PPULLVMRecompiler::~PPULLVMRecompiler() { - Stop(); - +Compiler::~Compiler() { delete m_execution_engine; delete m_fpm; delete m_ir_builder; delete m_llvm_context; } -std::pair PPULLVMRecompiler::GetExecutable(u32 address) { - std::lock_guard lock(m_compiled_shared_lock); +CompiledCodeFragment Compiler::Compile(const std::string & name, const CodeFragment & code_fragment) { + assert(!name.empty()); + assert(!code_fragment.empty()); - auto compiled = m_compiled_shared.lower_bound(std::make_pair(address, 0)); - if (compiled != m_compiled_shared.end() && compiled->first.first == address) { - compiled->second.second++; - return std::make_pair(compiled->second.first, compiled->first.second); - } + auto compilation_start = std::chrono::high_resolution_clock::now(); - return std::make_pair(nullptr, 0); -} + // Create the function + m_current_function = (Function *)m_module->getOrInsertFunction(name, m_ir_builder->getVoidTy(), + m_ir_builder->getInt8PtrTy() /*ppu_state*/, + m_ir_builder->getInt8PtrTy() /*interpreter*/, + m_ir_builder->getInt8PtrTy() /*tracer*/, nullptr); + m_current_function->setCallingConv(CallingConv::X86_64_Win64); + auto arg_i = m_current_function->arg_begin(); + arg_i->setName("ppu_state"); + (++arg_i)->setName("interpreter"); + (++arg_i)->setName("tracer"); -void PPULLVMRecompiler::ReleaseExecutable(u32 address, u32 revision) { - std::lock_guard lock(m_compiled_shared_lock); + // Create the entry block + GetBasicBlockFromAddress(0, m_current_function, true); - auto compiled = m_compiled_shared.find(std::make_pair(address, revision)); - if (compiled != m_compiled_shared.end()) { - compiled->second.second--; - } -} + // Create basic blocks for each instruction + for (auto i = code_fragment.begin(); i != code_fragment.end(); i++) { + u32 address = i->first.address; + while (1) { + GetBasicBlockFromAddress(address, m_current_function, true); -void PPULLVMRecompiler::RequestCompilation(u32 address) { - { - std::lock_guard lock(m_uncompiled_shared_lock); - m_uncompiled_shared.push_back(address); - } - - if (!IsAlive()) { - Start(); - } - - Notify(); -} - -u32 PPULLVMRecompiler::GetCurrentRevision() { - return m_revision.load(std::memory_order_relaxed); -} - -void PPULLVMRecompiler::Task() { - auto start = std::chrono::high_resolution_clock::now(); - - while (!TestDestroy() && !Emu.IsStopped()) { - // Wait a few ms for something to happen - auto idling_start = std::chrono::high_resolution_clock::now(); - WaitForAnySignal(250); - auto idling_end = std::chrono::high_resolution_clock::now(); - m_idling_time += std::chrono::duration_cast(idling_end - idling_start); - - // Update the set of blocks that have been hit with the set of blocks that have been requested for compilation. - { - std::lock_guard lock(m_uncompiled_shared_lock); - for (auto i = m_uncompiled_shared.begin(); i != m_uncompiled_shared.end(); i++) { - m_hit_blocks.insert(*i); + u32 instr = vm::read32(address); + if (IsBranchInstruction(instr)) { + break; } + + address += 4; } + } - u32 num_compiled = 0; - while (!TestDestroy() && !Emu.IsStopped()) { - u32 address; + // Add code to notify the tracer about this function and branch to the first instruction + m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0, m_current_function)); + //Call("Tracer.Trace", &Tracer::Trace, *arg_i, + // m_ir_builder->getIntN(sizeof(Tracer::BranchType) * 8, code_fragment[0].first.type == FunctionStart ? Tracer::BranchType::CompiledFunctionCall : Tracer::BranchType::CompiledBlock), + // m_ir_builder->getInt32(code_fragment[0].first.address)); + m_ir_builder->CreateBr(GetBasicBlockFromAddress(code_fragment[0].first.address, m_current_function)); - { - std::lock_guard lock(m_uncompiled_shared_lock); + // Convert each block in this code fragment to LLVM IR + for (auto i = code_fragment.begin(); i != code_fragment.end(); i++) { + m_current_instruction_address = i->first.address; + m_current_block_next_blocks = &(i->second); + auto block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); + m_hit_branch_instruction = false; + m_ir_builder->SetInsertPoint(block); - auto i = m_uncompiled_shared.begin(); - if (i != m_uncompiled_shared.end()) { - address = *i; - m_uncompiled_shared.erase(i); - } else { - break; - } + while (!m_hit_branch_instruction) { + if (!block->getInstList().empty()) { + break; } - m_hit_blocks.insert(address); - if (NeedsCompiling(address)) { - Compile(address); - num_compiled++; - } - } + u32 instr = vm::read32(m_current_instruction_address); + Decode(instr); - if (num_compiled == 0) { - // If we get here, it means the recompilation thread is idling. - // We use this oppurtunity to optimize the code. - RemoveUnusedOldVersions(); - for (auto i = m_compiled.begin(); i != m_compiled.end(); i++) { - if (NeedsCompiling(i->first.first)) { - Compile(i->first.first); - num_compiled++; - } + m_current_instruction_address += 4; + if (!m_hit_branch_instruction) { + block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); + m_ir_builder->CreateBr(block); + m_ir_builder->SetInsertPoint(block); } } } - std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); - m_total_time = std::chrono::duration_cast(end - start); + // If the function has an unknown block then notify the tracer + auto unknown_bb = GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function); + if (!unknown_bb) { + m_ir_builder->SetInsertPoint(unknown_bb); + auto branch_type_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 1); + for (auto i = pred_begin(unknown_bb); i != pred_end(unknown_bb); i++) { + // We assume that the last but one instruction of the predecessor sets the branch type + auto j = (*i)->rbegin(); + j--; + branch_type_i32->addIncoming(&(*j), *i); + } - std::string error; - raw_fd_ostream log_file("PPULLVMRecompiler.log", error, sys::fs::F_Text); - log_file << "Total time = " << m_total_time.count() / 1000000 << "ms\n"; - log_file << " Time spent compiling = " << m_compilation_time.count() / 1000000 << "ms\n"; - log_file << " Time spent building IR = " << m_ir_build_time.count() / 1000000 << "ms\n"; - log_file << " Time spent optimizing = " << m_optimizing_time.count() / 1000000 << "ms\n"; - log_file << " Time spent translating = " << m_translation_time.count() / 1000000 << "ms\n"; - log_file << " Time spent idling = " << m_idling_time.count() / 1000000 << "ms\n"; - log_file << " Time spent doing misc tasks = " << (m_total_time.count() - m_idling_time.count() - m_compilation_time.count()) / 1000000 << "ms\n"; - log_file << "Revision = " << m_revision << "\n"; - log_file << "\nInterpreter fallback stats:\n"; - for (auto i = m_interpreter_fallback_stats.begin(); i != m_interpreter_fallback_stats.end(); i++) { - log_file << i->first << " = " << i->second << "\n"; + //Call("NotifyBranch", &Tracer::NotifyBranch, *arg_i, + // m_ir_builder->CreateZExtOrTrunc(branch_type_i32, m_ir_builder->getIntNTy(sizeof(Tracer::BranchType) * 8)), GetPc()); + m_ir_builder->CreateRetVoid(); } - log_file << "\nDisassembly:\n"; - //auto disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr); - for (auto i = m_compiled.begin(); i != m_compiled.end(); i++) { - log_file << fmt::Format("%s: Size = %u bytes, Number of instructions = %u\n", i->second.llvm_function->getName().str().c_str(), i->second.size, i->second.num_instructions); + auto ir_build_end = std::chrono::high_resolution_clock::now(); + m_stats.ir_build_time += std::chrono::duration_cast(ir_build_end - compilation_start); - //uint8_t * fn_ptr = (uint8_t *)i->second.executable; - //for (size_t pc = 0; pc < i->second.size;) { - // char str[1024]; + // Optimize this function + m_fpm->run(*m_current_function); + auto optimize_end = std::chrono::high_resolution_clock::now(); + m_stats.optimization_time += std::chrono::duration_cast(optimize_end - ir_build_end); - // auto size = LLVMDisasmInstruction(disassembler, fn_ptr + pc, i->second.size - pc, (uint64_t)(fn_ptr + pc), str, sizeof(str)); - // log_file << str << '\n'; - // pc += size; - //} - } + // Translate to machine code + MachineCodeInfo mci; + m_execution_engine->runJITOnFunction(m_current_function, &mci); + auto translate_end = std::chrono::high_resolution_clock::now(); + m_stats.translation_time += std::chrono::duration_cast(translate_end - optimize_end); - //LLVMDisasmDispose(disassembler); + auto compilation_end = std::chrono::high_resolution_clock::now(); + m_stats.total_time += std::chrono::duration_cast(compilation_end - compilation_start); - //log_file << "\nLLVM IR:\n" << *m_module; - - LOG_NOTICE(PPU, "PPU LLVM compiler thread exiting."); + m_compiled[(CompiledCodeFragment)mci.address()] = m_current_function; + return (CompiledCodeFragment)mci.address(); } -void PPULLVMRecompiler::Decode(const u32 code) { +void Compiler::FreeCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment) { + auto i = m_compiled.find(compiled_code_fragment); + if (i != m_compiled.end()) { + m_execution_engine->freeMachineCodeForFunction(i->second); + i->second->eraseFromParent(); + } +} + +Compiler::Stats Compiler::GetStats() { + return m_stats; +} + +void Compiler::Decode(const u32 code) { (*PPU_instr::main_list)(this, code); } -void PPULLVMRecompiler::NULL_OP() { +void Compiler::NULL_OP() { InterpreterCall("NULL_OP", &PPUInterpreter::NULL_OP); } -void PPULLVMRecompiler::NOP() { +void Compiler::NOP() { InterpreterCall("NOP", &PPUInterpreter::NOP); } -void PPULLVMRecompiler::TDI(u32 to, u32 ra, s32 simm16) { +void Compiler::TDI(u32 to, u32 ra, s32 simm16) { InterpreterCall("TDI", &PPUInterpreter::TDI, to, ra, simm16); } -void PPULLVMRecompiler::TWI(u32 to, u32 ra, s32 simm16) { +void Compiler::TWI(u32 to, u32 ra, s32 simm16) { InterpreterCall("TWI", &PPUInterpreter::TWI, to, ra, simm16); } -void PPULLVMRecompiler::MFVSCR(u32 vd) { +void Compiler::MFVSCR(u32 vd) { auto vscr_i32 = GetVscr(); auto vscr_i128 = m_ir_builder->CreateZExt(vscr_i32, m_ir_builder->getIntNTy(128)); SetVr(vd, vscr_i128); } -void PPULLVMRecompiler::MTVSCR(u32 vb) { +void Compiler::MTVSCR(u32 vb) { auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto vscr_i32 = m_ir_builder->CreateExtractElement(vb_v4i32, m_ir_builder->getInt32(0)); vscr_i32 = m_ir_builder->CreateAnd(vscr_i32, 0x00010001); SetVscr(vscr_i32); } -void PPULLVMRecompiler::VADDCUW(u32 vd, u32 va, u32 vb) { +void Compiler::VADDCUW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); @@ -254,14 +236,14 @@ void PPULLVMRecompiler::VADDCUW(u32 vd, u32 va, u32 vb) { SetVr(vd, cmpv4i32); } -void PPULLVMRecompiler::VADDFP(u32 vd, u32 va, u32 vb) { +void Compiler::VADDFP(u32 vd, u32 va, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto sum_v4f32 = m_ir_builder->CreateFAdd(va_v4f32, vb_v4f32); SetVr(vd, sum_v4f32); } -void PPULLVMRecompiler::VADDSBS(u32 vd, u32 va, u32 vb) { +void Compiler::VADDSBS(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto sum_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_padds_b), va_v16i8, vb_v16i8); @@ -270,7 +252,7 @@ void PPULLVMRecompiler::VADDSBS(u32 vd, u32 va, u32 vb) { // TODO: Set VSCR.SAT } -void PPULLVMRecompiler::VADDSHS(u32 vd, u32 va, u32 vb) { +void Compiler::VADDSHS(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto sum_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_padds_w), va_v8i16, vb_v8i16); @@ -279,7 +261,7 @@ void PPULLVMRecompiler::VADDSHS(u32 vd, u32 va, u32 vb) { // TODO: Set VSCR.SAT } -void PPULLVMRecompiler::VADDSWS(u32 vd, u32 va, u32 vb) { +void Compiler::VADDSWS(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); @@ -320,14 +302,14 @@ void PPULLVMRecompiler::VADDSWS(u32 vd, u32 va, u32 vb) { // TODO: Set SAT } -void PPULLVMRecompiler::VADDUBM(u32 vd, u32 va, u32 vb) { +void Compiler::VADDUBM(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto sum_v16i8 = m_ir_builder->CreateAdd(va_v16i8, vb_v16i8); SetVr(vd, sum_v16i8); } -void PPULLVMRecompiler::VADDUBS(u32 vd, u32 va, u32 vb) { +void Compiler::VADDUBS(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto sum_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_paddus_b), va_v16i8, vb_v16i8); @@ -336,14 +318,14 @@ void PPULLVMRecompiler::VADDUBS(u32 vd, u32 va, u32 vb) { // TODO: Set SAT } -void PPULLVMRecompiler::VADDUHM(u32 vd, u32 va, u32 vb) { +void Compiler::VADDUHM(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto sum_v8i16 = m_ir_builder->CreateAdd(va_v8i16, vb_v8i16); SetVr(vd, sum_v8i16); } -void PPULLVMRecompiler::VADDUHS(u32 vd, u32 va, u32 vb) { +void Compiler::VADDUHS(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto sum_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_paddus_w), va_v8i16, vb_v8i16); @@ -352,14 +334,14 @@ void PPULLVMRecompiler::VADDUHS(u32 vd, u32 va, u32 vb) { // TODO: Set SAT } -void PPULLVMRecompiler::VADDUWM(u32 vd, u32 va, u32 vb) { +void Compiler::VADDUWM(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto sum_v4i32 = m_ir_builder->CreateAdd(va_v4i32, vb_v4i32); SetVr(vd, sum_v4i32); } -void PPULLVMRecompiler::VADDUWS(u32 vd, u32 va, u32 vb) { +void Compiler::VADDUWS(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto sum_v4i32 = m_ir_builder->CreateAdd(va_v4i32, vb_v4i32); @@ -371,14 +353,14 @@ void PPULLVMRecompiler::VADDUWS(u32 vd, u32 va, u32 vb) { // TODO: Set SAT } -void PPULLVMRecompiler::VAND(u32 vd, u32 va, u32 vb) { +void Compiler::VAND(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto res_v4i32 = m_ir_builder->CreateAnd(va_v4i32, vb_v4i32); SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VANDC(u32 vd, u32 va, u32 vb) { +void Compiler::VANDC(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); vb_v4i32 = m_ir_builder->CreateNot(vb_v4i32); @@ -386,7 +368,7 @@ void PPULLVMRecompiler::VANDC(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VAVGSB(u32 vd, u32 va, u32 vb) { +void Compiler::VAVGSB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto va_v16i16 = m_ir_builder->CreateSExt(va_v16i8, VectorType::get(m_ir_builder->getInt16Ty(), 16)); @@ -398,7 +380,7 @@ void PPULLVMRecompiler::VAVGSB(u32 vd, u32 va, u32 vb) { SetVr(vd, avg_v16i8); } -void PPULLVMRecompiler::VAVGSH(u32 vd, u32 va, u32 vb) { +void Compiler::VAVGSH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto va_v8i32 = m_ir_builder->CreateSExt(va_v8i16, VectorType::get(m_ir_builder->getInt32Ty(), 8)); @@ -410,7 +392,7 @@ void PPULLVMRecompiler::VAVGSH(u32 vd, u32 va, u32 vb) { SetVr(vd, avg_v8i16); } -void PPULLVMRecompiler::VAVGSW(u32 vd, u32 va, u32 vb) { +void Compiler::VAVGSW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto va_v4i64 = m_ir_builder->CreateSExt(va_v4i32, VectorType::get(m_ir_builder->getInt64Ty(), 4)); @@ -422,21 +404,21 @@ void PPULLVMRecompiler::VAVGSW(u32 vd, u32 va, u32 vb) { SetVr(vd, avg_v4i32); } -void PPULLVMRecompiler::VAVGUB(u32 vd, u32 va, u32 vb) { +void Compiler::VAVGUB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto avg_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_pavg_b), va_v16i8, vb_v16i8); SetVr(vd, avg_v16i8); } -void PPULLVMRecompiler::VAVGUH(u32 vd, u32 va, u32 vb) { +void Compiler::VAVGUH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto avg_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_pavg_w), va_v8i16, vb_v8i16); SetVr(vd, avg_v8i16); } -void PPULLVMRecompiler::VAVGUW(u32 vd, u32 va, u32 vb) { +void Compiler::VAVGUW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto va_v4i64 = m_ir_builder->CreateZExt(va_v4i32, VectorType::get(m_ir_builder->getInt64Ty(), 4)); @@ -448,7 +430,7 @@ void PPULLVMRecompiler::VAVGUW(u32 vd, u32 va, u32 vb) { SetVr(vd, avg_v4i32); } -void PPULLVMRecompiler::VCFSX(u32 vd, u32 uimm5, u32 vb) { +void Compiler::VCFSX(u32 vd, u32 uimm5, u32 vb) { auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto res_v4f32 = m_ir_builder->CreateSIToFP(vb_v4i32, VectorType::get(m_ir_builder->getFloatTy(), 4)); @@ -460,7 +442,7 @@ void PPULLVMRecompiler::VCFSX(u32 vd, u32 uimm5, u32 vb) { SetVr(vd, res_v4f32); } -void PPULLVMRecompiler::VCFUX(u32 vd, u32 uimm5, u32 vb) { +void Compiler::VCFUX(u32 vd, u32 uimm5, u32 vb) { auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto res_v4f32 = m_ir_builder->CreateUIToFP(vb_v4i32, VectorType::get(m_ir_builder->getFloatTy(), 4)); @@ -472,7 +454,7 @@ void PPULLVMRecompiler::VCFUX(u32 vd, u32 uimm5, u32 vb) { SetVr(vd, res_v4f32); } -void PPULLVMRecompiler::VCMPBFP(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPBFP(u32 vd, u32 va, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto cmp_gt_v4i1 = m_ir_builder->CreateFCmpOGT(va_v4f32, vb_v4f32); @@ -488,7 +470,7 @@ void PPULLVMRecompiler::VCMPBFP(u32 vd, u32 va, u32 vb) { // TODO: Implement NJ mode } -void PPULLVMRecompiler::VCMPBFP_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPBFP_(u32 vd, u32 va, u32 vb) { VCMPBFP(vd, va, vb); auto vd_v16i8 = GetVrAsIntVec(vd, 8); @@ -500,7 +482,7 @@ void PPULLVMRecompiler::VCMPBFP_(u32 vd, u32 va, u32 vb) { SetCrField(6, nullptr, nullptr, cmp_i1, nullptr); } -void PPULLVMRecompiler::VCMPEQFP(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPEQFP(u32 vd, u32 va, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto cmp_v4i1 = m_ir_builder->CreateFCmpOEQ(va_v4f32, vb_v4f32); @@ -508,12 +490,12 @@ void PPULLVMRecompiler::VCMPEQFP(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v4i32); } -void PPULLVMRecompiler::VCMPEQFP_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPEQFP_(u32 vd, u32 va, u32 vb) { VCMPEQFP(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPEQUB(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPEQUB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto cmp_v16i1 = m_ir_builder->CreateICmpEQ(va_v16i8, vb_v16i8); @@ -521,12 +503,12 @@ void PPULLVMRecompiler::VCMPEQUB(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v16i8); } -void PPULLVMRecompiler::VCMPEQUB_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPEQUB_(u32 vd, u32 va, u32 vb) { VCMPEQUB(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPEQUH(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPEQUH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto cmp_v8i1 = m_ir_builder->CreateICmpEQ(va_v8i16, vb_v8i16); @@ -534,12 +516,12 @@ void PPULLVMRecompiler::VCMPEQUH(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v8i16); } -void PPULLVMRecompiler::VCMPEQUH_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPEQUH_(u32 vd, u32 va, u32 vb) { VCMPEQUH(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPEQUW(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPEQUW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto cmp_v4i1 = m_ir_builder->CreateICmpEQ(va_v4i32, vb_v4i32); @@ -547,12 +529,12 @@ void PPULLVMRecompiler::VCMPEQUW(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v4i32); } -void PPULLVMRecompiler::VCMPEQUW_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPEQUW_(u32 vd, u32 va, u32 vb) { VCMPEQUW(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPGEFP(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGEFP(u32 vd, u32 va, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto cmp_v4i1 = m_ir_builder->CreateFCmpOGE(va_v4f32, vb_v4f32); @@ -560,12 +542,12 @@ void PPULLVMRecompiler::VCMPGEFP(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v4i32); } -void PPULLVMRecompiler::VCMPGEFP_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGEFP_(u32 vd, u32 va, u32 vb) { VCMPGEFP(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPGTFP(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTFP(u32 vd, u32 va, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto cmp_v4i1 = m_ir_builder->CreateFCmpOGT(va_v4f32, vb_v4f32); @@ -573,12 +555,12 @@ void PPULLVMRecompiler::VCMPGTFP(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v4i32); } -void PPULLVMRecompiler::VCMPGTFP_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTFP_(u32 vd, u32 va, u32 vb) { VCMPGTFP(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPGTSB(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTSB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto cmp_v16i1 = m_ir_builder->CreateICmpSGT(va_v16i8, vb_v16i8); @@ -586,12 +568,12 @@ void PPULLVMRecompiler::VCMPGTSB(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v16i8); } -void PPULLVMRecompiler::VCMPGTSB_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTSB_(u32 vd, u32 va, u32 vb) { VCMPGTSB(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPGTSH(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTSH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto cmp_v8i1 = m_ir_builder->CreateICmpSGT(va_v8i16, vb_v8i16); @@ -599,12 +581,12 @@ void PPULLVMRecompiler::VCMPGTSH(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v8i16); } -void PPULLVMRecompiler::VCMPGTSH_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTSH_(u32 vd, u32 va, u32 vb) { VCMPGTSH(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPGTSW(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTSW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto cmp_v4i1 = m_ir_builder->CreateICmpSGT(va_v4i32, vb_v4i32); @@ -612,12 +594,12 @@ void PPULLVMRecompiler::VCMPGTSW(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v4i32); } -void PPULLVMRecompiler::VCMPGTSW_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTSW_(u32 vd, u32 va, u32 vb) { VCMPGTSW(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPGTUB(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTUB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto cmp_v16i1 = m_ir_builder->CreateICmpUGT(va_v16i8, vb_v16i8); @@ -625,12 +607,12 @@ void PPULLVMRecompiler::VCMPGTUB(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v16i8); } -void PPULLVMRecompiler::VCMPGTUB_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTUB_(u32 vd, u32 va, u32 vb) { VCMPGTUB(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPGTUH(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTUH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto cmp_v8i1 = m_ir_builder->CreateICmpUGT(va_v8i16, vb_v8i16); @@ -638,12 +620,12 @@ void PPULLVMRecompiler::VCMPGTUH(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v8i16); } -void PPULLVMRecompiler::VCMPGTUH_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTUH_(u32 vd, u32 va, u32 vb) { VCMPGTUH(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCMPGTUW(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTUW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto cmp_v4i1 = m_ir_builder->CreateICmpUGT(va_v4i32, vb_v4i32); @@ -651,28 +633,28 @@ void PPULLVMRecompiler::VCMPGTUW(u32 vd, u32 va, u32 vb) { SetVr(vd, cmp_v4i32); } -void PPULLVMRecompiler::VCMPGTUW_(u32 vd, u32 va, u32 vb) { +void Compiler::VCMPGTUW_(u32 vd, u32 va, u32 vb) { VCMPGTUW(vd, va, vb); SetCr6AfterVectorCompare(vd); } -void PPULLVMRecompiler::VCTSXS(u32 vd, u32 uimm5, u32 vb) { +void Compiler::VCTSXS(u32 vd, u32 uimm5, u32 vb) { InterpreterCall("VCTSXS", &PPUInterpreter::VCTSXS, vd, uimm5, vb); } -void PPULLVMRecompiler::VCTUXS(u32 vd, u32 uimm5, u32 vb) { +void Compiler::VCTUXS(u32 vd, u32 uimm5, u32 vb) { InterpreterCall("VCTUXS", &PPUInterpreter::VCTUXS, vd, uimm5, vb); } -void PPULLVMRecompiler::VEXPTEFP(u32 vd, u32 vb) { +void Compiler::VEXPTEFP(u32 vd, u32 vb) { InterpreterCall("VEXPTEFP", &PPUInterpreter::VEXPTEFP, vd, vb); } -void PPULLVMRecompiler::VLOGEFP(u32 vd, u32 vb) { +void Compiler::VLOGEFP(u32 vd, u32 vb) { InterpreterCall("VLOGEFP", &PPUInterpreter::VLOGEFP, vd, vb); } -void PPULLVMRecompiler::VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) { +void Compiler::VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto vc_v4f32 = GetVrAsFloatVec(vc); @@ -680,117 +662,117 @@ void PPULLVMRecompiler::VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) { SetVr(vd, res_v4f32); } -void PPULLVMRecompiler::VMAXFP(u32 vd, u32 va, u32 vb) { +void Compiler::VMAXFP(u32 vd, u32 va, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto res_v4f32 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse_max_ps), va_v4f32, vb_v4f32); SetVr(vd, res_v4f32); } -void PPULLVMRecompiler::VMAXSB(u32 vd, u32 va, u32 vb) { +void Compiler::VMAXSB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto res_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pmaxsb), va_v16i8, vb_v16i8); SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VMAXSH(u32 vd, u32 va, u32 vb) { +void Compiler::VMAXSH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto res_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_pmaxs_w), va_v8i16, vb_v8i16); SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VMAXSW(u32 vd, u32 va, u32 vb) { +void Compiler::VMAXSW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto res_v4i32 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pmaxsd), va_v4i32, vb_v4i32); SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VMAXUB(u32 vd, u32 va, u32 vb) { +void Compiler::VMAXUB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto res_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_pmaxu_b), va_v16i8, vb_v16i8); SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VMAXUH(u32 vd, u32 va, u32 vb) { +void Compiler::VMAXUH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto res_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pmaxuw), va_v8i16, vb_v8i16); SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VMAXUW(u32 vd, u32 va, u32 vb) { +void Compiler::VMAXUW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto res_v4i32 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pmaxud), va_v4i32, vb_v4i32); SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VMHADDSHS(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMHADDSHS(u32 vd, u32 va, u32 vb, u32 vc) { InterpreterCall("VMHADDSHS", &PPUInterpreter::VMHADDSHS, vd, va, vb, vc); } -void PPULLVMRecompiler::VMHRADDSHS(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMHRADDSHS(u32 vd, u32 va, u32 vb, u32 vc) { InterpreterCall("VMHRADDSHS", &PPUInterpreter::VMHRADDSHS, vd, va, vb, vc); } -void PPULLVMRecompiler::VMINFP(u32 vd, u32 va, u32 vb) { +void Compiler::VMINFP(u32 vd, u32 va, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto res_v4f32 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse_min_ps), va_v4f32, vb_v4f32); SetVr(vd, res_v4f32); } -void PPULLVMRecompiler::VMINSB(u32 vd, u32 va, u32 vb) { +void Compiler::VMINSB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto res_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pminsb), va_v16i8, vb_v16i8); SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VMINSH(u32 vd, u32 va, u32 vb) { +void Compiler::VMINSH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto res_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_pmins_w), va_v8i16, vb_v8i16); SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VMINSW(u32 vd, u32 va, u32 vb) { +void Compiler::VMINSW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto res_v4i32 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pminsd), va_v4i32, vb_v4i32); SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VMINUB(u32 vd, u32 va, u32 vb) { +void Compiler::VMINUB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto res_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_pminu_b), va_v16i8, vb_v16i8); SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VMINUH(u32 vd, u32 va, u32 vb) { +void Compiler::VMINUH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto res_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pminuw), va_v8i16, vb_v8i16); SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VMINUW(u32 vd, u32 va, u32 vb) { +void Compiler::VMINUW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto res_v4i32 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse41_pminud), va_v4i32, vb_v4i32); SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VMLADDUHM(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMLADDUHM(u32 vd, u32 va, u32 vb, u32 vc) { InterpreterCall("VMLADDUHM", &PPUInterpreter::VMLADDUHM, vd, va, vb, vc); } -void PPULLVMRecompiler::VMRGHB(u32 vd, u32 va, u32 vb) { +void Compiler::VMRGHB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); u32 mask_v16i32[16] = {24, 8, 25, 9, 26, 10, 27, 11, 28, 12, 29, 13, 30, 14, 31, 15}; @@ -798,7 +780,7 @@ void PPULLVMRecompiler::VMRGHB(u32 vd, u32 va, u32 vb) { SetVr(vd, vd_v16i8); } -void PPULLVMRecompiler::VMRGHH(u32 vd, u32 va, u32 vb) { +void Compiler::VMRGHH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); u32 mask_v8i32[8] = {12, 4, 13, 5, 14, 6, 15, 7}; @@ -806,7 +788,7 @@ void PPULLVMRecompiler::VMRGHH(u32 vd, u32 va, u32 vb) { SetVr(vd, vd_v8i16); } -void PPULLVMRecompiler::VMRGHW(u32 vd, u32 va, u32 vb) { +void Compiler::VMRGHW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); u32 mask_v4i32[4] = {6, 2, 7, 3}; @@ -814,7 +796,7 @@ void PPULLVMRecompiler::VMRGHW(u32 vd, u32 va, u32 vb) { SetVr(vd, vd_v4i32); } -void PPULLVMRecompiler::VMRGLB(u32 vd, u32 va, u32 vb) { +void Compiler::VMRGLB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); u32 mask_v16i32[16] = {16, 0, 17, 1, 18, 2, 19, 3, 20, 4, 21, 5, 22, 6, 23, 7}; @@ -822,7 +804,7 @@ void PPULLVMRecompiler::VMRGLB(u32 vd, u32 va, u32 vb) { SetVr(vd, vd_v16i8); } -void PPULLVMRecompiler::VMRGLH(u32 vd, u32 va, u32 vb) { +void Compiler::VMRGLH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); u32 mask_v8i32[8] = {8, 0, 9, 1, 10, 2, 11, 3}; @@ -830,7 +812,7 @@ void PPULLVMRecompiler::VMRGLH(u32 vd, u32 va, u32 vb) { SetVr(vd, vd_v8i16); } -void PPULLVMRecompiler::VMRGLW(u32 vd, u32 va, u32 vb) { +void Compiler::VMRGLW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); u32 mask_v4i32[4] = {4, 0, 5, 1}; @@ -838,7 +820,7 @@ void PPULLVMRecompiler::VMRGLW(u32 vd, u32 va, u32 vb) { SetVr(vd, vd_v4i32); } -void PPULLVMRecompiler::VMSUMMBM(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMSUMMBM(u32 vd, u32 va, u32 vb, u32 vc) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto va_v16i16 = m_ir_builder->CreateSExt(va_v16i8, VectorType::get(m_ir_builder->getInt16Ty(), 16)); @@ -870,7 +852,7 @@ void PPULLVMRecompiler::VMSUMMBM(u32 vd, u32 va, u32 vb, u32 vc) { // TODO: Try to optimize with horizontal add } -void PPULLVMRecompiler::VMSUMSHM(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMSUMSHM(u32 vd, u32 va, u32 vb, u32 vc) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto vc_v4i32 = GetVrAsIntVec(vc, 32); @@ -879,11 +861,11 @@ void PPULLVMRecompiler::VMSUMSHM(u32 vd, u32 va, u32 vb, u32 vc) { SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VMSUMSHS(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMSUMSHS(u32 vd, u32 va, u32 vb, u32 vc) { InterpreterCall("VMSUMSHS", &PPUInterpreter::VMSUMSHS, vd, va, vb, vc); } -void PPULLVMRecompiler::VMSUMUBM(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMSUMUBM(u32 vd, u32 va, u32 vb, u32 vc) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto va_v16i16 = m_ir_builder->CreateZExt(va_v16i8, VectorType::get(m_ir_builder->getInt16Ty(), 16)); @@ -915,7 +897,7 @@ void PPULLVMRecompiler::VMSUMUBM(u32 vd, u32 va, u32 vb, u32 vc) { // TODO: Try to optimize with horizontal add } -void PPULLVMRecompiler::VMSUMUHM(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMSUMUHM(u32 vd, u32 va, u32 vb, u32 vc) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto va_v8i32 = m_ir_builder->CreateZExt(va_v8i16, VectorType::get(m_ir_builder->getInt32Ty(), 8)); @@ -937,43 +919,43 @@ void PPULLVMRecompiler::VMSUMUHM(u32 vd, u32 va, u32 vb, u32 vc) { // TODO: Try to optimize with horizontal add } -void PPULLVMRecompiler::VMSUMUHS(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VMSUMUHS(u32 vd, u32 va, u32 vb, u32 vc) { InterpreterCall("VMSUMUHS", &PPUInterpreter::VMSUMUHS, vd, va, vb, vc); } -void PPULLVMRecompiler::VMULESB(u32 vd, u32 va, u32 vb) { +void Compiler::VMULESB(u32 vd, u32 va, u32 vb) { InterpreterCall("VMULESB", &PPUInterpreter::VMULESB, vd, va, vb); } -void PPULLVMRecompiler::VMULESH(u32 vd, u32 va, u32 vb) { +void Compiler::VMULESH(u32 vd, u32 va, u32 vb) { InterpreterCall("VMULESH", &PPUInterpreter::VMULESH, vd, va, vb); } -void PPULLVMRecompiler::VMULEUB(u32 vd, u32 va, u32 vb) { +void Compiler::VMULEUB(u32 vd, u32 va, u32 vb) { InterpreterCall("VMULEUB", &PPUInterpreter::VMULEUB, vd, va, vb); } -void PPULLVMRecompiler::VMULEUH(u32 vd, u32 va, u32 vb) { +void Compiler::VMULEUH(u32 vd, u32 va, u32 vb) { InterpreterCall("VMULEUH", &PPUInterpreter::VMULEUH, vd, va, vb); } -void PPULLVMRecompiler::VMULOSB(u32 vd, u32 va, u32 vb) { +void Compiler::VMULOSB(u32 vd, u32 va, u32 vb) { InterpreterCall("VMULOSB", &PPUInterpreter::VMULOSB, vd, va, vb); } -void PPULLVMRecompiler::VMULOSH(u32 vd, u32 va, u32 vb) { +void Compiler::VMULOSH(u32 vd, u32 va, u32 vb) { InterpreterCall("VMULOSH", &PPUInterpreter::VMULOSH, vd, va, vb); } -void PPULLVMRecompiler::VMULOUB(u32 vd, u32 va, u32 vb) { +void Compiler::VMULOUB(u32 vd, u32 va, u32 vb) { InterpreterCall("VMULOUB", &PPUInterpreter::VMULOUB, vd, va, vb); } -void PPULLVMRecompiler::VMULOUH(u32 vd, u32 va, u32 vb) { +void Compiler::VMULOUH(u32 vd, u32 va, u32 vb) { InterpreterCall("VMULOUH", &PPUInterpreter::VMULOUH, vd, va, vb); } -void PPULLVMRecompiler::VNMSUBFP(u32 vd, u32 va, u32 vc, u32 vb) { +void Compiler::VNMSUBFP(u32 vd, u32 va, u32 vc, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto vc_v4f32 = GetVrAsFloatVec(vc); @@ -982,7 +964,7 @@ void PPULLVMRecompiler::VNMSUBFP(u32 vd, u32 va, u32 vc, u32 vb) { SetVr(vd, res_v4f32); } -void PPULLVMRecompiler::VNOR(u32 vd, u32 va, u32 vb) { +void Compiler::VNOR(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto res_v8i16 = m_ir_builder->CreateOr(va_v8i16, vb_v8i16); @@ -990,14 +972,14 @@ void PPULLVMRecompiler::VNOR(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VOR(u32 vd, u32 va, u32 vb) { +void Compiler::VOR(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto res_v8i16 = m_ir_builder->CreateOr(va_v8i16, vb_v8i16); SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VPERM(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VPERM(u32 vd, u32 va, u32 vb, u32 vc) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto vc_v16i8 = GetVrAsIntVec(vc, 8); @@ -1019,81 +1001,81 @@ void PPULLVMRecompiler::VPERM(u32 vd, u32 va, u32 vb, u32 vc) { SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VPKPX(u32 vd, u32 va, u32 vb) { +void Compiler::VPKPX(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKPX", &PPUInterpreter::VPKPX, vd, va, vb); } -void PPULLVMRecompiler::VPKSHSS(u32 vd, u32 va, u32 vb) { +void Compiler::VPKSHSS(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKSHSS", &PPUInterpreter::VPKSHSS, vd, va, vb); } -void PPULLVMRecompiler::VPKSHUS(u32 vd, u32 va, u32 vb) { +void Compiler::VPKSHUS(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKSHUS", &PPUInterpreter::VPKSHUS, vd, va, vb); } -void PPULLVMRecompiler::VPKSWSS(u32 vd, u32 va, u32 vb) { +void Compiler::VPKSWSS(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKSWSS", &PPUInterpreter::VPKSWSS, vd, va, vb); } -void PPULLVMRecompiler::VPKSWUS(u32 vd, u32 va, u32 vb) { +void Compiler::VPKSWUS(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKSWUS", &PPUInterpreter::VPKSWUS, vd, va, vb); } -void PPULLVMRecompiler::VPKUHUM(u32 vd, u32 va, u32 vb) { +void Compiler::VPKUHUM(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKUHUM", &PPUInterpreter::VPKUHUM, vd, va, vb); } -void PPULLVMRecompiler::VPKUHUS(u32 vd, u32 va, u32 vb) { +void Compiler::VPKUHUS(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKUHUS", &PPUInterpreter::VPKUHUS, vd, va, vb); } -void PPULLVMRecompiler::VPKUWUM(u32 vd, u32 va, u32 vb) { +void Compiler::VPKUWUM(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKUWUM", &PPUInterpreter::VPKUWUM, vd, va, vb); } -void PPULLVMRecompiler::VPKUWUS(u32 vd, u32 va, u32 vb) { +void Compiler::VPKUWUS(u32 vd, u32 va, u32 vb) { InterpreterCall("VPKUWUS", &PPUInterpreter::VPKUWUS, vd, va, vb); } -void PPULLVMRecompiler::VREFP(u32 vd, u32 vb) { +void Compiler::VREFP(u32 vd, u32 vb) { auto vb_v4f32 = GetVrAsFloatVec(vb); auto res_v4f32 = m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse_rcp_ps), vb_v4f32); SetVr(vd, res_v4f32); } -void PPULLVMRecompiler::VRFIM(u32 vd, u32 vb) { +void Compiler::VRFIM(u32 vd, u32 vb) { InterpreterCall("VRFIM", &PPUInterpreter::VRFIM, vd, vb); } -void PPULLVMRecompiler::VRFIN(u32 vd, u32 vb) { +void Compiler::VRFIN(u32 vd, u32 vb) { InterpreterCall("VRFIN", &PPUInterpreter::VRFIN, vd, vb); } -void PPULLVMRecompiler::VRFIP(u32 vd, u32 vb) { +void Compiler::VRFIP(u32 vd, u32 vb) { InterpreterCall("VRFIP", &PPUInterpreter::VRFIP, vd, vb); } -void PPULLVMRecompiler::VRFIZ(u32 vd, u32 vb) { +void Compiler::VRFIZ(u32 vd, u32 vb) { InterpreterCall("VRFIZ", &PPUInterpreter::VRFIZ, vd, vb); } -void PPULLVMRecompiler::VRLB(u32 vd, u32 va, u32 vb) { +void Compiler::VRLB(u32 vd, u32 va, u32 vb) { InterpreterCall("VRLB", &PPUInterpreter::VRLB, vd, va, vb); } -void PPULLVMRecompiler::VRLH(u32 vd, u32 va, u32 vb) { +void Compiler::VRLH(u32 vd, u32 va, u32 vb) { InterpreterCall("VRLH", &PPUInterpreter::VRLH, vd, va, vb); } -void PPULLVMRecompiler::VRLW(u32 vd, u32 va, u32 vb) { +void Compiler::VRLW(u32 vd, u32 va, u32 vb) { InterpreterCall("VRLW", &PPUInterpreter::VRLW, vd, va, vb); } -void PPULLVMRecompiler::VRSQRTEFP(u32 vd, u32 vb) { +void Compiler::VRSQRTEFP(u32 vd, u32 vb) { InterpreterCall("VRSQRTEFP", &PPUInterpreter::VRSQRTEFP, vd, vb); } -void PPULLVMRecompiler::VSEL(u32 vd, u32 va, u32 vb, u32 vc) { +void Compiler::VSEL(u32 vd, u32 va, u32 vb, u32 vc) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto vc_v4i32 = GetVrAsIntVec(vc, 32); @@ -1104,7 +1086,7 @@ void PPULLVMRecompiler::VSEL(u32 vd, u32 va, u32 vb, u32 vc) { SetVr(vd, vd_v4i32); } -void PPULLVMRecompiler::VSL(u32 vd, u32 va, u32 vb) { +void Compiler::VSL(u32 vd, u32 va, u32 vb) { auto va_i128 = GetVr(va); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto sh_i8 = m_ir_builder->CreateExtractElement(vb_v16i8, m_ir_builder->getInt8(0)); @@ -1114,7 +1096,7 @@ void PPULLVMRecompiler::VSL(u32 vd, u32 va, u32 vb) { SetVr(vd, va_i128); } -void PPULLVMRecompiler::VSLB(u32 vd, u32 va, u32 vb) { +void Compiler::VSLB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); vb_v16i8 = m_ir_builder->CreateAnd(vb_v16i8, m_ir_builder->CreateVectorSplat(16, m_ir_builder->getInt8(0x7))); @@ -1122,7 +1104,7 @@ void PPULLVMRecompiler::VSLB(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VSLDOI(u32 vd, u32 va, u32 vb, u32 sh) { +void Compiler::VSLDOI(u32 vd, u32 va, u32 vb, u32 sh) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); sh = 16 - sh; @@ -1131,7 +1113,7 @@ void PPULLVMRecompiler::VSLDOI(u32 vd, u32 va, u32 vb, u32 sh) { SetVr(vd, vd_v16i8); } -void PPULLVMRecompiler::VSLH(u32 vd, u32 va, u32 vb) { +void Compiler::VSLH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); vb_v8i16 = m_ir_builder->CreateAnd(vb_v8i16, m_ir_builder->CreateVectorSplat(8, m_ir_builder->getInt16(0xF))); @@ -1139,7 +1121,7 @@ void PPULLVMRecompiler::VSLH(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VSLO(u32 vd, u32 va, u32 vb) { +void Compiler::VSLO(u32 vd, u32 va, u32 vb) { auto va_i128 = GetVr(va); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto sh_i8 = m_ir_builder->CreateExtractElement(vb_v16i8, m_ir_builder->getInt8(0)); @@ -1149,7 +1131,7 @@ void PPULLVMRecompiler::VSLO(u32 vd, u32 va, u32 vb) { SetVr(vd, va_i128); } -void PPULLVMRecompiler::VSLW(u32 vd, u32 va, u32 vb) { +void Compiler::VSLW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); vb_v4i32 = m_ir_builder->CreateAnd(vb_v4i32, m_ir_builder->CreateVectorSplat(4, m_ir_builder->getInt32(0x1F))); @@ -1157,7 +1139,7 @@ void PPULLVMRecompiler::VSLW(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VSPLTB(u32 vd, u32 uimm5, u32 vb) { +void Compiler::VSPLTB(u32 vd, u32 uimm5, u32 vb) { auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto undef_v16i8 = UndefValue::get(VectorType::get(m_ir_builder->getInt8Ty(), 16)); auto mask_v16i32 = m_ir_builder->CreateVectorSplat(16, m_ir_builder->getInt32(15 - uimm5)); @@ -1165,7 +1147,7 @@ void PPULLVMRecompiler::VSPLTB(u32 vd, u32 uimm5, u32 vb) { SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VSPLTH(u32 vd, u32 uimm5, u32 vb) { +void Compiler::VSPLTH(u32 vd, u32 uimm5, u32 vb) { auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto undef_v8i16 = UndefValue::get(VectorType::get(m_ir_builder->getInt16Ty(), 8)); auto mask_v8i32 = m_ir_builder->CreateVectorSplat(8, m_ir_builder->getInt32(7 - uimm5)); @@ -1173,22 +1155,22 @@ void PPULLVMRecompiler::VSPLTH(u32 vd, u32 uimm5, u32 vb) { SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VSPLTISB(u32 vd, s32 simm5) { +void Compiler::VSPLTISB(u32 vd, s32 simm5) { auto vd_v16i8 = m_ir_builder->CreateVectorSplat(16, m_ir_builder->getInt8((s8)simm5)); SetVr(vd, vd_v16i8); } -void PPULLVMRecompiler::VSPLTISH(u32 vd, s32 simm5) { +void Compiler::VSPLTISH(u32 vd, s32 simm5) { auto vd_v8i16 = m_ir_builder->CreateVectorSplat(8, m_ir_builder->getInt16((s16)simm5)); SetVr(vd, vd_v8i16); } -void PPULLVMRecompiler::VSPLTISW(u32 vd, s32 simm5) { +void Compiler::VSPLTISW(u32 vd, s32 simm5) { auto vd_v4i32 = m_ir_builder->CreateVectorSplat(4, m_ir_builder->getInt32((s32)simm5)); SetVr(vd, vd_v4i32); } -void PPULLVMRecompiler::VSPLTW(u32 vd, u32 uimm5, u32 vb) { +void Compiler::VSPLTW(u32 vd, u32 uimm5, u32 vb) { auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto undef_v4i32 = UndefValue::get(VectorType::get(m_ir_builder->getInt32Ty(), 4)); auto mask_v4i32 = m_ir_builder->CreateVectorSplat(4, m_ir_builder->getInt32(3 - uimm5)); @@ -1196,7 +1178,7 @@ void PPULLVMRecompiler::VSPLTW(u32 vd, u32 uimm5, u32 vb) { SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VSR(u32 vd, u32 va, u32 vb) { +void Compiler::VSR(u32 vd, u32 va, u32 vb) { auto va_i128 = GetVr(va); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto sh_i8 = m_ir_builder->CreateExtractElement(vb_v16i8, m_ir_builder->getInt8(0)); @@ -1206,7 +1188,7 @@ void PPULLVMRecompiler::VSR(u32 vd, u32 va, u32 vb) { SetVr(vd, va_i128); } -void PPULLVMRecompiler::VSRAB(u32 vd, u32 va, u32 vb) { +void Compiler::VSRAB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); vb_v16i8 = m_ir_builder->CreateAnd(vb_v16i8, m_ir_builder->CreateVectorSplat(16, m_ir_builder->getInt8(0x7))); @@ -1214,7 +1196,7 @@ void PPULLVMRecompiler::VSRAB(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VSRAH(u32 vd, u32 va, u32 vb) { +void Compiler::VSRAH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); vb_v8i16 = m_ir_builder->CreateAnd(vb_v8i16, m_ir_builder->CreateVectorSplat(8, m_ir_builder->getInt16(0xF))); @@ -1222,7 +1204,7 @@ void PPULLVMRecompiler::VSRAH(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VSRAW(u32 vd, u32 va, u32 vb) { +void Compiler::VSRAW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); vb_v4i32 = m_ir_builder->CreateAnd(vb_v4i32, m_ir_builder->CreateVectorSplat(4, m_ir_builder->getInt32(0x1F))); @@ -1230,7 +1212,7 @@ void PPULLVMRecompiler::VSRAW(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VSRB(u32 vd, u32 va, u32 vb) { +void Compiler::VSRB(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); vb_v16i8 = m_ir_builder->CreateAnd(vb_v16i8, m_ir_builder->CreateVectorSplat(16, m_ir_builder->getInt8(0x7))); @@ -1238,7 +1220,7 @@ void PPULLVMRecompiler::VSRB(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v16i8); } -void PPULLVMRecompiler::VSRH(u32 vd, u32 va, u32 vb) { +void Compiler::VSRH(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); vb_v8i16 = m_ir_builder->CreateAnd(vb_v8i16, m_ir_builder->CreateVectorSplat(8, m_ir_builder->getInt16(0xF))); @@ -1246,7 +1228,7 @@ void PPULLVMRecompiler::VSRH(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::VSRO(u32 vd, u32 va, u32 vb) { +void Compiler::VSRO(u32 vd, u32 va, u32 vb) { auto va_i128 = GetVr(va); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto sh_i8 = m_ir_builder->CreateExtractElement(vb_v16i8, m_ir_builder->getInt8(0)); @@ -1256,7 +1238,7 @@ void PPULLVMRecompiler::VSRO(u32 vd, u32 va, u32 vb) { SetVr(vd, va_i128); } -void PPULLVMRecompiler::VSRW(u32 vd, u32 va, u32 vb) { +void Compiler::VSRW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); vb_v4i32 = m_ir_builder->CreateAnd(vb_v4i32, m_ir_builder->CreateVectorSplat(4, m_ir_builder->getInt32(0x1F))); @@ -1264,7 +1246,7 @@ void PPULLVMRecompiler::VSRW(u32 vd, u32 va, u32 vb) { SetVr(vd, res_v4i32); } -void PPULLVMRecompiler::VSUBCUW(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBCUW(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); @@ -1273,14 +1255,14 @@ void PPULLVMRecompiler::VSUBCUW(u32 vd, u32 va, u32 vb) { SetVr(vd, cmpv4i32); } -void PPULLVMRecompiler::VSUBFP(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBFP(u32 vd, u32 va, u32 vb) { auto va_v4f32 = GetVrAsFloatVec(va); auto vb_v4f32 = GetVrAsFloatVec(vb); auto diff_v4f32 = m_ir_builder->CreateFSub(va_v4f32, vb_v4f32); SetVr(vd, diff_v4f32); } -void PPULLVMRecompiler::VSUBSBS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBSBS(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto diff_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_psubs_b), va_v16i8, vb_v16i8); @@ -1289,7 +1271,7 @@ void PPULLVMRecompiler::VSUBSBS(u32 vd, u32 va, u32 vb) { // TODO: Set VSCR.SAT } -void PPULLVMRecompiler::VSUBSHS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBSHS(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto diff_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_psubs_w), va_v8i16, vb_v8i16); @@ -1298,7 +1280,7 @@ void PPULLVMRecompiler::VSUBSHS(u32 vd, u32 va, u32 vb) { // TODO: Set VSCR.SAT } -void PPULLVMRecompiler::VSUBSWS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBSWS(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); @@ -1329,14 +1311,14 @@ void PPULLVMRecompiler::VSUBSWS(u32 vd, u32 va, u32 vb) { // TODO: Set SAT } -void PPULLVMRecompiler::VSUBUBM(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBUBM(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto diff_v16i8 = m_ir_builder->CreateSub(va_v16i8, vb_v16i8); SetVr(vd, diff_v16i8); } -void PPULLVMRecompiler::VSUBUBS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBUBS(u32 vd, u32 va, u32 vb) { auto va_v16i8 = GetVrAsIntVec(va, 8); auto vb_v16i8 = GetVrAsIntVec(vb, 8); auto diff_v16i8 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_psubus_b), va_v16i8, vb_v16i8); @@ -1345,14 +1327,14 @@ void PPULLVMRecompiler::VSUBUBS(u32 vd, u32 va, u32 vb) { // TODO: Set SAT } -void PPULLVMRecompiler::VSUBUHM(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBUHM(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto diff_v8i16 = m_ir_builder->CreateSub(va_v8i16, vb_v8i16); SetVr(vd, diff_v8i16); } -void PPULLVMRecompiler::VSUBUHS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBUHS(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto diff_v8i16 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_psubus_w), va_v8i16, vb_v8i16); @@ -1361,14 +1343,14 @@ void PPULLVMRecompiler::VSUBUHS(u32 vd, u32 va, u32 vb) { // TODO: Set SAT } -void PPULLVMRecompiler::VSUBUWM(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBUWM(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto diff_v4i32 = m_ir_builder->CreateSub(va_v4i32, vb_v4i32); SetVr(vd, diff_v4i32); } -void PPULLVMRecompiler::VSUBUWS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUBUWS(u32 vd, u32 va, u32 vb) { auto va_v4i32 = GetVrAsIntVec(va, 32); auto vb_v4i32 = GetVrAsIntVec(vb, 32); auto diff_v4i32 = m_ir_builder->CreateSub(va_v4i32, vb_v4i32); @@ -1380,65 +1362,65 @@ void PPULLVMRecompiler::VSUBUWS(u32 vd, u32 va, u32 vb) { // TODO: Set SAT } -void PPULLVMRecompiler::VSUMSWS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUMSWS(u32 vd, u32 va, u32 vb) { InterpreterCall("VSUMSWS", &PPUInterpreter::VSUMSWS, vd, va, vb); } -void PPULLVMRecompiler::VSUM2SWS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUM2SWS(u32 vd, u32 va, u32 vb) { InterpreterCall("VSUM2SWS", &PPUInterpreter::VSUM2SWS, vd, va, vb); } -void PPULLVMRecompiler::VSUM4SBS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUM4SBS(u32 vd, u32 va, u32 vb) { InterpreterCall("VSUM4SBS", &PPUInterpreter::VSUM4SBS, vd, va, vb); } -void PPULLVMRecompiler::VSUM4SHS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUM4SHS(u32 vd, u32 va, u32 vb) { InterpreterCall("VSUM4SHS", &PPUInterpreter::VSUM4SHS, vd, va, vb); } -void PPULLVMRecompiler::VSUM4UBS(u32 vd, u32 va, u32 vb) { +void Compiler::VSUM4UBS(u32 vd, u32 va, u32 vb) { InterpreterCall("VSUM4UBS", &PPUInterpreter::VSUM4UBS, vd, va, vb); } -void PPULLVMRecompiler::VUPKHPX(u32 vd, u32 vb) { +void Compiler::VUPKHPX(u32 vd, u32 vb) { InterpreterCall("VUPKHPX", &PPUInterpreter::VUPKHPX, vd, vb); } -void PPULLVMRecompiler::VUPKHSB(u32 vd, u32 vb) { +void Compiler::VUPKHSB(u32 vd, u32 vb) { InterpreterCall("VUPKHSB", &PPUInterpreter::VUPKHSB, vd, vb); } -void PPULLVMRecompiler::VUPKHSH(u32 vd, u32 vb) { +void Compiler::VUPKHSH(u32 vd, u32 vb) { InterpreterCall("VUPKHSH", &PPUInterpreter::VUPKHSH, vd, vb); } -void PPULLVMRecompiler::VUPKLPX(u32 vd, u32 vb) { +void Compiler::VUPKLPX(u32 vd, u32 vb) { InterpreterCall("VUPKLPX", &PPUInterpreter::VUPKLPX, vd, vb); } -void PPULLVMRecompiler::VUPKLSB(u32 vd, u32 vb) { +void Compiler::VUPKLSB(u32 vd, u32 vb) { InterpreterCall("VUPKLSB", &PPUInterpreter::VUPKLSB, vd, vb); } -void PPULLVMRecompiler::VUPKLSH(u32 vd, u32 vb) { +void Compiler::VUPKLSH(u32 vd, u32 vb) { InterpreterCall("VUPKLSH", &PPUInterpreter::VUPKLSH, vd, vb); } -void PPULLVMRecompiler::VXOR(u32 vd, u32 va, u32 vb) { +void Compiler::VXOR(u32 vd, u32 va, u32 vb) { auto va_v8i16 = GetVrAsIntVec(va, 16); auto vb_v8i16 = GetVrAsIntVec(vb, 16); auto res_v8i16 = m_ir_builder->CreateXor(va_v8i16, vb_v8i16); SetVr(vd, res_v8i16); } -void PPULLVMRecompiler::MULLI(u32 rd, u32 ra, s32 simm16) { +void Compiler::MULLI(u32 rd, u32 ra, s32 simm16) { auto ra_i64 = GetGpr(ra); auto res_i64 = m_ir_builder->CreateMul(ra_i64, m_ir_builder->getInt64((s64)simm16)); SetGpr(rd, res_i64); //InterpreterCall("MULLI", &PPUInterpreter::MULLI, rd, ra, simm16); } -void PPULLVMRecompiler::SUBFIC(u32 rd, u32 ra, s32 simm16) { +void Compiler::SUBFIC(u32 rd, u32 ra, s32 simm16) { auto ra_i64 = GetGpr(ra); ra_i64 = m_ir_builder->CreateNeg(ra_i64); auto res_s = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::uadd_with_overflow, m_ir_builder->getInt64Ty()), ra_i64, m_ir_builder->getInt64((s64)simm16)); @@ -1449,7 +1431,7 @@ void PPULLVMRecompiler::SUBFIC(u32 rd, u32 ra, s32 simm16) { //InterpreterCall("SUBFIC", &PPUInterpreter::SUBFIC, rd, ra, simm16); } -void PPULLVMRecompiler::CMPLI(u32 crfd, u32 l, u32 ra, u32 uimm16) { +void Compiler::CMPLI(u32 crfd, u32 l, u32 ra, u32 uimm16) { Value * ra_i64; if (l == 0) { ra_i64 = m_ir_builder->CreateZExt(GetGpr(ra, 32), m_ir_builder->getInt64Ty()); @@ -1461,7 +1443,7 @@ void PPULLVMRecompiler::CMPLI(u32 crfd, u32 l, u32 ra, u32 uimm16) { //InterpreterCall("CMPLI", &PPUInterpreter::CMPLI, crfd, l, ra, uimm16); } -void PPULLVMRecompiler::CMPI(u32 crfd, u32 l, u32 ra, s32 simm16) { +void Compiler::CMPI(u32 crfd, u32 l, u32 ra, s32 simm16) { Value * ra_i64; if (l == 0) { ra_i64 = m_ir_builder->CreateSExt(GetGpr(ra, 32), m_ir_builder->getInt64Ty()); @@ -1473,7 +1455,7 @@ void PPULLVMRecompiler::CMPI(u32 crfd, u32 l, u32 ra, s32 simm16) { //InterpreterCall("CMPI", &PPUInterpreter::CMPI, crfd, l, ra, simm16); } -void PPULLVMRecompiler::ADDIC(u32 rd, u32 ra, s32 simm16) { +void Compiler::ADDIC(u32 rd, u32 ra, s32 simm16) { auto ra_i64 = GetGpr(ra); auto res_s = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::uadd_with_overflow, m_ir_builder->getInt64Ty()), m_ir_builder->getInt64((s64)simm16), ra_i64); auto sum_i64 = m_ir_builder->CreateExtractValue(res_s, {0}); @@ -1483,13 +1465,13 @@ void PPULLVMRecompiler::ADDIC(u32 rd, u32 ra, s32 simm16) { //InterpreterCall("ADDIC", &PPUInterpreter::ADDIC, rd, ra, simm16); } -void PPULLVMRecompiler::ADDIC_(u32 rd, u32 ra, s32 simm16) { +void Compiler::ADDIC_(u32 rd, u32 ra, s32 simm16) { ADDIC(rd, ra, simm16); SetCrFieldSignedCmp(0, GetGpr(rd), m_ir_builder->getInt64(0)); //InterpreterCall("ADDIC_", &PPUInterpreter::ADDIC_, rd, ra, simm16); } -void PPULLVMRecompiler::ADDI(u32 rd, u32 ra, s32 simm16) { +void Compiler::ADDI(u32 rd, u32 ra, s32 simm16) { if (ra == 0) { SetGpr(rd, m_ir_builder->getInt64((s64)simm16)); } else { @@ -1500,7 +1482,7 @@ void PPULLVMRecompiler::ADDI(u32 rd, u32 ra, s32 simm16) { //InterpreterCall("ADDI", &PPUInterpreter::ADDI, rd, ra, simm16); } -void PPULLVMRecompiler::ADDIS(u32 rd, u32 ra, s32 simm16) { +void Compiler::ADDIS(u32 rd, u32 ra, s32 simm16) { if (ra == 0) { SetGpr(rd, m_ir_builder->getInt64((s64)simm16 << 16)); } else { @@ -1511,7 +1493,7 @@ void PPULLVMRecompiler::ADDIS(u32 rd, u32 ra, s32 simm16) { //InterpreterCall("ADDIS", &PPUInterpreter::ADDIS, rd, ra, simm16); } -void PPULLVMRecompiler::BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) { +void Compiler::BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) { auto target_i64 = m_ir_builder->getInt64(branchTarget(aa ? 0 : m_current_instruction_address, bd)); CreateBranch(CheckBranchCondition(bo, bi), target_i64, lk ? true : false); //m_hit_branch_instruction = true; @@ -1521,11 +1503,11 @@ void PPULLVMRecompiler::BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) { //m_ir_builder->CreateRetVoid(); } -void PPULLVMRecompiler::SC(u32 sc_code) { +void Compiler::SC(u32 sc_code) { InterpreterCall("SC", &PPUInterpreter::SC, sc_code); } -void PPULLVMRecompiler::B(s32 ll, u32 aa, u32 lk) { +void Compiler::B(s32 ll, u32 aa, u32 lk) { auto target_i64 = m_ir_builder->getInt64(branchTarget(aa ? 0 : m_current_instruction_address, ll)); CreateBranch(nullptr, target_i64, lk ? true : false); //m_hit_branch_instruction = true; @@ -1534,7 +1516,7 @@ void PPULLVMRecompiler::B(s32 ll, u32 aa, u32 lk) { //m_ir_builder->CreateRetVoid(); } -void PPULLVMRecompiler::MCRF(u32 crfd, u32 crfs) { +void Compiler::MCRF(u32 crfd, u32 crfs) { if (crfd != crfs) { auto cr_i32 = GetCr(); auto crf_i32 = GetNibble(cr_i32, crfs); @@ -1544,10 +1526,10 @@ void PPULLVMRecompiler::MCRF(u32 crfd, u32 crfs) { //InterpreterCall("MCRF", &PPUInterpreter::MCRF, crfd, crfs); } -void PPULLVMRecompiler::BCLR(u32 bo, u32 bi, u32 bh, u32 lk) { +void Compiler::BCLR(u32 bo, u32 bi, u32 bh, u32 lk) { auto lr_i64 = GetLr(); lr_i64 = m_ir_builder->CreateAnd(lr_i64, ~0x3ULL); - CreateBranch(CheckBranchCondition(bo, bi), lr_i64, lk ? true : false); + CreateBranch(CheckBranchCondition(bo, bi), lr_i64, lk ? true : false, true); //m_hit_branch_instruction = true; //SetPc(m_ir_builder->getInt32(m_current_instruction_address)); //InterpreterCall("BCLR", &PPUInterpreter::BCLR, bo, bi, bh, lk); @@ -1555,7 +1537,7 @@ void PPULLVMRecompiler::BCLR(u32 bo, u32 bi, u32 bh, u32 lk) { //m_ir_builder->CreateRetVoid(); } -void PPULLVMRecompiler::CRNOR(u32 crbd, u32 crba, u32 crbb) { +void Compiler::CRNOR(u32 crbd, u32 crba, u32 crbb) { auto cr_i32 = GetCr(); auto ba_i32 = GetBit(cr_i32, crba); auto bb_i32 = GetBit(cr_i32, crbb); @@ -1566,7 +1548,7 @@ void PPULLVMRecompiler::CRNOR(u32 crbd, u32 crba, u32 crbb) { //InterpreterCall("CRNOR", &PPUInterpreter::CRNOR, crbd, crba, crbb); } -void PPULLVMRecompiler::CRANDC(u32 crbd, u32 crba, u32 crbb) { +void Compiler::CRANDC(u32 crbd, u32 crba, u32 crbb) { auto cr_i32 = GetCr(); auto ba_i32 = GetBit(cr_i32, crba); auto bb_i32 = GetBit(cr_i32, crbb); @@ -1577,12 +1559,12 @@ void PPULLVMRecompiler::CRANDC(u32 crbd, u32 crba, u32 crbb) { //InterpreterCall("CRANDC", &PPUInterpreter::CRANDC, crbd, crba, crbb); } -void PPULLVMRecompiler::ISYNC() { +void Compiler::ISYNC() { m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_mfence)); //InterpreterCall("ISYNC", &PPUInterpreter::ISYNC); } -void PPULLVMRecompiler::CRXOR(u32 crbd, u32 crba, u32 crbb) { +void Compiler::CRXOR(u32 crbd, u32 crba, u32 crbb) { auto cr_i32 = GetCr(); auto ba_i32 = GetBit(cr_i32, crba); auto bb_i32 = GetBit(cr_i32, crbb); @@ -1592,7 +1574,7 @@ void PPULLVMRecompiler::CRXOR(u32 crbd, u32 crba, u32 crbb) { //InterpreterCall("CRXOR", &PPUInterpreter::CRXOR, crbd, crba, crbb); } -void PPULLVMRecompiler::CRNAND(u32 crbd, u32 crba, u32 crbb) { +void Compiler::CRNAND(u32 crbd, u32 crba, u32 crbb) { auto cr_i32 = GetCr(); auto ba_i32 = GetBit(cr_i32, crba); auto bb_i32 = GetBit(cr_i32, crbb); @@ -1603,7 +1585,7 @@ void PPULLVMRecompiler::CRNAND(u32 crbd, u32 crba, u32 crbb) { //InterpreterCall("CRNAND", &PPUInterpreter::CRNAND, crbd, crba, crbb); } -void PPULLVMRecompiler::CRAND(u32 crbd, u32 crba, u32 crbb) { +void Compiler::CRAND(u32 crbd, u32 crba, u32 crbb) { auto cr_i32 = GetCr(); auto ba_i32 = GetBit(cr_i32, crba); auto bb_i32 = GetBit(cr_i32, crbb); @@ -1613,7 +1595,7 @@ void PPULLVMRecompiler::CRAND(u32 crbd, u32 crba, u32 crbb) { //InterpreterCall("CRAND", &PPUInterpreter::CRAND, crbd, crba, crbb); } -void PPULLVMRecompiler::CREQV(u32 crbd, u32 crba, u32 crbb) { +void Compiler::CREQV(u32 crbd, u32 crba, u32 crbb) { auto cr_i32 = GetCr(); auto ba_i32 = GetBit(cr_i32, crba); auto bb_i32 = GetBit(cr_i32, crbb); @@ -1624,7 +1606,7 @@ void PPULLVMRecompiler::CREQV(u32 crbd, u32 crba, u32 crbb) { //InterpreterCall("CREQV", &PPUInterpreter::CREQV, crbd, crba, crbb); } -void PPULLVMRecompiler::CRORC(u32 crbd, u32 crba, u32 crbb) { +void Compiler::CRORC(u32 crbd, u32 crba, u32 crbb) { auto cr_i32 = GetCr(); auto ba_i32 = GetBit(cr_i32, crba); auto bb_i32 = GetBit(cr_i32, crbb); @@ -1635,7 +1617,7 @@ void PPULLVMRecompiler::CRORC(u32 crbd, u32 crba, u32 crbb) { //InterpreterCall("CRORC", &PPUInterpreter::CRORC, crbd, crba, crbb); } -void PPULLVMRecompiler::CROR(u32 crbd, u32 crba, u32 crbb) { +void Compiler::CROR(u32 crbd, u32 crba, u32 crbb) { auto cr_i32 = GetCr(); auto ba_i32 = GetBit(cr_i32, crba); auto bb_i32 = GetBit(cr_i32, crbb); @@ -1645,7 +1627,7 @@ void PPULLVMRecompiler::CROR(u32 crbd, u32 crba, u32 crbb) { //InterpreterCall("CROR", &PPUInterpreter::CROR, crbd, crba, crbb); } -void PPULLVMRecompiler::BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) { +void Compiler::BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) { auto ctr_i64 = GetCtr(); ctr_i64 = m_ir_builder->CreateAnd(ctr_i64, ~0x3ULL); CreateBranch(CheckBranchCondition(bo, bi), ctr_i64, lk ? true : false); @@ -1656,7 +1638,7 @@ void PPULLVMRecompiler::BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) { //m_ir_builder->CreateRetVoid(); } -void PPULLVMRecompiler::RLWIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) { +void Compiler::RLWIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto rs_i64 = m_ir_builder->CreateZExt(rs_i32, m_ir_builder->getInt64Ty()); auto rsh_i64 = m_ir_builder->CreateShl(rs_i64, 32); @@ -1681,7 +1663,7 @@ void PPULLVMRecompiler::RLWIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) //InterpreterCall("RLWIMI", &PPUInterpreter::RLWIMI, ra, rs, sh, mb, me, rc); } -void PPULLVMRecompiler::RLWINM(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) { +void Compiler::RLWINM(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto rs_i64 = m_ir_builder->CreateZExt(rs_i32, m_ir_builder->getInt64Ty()); auto rsh_i64 = m_ir_builder->CreateShl(rs_i64, 32); @@ -1702,7 +1684,7 @@ void PPULLVMRecompiler::RLWINM(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) //InterpreterCall("RLWINM", &PPUInterpreter::RLWINM, ra, rs, sh, mb, me, rc); } -void PPULLVMRecompiler::RLWNM(u32 ra, u32 rs, u32 rb, u32 mb, u32 me, bool rc) { +void Compiler::RLWNM(u32 ra, u32 rs, u32 rb, u32 mb, u32 me, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto rs_i64 = m_ir_builder->CreateZExt(rs_i32, m_ir_builder->getInt64Ty()); auto rsh_i64 = m_ir_builder->CreateShl(rs_i64, 32); @@ -1722,35 +1704,35 @@ void PPULLVMRecompiler::RLWNM(u32 ra, u32 rs, u32 rb, u32 mb, u32 me, bool rc) { //InterpreterCall("RLWNM", &PPUInterpreter::RLWNM, ra, rs, rb, mb, me, rc); } -void PPULLVMRecompiler::ORI(u32 ra, u32 rs, u32 uimm16) { +void Compiler::ORI(u32 ra, u32 rs, u32 uimm16) { auto rs_i64 = GetGpr(rs); auto res_i64 = m_ir_builder->CreateOr(rs_i64, uimm16); SetGpr(ra, res_i64); //InterpreterCall("ORI", &PPUInterpreter::ORI, ra, rs, uimm16); } -void PPULLVMRecompiler::ORIS(u32 ra, u32 rs, u32 uimm16) { +void Compiler::ORIS(u32 ra, u32 rs, u32 uimm16) { auto rs_i64 = GetGpr(rs); auto res_i64 = m_ir_builder->CreateOr(rs_i64, (u64)uimm16 << 16); SetGpr(ra, res_i64); //InterpreterCall("ORIS", &PPUInterpreter::ORIS, ra, rs, uimm16); } -void PPULLVMRecompiler::XORI(u32 ra, u32 rs, u32 uimm16) { +void Compiler::XORI(u32 ra, u32 rs, u32 uimm16) { auto rs_i64 = GetGpr(rs); auto res_i64 = m_ir_builder->CreateXor(rs_i64, uimm16); SetGpr(ra, res_i64); //InterpreterCall("XORI", &PPUInterpreter::XORI, ra, rs, uimm16); } -void PPULLVMRecompiler::XORIS(u32 ra, u32 rs, u32 uimm16) { +void Compiler::XORIS(u32 ra, u32 rs, u32 uimm16) { auto rs_i64 = GetGpr(rs); auto res_i64 = m_ir_builder->CreateXor(rs_i64, (u64)uimm16 << 16); SetGpr(ra, res_i64); //InterpreterCall("XORIS", &PPUInterpreter::XORIS, ra, rs, uimm16); } -void PPULLVMRecompiler::ANDI_(u32 ra, u32 rs, u32 uimm16) { +void Compiler::ANDI_(u32 ra, u32 rs, u32 uimm16) { auto rs_i64 = GetGpr(rs); auto res_i64 = m_ir_builder->CreateAnd(rs_i64, uimm16); SetGpr(ra, res_i64); @@ -1758,7 +1740,7 @@ void PPULLVMRecompiler::ANDI_(u32 ra, u32 rs, u32 uimm16) { //InterpreterCall("ANDI_", &PPUInterpreter::ANDI_, ra, rs, uimm16); } -void PPULLVMRecompiler::ANDIS_(u32 ra, u32 rs, u32 uimm16) { +void Compiler::ANDIS_(u32 ra, u32 rs, u32 uimm16) { auto rs_i64 = GetGpr(rs); auto res_i64 = m_ir_builder->CreateAnd(rs_i64, (u64)uimm16 << 16); SetGpr(ra, res_i64); @@ -1766,7 +1748,7 @@ void PPULLVMRecompiler::ANDIS_(u32 ra, u32 rs, u32 uimm16) { //InterpreterCall("ANDIS_", &PPUInterpreter::ANDIS_, ra, rs, uimm16); } -void PPULLVMRecompiler::RLDICL(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { +void Compiler::RLDICL(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { auto rs_i64 = GetGpr(rs); auto res_i64 = rs_i64; if (sh) { @@ -1784,7 +1766,7 @@ void PPULLVMRecompiler::RLDICL(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { //InterpreterCall("RLDICL", &PPUInterpreter::RLDICL, ra, rs, sh, mb, rc); } -void PPULLVMRecompiler::RLDICR(u32 ra, u32 rs, u32 sh, u32 me, bool rc) { +void Compiler::RLDICR(u32 ra, u32 rs, u32 sh, u32 me, bool rc) { auto rs_i64 = GetGpr(rs); auto res_i64 = rs_i64; if (sh) { @@ -1802,7 +1784,7 @@ void PPULLVMRecompiler::RLDICR(u32 ra, u32 rs, u32 sh, u32 me, bool rc) { //InterpreterCall("RLDICR", &PPUInterpreter::RLDICR, ra, rs, sh, me, rc); } -void PPULLVMRecompiler::RLDIC(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { +void Compiler::RLDIC(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { auto rs_i64 = GetGpr(rs); auto res_i64 = rs_i64; if (sh) { @@ -1820,7 +1802,7 @@ void PPULLVMRecompiler::RLDIC(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { //InterpreterCall("RLDIC", &PPUInterpreter::RLDIC, ra, rs, sh, mb, rc); } -void PPULLVMRecompiler::RLDIMI(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { +void Compiler::RLDIMI(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { auto rs_i64 = GetGpr(rs); auto ra_i64 = GetGpr(ra); auto res_i64 = rs_i64; @@ -1842,7 +1824,7 @@ void PPULLVMRecompiler::RLDIMI(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) { //InterpreterCall("RLDIMI", &PPUInterpreter::RLDIMI, ra, rs, sh, mb, rc); } -void PPULLVMRecompiler::RLDC_LR(u32 ra, u32 rs, u32 rb, u32 m_eb, bool is_r, bool rc) { +void Compiler::RLDC_LR(u32 ra, u32 rs, u32 rb, u32 m_eb, bool is_r, bool rc) { auto rs_i64 = GetGpr(rs); auto rb_i64 = GetGpr(rb); auto shl_i64 = m_ir_builder->CreateAnd(rb_i64, 0x3F); @@ -1865,7 +1847,7 @@ void PPULLVMRecompiler::RLDC_LR(u32 ra, u32 rs, u32 rb, u32 m_eb, bool is_r, boo //InterpreterCall("RLDC_LR", &PPUInterpreter::RLDC_LR, ra, rs, rb, m_eb, is_r, rc); } -void PPULLVMRecompiler::CMP(u32 crfd, u32 l, u32 ra, u32 rb) { +void Compiler::CMP(u32 crfd, u32 l, u32 ra, u32 rb) { Value * ra_i64; Value * rb_i64; if (l == 0) { @@ -1880,11 +1862,11 @@ void PPULLVMRecompiler::CMP(u32 crfd, u32 l, u32 ra, u32 rb) { //InterpreterCall("CMP", &PPUInterpreter::CMP, crfd, l, ra, rb); } -void PPULLVMRecompiler::TW(u32 to, u32 ra, u32 rb) { +void Compiler::TW(u32 to, u32 ra, u32 rb) { InterpreterCall("TW", &PPUInterpreter::TW, to, ra, rb); } -void PPULLVMRecompiler::LVSL(u32 vd, u32 ra, u32 rb) { +void Compiler::LVSL(u32 vd, u32 ra, u32 rb) { static const u128 s_lvsl_values[] = { {0x08090A0B0C0D0E0F, 0x0001020304050607}, {0x090A0B0C0D0E0F10, 0x0102030405060708}, @@ -1919,7 +1901,7 @@ void PPULLVMRecompiler::LVSL(u32 vd, u32 ra, u32 rb) { //InterpreterCall("LVSL", &PPUInterpreter::LVSL, vd, ra, rb); } -void PPULLVMRecompiler::LVEBX(u32 vd, u32 ra, u32 rb) { +void Compiler::LVEBX(u32 vd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -1936,7 +1918,7 @@ void PPULLVMRecompiler::LVEBX(u32 vd, u32 ra, u32 rb) { //InterpreterCall("LVEBX", &PPUInterpreter::LVEBX, vd, ra, rb); } -void PPULLVMRecompiler::SUBFC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::SUBFC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); ra_i64 = m_ir_builder->CreateNeg(ra_i64); auto rb_i64 = GetGpr(rb); @@ -1956,7 +1938,7 @@ void PPULLVMRecompiler::SUBFC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("SUBFC", &PPUInterpreter::SUBFC, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::ADDC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::ADDC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); auto rb_i64 = GetGpr(rb); auto res_s = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::uadd_with_overflow, m_ir_builder->getInt64Ty()), ra_i64, rb_i64); @@ -1975,7 +1957,7 @@ void PPULLVMRecompiler::ADDC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("ADDC", &PPUInterpreter::ADDC, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::MULHDU(u32 rd, u32 ra, u32 rb, bool rc) { +void Compiler::MULHDU(u32 rd, u32 ra, u32 rb, bool rc) { auto ra_i64 = GetGpr(ra); auto rb_i64 = GetGpr(rb); auto ra_i128 = m_ir_builder->CreateZExt(ra_i64, m_ir_builder->getIntNTy(128)); @@ -1992,7 +1974,7 @@ void PPULLVMRecompiler::MULHDU(u32 rd, u32 ra, u32 rb, bool rc) { //InterpreterCall("MULHDU", &PPUInterpreter::MULHDU, rd, ra, rb, rc); } -void PPULLVMRecompiler::MULHWU(u32 rd, u32 ra, u32 rb, bool rc) { +void Compiler::MULHWU(u32 rd, u32 ra, u32 rb, bool rc) { auto ra_i32 = GetGpr(ra, 32); auto rb_i32 = GetGpr(rb, 32); auto ra_i64 = m_ir_builder->CreateZExt(ra_i32, m_ir_builder->getInt64Ty()); @@ -2007,27 +1989,27 @@ void PPULLVMRecompiler::MULHWU(u32 rd, u32 ra, u32 rb, bool rc) { //InterpreterCall("MULHWU", &PPUInterpreter::MULHWU, rd, ra, rb, rc); } -void PPULLVMRecompiler::MFOCRF(u32 a, u32 rd, u32 crm) { +void Compiler::MFOCRF(u32 a, u32 rd, u32 crm) { auto cr_i32 = GetCr(); auto cr_i64 = m_ir_builder->CreateZExt(cr_i32, m_ir_builder->getInt64Ty()); SetGpr(rd, cr_i64); //InterpreterCall("MFOCRF", &PPUInterpreter::MFOCRF, a, rd, crm); } -void PPULLVMRecompiler::LWARX(u32 rd, u32 ra, u32 rb) { +void Compiler::LWARX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); } - auto resv_addr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, R_ADDR)); + auto resv_addr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, R_ADDR)); auto resv_addr_i64_ptr = m_ir_builder->CreateBitCast(resv_addr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(addr_i64, resv_addr_i64_ptr, 8); auto resv_val_i32 = ReadMemory(addr_i64, 32, 4, false, false); auto resv_val_i64 = m_ir_builder->CreateZExt(resv_val_i32, m_ir_builder->getInt64Ty()); - auto resv_val_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, R_VALUE)); + auto resv_val_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, R_VALUE)); auto resv_val_i64_ptr = m_ir_builder->CreateBitCast(resv_val_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(resv_val_i64, resv_val_i64_ptr, 8); @@ -2037,7 +2019,7 @@ void PPULLVMRecompiler::LWARX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LWARX", &PPUInterpreter::LWARX, rd, ra, rb); } -void PPULLVMRecompiler::LDX(u32 rd, u32 ra, u32 rb) { +void Compiler::LDX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2049,7 +2031,7 @@ void PPULLVMRecompiler::LDX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LDX", &PPUInterpreter::LDX, rd, ra, rb); } -void PPULLVMRecompiler::LWZX(u32 rd, u32 ra, u32 rb) { +void Compiler::LWZX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2062,7 +2044,7 @@ void PPULLVMRecompiler::LWZX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LWZX", &PPUInterpreter::LWZX, rd, ra, rb); } -void PPULLVMRecompiler::SLW(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::SLW(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto rs_i64 = m_ir_builder->CreateZExt(rs_i32, m_ir_builder->getInt64Ty()); auto rb_i8 = GetGpr(rb, 8); @@ -2080,7 +2062,7 @@ void PPULLVMRecompiler::SLW(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("SLW", &PPUInterpreter::SLW, ra, rs, rb, rc); } -void PPULLVMRecompiler::CNTLZW(u32 ra, u32 rs, bool rc) { +void Compiler::CNTLZW(u32 ra, u32 rs, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto res_i32 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::ctlz, m_ir_builder->getInt32Ty()), rs_i32, m_ir_builder->getInt1(false)); auto res_i64 = m_ir_builder->CreateZExt(res_i32, m_ir_builder->getInt64Ty()); @@ -2093,7 +2075,7 @@ void PPULLVMRecompiler::CNTLZW(u32 ra, u32 rs, bool rc) { //InterpreterCall("CNTLZW", &PPUInterpreter::CNTLZW, ra, rs, rc); } -void PPULLVMRecompiler::SLD(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::SLD(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i64 = GetGpr(rs); auto rs_i128 = m_ir_builder->CreateZExt(rs_i64, m_ir_builder->getIntNTy(128)); auto rb_i8 = GetGpr(rb, 8); @@ -2110,7 +2092,7 @@ void PPULLVMRecompiler::SLD(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("SLD", &PPUInterpreter::SLD, ra, rs, rb, rc); } -void PPULLVMRecompiler::AND(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::AND(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i64 = GetGpr(rs); auto rb_i64 = GetGpr(rb); auto res_i64 = m_ir_builder->CreateAnd(rs_i64, rb_i64); @@ -2122,7 +2104,7 @@ void PPULLVMRecompiler::AND(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("AND", &PPUInterpreter::AND, ra, rs, rb, rc); } -void PPULLVMRecompiler::CMPL(u32 crfd, u32 l, u32 ra, u32 rb) { +void Compiler::CMPL(u32 crfd, u32 l, u32 ra, u32 rb) { Value * ra_i64; Value * rb_i64; if (l == 0) { @@ -2137,7 +2119,7 @@ void PPULLVMRecompiler::CMPL(u32 crfd, u32 l, u32 ra, u32 rb) { //InterpreterCall("CMPL", &PPUInterpreter::CMPL, crfd, l, ra, rb); } -void PPULLVMRecompiler::LVSR(u32 vd, u32 ra, u32 rb) { +void Compiler::LVSR(u32 vd, u32 ra, u32 rb) { static const u128 s_lvsr_values[] = { {0x18191A1B1C1D1E1F, 0x1011121314151617}, {0x1718191A1B1C1D1E, 0x0F10111213141516}, @@ -2172,7 +2154,7 @@ void PPULLVMRecompiler::LVSR(u32 vd, u32 ra, u32 rb) { //InterpreterCall("LVSR", &PPUInterpreter::LVSR, vd, ra, rb); } -void PPULLVMRecompiler::LVEHX(u32 vd, u32 ra, u32 rb) { +void Compiler::LVEHX(u32 vd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2191,7 +2173,7 @@ void PPULLVMRecompiler::LVEHX(u32 vd, u32 ra, u32 rb) { //InterpreterCall("LVEHX", &PPUInterpreter::LVEHX, vd, ra, rb); } -void PPULLVMRecompiler::SUBF(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::SUBF(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); auto rb_i64 = GetGpr(rb); auto diff_i64 = m_ir_builder->CreateSub(rb_i64, ra_i64); @@ -2207,7 +2189,7 @@ void PPULLVMRecompiler::SUBF(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("SUBF", &PPUInterpreter::SUBF, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::LDUX(u32 rd, u32 ra, u32 rb) { +void Compiler::LDUX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2218,12 +2200,12 @@ void PPULLVMRecompiler::LDUX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LDUX", &PPUInterpreter::LDUX, rd, ra, rb); } -void PPULLVMRecompiler::DCBST(u32 ra, u32 rb) { +void Compiler::DCBST(u32 ra, u32 rb) { // TODO: Implement this //InterpreterCall("DCBST", &PPUInterpreter::DCBST, ra, rb); } -void PPULLVMRecompiler::LWZUX(u32 rd, u32 ra, u32 rb) { +void Compiler::LWZUX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2235,7 +2217,7 @@ void PPULLVMRecompiler::LWZUX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LWZUX", &PPUInterpreter::LWZUX, rd, ra, rb); } -void PPULLVMRecompiler::CNTLZD(u32 ra, u32 rs, bool rc) { +void Compiler::CNTLZD(u32 ra, u32 rs, bool rc) { auto rs_i64 = GetGpr(rs); auto res_i64 = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::ctlz, m_ir_builder->getInt64Ty()), rs_i64, m_ir_builder->getInt1(false)); SetGpr(ra, res_i64); @@ -2247,15 +2229,15 @@ void PPULLVMRecompiler::CNTLZD(u32 ra, u32 rs, bool rc) { //InterpreterCall("CNTLZD", &PPUInterpreter::CNTLZD, ra, rs, rc); } -void PPULLVMRecompiler::ANDC(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::ANDC(u32 ra, u32 rs, u32 rb, bool rc) { InterpreterCall("ANDC", &PPUInterpreter::ANDC, ra, rs, rb, rc); } -void PPULLVMRecompiler::TD(u32 to, u32 ra, u32 rb) { +void Compiler::TD(u32 to, u32 ra, u32 rb) { InterpreterCall("TD", &PPUInterpreter::TD, to, ra, rb); } -void PPULLVMRecompiler::LVEWX(u32 vd, u32 ra, u32 rb) { +void Compiler::LVEWX(u32 vd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2274,7 +2256,7 @@ void PPULLVMRecompiler::LVEWX(u32 vd, u32 ra, u32 rb) { //InterpreterCall("LVEWX", &PPUInterpreter::LVEWX, vd, ra, rb); } -void PPULLVMRecompiler::MULHD(u32 rd, u32 ra, u32 rb, bool rc) { +void Compiler::MULHD(u32 rd, u32 ra, u32 rb, bool rc) { auto ra_i64 = GetGpr(ra); auto rb_i64 = GetGpr(rb); auto ra_i128 = m_ir_builder->CreateSExt(ra_i64, m_ir_builder->getIntNTy(128)); @@ -2291,7 +2273,7 @@ void PPULLVMRecompiler::MULHD(u32 rd, u32 ra, u32 rb, bool rc) { //InterpreterCall("MULHD", &PPUInterpreter::MULHD, rd, ra, rb, rc); } -void PPULLVMRecompiler::MULHW(u32 rd, u32 ra, u32 rb, bool rc) { +void Compiler::MULHW(u32 rd, u32 ra, u32 rb, bool rc) { auto ra_i32 = GetGpr(ra, 32); auto rb_i32 = GetGpr(rb, 32); auto ra_i64 = m_ir_builder->CreateSExt(ra_i32, m_ir_builder->getInt64Ty()); @@ -2306,19 +2288,19 @@ void PPULLVMRecompiler::MULHW(u32 rd, u32 ra, u32 rb, bool rc) { //InterpreterCall("MULHW", &PPUInterpreter::MULHW, rd, ra, rb, rc); } -void PPULLVMRecompiler::LDARX(u32 rd, u32 ra, u32 rb) { +void Compiler::LDARX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); } - auto resv_addr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, R_ADDR)); + auto resv_addr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, R_ADDR)); auto resv_addr_i64_ptr = m_ir_builder->CreateBitCast(resv_addr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(addr_i64, resv_addr_i64_ptr, 8); auto resv_val_i64 = ReadMemory(addr_i64, 64, 8, false); - auto resv_val_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, R_VALUE)); + auto resv_val_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, R_VALUE)); auto resv_val_i64_ptr = m_ir_builder->CreateBitCast(resv_val_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(resv_val_i64, resv_val_i64_ptr, 8); @@ -2327,12 +2309,12 @@ void PPULLVMRecompiler::LDARX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LDARX", &PPUInterpreter::LDARX, rd, ra, rb); } -void PPULLVMRecompiler::DCBF(u32 ra, u32 rb) { +void Compiler::DCBF(u32 ra, u32 rb) { // TODO: Implement this //InterpreterCall("DCBF", &PPUInterpreter::DCBF, ra, rb); } -void PPULLVMRecompiler::LBZX(u32 rd, u32 ra, u32 rb) { +void Compiler::LBZX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2345,7 +2327,7 @@ void PPULLVMRecompiler::LBZX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LBZX", &PPUInterpreter::LBZX, rd, ra, rb); } -void PPULLVMRecompiler::LVX(u32 vd, u32 ra, u32 rb) { +void Compiler::LVX(u32 vd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2358,7 +2340,7 @@ void PPULLVMRecompiler::LVX(u32 vd, u32 ra, u32 rb) { //InterpreterCall("LVX", &PPUInterpreter::LVX, vd, ra, rb); } -void PPULLVMRecompiler::NEG(u32 rd, u32 ra, u32 oe, bool rc) { +void Compiler::NEG(u32 rd, u32 ra, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); auto diff_i64 = m_ir_builder->CreateSub(m_ir_builder->getInt64(0), ra_i64); SetGpr(rd, diff_i64); @@ -2373,7 +2355,7 @@ void PPULLVMRecompiler::NEG(u32 rd, u32 ra, u32 oe, bool rc) { //InterpreterCall("NEG", &PPUInterpreter::NEG, rd, ra, oe, rc); } -void PPULLVMRecompiler::LBZUX(u32 rd, u32 ra, u32 rb) { +void Compiler::LBZUX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2385,7 +2367,7 @@ void PPULLVMRecompiler::LBZUX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LBZUX", &PPUInterpreter::LBZUX, rd, ra, rb); } -void PPULLVMRecompiler::NOR(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::NOR(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i64 = GetGpr(rs); auto rb_i64 = GetGpr(rb); auto res_i64 = m_ir_builder->CreateOr(rs_i64, rb_i64); @@ -2398,7 +2380,7 @@ void PPULLVMRecompiler::NOR(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("NOR", &PPUInterpreter::NOR, ra, rs, rb, rc); } -void PPULLVMRecompiler::STVEBX(u32 vs, u32 ra, u32 rb) { +void Compiler::STVEBX(u32 vs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2413,15 +2395,15 @@ void PPULLVMRecompiler::STVEBX(u32 vs, u32 ra, u32 rb) { //InterpreterCall("STVEBX", &PPUInterpreter::STVEBX, vs, ra, rb); } -void PPULLVMRecompiler::SUBFE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::SUBFE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { InterpreterCall("SUBFE", &PPUInterpreter::SUBFE, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::ADDE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::ADDE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { InterpreterCall("ADDE", &PPUInterpreter::ADDE, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::MTOCRF(u32 l, u32 crm, u32 rs) { +void Compiler::MTOCRF(u32 l, u32 crm, u32 rs) { auto rs_i32 = GetGpr(rs, 32); auto cr_i32 = GetCr(); u32 mask = 0; @@ -2442,7 +2424,7 @@ void PPULLVMRecompiler::MTOCRF(u32 l, u32 crm, u32 rs) { //InterpreterCall("MTOCRF", &PPUInterpreter::MTOCRF, l, crm, rs); } -void PPULLVMRecompiler::STDX(u32 rs, u32 ra, u32 rb) { +void Compiler::STDX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2453,11 +2435,11 @@ void PPULLVMRecompiler::STDX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STDX", &PPUInterpreter::STDX, rs, ra, rb); } -void PPULLVMRecompiler::STWCX_(u32 rs, u32 ra, u32 rb) { +void Compiler::STWCX_(u32 rs, u32 ra, u32 rb) { InterpreterCall("STWCX_", &PPUInterpreter::STWCX_, rs, ra, rb); } -void PPULLVMRecompiler::STWX(u32 rs, u32 ra, u32 rb) { +void Compiler::STWX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2468,7 +2450,7 @@ void PPULLVMRecompiler::STWX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STWX", &PPUInterpreter::STWX, rs, ra, rb); } -void PPULLVMRecompiler::STVEHX(u32 vs, u32 ra, u32 rb) { +void Compiler::STVEHX(u32 vs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2485,7 +2467,7 @@ void PPULLVMRecompiler::STVEHX(u32 vs, u32 ra, u32 rb) { //InterpreterCall("STVEHX", &PPUInterpreter::STVEHX, vs, ra, rb); } -void PPULLVMRecompiler::STDUX(u32 rs, u32 ra, u32 rb) { +void Compiler::STDUX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2495,7 +2477,7 @@ void PPULLVMRecompiler::STDUX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STDUX", &PPUInterpreter::STDUX, rs, ra, rb); } -void PPULLVMRecompiler::STWUX(u32 rs, u32 ra, u32 rb) { +void Compiler::STWUX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2505,7 +2487,7 @@ void PPULLVMRecompiler::STWUX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STWUX", &PPUInterpreter::STWUX, rs, ra, rb); } -void PPULLVMRecompiler::STVEWX(u32 vs, u32 ra, u32 rb) { +void Compiler::STVEWX(u32 vs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2522,7 +2504,7 @@ void PPULLVMRecompiler::STVEWX(u32 vs, u32 ra, u32 rb) { //InterpreterCall("STVEWX", &PPUInterpreter::STVEWX, vs, ra, rb); } -void PPULLVMRecompiler::ADDZE(u32 rd, u32 ra, u32 oe, bool rc) { +void Compiler::ADDZE(u32 rd, u32 ra, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); auto ca_i64 = GetXerCa(); auto res_s = m_ir_builder->CreateCall2(Intrinsic::getDeclaration(m_module, Intrinsic::uadd_with_overflow, m_ir_builder->getInt64Ty()), ra_i64, ca_i64); @@ -2537,15 +2519,15 @@ void PPULLVMRecompiler::ADDZE(u32 rd, u32 ra, u32 oe, bool rc) { //InterpreterCall("ADDZE", &PPUInterpreter::ADDZE, rd, ra, oe, rc); } -void PPULLVMRecompiler::SUBFZE(u32 rd, u32 ra, u32 oe, bool rc) { +void Compiler::SUBFZE(u32 rd, u32 ra, u32 oe, bool rc) { InterpreterCall("SUBFZE", &PPUInterpreter::SUBFZE, rd, ra, oe, rc); } -void PPULLVMRecompiler::STDCX_(u32 rs, u32 ra, u32 rb) { +void Compiler::STDCX_(u32 rs, u32 ra, u32 rb) { InterpreterCall("STDCX_", &PPUInterpreter::STDCX_, rs, ra, rb); } -void PPULLVMRecompiler::STBX(u32 rs, u32 ra, u32 rb) { +void Compiler::STBX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2556,7 +2538,7 @@ void PPULLVMRecompiler::STBX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STBX", &PPUInterpreter::STBX, rs, ra, rb); } -void PPULLVMRecompiler::STVX(u32 vs, u32 ra, u32 rb) { +void Compiler::STVX(u32 vs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2568,11 +2550,11 @@ void PPULLVMRecompiler::STVX(u32 vs, u32 ra, u32 rb) { //InterpreterCall("STVX", &PPUInterpreter::STVX, vs, ra, rb); } -void PPULLVMRecompiler::SUBFME(u32 rd, u32 ra, u32 oe, bool rc) { +void Compiler::SUBFME(u32 rd, u32 ra, u32 oe, bool rc) { InterpreterCall("SUBFME", &PPUInterpreter::SUBFME, rd, ra, oe, rc); } -void PPULLVMRecompiler::MULLD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::MULLD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); auto rb_i64 = GetGpr(rb); auto prod_i64 = m_ir_builder->CreateMul(ra_i64, rb_i64); @@ -2586,11 +2568,11 @@ void PPULLVMRecompiler::MULLD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("MULLD", &PPUInterpreter::MULLD, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::ADDME(u32 rd, u32 ra, u32 oe, bool rc) { +void Compiler::ADDME(u32 rd, u32 ra, u32 oe, bool rc) { InterpreterCall("ADDME", &PPUInterpreter::ADDME, rd, ra, oe, rc); } -void PPULLVMRecompiler::MULLW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::MULLW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i32 = GetGpr(ra, 32); auto rb_i32 = GetGpr(rb, 32); auto ra_i64 = m_ir_builder->CreateSExt(ra_i32, m_ir_builder->getInt64Ty()); @@ -2606,12 +2588,12 @@ void PPULLVMRecompiler::MULLW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("MULLW", &PPUInterpreter::MULLW, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::DCBTST(u32 ra, u32 rb, u32 th) { +void Compiler::DCBTST(u32 ra, u32 rb, u32 th) { // TODO: Implement this //InterpreterCall("DCBTST", &PPUInterpreter::DCBTST, ra, rb, th); } -void PPULLVMRecompiler::STBUX(u32 rs, u32 ra, u32 rb) { +void Compiler::STBUX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2621,7 +2603,7 @@ void PPULLVMRecompiler::STBUX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STBUX", &PPUInterpreter::STBUX, rs, ra, rb); } -void PPULLVMRecompiler::ADD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::ADD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); auto rb_i64 = GetGpr(rb); auto sum_i64 = m_ir_builder->CreateAdd(ra_i64, rb_i64); @@ -2637,12 +2619,12 @@ void PPULLVMRecompiler::ADD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("ADD", &PPUInterpreter::ADD, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::DCBT(u32 ra, u32 rb, u32 th) { +void Compiler::DCBT(u32 ra, u32 rb, u32 th) { // TODO: Implement this using prefetch //InterpreterCall("DCBT", &PPUInterpreter::DCBT, ra, rb, th); } -void PPULLVMRecompiler::LHZX(u32 rd, u32 ra, u32 rb) { +void Compiler::LHZX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2655,15 +2637,15 @@ void PPULLVMRecompiler::LHZX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LHZX", &PPUInterpreter::LHZX, rd, ra, rb); } -void PPULLVMRecompiler::EQV(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::EQV(u32 ra, u32 rs, u32 rb, bool rc) { InterpreterCall("EQV", &PPUInterpreter::EQV, ra, rs, rb, rc); } -void PPULLVMRecompiler::ECIWX(u32 rd, u32 ra, u32 rb) { +void Compiler::ECIWX(u32 rd, u32 ra, u32 rb) { InterpreterCall("ECIWX", &PPUInterpreter::ECIWX, rd, ra, rb); } -void PPULLVMRecompiler::LHZUX(u32 rd, u32 ra, u32 rb) { +void Compiler::LHZUX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2675,7 +2657,7 @@ void PPULLVMRecompiler::LHZUX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LHZUX", &PPUInterpreter::LHZUX, rd, ra, rb); } -void PPULLVMRecompiler::XOR(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::XOR(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i64 = GetGpr(rs); auto rb_i64 = GetGpr(rb); auto res_i64 = m_ir_builder->CreateXor(rs_i64, rb_i64); @@ -2687,7 +2669,7 @@ void PPULLVMRecompiler::XOR(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("XOR", &PPUInterpreter::XOR, ra, rs, rb, rc); } -void PPULLVMRecompiler::MFSPR(u32 rd, u32 spr) { +void Compiler::MFSPR(u32 rd, u32 spr) { Value * rd_i64; auto n = (spr >> 5) | ((spr & 0x1f) << 5); @@ -2713,7 +2695,7 @@ void PPULLVMRecompiler::MFSPR(u32 rd, u32 spr) { //InterpreterCall("MFSPR", &PPUInterpreter::MFSPR, rd, spr); } -void PPULLVMRecompiler::LWAX(u32 rd, u32 ra, u32 rb) { +void Compiler::LWAX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2726,11 +2708,11 @@ void PPULLVMRecompiler::LWAX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LWAX", &PPUInterpreter::LWAX, rd, ra, rb); } -void PPULLVMRecompiler::DST(u32 ra, u32 rb, u32 strm, u32 t) { +void Compiler::DST(u32 ra, u32 rb, u32 strm, u32 t) { InterpreterCall("DST", &PPUInterpreter::DST, ra, rb, strm, t); } -void PPULLVMRecompiler::LHAX(u32 rd, u32 ra, u32 rb) { +void Compiler::LHAX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2743,21 +2725,13 @@ void PPULLVMRecompiler::LHAX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LHAX", &PPUInterpreter::LHAX, rd, ra, rb); } -void PPULLVMRecompiler::LVXL(u32 vd, u32 ra, u32 rb) { +void Compiler::LVXL(u32 vd, u32 ra, u32 rb) { LVX(vd, ra, rb); //InterpreterCall("LVXL", &PPUInterpreter::LVXL, vd, ra, rb); } -void PPULLVMRecompiler::MFTB(u32 rd, u32 spr) { - static Function * s_get_time_fn = nullptr; - - if (s_get_time_fn == nullptr) { - s_get_time_fn = (Function *)m_module->getOrInsertFunction("get_time", m_ir_builder->getInt64Ty(), nullptr); - s_get_time_fn->setCallingConv(CallingConv::X86_64_Win64); - m_execution_engine->addGlobalMapping(s_get_time_fn, (void *)get_time); - } - - auto tb_i64 = (Value *)m_ir_builder->CreateCall(s_get_time_fn); +void Compiler::MFTB(u32 rd, u32 spr) { + auto tb_i64 = Call("get_time", get_time); u32 n = (spr >> 5) | ((spr & 0x1f) << 5); if (n == 0x10D) { @@ -2767,7 +2741,7 @@ void PPULLVMRecompiler::MFTB(u32 rd, u32 spr) { SetGpr(rd, tb_i64); } -void PPULLVMRecompiler::LWAUX(u32 rd, u32 ra, u32 rb) { +void Compiler::LWAUX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2779,11 +2753,11 @@ void PPULLVMRecompiler::LWAUX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LWAUX", &PPUInterpreter::LWAUX, rd, ra, rb); } -void PPULLVMRecompiler::DSTST(u32 ra, u32 rb, u32 strm, u32 t) { +void Compiler::DSTST(u32 ra, u32 rb, u32 strm, u32 t) { InterpreterCall("DSTST", &PPUInterpreter::DSTST, ra, rb, strm, t); } -void PPULLVMRecompiler::LHAUX(u32 rd, u32 ra, u32 rb) { +void Compiler::LHAUX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2795,7 +2769,7 @@ void PPULLVMRecompiler::LHAUX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LHAUX", &PPUInterpreter::LHAUX, rd, ra, rb); } -void PPULLVMRecompiler::STHX(u32 rs, u32 ra, u32 rb) { +void Compiler::STHX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2806,15 +2780,15 @@ void PPULLVMRecompiler::STHX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STHX", &PPUInterpreter::STHX, rs, ra, rb); } -void PPULLVMRecompiler::ORC(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::ORC(u32 ra, u32 rs, u32 rb, bool rc) { InterpreterCall("ORC", &PPUInterpreter::ORC, ra, rs, rb, rc); } -void PPULLVMRecompiler::ECOWX(u32 rs, u32 ra, u32 rb) { +void Compiler::ECOWX(u32 rs, u32 ra, u32 rb) { InterpreterCall("ECOWX", &PPUInterpreter::ECOWX, rs, ra, rb); } -void PPULLVMRecompiler::STHUX(u32 rs, u32 ra, u32 rb) { +void Compiler::STHUX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -2824,7 +2798,7 @@ void PPULLVMRecompiler::STHUX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STHUX", &PPUInterpreter::STHUX, rs, ra, rb); } -void PPULLVMRecompiler::OR(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::OR(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i64 = GetGpr(rs); auto rb_i64 = GetGpr(rb); auto res_i64 = m_ir_builder->CreateOr(rs_i64, rb_i64); @@ -2836,7 +2810,7 @@ void PPULLVMRecompiler::OR(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("OR", &PPUInterpreter::OR, ra, rs, rb, rc); } -void PPULLVMRecompiler::DIVDU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::DIVDU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); auto rb_i64 = GetGpr(rb); auto res_i64 = m_ir_builder->CreateUDiv(ra_i64, rb_i64); @@ -2851,7 +2825,7 @@ void PPULLVMRecompiler::DIVDU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("DIVDU", &PPUInterpreter::DIVDU, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::DIVWU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::DIVWU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i32 = GetGpr(ra, 32); auto rb_i32 = GetGpr(rb, 32); auto res_i32 = m_ir_builder->CreateUDiv(ra_i32, rb_i32); @@ -2867,7 +2841,7 @@ void PPULLVMRecompiler::DIVWU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("DIVWU", &PPUInterpreter::DIVWU, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::MTSPR(u32 spr, u32 rs) { +void Compiler::MTSPR(u32 spr, u32 rs) { auto rs_i64 = GetGpr(rs); auto n = (spr >> 5) | ((spr & 0x1f) << 5); @@ -2892,16 +2866,16 @@ void PPULLVMRecompiler::MTSPR(u32 spr, u32 rs) { //InterpreterCall("MTSPR", &PPUInterpreter::MTSPR, spr, rs); } -void PPULLVMRecompiler::NAND(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::NAND(u32 ra, u32 rs, u32 rb, bool rc) { InterpreterCall("NAND", &PPUInterpreter::NAND, ra, rs, rb, rc); } -void PPULLVMRecompiler::STVXL(u32 vs, u32 ra, u32 rb) { +void Compiler::STVXL(u32 vs, u32 ra, u32 rb) { STVX(vs, ra, rb); //InterpreterCall("STVXL", &PPUInterpreter::STVXL, vs, ra, rb); } -void PPULLVMRecompiler::DIVD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::DIVD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i64 = GetGpr(ra); auto rb_i64 = GetGpr(rb); auto res_i64 = m_ir_builder->CreateSDiv(ra_i64, rb_i64); @@ -2916,7 +2890,7 @@ void PPULLVMRecompiler::DIVD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("DIVD", &PPUInterpreter::DIVD, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::DIVW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { +void Compiler::DIVW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { auto ra_i32 = GetGpr(ra, 32); auto rb_i32 = GetGpr(rb, 32); auto res_i32 = m_ir_builder->CreateSDiv(ra_i32, rb_i32); @@ -2932,7 +2906,7 @@ void PPULLVMRecompiler::DIVW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { //InterpreterCall("DIVW", &PPUInterpreter::DIVW, rd, ra, rb, oe, rc); } -void PPULLVMRecompiler::LVLX(u32 vd, u32 ra, u32 rb) { +void Compiler::LVLX(u32 vd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2949,7 +2923,7 @@ void PPULLVMRecompiler::LVLX(u32 vd, u32 ra, u32 rb) { //InterpreterCall("LVLX", &PPUInterpreter::LVLX, vd, ra, rb); } -void PPULLVMRecompiler::LDBRX(u32 rd, u32 ra, u32 rb) { +void Compiler::LDBRX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2961,11 +2935,11 @@ void PPULLVMRecompiler::LDBRX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LDBRX", &PPUInterpreter::LDBRX, rd, ra, rb); } -void PPULLVMRecompiler::LSWX(u32 rd, u32 ra, u32 rb) { +void Compiler::LSWX(u32 rd, u32 ra, u32 rb) { InterpreterCall("LSWX", &PPUInterpreter::LSWX, rd, ra, rb); } -void PPULLVMRecompiler::LWBRX(u32 rd, u32 ra, u32 rb) { +void Compiler::LWBRX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2978,7 +2952,7 @@ void PPULLVMRecompiler::LWBRX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LWBRX", &PPUInterpreter::LWBRX, rd, ra, rb); } -void PPULLVMRecompiler::LFSX(u32 frd, u32 ra, u32 rb) { +void Compiler::LFSX(u32 frd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -2990,7 +2964,7 @@ void PPULLVMRecompiler::LFSX(u32 frd, u32 ra, u32 rb) { //InterpreterCall("LFSX", &PPUInterpreter::LFSX, frd, ra, rb); } -void PPULLVMRecompiler::SRW(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::SRW(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto rs_i64 = m_ir_builder->CreateZExt(rs_i32, m_ir_builder->getInt64Ty()); auto rb_i8 = GetGpr(rb, 8); @@ -3006,7 +2980,7 @@ void PPULLVMRecompiler::SRW(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("SRW", &PPUInterpreter::SRW, ra, rs, rb, rc); } -void PPULLVMRecompiler::SRD(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::SRD(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i64 = GetGpr(rs); auto rs_i128 = m_ir_builder->CreateZExt(rs_i64, m_ir_builder->getIntNTy(128)); auto rb_i8 = GetGpr(rb, 8); @@ -3023,7 +2997,7 @@ void PPULLVMRecompiler::SRD(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("SRD", &PPUInterpreter::SRD, ra, rs, rb, rc); } -void PPULLVMRecompiler::LVRX(u32 vd, u32 ra, u32 rb) { +void Compiler::LVRX(u32 vd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3045,7 +3019,7 @@ void PPULLVMRecompiler::LVRX(u32 vd, u32 ra, u32 rb) { //InterpreterCall("LVRX", &PPUInterpreter::LVRX, vd, ra, rb); } -void PPULLVMRecompiler::LSWI(u32 rd, u32 ra, u32 nb) { +void Compiler::LSWI(u32 rd, u32 ra, u32 nb) { auto addr_i64 = ra ? GetGpr(ra) : m_ir_builder->getInt64(0); nb = nb ? nb : 32; @@ -3067,7 +3041,7 @@ void PPULLVMRecompiler::LSWI(u32 rd, u32 ra, u32 nb) { //InterpreterCall("LSWI", &PPUInterpreter::LSWI, rd, ra, nb); } -void PPULLVMRecompiler::LFSUX(u32 frd, u32 ra, u32 rb) { +void Compiler::LFSUX(u32 frd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3077,12 +3051,12 @@ void PPULLVMRecompiler::LFSUX(u32 frd, u32 ra, u32 rb) { //InterpreterCall("LFSUX", &PPUInterpreter::LFSUX, frd, ra, rb); } -void PPULLVMRecompiler::SYNC(u32 l) { +void Compiler::SYNC(u32 l) { m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_mfence)); //InterpreterCall("SYNC", &PPUInterpreter::SYNC, l); } -void PPULLVMRecompiler::LFDX(u32 frd, u32 ra, u32 rb) { +void Compiler::LFDX(u32 frd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3094,7 +3068,7 @@ void PPULLVMRecompiler::LFDX(u32 frd, u32 ra, u32 rb) { //InterpreterCall("LFDX", &PPUInterpreter::LFDX, frd, ra, rb); } -void PPULLVMRecompiler::LFDUX(u32 frd, u32 ra, u32 rb) { +void Compiler::LFDUX(u32 frd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3104,15 +3078,15 @@ void PPULLVMRecompiler::LFDUX(u32 frd, u32 ra, u32 rb) { //InterpreterCall("LFDUX", &PPUInterpreter::LFDUX, frd, ra, rb); } -void PPULLVMRecompiler::STVLX(u32 vs, u32 ra, u32 rb) { +void Compiler::STVLX(u32 vs, u32 ra, u32 rb) { InterpreterCall("STVLX", &PPUInterpreter::STVLX, vs, ra, rb); } -void PPULLVMRecompiler::STSWX(u32 rs, u32 ra, u32 rb) { +void Compiler::STSWX(u32 rs, u32 ra, u32 rb) { InterpreterCall("STSWX", &PPUInterpreter::STSWX, rs, ra, rb); } -void PPULLVMRecompiler::STWBRX(u32 rs, u32 ra, u32 rb) { +void Compiler::STWBRX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3123,7 +3097,7 @@ void PPULLVMRecompiler::STWBRX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STWBRX", &PPUInterpreter::STWBRX, rs, ra, rb); } -void PPULLVMRecompiler::STFSX(u32 frs, u32 ra, u32 rb) { +void Compiler::STFSX(u32 frs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3135,11 +3109,11 @@ void PPULLVMRecompiler::STFSX(u32 frs, u32 ra, u32 rb) { //InterpreterCall("STFSX", &PPUInterpreter::STFSX, frs, ra, rb); } -void PPULLVMRecompiler::STVRX(u32 vs, u32 ra, u32 rb) { +void Compiler::STVRX(u32 vs, u32 ra, u32 rb) { InterpreterCall("STVRX", &PPUInterpreter::STVRX, vs, ra, rb); } -void PPULLVMRecompiler::STFSUX(u32 frs, u32 ra, u32 rb) { +void Compiler::STFSUX(u32 frs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3150,7 +3124,7 @@ void PPULLVMRecompiler::STFSUX(u32 frs, u32 ra, u32 rb) { //InterpreterCall("STFSUX", &PPUInterpreter::STFSUX, frs, ra, rb); } -void PPULLVMRecompiler::STSWI(u32 rd, u32 ra, u32 nb) { +void Compiler::STSWI(u32 rd, u32 ra, u32 nb) { auto addr_i64 = ra ? GetGpr(ra) : m_ir_builder->getInt64(0); nb = nb ? nb : 32; @@ -3185,7 +3159,7 @@ void PPULLVMRecompiler::STSWI(u32 rd, u32 ra, u32 nb) { //InterpreterCall("STSWI", &PPUInterpreter::STSWI, rd, ra, nb); } -void PPULLVMRecompiler::STFDX(u32 frs, u32 ra, u32 rb) { +void Compiler::STFDX(u32 frs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3197,7 +3171,7 @@ void PPULLVMRecompiler::STFDX(u32 frs, u32 ra, u32 rb) { //InterpreterCall("STFDX", &PPUInterpreter::STFDX, frs, ra, rb); } -void PPULLVMRecompiler::STFDUX(u32 frs, u32 ra, u32 rb) { +void Compiler::STFDUX(u32 frs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3208,12 +3182,12 @@ void PPULLVMRecompiler::STFDUX(u32 frs, u32 ra, u32 rb) { //InterpreterCall("STFDUX", &PPUInterpreter::STFDUX, frs, ra, rb); } -void PPULLVMRecompiler::LVLXL(u32 vd, u32 ra, u32 rb) { +void Compiler::LVLXL(u32 vd, u32 ra, u32 rb) { LVLX(vd, ra, rb); //InterpreterCall("LVLXL", &PPUInterpreter::LVLXL, vd, ra, rb); } -void PPULLVMRecompiler::LHBRX(u32 rd, u32 ra, u32 rb) { +void Compiler::LHBRX(u32 rd, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3226,7 +3200,7 @@ void PPULLVMRecompiler::LHBRX(u32 rd, u32 ra, u32 rb) { //InterpreterCall("LHBRX", &PPUInterpreter::LHBRX, rd, ra, rb); } -void PPULLVMRecompiler::SRAW(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::SRAW(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto rs_i64 = m_ir_builder->CreateZExt(rs_i32, m_ir_builder->getInt64Ty()); rs_i64 = m_ir_builder->CreateShl(rs_i64, 32); @@ -3250,7 +3224,7 @@ void PPULLVMRecompiler::SRAW(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("SRAW", &PPUInterpreter::SRAW, ra, rs, rb, rc); } -void PPULLVMRecompiler::SRAD(u32 ra, u32 rs, u32 rb, bool rc) { +void Compiler::SRAD(u32 ra, u32 rs, u32 rb, bool rc) { auto rs_i64 = GetGpr(rs); auto rs_i128 = m_ir_builder->CreateZExt(rs_i64, m_ir_builder->getIntNTy(128)); rs_i128 = m_ir_builder->CreateShl(rs_i128, 64); @@ -3275,16 +3249,16 @@ void PPULLVMRecompiler::SRAD(u32 ra, u32 rs, u32 rb, bool rc) { //InterpreterCall("SRAD", &PPUInterpreter::SRAD, ra, rs, rb, rc); } -void PPULLVMRecompiler::LVRXL(u32 vd, u32 ra, u32 rb) { +void Compiler::LVRXL(u32 vd, u32 ra, u32 rb) { LVRX(vd, ra, rb); //InterpreterCall("LVRXL", &PPUInterpreter::LVRXL, vd, ra, rb); } -void PPULLVMRecompiler::DSS(u32 strm, u32 a) { +void Compiler::DSS(u32 strm, u32 a) { InterpreterCall("DSS", &PPUInterpreter::DSS, strm, a); } -void PPULLVMRecompiler::SRAWI(u32 ra, u32 rs, u32 sh, bool rc) { +void Compiler::SRAWI(u32 ra, u32 rs, u32 sh, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto rs_i64 = m_ir_builder->CreateZExt(rs_i32, m_ir_builder->getInt64Ty()); rs_i64 = m_ir_builder->CreateShl(rs_i64, 32); @@ -3305,7 +3279,7 @@ void PPULLVMRecompiler::SRAWI(u32 ra, u32 rs, u32 sh, bool rc) { //InterpreterCall("SRAWI", &PPUInterpreter::SRAWI, ra, rs, sh, rc); } -void PPULLVMRecompiler::SRADI1(u32 ra, u32 rs, u32 sh, bool rc) { +void Compiler::SRADI1(u32 ra, u32 rs, u32 sh, bool rc) { auto rs_i64 = GetGpr(rs); auto rs_i128 = m_ir_builder->CreateZExt(rs_i64, m_ir_builder->getIntNTy(128)); rs_i128 = m_ir_builder->CreateShl(rs_i128, 64); @@ -3327,22 +3301,22 @@ void PPULLVMRecompiler::SRADI1(u32 ra, u32 rs, u32 sh, bool rc) { //InterpreterCall("SRADI1", &PPUInterpreter::SRADI1, ra, rs, sh, rc); } -void PPULLVMRecompiler::SRADI2(u32 ra, u32 rs, u32 sh, bool rc) { +void Compiler::SRADI2(u32 ra, u32 rs, u32 sh, bool rc) { SRADI1(ra, rs, sh, rc); //InterpreterCall("SRADI2", &PPUInterpreter::SRADI2, ra, rs, sh, rc); } -void PPULLVMRecompiler::EIEIO() { +void Compiler::EIEIO() { m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_mfence)); //InterpreterCall("EIEIO", &PPUInterpreter::EIEIO); } -void PPULLVMRecompiler::STVLXL(u32 vs, u32 ra, u32 rb) { +void Compiler::STVLXL(u32 vs, u32 ra, u32 rb) { STVLX(vs, ra, rb); //InterpreterCall("STVLXL", &PPUInterpreter::STVLXL, vs, ra, rb); } -void PPULLVMRecompiler::STHBRX(u32 rs, u32 ra, u32 rb) { +void Compiler::STHBRX(u32 rs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3353,7 +3327,7 @@ void PPULLVMRecompiler::STHBRX(u32 rs, u32 ra, u32 rb) { //InterpreterCall("STHBRX", &PPUInterpreter::STHBRX, rs, ra, rb); } -void PPULLVMRecompiler::EXTSH(u32 ra, u32 rs, bool rc) { +void Compiler::EXTSH(u32 ra, u32 rs, bool rc) { auto rs_i16 = GetGpr(rs, 16); auto rs_i64 = m_ir_builder->CreateSExt(rs_i16, m_ir_builder->getInt64Ty()); SetGpr(ra, rs_i64); @@ -3364,12 +3338,12 @@ void PPULLVMRecompiler::EXTSH(u32 ra, u32 rs, bool rc) { //InterpreterCall("EXTSH", &PPUInterpreter::EXTSH, ra, rs, rc); } -void PPULLVMRecompiler::STVRXL(u32 vs, u32 ra, u32 rb) { +void Compiler::STVRXL(u32 vs, u32 ra, u32 rb) { STVRX(vs, ra, rb); //InterpreterCall("STVRXL", &PPUInterpreter::STVRXL, vs, ra, rb); } -void PPULLVMRecompiler::EXTSB(u32 ra, u32 rs, bool rc) { +void Compiler::EXTSB(u32 ra, u32 rs, bool rc) { auto rs_i8 = GetGpr(rs, 8); auto rs_i64 = m_ir_builder->CreateSExt(rs_i8, m_ir_builder->getInt64Ty()); SetGpr(ra, rs_i64); @@ -3380,7 +3354,7 @@ void PPULLVMRecompiler::EXTSB(u32 ra, u32 rs, bool rc) { //InterpreterCall("EXTSB", &PPUInterpreter::EXTSB, ra, rs, rc); } -void PPULLVMRecompiler::STFIWX(u32 frs, u32 ra, u32 rb) { +void Compiler::STFIWX(u32 frs, u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3393,7 +3367,7 @@ void PPULLVMRecompiler::STFIWX(u32 frs, u32 ra, u32 rb) { //InterpreterCall("STFIWX", &PPUInterpreter::STFIWX, frs, ra, rb); } -void PPULLVMRecompiler::EXTSW(u32 ra, u32 rs, bool rc) { +void Compiler::EXTSW(u32 ra, u32 rs, bool rc) { auto rs_i32 = GetGpr(rs, 32); auto rs_i64 = m_ir_builder->CreateSExt(rs_i32, m_ir_builder->getInt64Ty()); SetGpr(ra, rs_i64); @@ -3404,11 +3378,11 @@ void PPULLVMRecompiler::EXTSW(u32 ra, u32 rs, bool rc) { //InterpreterCall("EXTSW", &PPUInterpreter::EXTSW, ra, rs, rc); } -void PPULLVMRecompiler::ICBI(u32 ra, u32 rs) { +void Compiler::ICBI(u32 ra, u32 rs) { InterpreterCall("ICBI", &PPUInterpreter::ICBI, ra, rs); } -void PPULLVMRecompiler::DCBZ(u32 ra, u32 rb) { +void Compiler::DCBZ(u32 ra, u32 rb) { auto addr_i64 = GetGpr(rb); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3425,7 +3399,7 @@ void PPULLVMRecompiler::DCBZ(u32 ra, u32 rb) { //InterpreterCall("DCBZ", &PPUInterpreter::DCBZ, ra, rb);L } -void PPULLVMRecompiler::LWZ(u32 rd, u32 ra, s32 d) { +void Compiler::LWZ(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3438,7 +3412,7 @@ void PPULLVMRecompiler::LWZ(u32 rd, u32 ra, s32 d) { //InterpreterCall("LWZ", &PPUInterpreter::LWZ, rd, ra, d); } -void PPULLVMRecompiler::LWZU(u32 rd, u32 ra, s32 d) { +void Compiler::LWZU(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3450,7 +3424,7 @@ void PPULLVMRecompiler::LWZU(u32 rd, u32 ra, s32 d) { //InterpreterCall("LWZU", &PPUInterpreter::LWZU, rd, ra, d); } -void PPULLVMRecompiler::LBZ(u32 rd, u32 ra, s32 d) { +void Compiler::LBZ(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3463,7 +3437,7 @@ void PPULLVMRecompiler::LBZ(u32 rd, u32 ra, s32 d) { //InterpreterCall("LBZ", &PPUInterpreter::LBZ, rd, ra, d); } -void PPULLVMRecompiler::LBZU(u32 rd, u32 ra, s32 d) { +void Compiler::LBZU(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3475,7 +3449,7 @@ void PPULLVMRecompiler::LBZU(u32 rd, u32 ra, s32 d) { //InterpreterCall("LBZU", &PPUInterpreter::LBZU, rd, ra, d); } -void PPULLVMRecompiler::STW(u32 rs, u32 ra, s32 d) { +void Compiler::STW(u32 rs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3486,7 +3460,7 @@ void PPULLVMRecompiler::STW(u32 rs, u32 ra, s32 d) { //InterpreterCall("STW", &PPUInterpreter::STW, rs, ra, d); } -void PPULLVMRecompiler::STWU(u32 rs, u32 ra, s32 d) { +void Compiler::STWU(u32 rs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3496,7 +3470,7 @@ void PPULLVMRecompiler::STWU(u32 rs, u32 ra, s32 d) { //InterpreterCall("STWU", &PPUInterpreter::STWU, rs, ra, d); } -void PPULLVMRecompiler::STB(u32 rs, u32 ra, s32 d) { +void Compiler::STB(u32 rs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3507,7 +3481,7 @@ void PPULLVMRecompiler::STB(u32 rs, u32 ra, s32 d) { //InterpreterCall("STB", &PPUInterpreter::STB, rs, ra, d); } -void PPULLVMRecompiler::STBU(u32 rs, u32 ra, s32 d) { +void Compiler::STBU(u32 rs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3517,7 +3491,7 @@ void PPULLVMRecompiler::STBU(u32 rs, u32 ra, s32 d) { //InterpreterCall("STBU", &PPUInterpreter::STBU, rs, ra, d); } -void PPULLVMRecompiler::LHZ(u32 rd, u32 ra, s32 d) { +void Compiler::LHZ(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3530,7 +3504,7 @@ void PPULLVMRecompiler::LHZ(u32 rd, u32 ra, s32 d) { //InterpreterCall("LHZ", &PPUInterpreter::LHZ, rd, ra, d); } -void PPULLVMRecompiler::LHZU(u32 rd, u32 ra, s32 d) { +void Compiler::LHZU(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3542,7 +3516,7 @@ void PPULLVMRecompiler::LHZU(u32 rd, u32 ra, s32 d) { //InterpreterCall("LHZU", &PPUInterpreter::LHZU, rd, ra, d); } -void PPULLVMRecompiler::LHA(u32 rd, u32 ra, s32 d) { +void Compiler::LHA(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3555,7 +3529,7 @@ void PPULLVMRecompiler::LHA(u32 rd, u32 ra, s32 d) { //InterpreterCall("LHA", &PPUInterpreter::LHA, rd, ra, d); } -void PPULLVMRecompiler::LHAU(u32 rd, u32 ra, s32 d) { +void Compiler::LHAU(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3567,7 +3541,7 @@ void PPULLVMRecompiler::LHAU(u32 rd, u32 ra, s32 d) { //InterpreterCall("LHAU", &PPUInterpreter::LHAU, rd, ra, d); } -void PPULLVMRecompiler::STH(u32 rs, u32 ra, s32 d) { +void Compiler::STH(u32 rs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3578,7 +3552,7 @@ void PPULLVMRecompiler::STH(u32 rs, u32 ra, s32 d) { //InterpreterCall("STH", &PPUInterpreter::STH, rs, ra, d); } -void PPULLVMRecompiler::STHU(u32 rs, u32 ra, s32 d) { +void Compiler::STHU(u32 rs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3588,7 +3562,7 @@ void PPULLVMRecompiler::STHU(u32 rs, u32 ra, s32 d) { //InterpreterCall("STHU", &PPUInterpreter::STHU, rs, ra, d); } -void PPULLVMRecompiler::LMW(u32 rd, u32 ra, s32 d) { +void Compiler::LMW(u32 rd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { addr_i64 = m_ir_builder->CreateAdd(addr_i64, GetGpr(ra)); @@ -3604,7 +3578,7 @@ void PPULLVMRecompiler::LMW(u32 rd, u32 ra, s32 d) { //InterpreterCall("LMW", &PPUInterpreter::LMW, rd, ra, d); } -void PPULLVMRecompiler::STMW(u32 rs, u32 ra, s32 d) { +void Compiler::STMW(u32 rs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { addr_i64 = m_ir_builder->CreateAdd(addr_i64, GetGpr(ra)); @@ -3619,7 +3593,7 @@ void PPULLVMRecompiler::STMW(u32 rs, u32 ra, s32 d) { //InterpreterCall("STMW", &PPUInterpreter::STMW, rs, ra, d); } -void PPULLVMRecompiler::LFS(u32 frd, u32 ra, s32 d) { +void Compiler::LFS(u32 frd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3631,7 +3605,7 @@ void PPULLVMRecompiler::LFS(u32 frd, u32 ra, s32 d) { //InterpreterCall("LFS", &PPUInterpreter::LFS, frd, ra, d); } -void PPULLVMRecompiler::LFSU(u32 frd, u32 ra, s32 ds) { +void Compiler::LFSU(u32 frd, u32 ra, s32 ds) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)ds); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3641,7 +3615,7 @@ void PPULLVMRecompiler::LFSU(u32 frd, u32 ra, s32 ds) { //InterpreterCall("LFSU", &PPUInterpreter::LFSU, frd, ra, ds); } -void PPULLVMRecompiler::LFD(u32 frd, u32 ra, s32 d) { +void Compiler::LFD(u32 frd, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3653,7 +3627,7 @@ void PPULLVMRecompiler::LFD(u32 frd, u32 ra, s32 d) { //InterpreterCall("LFD", &PPUInterpreter::LFD, frd, ra, d); } -void PPULLVMRecompiler::LFDU(u32 frd, u32 ra, s32 ds) { +void Compiler::LFDU(u32 frd, u32 ra, s32 ds) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)ds); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3664,7 +3638,7 @@ void PPULLVMRecompiler::LFDU(u32 frd, u32 ra, s32 ds) { //InterpreterCall("LFDU", &PPUInterpreter::LFDU, frd, ra, ds); } -void PPULLVMRecompiler::STFS(u32 frs, u32 ra, s32 d) { +void Compiler::STFS(u32 frs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3676,7 +3650,7 @@ void PPULLVMRecompiler::STFS(u32 frs, u32 ra, s32 d) { //InterpreterCall("STFS", &PPUInterpreter::STFS, frs, ra, d); } -void PPULLVMRecompiler::STFSU(u32 frs, u32 ra, s32 d) { +void Compiler::STFSU(u32 frs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3687,7 +3661,7 @@ void PPULLVMRecompiler::STFSU(u32 frs, u32 ra, s32 d) { //InterpreterCall("STFSU", &PPUInterpreter::STFSU, frs, ra, d); } -void PPULLVMRecompiler::STFD(u32 frs, u32 ra, s32 d) { +void Compiler::STFD(u32 frs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3699,7 +3673,7 @@ void PPULLVMRecompiler::STFD(u32 frs, u32 ra, s32 d) { //InterpreterCall("STFD", &PPUInterpreter::STFD, frs, ra, d); } -void PPULLVMRecompiler::STFDU(u32 frs, u32 ra, s32 d) { +void Compiler::STFDU(u32 frs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3710,7 +3684,7 @@ void PPULLVMRecompiler::STFDU(u32 frs, u32 ra, s32 d) { //InterpreterCall("STFDU", &PPUInterpreter::STFDU, frs, ra, d); } -void PPULLVMRecompiler::LD(u32 rd, u32 ra, s32 ds) { +void Compiler::LD(u32 rd, u32 ra, s32 ds) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)ds); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3722,7 +3696,7 @@ void PPULLVMRecompiler::LD(u32 rd, u32 ra, s32 ds) { //InterpreterCall("LD", &PPUInterpreter::LD, rd, ra, ds); } -void PPULLVMRecompiler::LDU(u32 rd, u32 ra, s32 ds) { +void Compiler::LDU(u32 rd, u32 ra, s32 ds) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)ds); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3733,7 +3707,7 @@ void PPULLVMRecompiler::LDU(u32 rd, u32 ra, s32 ds) { //InterpreterCall("LDU", &PPUInterpreter::LDU, rd, ra, ds); } -void PPULLVMRecompiler::LWA(u32 rd, u32 ra, s32 ds) { +void Compiler::LWA(u32 rd, u32 ra, s32 ds) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)ds); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3746,7 +3720,7 @@ void PPULLVMRecompiler::LWA(u32 rd, u32 ra, s32 ds) { //InterpreterCall("LWA", &PPUInterpreter::LWA, rd, ra, ds); } -void PPULLVMRecompiler::FDIVS(u32 frd, u32 fra, u32 frb, bool rc) { +void Compiler::FDIVS(u32 frd, u32 fra, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto res_f64 = m_ir_builder->CreateFDiv(ra_f64, rb_f64); @@ -3758,7 +3732,7 @@ void PPULLVMRecompiler::FDIVS(u32 frd, u32 fra, u32 frb, bool rc) { //InterpreterCall("FDIVS", &PPUInterpreter::FDIVS, frd, fra, frb, rc); } -void PPULLVMRecompiler::FSUBS(u32 frd, u32 fra, u32 frb, bool rc) { +void Compiler::FSUBS(u32 frd, u32 fra, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto res_f64 = m_ir_builder->CreateFSub(ra_f64, rb_f64); @@ -3770,7 +3744,7 @@ void PPULLVMRecompiler::FSUBS(u32 frd, u32 fra, u32 frb, bool rc) { //InterpreterCall("FSUBS", &PPUInterpreter::FSUBS, frd, fra, frb, rc); } -void PPULLVMRecompiler::FADDS(u32 frd, u32 fra, u32 frb, bool rc) { +void Compiler::FADDS(u32 frd, u32 fra, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto res_f64 = m_ir_builder->CreateFAdd(ra_f64, rb_f64); @@ -3782,7 +3756,7 @@ void PPULLVMRecompiler::FADDS(u32 frd, u32 fra, u32 frb, bool rc) { //InterpreterCall("FADDS", &PPUInterpreter::FADDS, frd, fra, frb, rc); } -void PPULLVMRecompiler::FSQRTS(u32 frd, u32 frb, bool rc) { +void Compiler::FSQRTS(u32 frd, u32 frb, bool rc) { auto rb_f64 = GetFpr(frb); auto res_f64 = (Value *)m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::sqrt, m_ir_builder->getDoubleTy()), rb_f64); auto res_f32 = m_ir_builder->CreateFPTrunc(res_f64, m_ir_builder->getFloatTy()); @@ -3793,11 +3767,11 @@ void PPULLVMRecompiler::FSQRTS(u32 frd, u32 frb, bool rc) { //InterpreterCall("FSQRTS", &PPUInterpreter::FSQRTS, frd, frb, rc); } -void PPULLVMRecompiler::FRES(u32 frd, u32 frb, bool rc) { +void Compiler::FRES(u32 frd, u32 frb, bool rc) { InterpreterCall("FRES", &PPUInterpreter::FRES, frd, frb, rc); } -void PPULLVMRecompiler::FMULS(u32 frd, u32 fra, u32 frc, bool rc) { +void Compiler::FMULS(u32 frd, u32 fra, u32 frc, bool rc) { auto ra_f64 = GetFpr(fra); auto rc_f64 = GetFpr(frc); auto res_f64 = m_ir_builder->CreateFMul(ra_f64, rc_f64); @@ -3809,7 +3783,7 @@ void PPULLVMRecompiler::FMULS(u32 frd, u32 fra, u32 frc, bool rc) { //InterpreterCall("FMULS", &PPUInterpreter::FMULS, frd, fra, frc, rc); } -void PPULLVMRecompiler::FMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto rc_f64 = GetFpr(frc); @@ -3822,7 +3796,7 @@ void PPULLVMRecompiler::FMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { //InterpreterCall("FMADDS", &PPUInterpreter::FMADDS, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::FMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto rc_f64 = GetFpr(frc); @@ -3836,7 +3810,7 @@ void PPULLVMRecompiler::FMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { //InterpreterCall("FMSUBS", &PPUInterpreter::FMSUBS, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::FNMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FNMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto rc_f64 = GetFpr(frc); @@ -3851,7 +3825,7 @@ void PPULLVMRecompiler::FNMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { //InterpreterCall("FNMSUBS", &PPUInterpreter::FNMSUBS, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::FNMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FNMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto rc_f64 = GetFpr(frc); @@ -3865,7 +3839,7 @@ void PPULLVMRecompiler::FNMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { //InterpreterCall("FNMADDS", &PPUInterpreter::FNMADDS, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::STD(u32 rs, u32 ra, s32 d) { +void Compiler::STD(u32 rs, u32 ra, s32 d) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)d); if (ra) { auto ra_i64 = GetGpr(ra); @@ -3876,7 +3850,7 @@ void PPULLVMRecompiler::STD(u32 rs, u32 ra, s32 d) { //InterpreterCall("STD", &PPUInterpreter::STD, rs, ra, d); } -void PPULLVMRecompiler::STDU(u32 rs, u32 ra, s32 ds) { +void Compiler::STDU(u32 rs, u32 ra, s32 ds) { auto addr_i64 = (Value *)m_ir_builder->getInt64((s64)ds); auto ra_i64 = GetGpr(ra); addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); @@ -3886,47 +3860,47 @@ void PPULLVMRecompiler::STDU(u32 rs, u32 ra, s32 ds) { //InterpreterCall("STDU", &PPUInterpreter::STDU, rs, ra, ds); } -void PPULLVMRecompiler::MTFSB1(u32 crbd, bool rc) { +void Compiler::MTFSB1(u32 crbd, bool rc) { InterpreterCall("MTFSB1", &PPUInterpreter::MTFSB1, crbd, rc); } -void PPULLVMRecompiler::MCRFS(u32 crbd, u32 crbs) { +void Compiler::MCRFS(u32 crbd, u32 crbs) { InterpreterCall("MCRFS", &PPUInterpreter::MCRFS, crbd, crbs); } -void PPULLVMRecompiler::MTFSB0(u32 crbd, bool rc) { +void Compiler::MTFSB0(u32 crbd, bool rc) { InterpreterCall("MTFSB0", &PPUInterpreter::MTFSB0, crbd, rc); } -void PPULLVMRecompiler::MTFSFI(u32 crfd, u32 i, bool rc) { +void Compiler::MTFSFI(u32 crfd, u32 i, bool rc) { InterpreterCall("MTFSFI", &PPUInterpreter::MTFSFI, crfd, i, rc); } -void PPULLVMRecompiler::MFFS(u32 frd, bool rc) { +void Compiler::MFFS(u32 frd, bool rc) { InterpreterCall("MFFS", &PPUInterpreter::MFFS, frd, rc); } -void PPULLVMRecompiler::MTFSF(u32 flm, u32 frb, bool rc) { +void Compiler::MTFSF(u32 flm, u32 frb, bool rc) { InterpreterCall("MTFSF", &PPUInterpreter::MTFSF, flm, frb, rc); } -void PPULLVMRecompiler::FCMPU(u32 crfd, u32 fra, u32 frb) { +void Compiler::FCMPU(u32 crfd, u32 fra, u32 frb) { InterpreterCall("FCMPU", &PPUInterpreter::FCMPU, crfd, fra, frb); } -void PPULLVMRecompiler::FRSP(u32 frd, u32 frb, bool rc) { +void Compiler::FRSP(u32 frd, u32 frb, bool rc) { InterpreterCall("FRSP", &PPUInterpreter::FRSP, frd, frb, rc); } -void PPULLVMRecompiler::FCTIW(u32 frd, u32 frb, bool rc) { +void Compiler::FCTIW(u32 frd, u32 frb, bool rc) { InterpreterCall("FCTIW", &PPUInterpreter::FCTIW, frd, frb, rc); } -void PPULLVMRecompiler::FCTIWZ(u32 frd, u32 frb, bool rc) { +void Compiler::FCTIWZ(u32 frd, u32 frb, bool rc) { InterpreterCall("FCTIWZ", &PPUInterpreter::FCTIWZ, frd, frb, rc); } -void PPULLVMRecompiler::FDIV(u32 frd, u32 fra, u32 frb, bool rc) { +void Compiler::FDIV(u32 frd, u32 fra, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto res_f64 = m_ir_builder->CreateFDiv(ra_f64, rb_f64); @@ -3936,7 +3910,7 @@ void PPULLVMRecompiler::FDIV(u32 frd, u32 fra, u32 frb, bool rc) { //InterpreterCall("FDIV", &PPUInterpreter::FDIV, frd, fra, frb, rc); } -void PPULLVMRecompiler::FSUB(u32 frd, u32 fra, u32 frb, bool rc) { +void Compiler::FSUB(u32 frd, u32 fra, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto res_f64 = m_ir_builder->CreateFSub(ra_f64, rb_f64); @@ -3946,7 +3920,7 @@ void PPULLVMRecompiler::FSUB(u32 frd, u32 fra, u32 frb, bool rc) { //InterpreterCall("FSUB", &PPUInterpreter::FSUB, frd, fra, frb, rc); } -void PPULLVMRecompiler::FADD(u32 frd, u32 fra, u32 frb, bool rc) { +void Compiler::FADD(u32 frd, u32 fra, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto res_f64 = m_ir_builder->CreateFAdd(ra_f64, rb_f64); @@ -3956,7 +3930,7 @@ void PPULLVMRecompiler::FADD(u32 frd, u32 fra, u32 frb, bool rc) { //InterpreterCall("FADD", &PPUInterpreter::FADD, frd, fra, frb, rc); } -void PPULLVMRecompiler::FSQRT(u32 frd, u32 frb, bool rc) { +void Compiler::FSQRT(u32 frd, u32 frb, bool rc) { auto rb_f64 = GetFpr(frb); auto res_f64 = (Value *)m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::sqrt, m_ir_builder->getDoubleTy()), rb_f64); SetFpr(frd, res_f64); @@ -3965,11 +3939,11 @@ void PPULLVMRecompiler::FSQRT(u32 frd, u32 frb, bool rc) { //InterpreterCall("FSQRT", &PPUInterpreter::FSQRT, frd, frb, rc); } -void PPULLVMRecompiler::FSEL(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FSEL(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { InterpreterCall("FSEL", &PPUInterpreter::FSEL, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::FMUL(u32 frd, u32 fra, u32 frc, bool rc) { +void Compiler::FMUL(u32 frd, u32 fra, u32 frc, bool rc) { auto ra_f64 = GetFpr(fra); auto rc_f64 = GetFpr(frc); auto res_f64 = m_ir_builder->CreateFMul(ra_f64, rc_f64); @@ -3979,11 +3953,11 @@ void PPULLVMRecompiler::FMUL(u32 frd, u32 fra, u32 frc, bool rc) { //InterpreterCall("FMUL", &PPUInterpreter::FMUL, frd, fra, frc, rc); } -void PPULLVMRecompiler::FRSQRTE(u32 frd, u32 frb, bool rc) { +void Compiler::FRSQRTE(u32 frd, u32 frb, bool rc) { InterpreterCall("FRSQRTE", &PPUInterpreter::FRSQRTE, frd, frb, rc); } -void PPULLVMRecompiler::FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto rc_f64 = GetFpr(frc); @@ -3995,7 +3969,7 @@ void PPULLVMRecompiler::FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { //InterpreterCall("FMSUB", &PPUInterpreter::FMSUB, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto rc_f64 = GetFpr(frc); @@ -4006,7 +3980,7 @@ void PPULLVMRecompiler::FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { //InterpreterCall("FMADD", &PPUInterpreter::FMADD, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::FNMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FNMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto rc_f64 = GetFpr(frc); @@ -4018,7 +3992,7 @@ void PPULLVMRecompiler::FNMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { //InterpreterCall("FNMSUB", &PPUInterpreter::FNMSUB, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::FNMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { +void Compiler::FNMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { auto ra_f64 = GetFpr(fra); auto rb_f64 = GetFpr(frb); auto rc_f64 = GetFpr(frc); @@ -4031,11 +4005,11 @@ void PPULLVMRecompiler::FNMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) { //InterpreterCall("FNMADD", &PPUInterpreter::FNMADD, frd, fra, frc, frb, rc); } -void PPULLVMRecompiler::FCMPO(u32 crfd, u32 fra, u32 frb) { +void Compiler::FCMPO(u32 crfd, u32 fra, u32 frb) { InterpreterCall("FCMPO", &PPUInterpreter::FCMPO, crfd, fra, frb); } -void PPULLVMRecompiler::FNEG(u32 frd, u32 frb, bool rc) { +void Compiler::FNEG(u32 frd, u32 frb, bool rc) { auto rb_f64 = GetFpr(frb); rb_f64 = m_ir_builder->CreateFNeg(rb_f64); SetFpr(frd, rb_f64); @@ -4044,13 +4018,13 @@ void PPULLVMRecompiler::FNEG(u32 frd, u32 frb, bool rc) { //InterpreterCall("FNEG", &PPUInterpreter::FNEG, frd, frb, rc); } -void PPULLVMRecompiler::FMR(u32 frd, u32 frb, bool rc) { +void Compiler::FMR(u32 frd, u32 frb, bool rc) { SetFpr(frd, GetFpr(frb)); // TODO: Set flags //InterpreterCall("FMR", &PPUInterpreter::FMR, frd, frb, rc); } -void PPULLVMRecompiler::FNABS(u32 frd, u32 frb, bool rc) { +void Compiler::FNABS(u32 frd, u32 frb, bool rc) { auto rb_f64 = GetFpr(frb); auto res_f64 = (Value *)m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::fabs, m_ir_builder->getDoubleTy()), rb_f64); res_f64 = m_ir_builder->CreateFNeg(res_f64); @@ -4060,7 +4034,7 @@ void PPULLVMRecompiler::FNABS(u32 frd, u32 frb, bool rc) { //InterpreterCall("FNABS", &PPUInterpreter::FNABS, frd, frb, rc); } -void PPULLVMRecompiler::FABS(u32 frd, u32 frb, bool rc) { +void Compiler::FABS(u32 frd, u32 frb, bool rc) { auto rb_f64 = GetFpr(frb); auto res_f64 = (Value *)m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::fabs, m_ir_builder->getDoubleTy()), rb_f64); SetFpr(frd, res_f64); @@ -4069,15 +4043,15 @@ void PPULLVMRecompiler::FABS(u32 frd, u32 frb, bool rc) { //InterpreterCall("FABS", &PPUInterpreter::FABS, frd, frb, rc); } -void PPULLVMRecompiler::FCTID(u32 frd, u32 frb, bool rc) { +void Compiler::FCTID(u32 frd, u32 frb, bool rc) { InterpreterCall("FCTID", &PPUInterpreter::FCTID, frd, frb, rc); } -void PPULLVMRecompiler::FCTIDZ(u32 frd, u32 frb, bool rc) { +void Compiler::FCTIDZ(u32 frd, u32 frb, bool rc) { InterpreterCall("FCTIDZ", &PPUInterpreter::FCTIDZ, frd, frb, rc); } -void PPULLVMRecompiler::FCFID(u32 frd, u32 frb, bool rc) { +void Compiler::FCFID(u32 frd, u32 frb, bool rc) { auto rb_i64 = GetFpr(frb, 64, true); auto res_f64 = m_ir_builder->CreateSIToFP(rb_i64, m_ir_builder->getDoubleTy()); SetFpr(frd, res_f64); @@ -4086,12 +4060,26 @@ void PPULLVMRecompiler::FCFID(u32 frd, u32 frb, bool rc) { //InterpreterCall("FCFID", &PPUInterpreter::FCFID, frd, frb, rc); } -void PPULLVMRecompiler::UNK(const u32 code, const u32 opcode, const u32 gcode) { +void Compiler::UNK(const u32 code, const u32 opcode, const u32 gcode) { //InterpreterCall("UNK", &PPUInterpreter::UNK, code, opcode, gcode); } -BasicBlock * PPULLVMRecompiler::GetBlockInFunction(u32 address, Function * function, bool create_if_not_exist) { - auto block_name = fmt::Format("instr_0x%X", address); +std::string Compiler::GetBasicBlockNameFromAddress(u32 address) { + std::string name; + + if (address == 0) { + name = "entry"; + } else if (address == 0xFFFFFFFF) { + name = "unknown"; + } else { + name = fmt::Format("instr_0x%X", address); + } + + return name; +} + +BasicBlock * Compiler::GetBasicBlockFromAddress(u32 address, Function * function, bool create_if_not_exist) { + auto block_name = GetBasicBlockNameFromAddress(address); BasicBlock * block = nullptr; for (auto i = function->getBasicBlockList().begin(); i != function->getBasicBlockList().end(); i++) { if (i->getName() == block_name) { @@ -4107,169 +4095,24 @@ BasicBlock * PPULLVMRecompiler::GetBlockInFunction(u32 address, Function * funct return block; } -void PPULLVMRecompiler::Compile(u32 address) { - auto compilation_start = std::chrono::high_resolution_clock::now(); - - // Get the revision number for this section - u32 revision = 0; - auto compiled = m_compiled.lower_bound(std::make_pair(address, 0)); - if (compiled != m_compiled.end() && compiled->first.first == address) { - revision = ~(compiled->first.second); - revision++; - } - - auto ir_build_start = std::chrono::high_resolution_clock::now(); - - // Create a function for this section - auto function_name = fmt::Format("fn_0x%X_%u", address, revision); - m_current_function = (Function *)m_module->getOrInsertFunction(function_name, m_ir_builder->getVoidTy(), - m_ir_builder->getInt8PtrTy() /*ppu_state*/, - m_ir_builder->getInt8PtrTy() /*interpreter*/, nullptr); - m_current_function->setCallingConv(CallingConv::X86_64_Win64); - auto arg_i = m_current_function->arg_begin(); - arg_i->setName("ppu_state"); - (++arg_i)->setName("interpreter"); - - // Add an entry block that branches to the first instruction - m_ir_builder->SetInsertPoint(BasicBlock::Create(m_ir_builder->getContext(), "entry", m_current_function)); - m_ir_builder->CreateBr(GetBlockInFunction(address, m_current_function, true)); - - // Convert each block in this section to LLVM IR - m_num_instructions = 0; - m_current_function_uncompiled_blocks_list.clear(); - m_current_function_unhit_blocks_list.clear(); - m_current_function_uncompiled_blocks_list.push_back(address); - while (!m_current_function_uncompiled_blocks_list.empty()) { - m_current_instruction_address = m_current_function_uncompiled_blocks_list.front(); - auto block = GetBlockInFunction(m_current_instruction_address, m_current_function, true); - m_hit_branch_instruction = false; - m_ir_builder->SetInsertPoint(block); - m_current_function_uncompiled_blocks_list.pop_front(); - - while (!m_hit_branch_instruction) { - if (!block->getInstList().empty()) { - break; - } - - u32 instr = vm::read32(m_current_instruction_address); - Decode(instr); - m_num_instructions++; - - m_current_instruction_address += 4; - if (!m_hit_branch_instruction) { - block = GetBlockInFunction(m_current_instruction_address, m_current_function, true); - m_ir_builder->CreateBr(block); - m_ir_builder->SetInsertPoint(block); - } - } - } - - auto ir_build_end = std::chrono::high_resolution_clock::now(); - m_ir_build_time += std::chrono::duration_cast(ir_build_end - ir_build_start); - - // Optimize this function - auto optimize_start = std::chrono::high_resolution_clock::now(); - m_fpm->run(*m_current_function); - auto optimize_end = std::chrono::high_resolution_clock::now(); - m_optimizing_time += std::chrono::duration_cast(optimize_end - optimize_start); - - // Translate to machine code - auto translate_start = std::chrono::high_resolution_clock::now(); - MachineCodeInfo mci; - m_execution_engine->runJITOnFunction(m_current_function, &mci); - auto translate_end = std::chrono::high_resolution_clock::now(); - m_translation_time += std::chrono::duration_cast(translate_end - translate_start); - - // Add the executable to private and shared data stores - ExecutableInfo executable_info; - executable_info.executable = (Executable)mci.address(); - executable_info.size = mci.size(); - executable_info.num_instructions = m_num_instructions; - executable_info.unhit_blocks_list = std::move(m_current_function_unhit_blocks_list); - executable_info.llvm_function = m_current_function; - m_compiled[std::make_pair(address, ~revision)] = executable_info; - - { - std::lock_guard lock(m_compiled_shared_lock); - m_compiled_shared[std::make_pair(address, ~revision)] = std::make_pair(executable_info.executable, 0); - } - - if (revision) { - m_revision.fetch_add(1, std::memory_order_relaxed); - } - - auto compilation_end = std::chrono::high_resolution_clock::now(); - m_compilation_time += std::chrono::duration_cast(compilation_end - compilation_start); -} - -void PPULLVMRecompiler::RemoveUnusedOldVersions() { - u32 num_removed = 0; - u32 prev_address = 0; - for (auto i = m_compiled.begin(); i != m_compiled.end(); i++) { - u32 current_address = i->first.first; - if (prev_address == current_address) { - bool erase_this_entry = false; - - { - std::lock_guard lock(m_compiled_shared_lock); - auto j = m_compiled_shared.find(i->first); - if (j->second.second == 0) { - m_compiled_shared.erase(j); - erase_this_entry = true; - } - } - - if (erase_this_entry) { - auto tmp = i; - i--; - m_execution_engine->freeMachineCodeForFunction(tmp->second.llvm_function); - tmp->second.llvm_function->eraseFromParent(); - m_compiled.erase(tmp); - num_removed++; - } - } - - prev_address = current_address; - } - - if (num_removed > 0) { - LOG_NOTICE(PPU, "Removed %u old versions", num_removed); - } -} - -bool PPULLVMRecompiler::NeedsCompiling(u32 address) { - auto i = m_compiled.lower_bound(std::make_pair(address, 0)); - if (i != m_compiled.end() && i->first.first == address) { - if (i->second.num_instructions >= 300) { - // This section has reached its limit. Don't allow further expansion. - return false; - } - - // If any of the unhit blocks in this function have been hit, then recompile this section - for (auto j = i->second.unhit_blocks_list.begin(); j != i->second.unhit_blocks_list.end(); j++) { - if (m_hit_blocks.find(*j) != m_hit_blocks.end()) { - return true; - } - } - - return false; - } else { - // This section has not been encountered before - return true; - } -} - -Value * PPULLVMRecompiler::GetPPUState() { +Value * Compiler::GetPPUStateArg() { return m_current_function->arg_begin(); } -Value * PPULLVMRecompiler::GetInterpreter() { +Value * Compiler::GetInterpreterArg() { auto i = m_current_function->arg_begin(); i++; return i; } -Value * PPULLVMRecompiler::GetBit(Value * val, u32 n) { +Value * Compiler::GetTracerArg() { + auto i = m_current_function->arg_begin(); + i++; + i++; + return i; +} + +Value * Compiler::GetBit(Value * val, u32 n) { Value * bit; #ifdef PPU_LLVM_RECOMPILER_USE_BMI @@ -4291,11 +4134,11 @@ Value * PPULLVMRecompiler::GetBit(Value * val, u32 n) { return bit; } -Value * PPULLVMRecompiler::ClrBit(Value * val, u32 n) { +Value * Compiler::ClrBit(Value * val, u32 n) { return m_ir_builder->CreateAnd(val, ~((u64)1 << (val->getType()->getIntegerBitWidth() - n - 1))); } -Value * PPULLVMRecompiler::SetBit(Value * val, u32 n, Value * bit, bool doClear) { +Value * Compiler::SetBit(Value * val, u32 n, Value * bit, bool doClear) { if (doClear) { val = ClrBit(val, n); } @@ -4313,7 +4156,7 @@ Value * PPULLVMRecompiler::SetBit(Value * val, u32 n, Value * bit, bool doClear) return m_ir_builder->CreateOr(val, bit); } -Value * PPULLVMRecompiler::GetNibble(Value * val, u32 n) { +Value * Compiler::GetNibble(Value * val, u32 n) { Value * nibble; #ifdef PPU_LLVM_RECOMPILER_USE_BMI @@ -4335,11 +4178,11 @@ Value * PPULLVMRecompiler::GetNibble(Value * val, u32 n) { return nibble; } -Value * PPULLVMRecompiler::ClrNibble(Value * val, u32 n) { +Value * Compiler::ClrNibble(Value * val, u32 n) { return m_ir_builder->CreateAnd(val, ~((u64)0xF << ((((val->getType()->getIntegerBitWidth() >> 2) - 1) - n) * 4))); } -Value * PPULLVMRecompiler::SetNibble(Value * val, u32 n, Value * nibble, bool doClear) { +Value * Compiler::SetNibble(Value * val, u32 n, Value * nibble, bool doClear) { if (doClear) { val = ClrNibble(val, n); } @@ -4357,7 +4200,7 @@ Value * PPULLVMRecompiler::SetNibble(Value * val, u32 n, Value * nibble, bool do return m_ir_builder->CreateOr(val, nibble); } -Value * PPULLVMRecompiler::SetNibble(Value * val, u32 n, Value * b0, Value * b1, Value * b2, Value * b3, bool doClear) { +Value * Compiler::SetNibble(Value * val, u32 n, Value * b0, Value * b1, Value * b2, Value * b3, bool doClear) { if (doClear) { val = ClrNibble(val, n); } @@ -4381,58 +4224,58 @@ Value * PPULLVMRecompiler::SetNibble(Value * val, u32 n, Value * b0, Value * b1, return val; } -Value * PPULLVMRecompiler::GetPc() { - auto pc_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, PC)); +Value * Compiler::GetPc() { + auto pc_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, PC)); auto pc_i32_ptr = m_ir_builder->CreateBitCast(pc_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(pc_i32_ptr, 4); } -void PPULLVMRecompiler::SetPc(Value * val_ix) { - auto pc_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, PC)); +void Compiler::SetPc(Value * val_ix) { + auto pc_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, PC)); auto pc_i32_ptr = m_ir_builder->CreateBitCast(pc_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); auto val_i32 = m_ir_builder->CreateZExtOrTrunc(val_ix, m_ir_builder->getInt32Ty()); m_ir_builder->CreateAlignedStore(val_i32, pc_i32_ptr, 4); } -Value * PPULLVMRecompiler::GetGpr(u32 r, u32 num_bits) { - auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, GPR[r])); +Value * Compiler::GetGpr(u32 r, u32 num_bits) { + auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, GPR[r])); auto r_ix_ptr = m_ir_builder->CreateBitCast(r_i8_ptr, m_ir_builder->getIntNTy(num_bits)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(r_ix_ptr, 8); } -void PPULLVMRecompiler::SetGpr(u32 r, Value * val_x64) { - auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, GPR[r])); +void Compiler::SetGpr(u32 r, Value * val_x64) { + auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, GPR[r])); auto r_i64_ptr = m_ir_builder->CreateBitCast(r_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); m_ir_builder->CreateAlignedStore(val_i64, r_i64_ptr, 8); } -Value * PPULLVMRecompiler::GetCr() { - auto cr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, CR)); +Value * Compiler::GetCr() { + auto cr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, CR)); auto cr_i32_ptr = m_ir_builder->CreateBitCast(cr_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(cr_i32_ptr, 4); } -Value * PPULLVMRecompiler::GetCrField(u32 n) { +Value * Compiler::GetCrField(u32 n) { return GetNibble(GetCr(), n); } -void PPULLVMRecompiler::SetCr(Value * val_x32) { +void Compiler::SetCr(Value * val_x32) { auto val_i32 = m_ir_builder->CreateBitCast(val_x32, m_ir_builder->getInt32Ty()); - auto cr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, CR)); + auto cr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, CR)); auto cr_i32_ptr = m_ir_builder->CreateBitCast(cr_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i32, cr_i32_ptr, 4); } -void PPULLVMRecompiler::SetCrField(u32 n, Value * field) { +void Compiler::SetCrField(u32 n, Value * field) { SetCr(SetNibble(GetCr(), n, field)); } -void PPULLVMRecompiler::SetCrField(u32 n, Value * b0, Value * b1, Value * b2, Value * b3) { +void Compiler::SetCrField(u32 n, Value * b0, Value * b1, Value * b2, Value * b3) { SetCr(SetNibble(GetCr(), n, b0, b1, b2, b3)); } -void PPULLVMRecompiler::SetCrFieldSignedCmp(u32 n, Value * a, Value * b) { +void Compiler::SetCrFieldSignedCmp(u32 n, Value * a, Value * b) { auto lt_i1 = m_ir_builder->CreateICmpSLT(a, b); auto gt_i1 = m_ir_builder->CreateICmpSGT(a, b); auto eq_i1 = m_ir_builder->CreateICmpEQ(a, b); @@ -4441,7 +4284,7 @@ void PPULLVMRecompiler::SetCrFieldSignedCmp(u32 n, Value * a, Value * b) { SetCr(cr_i32); } -void PPULLVMRecompiler::SetCrFieldUnsignedCmp(u32 n, Value * a, Value * b) { +void Compiler::SetCrFieldUnsignedCmp(u32 n, Value * a, Value * b) { auto lt_i1 = m_ir_builder->CreateICmpULT(a, b); auto gt_i1 = m_ir_builder->CreateICmpUGT(a, b); auto eq_i1 = m_ir_builder->CreateICmpEQ(a, b); @@ -4450,7 +4293,7 @@ void PPULLVMRecompiler::SetCrFieldUnsignedCmp(u32 n, Value * a, Value * b) { SetCr(cr_i32); } -void PPULLVMRecompiler::SetCr6AfterVectorCompare(u32 vr) { +void Compiler::SetCr6AfterVectorCompare(u32 vr) { auto vr_v16i8 = GetVrAsIntVec(vr, 8); auto vr_mask_i32 = m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::x86_sse2_pmovmskb_128), vr_v16i8); auto cmp0_i1 = m_ir_builder->CreateICmpEQ(vr_mask_i32, m_ir_builder->getInt32(0)); @@ -4460,80 +4303,80 @@ void PPULLVMRecompiler::SetCr6AfterVectorCompare(u32 vr) { SetCr(cr_i32); } -Value * PPULLVMRecompiler::GetLr() { - auto lr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, LR)); +Value * Compiler::GetLr() { + auto lr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, LR)); auto lr_i64_ptr = m_ir_builder->CreateBitCast(lr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(lr_i64_ptr, 8); } -void PPULLVMRecompiler::SetLr(Value * val_x64) { +void Compiler::SetLr(Value * val_x64) { auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); - auto lr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, LR)); + auto lr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, LR)); auto lr_i64_ptr = m_ir_builder->CreateBitCast(lr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i64, lr_i64_ptr, 8); } -Value * PPULLVMRecompiler::GetCtr() { - auto ctr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, CTR)); +Value * Compiler::GetCtr() { + auto ctr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, CTR)); auto ctr_i64_ptr = m_ir_builder->CreateBitCast(ctr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(ctr_i64_ptr, 8); } -void PPULLVMRecompiler::SetCtr(Value * val_x64) { +void Compiler::SetCtr(Value * val_x64) { auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); - auto ctr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, CTR)); + auto ctr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, CTR)); auto ctr_i64_ptr = m_ir_builder->CreateBitCast(ctr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i64, ctr_i64_ptr, 8); } -Value * PPULLVMRecompiler::GetXer() { - auto xer_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, XER)); +Value * Compiler::GetXer() { + auto xer_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, XER)); auto xer_i64_ptr = m_ir_builder->CreateBitCast(xer_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(xer_i64_ptr, 8); } -Value * PPULLVMRecompiler::GetXerCa() { +Value * Compiler::GetXerCa() { return GetBit(GetXer(), 34); } -Value * PPULLVMRecompiler::GetXerSo() { +Value * Compiler::GetXerSo() { return GetBit(GetXer(), 32); } -void PPULLVMRecompiler::SetXer(Value * val_x64) { +void Compiler::SetXer(Value * val_x64) { auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); - auto xer_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, XER)); + auto xer_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, XER)); auto xer_i64_ptr = m_ir_builder->CreateBitCast(xer_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i64, xer_i64_ptr, 8); } -void PPULLVMRecompiler::SetXerCa(Value * ca) { +void Compiler::SetXerCa(Value * ca) { auto xer_i64 = GetXer(); xer_i64 = SetBit(xer_i64, 34, ca); SetXer(xer_i64); } -void PPULLVMRecompiler::SetXerSo(Value * so) { +void Compiler::SetXerSo(Value * so) { auto xer_i64 = GetXer(); xer_i64 = SetBit(xer_i64, 32, so); SetXer(xer_i64); } -Value * PPULLVMRecompiler::GetUsprg0() { - auto usrpg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, USPRG0)); +Value * Compiler::GetUsprg0() { + auto usrpg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, USPRG0)); auto usprg0_i64_ptr = m_ir_builder->CreateBitCast(usrpg0_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(usprg0_i64_ptr, 8); } -void PPULLVMRecompiler::SetUsprg0(Value * val_x64) { +void Compiler::SetUsprg0(Value * val_x64) { auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); - auto usprg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, USPRG0)); + auto usprg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, USPRG0)); auto usprg0_i64_ptr = m_ir_builder->CreateBitCast(usprg0_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i64, usprg0_i64_ptr, 8); } -Value * PPULLVMRecompiler::GetFpr(u32 r, u32 bits, bool as_int) { - auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, FPR[r])); +Value * Compiler::GetFpr(u32 r, u32 bits, bool as_int) { + auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, FPR[r])); if (!as_int) { auto r_f64_ptr = m_ir_builder->CreateBitCast(r_i8_ptr, m_ir_builder->getDoubleTy()->getPointerTo()); auto r_f64 = m_ir_builder->CreateAlignedLoad(r_f64_ptr, 8); @@ -4553,8 +4396,8 @@ Value * PPULLVMRecompiler::GetFpr(u32 r, u32 bits, bool as_int) { } } -void PPULLVMRecompiler::SetFpr(u32 r, Value * val) { - auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, FPR[r])); +void Compiler::SetFpr(u32 r, Value * val) { + auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, FPR[r])); auto r_f64_ptr = m_ir_builder->CreateBitCast(r_i8_ptr, m_ir_builder->getDoubleTy()->getPointerTo()); Value* val_f64; @@ -4570,54 +4413,54 @@ void PPULLVMRecompiler::SetFpr(u32 r, Value * val) { m_ir_builder->CreateAlignedStore(val_f64, r_f64_ptr, 8); } -Value * PPULLVMRecompiler::GetVscr() { - auto vscr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, VSCR)); +Value * Compiler::GetVscr() { + auto vscr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VSCR)); auto vscr_i32_ptr = m_ir_builder->CreateBitCast(vscr_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vscr_i32_ptr, 4); } -void PPULLVMRecompiler::SetVscr(Value * val_x32) { +void Compiler::SetVscr(Value * val_x32) { auto val_i32 = m_ir_builder->CreateBitCast(val_x32, m_ir_builder->getInt32Ty()); - auto vscr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, VSCR)); + auto vscr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VSCR)); auto vscr_i32_ptr = m_ir_builder->CreateBitCast(vscr_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i32, vscr_i32_ptr, 4); } -Value * PPULLVMRecompiler::GetVr(u32 vr) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, VPR[vr])); +Value * Compiler::GetVr(u32 vr) { + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vr_i128_ptr, 16); } -Value * PPULLVMRecompiler::GetVrAsIntVec(u32 vr, u32 vec_elt_num_bits) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, VPR[vr])); +Value * Compiler::GetVrAsIntVec(u32 vr, u32 vec_elt_num_bits) { + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); auto vr_vec_ptr = m_ir_builder->CreateBitCast(vr_i128_ptr, VectorType::get(m_ir_builder->getIntNTy(vec_elt_num_bits), 128 / vec_elt_num_bits)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vr_vec_ptr, 16); } -Value * PPULLVMRecompiler::GetVrAsFloatVec(u32 vr) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, VPR[vr])); +Value * Compiler::GetVrAsFloatVec(u32 vr) { + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); auto vr_v4f32_ptr = m_ir_builder->CreateBitCast(vr_i128_ptr, VectorType::get(m_ir_builder->getFloatTy(), 4)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vr_v4f32_ptr, 16); } -Value * PPULLVMRecompiler::GetVrAsDoubleVec(u32 vr) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, VPR[vr])); +Value * Compiler::GetVrAsDoubleVec(u32 vr) { + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); auto vr_v2f64_ptr = m_ir_builder->CreateBitCast(vr_i128_ptr, VectorType::get(m_ir_builder->getDoubleTy(), 2)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vr_v2f64_ptr, 16); } -void PPULLVMRecompiler::SetVr(u32 vr, Value * val_x128) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUState(), (unsigned int)offsetof(PPUThread, VPR[vr])); +void Compiler::SetVr(u32 vr, Value * val_x128) { + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); auto val_i128 = m_ir_builder->CreateBitCast(val_x128, m_ir_builder->getIntNTy(128)); m_ir_builder->CreateAlignedStore(val_i128, vr_i128_ptr, 16); } -Value * PPULLVMRecompiler::CheckBranchCondition(u32 bo, u32 bi) { +Value * Compiler::CheckBranchCondition(u32 bo, u32 bi) { bool bo0 = bo & 0x10 ? true : false; bool bo1 = bo & 0x08 ? true : false; bool bo2 = bo & 0x04 ? true : false; @@ -4659,54 +4502,49 @@ Value * PPULLVMRecompiler::CheckBranchCondition(u32 bo, u32 bi) { return cmp_i1; } -void PPULLVMRecompiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool lk) { +void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool lk, bool target_is_lr) { if (lk) { SetLr(m_ir_builder->getInt64(m_current_instruction_address + 4)); } - auto current_block = m_ir_builder->GetInsertBlock(); + auto current_block = m_ir_builder->GetInsertBlock(); + BasicBlock * target_block = nullptr; if (dyn_cast(target_i64)) { // Target address is an immediate value. u32 target_address = (u32)(dyn_cast(target_i64)->getLimitedValue()); - target_block = GetBlockInFunction(target_address, m_current_function); + target_block = GetBasicBlockFromAddress(target_address, m_current_function); if (!target_block) { - target_block = GetBlockInFunction(target_address, m_current_function, true); - if ((m_hit_blocks.find(target_address) != m_hit_blocks.end() || !cmp_i1) && m_num_instructions < 300) { - // Target block has either been hit or this is an unconditional branch. - m_current_function_uncompiled_blocks_list.push_back(target_address); - m_hit_blocks.insert(target_address); - } else { - // Target block has not been encountered yet and this is not an unconditional branch - m_ir_builder->SetInsertPoint(target_block); - SetPc(target_i64); - m_ir_builder->CreateRetVoid(); - m_current_function_unhit_blocks_list.push_back(target_address); - } + target_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function); + m_ir_builder->SetInsertPoint(target_block); + SetPc(target_i64); + m_ir_builder->CreateBr(GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function, true)); } } else { - // Target addres is in a register + // Target address is in a register target_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function); m_ir_builder->SetInsertPoint(target_block); SetPc(target_i64); - m_ir_builder->CreateRetVoid(); + + if (target_is_lr && !lk) { + // Return from function call + m_ir_builder->CreateRetVoid(); + } else { + auto switch_instr = m_ir_builder->CreateSwitch(target_i64, GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function, true)); + for (auto i = m_current_block_next_blocks->begin(); i != m_current_block_next_blocks->end(); i++) { + switch_instr->addCase(m_ir_builder->getInt32(i->address), GetBasicBlockFromAddress(i->address, m_current_function)); + } + } } if (cmp_i1) { // Conditional branch - auto next_block = GetBlockInFunction(m_current_instruction_address + 4, m_current_function); + auto next_block = GetBasicBlockFromAddress(m_current_instruction_address + 4, m_current_function); if (!next_block) { - next_block = GetBlockInFunction(m_current_instruction_address + 4, m_current_function, true); - if (m_hit_blocks.find(m_current_instruction_address + 4) != m_hit_blocks.end() && m_num_instructions < 300) { - // Next block has already been hit. - m_current_function_uncompiled_blocks_list.push_back(m_current_instruction_address + 4); - } else { - // Next block has not been encountered yet - m_ir_builder->SetInsertPoint(next_block); - SetPc(m_ir_builder->getInt32(m_current_instruction_address + 4)); - m_ir_builder->CreateRetVoid(); - m_current_function_unhit_blocks_list.push_back(m_current_instruction_address + 4); - } + next_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function); + m_ir_builder->SetInsertPoint(next_block); + SetPc(m_ir_builder->getInt32(m_current_instruction_address + 4)); + m_ir_builder->CreateBr(GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function, true)); } m_ir_builder->SetInsertPoint(current_block); @@ -4720,7 +4558,7 @@ void PPULLVMRecompiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_ m_hit_branch_instruction = true; } -Value * PPULLVMRecompiler::ReadMemory(Value * addr_i64, u32 bits, u32 alignment, bool bswap, bool could_be_mmio) { +Value * Compiler::ReadMemory(Value * addr_i64, u32 bits, u32 alignment, bool bswap, bool could_be_mmio) { if (bits != 32 || could_be_mmio == false) { auto eaddr_i64 = m_ir_builder->CreateAdd(addr_i64, m_ir_builder->getInt64((u64)vm::get_ptr(0))); auto eaddr_ix_ptr = m_ir_builder->CreateIntToPtr(eaddr_i64, m_ir_builder->getIntNTy(bits)->getPointerTo()); @@ -4760,7 +4598,7 @@ Value * PPULLVMRecompiler::ReadMemory(Value * addr_i64, u32 bits, u32 alignment, m_ir_builder->CreateBr(merge_bb); m_ir_builder->SetInsertPoint(else_bb); - auto val_else_i32 = Call("vm_read32", (u32(*)(u64))vm::read32, addr_i64); + auto val_else_i32 = Call("vm.read32", (u32(*)(u64))vm::read32, addr_i64); if (!bswap) { val_else_i32 = m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::bswap, m_ir_builder->getInt32Ty()), val_else_i32); } @@ -4774,7 +4612,7 @@ Value * PPULLVMRecompiler::ReadMemory(Value * addr_i64, u32 bits, u32 alignment, } } -void PPULLVMRecompiler::WriteMemory(Value * addr_i64, Value * val_ix, u32 alignment, bool bswap, bool could_be_mmio) { +void Compiler::WriteMemory(Value * addr_i64, Value * val_ix, u32 alignment, bool bswap, bool could_be_mmio) { addr_i64 = m_ir_builder->CreateAnd(addr_i64, 0xFFFFFFFF); if (val_ix->getType()->getIntegerBitWidth() != 32 || could_be_mmio == false) { if (val_ix->getType()->getIntegerBitWidth() > 8 && bswap) { @@ -4820,7 +4658,7 @@ void PPULLVMRecompiler::WriteMemory(Value * addr_i64, Value * val_ix, u32 alignm val_else_i32 = m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::bswap, m_ir_builder->getInt32Ty()), val_else_i32); } - Call("vm_write32", (void(*)(u64, u32))vm::write32, addr_i64, val_else_i32); + Call("vm.write32", (void(*)(u64, u32))vm::write32, addr_i64, val_else_i32); m_ir_builder->CreateBr(merge_bb); m_ir_builder->SetInsertPoint(merge_bb); @@ -4828,19 +4666,19 @@ void PPULLVMRecompiler::WriteMemory(Value * addr_i64, Value * val_ix, u32 alignm } template -Value * PPULLVMRecompiler::InterpreterCall(const char * name, Func function, Args... args) { - auto i = m_interpreter_fallback_stats.find(name); - if (i == m_interpreter_fallback_stats.end()) { - i = m_interpreter_fallback_stats.insert(m_interpreter_fallback_stats.end(), std::make_pair(name, 0)); +Value * Compiler::InterpreterCall(const char * name, Func function, Args... args) { + auto i = m_stats.interpreter_fallback_stats.find(name); + if (i == m_stats.interpreter_fallback_stats.end()) { + i = m_stats.interpreter_fallback_stats.insert(m_stats.interpreter_fallback_stats.end(), std::make_pair(name, 0)); } i->second++; - return Call(name, function, GetInterpreter(), m_ir_builder->getInt32(args)...); + return Call(name, function, GetInterpreterArg(), m_ir_builder->getInt32(args)...); } template -Type * PPULLVMRecompiler::CppToLlvmType() { +Type * Compiler::CppToLlvmType() { if (std::is_void::value) { return m_ir_builder->getVoidTy(); } else if (std::is_same::value || std::is_same::value) { @@ -4865,7 +4703,7 @@ Type * PPULLVMRecompiler::CppToLlvmType() { } template -Value * PPULLVMRecompiler::Call(const char * name, Func function, Args... args) { +Value * Compiler::Call(const char * name, Func function, Args... args) { auto fn = m_module->getFunction(name); if (!fn) { std::vector fn_args_type = {args->getType()...}; @@ -4879,7 +4717,22 @@ Value * PPULLVMRecompiler::Call(const char * name, Func function, Args... args) return m_ir_builder->CreateCall(fn, fn_args); } -void PPULLVMRecompiler::InitRotateMask() { +bool Compiler::IsBranchInstruction(u32 instruction) { + bool is_branch = false; + u32 field1 = instruction >> 26; + if (field1 == 16 || field1 == 18) { + is_branch = true; + } else if (field1 == 19) { + u32 field2 = (instruction >> 1) & 0x3FF; + if (field2 == 16 || field2 == 528) { + is_branch = true; + } + } + + return is_branch; +} + +void Compiler::InitRotateMask() { for (u32 mb = 0; mb < 64; mb++) { for (u32 me = 0; me < 64; me++) { u64 mask = ((u64)-1 >> mb) ^ ((me >= 63) ? 0 : (u64)-1 >> (me + 1)); @@ -4888,101 +4741,188 @@ void PPULLVMRecompiler::InitRotateMask() { } } -u32 PPULLVMEmulator::s_num_instances = 0; -std::mutex PPULLVMEmulator::s_recompiler_mutex; -PPULLVMRecompiler * PPULLVMEmulator::s_recompiler = nullptr; +std::mutex RecompilationEngine::s_mutex; +std::shared_ptr RecompilationEngine::s_the_instance; -PPULLVMEmulator::PPULLVMEmulator(PPUThread & ppu) +CompiledCodeFragment RecompilationEngine::GetCompiledCodeFragment(u32 address) { + return nullptr; +} + +void ReleaseCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment) { + +} + +u32 RecompilationEngine::GetCurrentRevision() { + return 0; +} + +std::shared_ptr RecompilationEngine::GetInstance() { + if (s_the_instance == nullptr) { + std::lock_guard lock(s_mutex); + s_the_instance = std::shared_ptr(new RecompilationEngine()); + } + + return s_the_instance; +} + +Tracer::Tracer() { + m_trace.reserve(1000); + m_stack.reserve(100); +} + +Tracer::~Tracer() { + Terminate(); +} + +void Tracer::Trace(BranchType branch_type, u32 address) { + ExecutionTrace * execution_trace = nullptr; + BlockId block_id; + int function; + int start; + + block_id.address = address; + block_id.type = branch_type; + switch (branch_type) { + case FunctionCall: + m_stack.push_back((u32)m_trace.size()); + m_trace.push_back(block_id); + break; + case Block: + function = m_stack.back(); + for (int i = (int)m_trace.size() - 1; i >= function; i--) { + if (m_trace[i].address == address) { + // Found a loop within the current function + execution_trace = new ExecutionTrace(); + execution_trace->type = ExecutionTrace::Loop; + execution_trace->function_address = m_trace[function].address; + execution_trace->blocks.insert(execution_trace->blocks.begin(), m_trace.begin() + i, m_trace.end()); + m_trace.erase(m_trace.begin() + i + 1, m_trace.end()); + break; + } + } + + if (!execution_trace) { + // A loop was not found + m_trace.push_back(block_id); + } + break; + case Return: + function = m_stack.back(); + m_stack.pop_back(); + + start = function; + + execution_trace = new ExecutionTrace(); + execution_trace->function_address = m_trace[function].address; + execution_trace->type = ExecutionTrace::Linear; + execution_trace->blocks.insert(execution_trace->blocks.begin(), m_trace.begin() + start, m_trace.end()); + m_trace.erase(m_trace.begin() + start + 1, m_trace.end()); + break; + case None: + break; + default: + assert(0); + break; + } + + if (execution_trace) { + auto s = fmt::Format("Trace: 0x%08X, %s -> ", execution_trace->function_address, execution_trace->type == ExecutionTrace::Loop ? "Loop" : "Linear"); + for (auto i = 0; i < execution_trace->blocks.size(); i++) { + s += fmt::Format("0x%08X ", execution_trace->blocks[i]); + } + + LOG_NOTICE(PPU, s.c_str()); + delete execution_trace; + // TODO: Notify recompilation engine + } +} + +void Tracer::Terminate() { + // TODO: Notify recompilation engine +} + +ppu_recompiler_llvm::ExecutionEngine::ExecutionEngine(PPUThread & ppu) : m_ppu(ppu) , m_interpreter(new PPUInterpreter(ppu)) , m_decoder(m_interpreter) - , m_last_instr_was_branch(true) + , m_last_branch_type(FunctionCall) , m_last_cache_clear_time(std::chrono::high_resolution_clock::now()) - , m_recompiler_revision(0) { - std::lock_guard lock(s_recompiler_mutex); + , m_recompiler_revision(0) + , m_recompilation_engine(RecompilationEngine::GetInstance()) { +} - s_num_instances++; - if (!s_recompiler) { - s_recompiler = new PPULLVMRecompiler(); - s_recompiler->RunAllTests(&m_ppu, m_interpreter); +ppu_recompiler_llvm::ExecutionEngine::~ExecutionEngine() { + for (auto iter = m_address_to_compiled_code_fragment.begin(); iter != m_address_to_compiled_code_fragment.end(); iter++) { + m_recompilation_engine->ReleaseCompiledCodeFragment(iter->second.first); } } -PPULLVMEmulator::~PPULLVMEmulator() { - for (auto iter = m_address_to_executable.begin(); iter != m_address_to_executable.end(); iter++) { - s_recompiler->ReleaseExecutable(iter->first, iter->second.revision); - } - - std::lock_guard lock(s_recompiler_mutex); - - s_num_instances--; - if (s_recompiler && s_num_instances == 0) { - delete s_recompiler; - s_recompiler = nullptr; - } -} - -u8 PPULLVMEmulator::DecodeMemory(const u32 address) { +u8 ppu_recompiler_llvm::ExecutionEngine::DecodeMemory(const u32 address) { auto now = std::chrono::high_resolution_clock::now(); - if (std::chrono::duration_cast(now - m_last_cache_clear_time).count() > 1000) { - bool clear_all = false; + if (std::chrono::duration_cast(now - m_last_cache_clear_time).count() > 10000) { + bool clear_all = false; - u32 revision = s_recompiler->GetCurrentRevision(); + u32 revision = m_recompilation_engine->GetCurrentRevision(); if (m_recompiler_revision != revision) { m_recompiler_revision = revision; clear_all = true; } - for (auto iter = m_address_to_executable.begin(); iter != m_address_to_executable.end();) { - auto tmp = iter; - iter++; - if (tmp->second.num_hits == 0 || clear_all) { - m_address_to_executable.erase(tmp); - s_recompiler->ReleaseExecutable(tmp->first, tmp->second.revision); + for (auto i = m_address_to_compiled_code_fragment.begin(); i != m_address_to_compiled_code_fragment.end();) { + auto tmp = i; + i++; + if (tmp->second.second == 0 || clear_all) { + m_address_to_compiled_code_fragment.erase(tmp); + m_recompilation_engine->ReleaseCompiledCodeFragment(tmp->second.first); } else { - tmp->second.num_hits = 0; + tmp->second.second = 0; } } m_last_cache_clear_time = now; } - auto address_to_executable_iter = m_address_to_executable.find(address); - if (address_to_executable_iter == m_address_to_executable.end()) { - auto executable_and_revision = s_recompiler->GetExecutable(address); - if (executable_and_revision.first) { - ExecutableInfo executable_info; - executable_info.executable = executable_and_revision.first; - executable_info.revision = executable_and_revision.second; - executable_info.num_hits = 0; - - address_to_executable_iter = m_address_to_executable.insert(m_address_to_executable.end(), std::make_pair(address, executable_info)); - m_uncompiled.erase(address); - } else { - if (m_last_instr_was_branch) { - auto uncompiled_iter = m_uncompiled.find(address); - if (uncompiled_iter != m_uncompiled.end()) { - uncompiled_iter->second++; - if ((uncompiled_iter->second % 1000) == 0) { - s_recompiler->RequestCompilation(address); - } - } else { - m_uncompiled[address] = 0; - } - } + auto i = m_address_to_compiled_code_fragment.find(address); + if (i == m_address_to_compiled_code_fragment.end()) { + auto compiled_code_fragment = m_recompilation_engine->GetCompiledCodeFragment(address); + if (compiled_code_fragment) { + i = m_address_to_compiled_code_fragment.insert(m_address_to_compiled_code_fragment.end(), std::make_pair(address, std::make_pair(compiled_code_fragment, 0))); } } u8 ret = 0; - if (address_to_executable_iter != m_address_to_executable.end()) { - address_to_executable_iter->second.executable(&m_ppu, m_interpreter); - address_to_executable_iter->second.num_hits++; - m_last_instr_was_branch = true; + if (i != m_address_to_compiled_code_fragment.end()) { + m_last_branch_type = None; + i->second.second++; + i->second.first(&m_ppu, m_interpreter); } else { - ret = m_decoder.DecodeMemory(address); - m_last_instr_was_branch = m_ppu.m_is_branch; + if (m_last_branch_type != None) { + m_tracer.Trace(m_last_branch_type, address); + } + + ret = m_decoder.DecodeMemory(address); + m_last_branch_type = m_ppu.m_is_branch ? GetBranchTypeFromInstruction(vm::read32(address)) : None; } return ret; } + +BranchType GetBranchTypeFromInstruction(u32 instruction) { + auto type = BranchType::None; + auto field1 = instruction >> 26; + auto lk = instruction & 1; + + if (field1 == 16 || field1 == 18) { + type = lk ? FunctionCall : Block; + } else if (field1 == 19) { + u32 field2 = (instruction >> 1) & 0x3FF; + if (field2 == 16) { + type = lk ? FunctionCall : Return; + } else if (field2 == 528) { + type = lk ? FunctionCall : Block; + } + } + + return type; +} diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index 218dad9721..a93da8021b 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -11,779 +11,822 @@ #include "llvm/ExecutionEngine/JIT.h" #include "llvm/PassManager.h" -struct PPUState; - -/// PPU recompiler that uses LLVM for code generation and optimization -class PPULLVMRecompiler : public ThreadBase, protected PPUOpcodes, protected PPCDecoder { -public: - typedef void(*Executable)(PPUThread * ppu_state, PPUInterpreter * interpreter); - - PPULLVMRecompiler(); - - PPULLVMRecompiler(const PPULLVMRecompiler & other) = delete; - PPULLVMRecompiler(PPULLVMRecompiler && other) = delete; - - virtual ~PPULLVMRecompiler(); - - PPULLVMRecompiler & operator = (const PPULLVMRecompiler & other) = delete; - PPULLVMRecompiler & operator = (PPULLVMRecompiler && other) = delete; - - /// Get the executable for the code starting at address - std::pair GetExecutable(u32 address); - - /// Release an executable earlier obtained through GetExecutable - void ReleaseExecutable(u32 address, u32 revision); - - /// Request the code at the sepcified address to be compiled - void RequestCompilation(u32 address); - - /// Get the current revision - u32 GetCurrentRevision(); - - /// Execute all tests - void RunAllTests(PPUThread * ppu_state, PPUInterpreter * interpreter); - - void Task() override; - -protected: - void Decode(const u32 code) override; - - void NULL_OP() override; - void NOP() override; - - void TDI(u32 to, u32 ra, s32 simm16) override; - void TWI(u32 to, u32 ra, s32 simm16) override; - - void MFVSCR(u32 vd) override; - void MTVSCR(u32 vb) override; - void VADDCUW(u32 vd, u32 va, u32 vb) override; - void VADDFP(u32 vd, u32 va, u32 vb) override; - void VADDSBS(u32 vd, u32 va, u32 vb) override; - void VADDSHS(u32 vd, u32 va, u32 vb) override; - void VADDSWS(u32 vd, u32 va, u32 vb) override; - void VADDUBM(u32 vd, u32 va, u32 vb) override; - void VADDUBS(u32 vd, u32 va, u32 vb) override; - void VADDUHM(u32 vd, u32 va, u32 vb) override; - void VADDUHS(u32 vd, u32 va, u32 vb) override; - void VADDUWM(u32 vd, u32 va, u32 vb) override; - void VADDUWS(u32 vd, u32 va, u32 vb) override; - void VAND(u32 vd, u32 va, u32 vb) override; - void VANDC(u32 vd, u32 va, u32 vb) override; - void VAVGSB(u32 vd, u32 va, u32 vb) override; - void VAVGSH(u32 vd, u32 va, u32 vb) override; - void VAVGSW(u32 vd, u32 va, u32 vb) override; - void VAVGUB(u32 vd, u32 va, u32 vb) override; - void VAVGUH(u32 vd, u32 va, u32 vb) override; - void VAVGUW(u32 vd, u32 va, u32 vb) override; - void VCFSX(u32 vd, u32 uimm5, u32 vb) override; - void VCFUX(u32 vd, u32 uimm5, u32 vb) override; - void VCMPBFP(u32 vd, u32 va, u32 vb) override; - void VCMPBFP_(u32 vd, u32 va, u32 vb) override; - void VCMPEQFP(u32 vd, u32 va, u32 vb) override; - void VCMPEQFP_(u32 vd, u32 va, u32 vb) override; - void VCMPEQUB(u32 vd, u32 va, u32 vb) override; - void VCMPEQUB_(u32 vd, u32 va, u32 vb) override; - void VCMPEQUH(u32 vd, u32 va, u32 vb) override; - void VCMPEQUH_(u32 vd, u32 va, u32 vb) override; - void VCMPEQUW(u32 vd, u32 va, u32 vb) override; - void VCMPEQUW_(u32 vd, u32 va, u32 vb) override; - void VCMPGEFP(u32 vd, u32 va, u32 vb) override; - void VCMPGEFP_(u32 vd, u32 va, u32 vb) override; - void VCMPGTFP(u32 vd, u32 va, u32 vb) override; - void VCMPGTFP_(u32 vd, u32 va, u32 vb) override; - void VCMPGTSB(u32 vd, u32 va, u32 vb) override; - void VCMPGTSB_(u32 vd, u32 va, u32 vb) override; - void VCMPGTSH(u32 vd, u32 va, u32 vb) override; - void VCMPGTSH_(u32 vd, u32 va, u32 vb) override; - void VCMPGTSW(u32 vd, u32 va, u32 vb) override; - void VCMPGTSW_(u32 vd, u32 va, u32 vb) override; - void VCMPGTUB(u32 vd, u32 va, u32 vb) override; - void VCMPGTUB_(u32 vd, u32 va, u32 vb) override; - void VCMPGTUH(u32 vd, u32 va, u32 vb) override; - void VCMPGTUH_(u32 vd, u32 va, u32 vb) override; - void VCMPGTUW(u32 vd, u32 va, u32 vb) override; - void VCMPGTUW_(u32 vd, u32 va, u32 vb) override; - void VCTSXS(u32 vd, u32 uimm5, u32 vb) override; - void VCTUXS(u32 vd, u32 uimm5, u32 vb) override; - void VEXPTEFP(u32 vd, u32 vb) override; - void VLOGEFP(u32 vd, u32 vb) override; - void VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) override; - void VMAXFP(u32 vd, u32 va, u32 vb) override; - void VMAXSB(u32 vd, u32 va, u32 vb) override; - void VMAXSH(u32 vd, u32 va, u32 vb) override; - void VMAXSW(u32 vd, u32 va, u32 vb) override; - void VMAXUB(u32 vd, u32 va, u32 vb) override; - void VMAXUH(u32 vd, u32 va, u32 vb) override; - void VMAXUW(u32 vd, u32 va, u32 vb) override; - void VMHADDSHS(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMHRADDSHS(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMINFP(u32 vd, u32 va, u32 vb) override; - void VMINSB(u32 vd, u32 va, u32 vb) override; - void VMINSH(u32 vd, u32 va, u32 vb) override; - void VMINSW(u32 vd, u32 va, u32 vb) override; - void VMINUB(u32 vd, u32 va, u32 vb) override; - void VMINUH(u32 vd, u32 va, u32 vb) override; - void VMINUW(u32 vd, u32 va, u32 vb) override; - void VMLADDUHM(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMRGHB(u32 vd, u32 va, u32 vb) override; - void VMRGHH(u32 vd, u32 va, u32 vb) override; - void VMRGHW(u32 vd, u32 va, u32 vb) override; - void VMRGLB(u32 vd, u32 va, u32 vb) override; - void VMRGLH(u32 vd, u32 va, u32 vb) override; - void VMRGLW(u32 vd, u32 va, u32 vb) override; - void VMSUMMBM(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMSUMSHM(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMSUMSHS(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMSUMUBM(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMSUMUHM(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMSUMUHS(u32 vd, u32 va, u32 vb, u32 vc) override; - void VMULESB(u32 vd, u32 va, u32 vb) override; - void VMULESH(u32 vd, u32 va, u32 vb) override; - void VMULEUB(u32 vd, u32 va, u32 vb) override; - void VMULEUH(u32 vd, u32 va, u32 vb) override; - void VMULOSB(u32 vd, u32 va, u32 vb) override; - void VMULOSH(u32 vd, u32 va, u32 vb) override; - void VMULOUB(u32 vd, u32 va, u32 vb) override; - void VMULOUH(u32 vd, u32 va, u32 vb) override; - void VNMSUBFP(u32 vd, u32 va, u32 vc, u32 vb) override; - void VNOR(u32 vd, u32 va, u32 vb) override; - void VOR(u32 vd, u32 va, u32 vb) override; - void VPERM(u32 vd, u32 va, u32 vb, u32 vc) override; - void VPKPX(u32 vd, u32 va, u32 vb) override; - void VPKSHSS(u32 vd, u32 va, u32 vb) override; - void VPKSHUS(u32 vd, u32 va, u32 vb) override; - void VPKSWSS(u32 vd, u32 va, u32 vb) override; - void VPKSWUS(u32 vd, u32 va, u32 vb) override; - void VPKUHUM(u32 vd, u32 va, u32 vb) override; - void VPKUHUS(u32 vd, u32 va, u32 vb) override; - void VPKUWUM(u32 vd, u32 va, u32 vb) override; - void VPKUWUS(u32 vd, u32 va, u32 vb) override; - void VREFP(u32 vd, u32 vb) override; - void VRFIM(u32 vd, u32 vb) override; - void VRFIN(u32 vd, u32 vb) override; - void VRFIP(u32 vd, u32 vb) override; - void VRFIZ(u32 vd, u32 vb) override; - void VRLB(u32 vd, u32 va, u32 vb) override; - void VRLH(u32 vd, u32 va, u32 vb) override; - void VRLW(u32 vd, u32 va, u32 vb) override; - void VRSQRTEFP(u32 vd, u32 vb) override; - void VSEL(u32 vd, u32 va, u32 vb, u32 vc) override; - void VSL(u32 vd, u32 va, u32 vb) override; - void VSLB(u32 vd, u32 va, u32 vb) override; - void VSLDOI(u32 vd, u32 va, u32 vb, u32 sh) override; - void VSLH(u32 vd, u32 va, u32 vb) override; - void VSLO(u32 vd, u32 va, u32 vb) override; - void VSLW(u32 vd, u32 va, u32 vb) override; - void VSPLTB(u32 vd, u32 uimm5, u32 vb) override; - void VSPLTH(u32 vd, u32 uimm5, u32 vb) override; - void VSPLTISB(u32 vd, s32 simm5) override; - void VSPLTISH(u32 vd, s32 simm5) override; - void VSPLTISW(u32 vd, s32 simm5) override; - void VSPLTW(u32 vd, u32 uimm5, u32 vb) override; - void VSR(u32 vd, u32 va, u32 vb) override; - void VSRAB(u32 vd, u32 va, u32 vb) override; - void VSRAH(u32 vd, u32 va, u32 vb) override; - void VSRAW(u32 vd, u32 va, u32 vb) override; - void VSRB(u32 vd, u32 va, u32 vb) override; - void VSRH(u32 vd, u32 va, u32 vb) override; - void VSRO(u32 vd, u32 va, u32 vb) override; - void VSRW(u32 vd, u32 va, u32 vb) override; - void VSUBCUW(u32 vd, u32 va, u32 vb) override; - void VSUBFP(u32 vd, u32 va, u32 vb) override; - void VSUBSBS(u32 vd, u32 va, u32 vb) override; - void VSUBSHS(u32 vd, u32 va, u32 vb) override; - void VSUBSWS(u32 vd, u32 va, u32 vb) override; - void VSUBUBM(u32 vd, u32 va, u32 vb) override; - void VSUBUBS(u32 vd, u32 va, u32 vb) override; - void VSUBUHM(u32 vd, u32 va, u32 vb) override; - void VSUBUHS(u32 vd, u32 va, u32 vb) override; - void VSUBUWM(u32 vd, u32 va, u32 vb) override; - void VSUBUWS(u32 vd, u32 va, u32 vb) override; - void VSUMSWS(u32 vd, u32 va, u32 vb) override; - void VSUM2SWS(u32 vd, u32 va, u32 vb) override; - void VSUM4SBS(u32 vd, u32 va, u32 vb) override; - void VSUM4SHS(u32 vd, u32 va, u32 vb) override; - void VSUM4UBS(u32 vd, u32 va, u32 vb) override; - void VUPKHPX(u32 vd, u32 vb) override; - void VUPKHSB(u32 vd, u32 vb) override; - void VUPKHSH(u32 vd, u32 vb) override; - void VUPKLPX(u32 vd, u32 vb) override; - void VUPKLSB(u32 vd, u32 vb) override; - void VUPKLSH(u32 vd, u32 vb) override; - void VXOR(u32 vd, u32 va, u32 vb) override; - void MULLI(u32 rd, u32 ra, s32 simm16) override; - void SUBFIC(u32 rd, u32 ra, s32 simm16) override; - void CMPLI(u32 bf, u32 l, u32 ra, u32 uimm16) override; - void CMPI(u32 bf, u32 l, u32 ra, s32 simm16) override; - void ADDIC(u32 rd, u32 ra, s32 simm16) override; - void ADDIC_(u32 rd, u32 ra, s32 simm16) override; - void ADDI(u32 rd, u32 ra, s32 simm16) override; - void ADDIS(u32 rd, u32 ra, s32 simm16) override; - void BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) override; - void SC(u32 sc_code) override; - void B(s32 ll, u32 aa, u32 lk) override; - void MCRF(u32 crfd, u32 crfs) override; - void BCLR(u32 bo, u32 bi, u32 bh, u32 lk) override; - void CRNOR(u32 bt, u32 ba, u32 bb) override; - void CRANDC(u32 bt, u32 ba, u32 bb) override; - void ISYNC() override; - void CRXOR(u32 bt, u32 ba, u32 bb) override; - void CRNAND(u32 bt, u32 ba, u32 bb) override; - void CRAND(u32 bt, u32 ba, u32 bb) override; - void CREQV(u32 bt, u32 ba, u32 bb) override; - void CRORC(u32 bt, u32 ba, u32 bb) override; - void CROR(u32 bt, u32 ba, u32 bb) override; - void BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) override; - void RLWIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) override; - void RLWINM(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) override; - void RLWNM(u32 ra, u32 rs, u32 rb, u32 MB, u32 ME, bool rc) override; - void ORI(u32 rs, u32 ra, u32 uimm16) override; - void ORIS(u32 rs, u32 ra, u32 uimm16) override; - void XORI(u32 ra, u32 rs, u32 uimm16) override; - void XORIS(u32 ra, u32 rs, u32 uimm16) override; - void ANDI_(u32 ra, u32 rs, u32 uimm16) override; - void ANDIS_(u32 ra, u32 rs, u32 uimm16) override; - void RLDICL(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) override; - void RLDICR(u32 ra, u32 rs, u32 sh, u32 me, bool rc) override; - void RLDIC(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) override; - void RLDIMI(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) override; - void RLDC_LR(u32 ra, u32 rs, u32 rb, u32 m_eb, bool is_r, bool rc) override; - void CMP(u32 crfd, u32 l, u32 ra, u32 rb) override; - void TW(u32 to, u32 ra, u32 rb) override; - void LVSL(u32 vd, u32 ra, u32 rb) override; - void LVEBX(u32 vd, u32 ra, u32 rb) override; - void SUBFC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void MULHDU(u32 rd, u32 ra, u32 rb, bool rc) override; - void ADDC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void MULHWU(u32 rd, u32 ra, u32 rb, bool rc) override; - void MFOCRF(u32 a, u32 rd, u32 crm) override; - void LWARX(u32 rd, u32 ra, u32 rb) override; - void LDX(u32 ra, u32 rs, u32 rb) override; - void LWZX(u32 rd, u32 ra, u32 rb) override; - void SLW(u32 ra, u32 rs, u32 rb, bool rc) override; - void CNTLZW(u32 ra, u32 rs, bool rc) override; - void SLD(u32 ra, u32 rs, u32 rb, bool rc) override; - void AND(u32 ra, u32 rs, u32 rb, bool rc) override; - void CMPL(u32 bf, u32 l, u32 ra, u32 rb) override; - void LVSR(u32 vd, u32 ra, u32 rb) override; - void LVEHX(u32 vd, u32 ra, u32 rb) override; - void SUBF(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void LDUX(u32 rd, u32 ra, u32 rb) override; - void DCBST(u32 ra, u32 rb) override; - void LWZUX(u32 rd, u32 ra, u32 rb) override; - void CNTLZD(u32 ra, u32 rs, bool rc) override; - void ANDC(u32 ra, u32 rs, u32 rb, bool rc) override; - void TD(u32 to, u32 ra, u32 rb) override; - void LVEWX(u32 vd, u32 ra, u32 rb) override; - void MULHD(u32 rd, u32 ra, u32 rb, bool rc) override; - void MULHW(u32 rd, u32 ra, u32 rb, bool rc) override; - void LDARX(u32 rd, u32 ra, u32 rb) override; - void DCBF(u32 ra, u32 rb) override; - void LBZX(u32 rd, u32 ra, u32 rb) override; - void LVX(u32 vd, u32 ra, u32 rb) override; - void NEG(u32 rd, u32 ra, u32 oe, bool rc) override; - void LBZUX(u32 rd, u32 ra, u32 rb) override; - void NOR(u32 ra, u32 rs, u32 rb, bool rc) override; - void STVEBX(u32 vs, u32 ra, u32 rb) override; - void SUBFE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void ADDE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void MTOCRF(u32 l, u32 crm, u32 rs) override; - void STDX(u32 rs, u32 ra, u32 rb) override; - void STWCX_(u32 rs, u32 ra, u32 rb) override; - void STWX(u32 rs, u32 ra, u32 rb) override; - void STVEHX(u32 vs, u32 ra, u32 rb) override; - void STDUX(u32 rs, u32 ra, u32 rb) override; - void STWUX(u32 rs, u32 ra, u32 rb) override; - void STVEWX(u32 vs, u32 ra, u32 rb) override; - void SUBFZE(u32 rd, u32 ra, u32 oe, bool rc) override; - void ADDZE(u32 rd, u32 ra, u32 oe, bool rc) override; - void STDCX_(u32 rs, u32 ra, u32 rb) override; - void STBX(u32 rs, u32 ra, u32 rb) override; - void STVX(u32 vs, u32 ra, u32 rb) override; - void MULLD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void SUBFME(u32 rd, u32 ra, u32 oe, bool rc) override; - void ADDME(u32 rd, u32 ra, u32 oe, bool rc) override; - void MULLW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void DCBTST(u32 ra, u32 rb, u32 th) override; - void STBUX(u32 rs, u32 ra, u32 rb) override; - void ADD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void DCBT(u32 ra, u32 rb, u32 th) override; - void LHZX(u32 rd, u32 ra, u32 rb) override; - void EQV(u32 ra, u32 rs, u32 rb, bool rc) override; - void ECIWX(u32 rd, u32 ra, u32 rb) override; - void LHZUX(u32 rd, u32 ra, u32 rb) override; - void XOR(u32 rs, u32 ra, u32 rb, bool rc) override; - void MFSPR(u32 rd, u32 spr) override; - void LWAX(u32 rd, u32 ra, u32 rb) override; - void DST(u32 ra, u32 rb, u32 strm, u32 t) override; - void LHAX(u32 rd, u32 ra, u32 rb) override; - void LVXL(u32 vd, u32 ra, u32 rb) override; - void MFTB(u32 rd, u32 spr) override; - void LWAUX(u32 rd, u32 ra, u32 rb) override; - void DSTST(u32 ra, u32 rb, u32 strm, u32 t) override; - void LHAUX(u32 rd, u32 ra, u32 rb) override; - void STHX(u32 rs, u32 ra, u32 rb) override; - void ORC(u32 rs, u32 ra, u32 rb, bool rc) override; - void ECOWX(u32 rs, u32 ra, u32 rb) override; - void STHUX(u32 rs, u32 ra, u32 rb) override; - void OR(u32 ra, u32 rs, u32 rb, bool rc) override; - void DIVDU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void DIVWU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void MTSPR(u32 spr, u32 rs) override; - //DCBI - void NAND(u32 ra, u32 rs, u32 rb, bool rc) override; - void STVXL(u32 vs, u32 ra, u32 rb) override; - void DIVD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void DIVW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; - void LVLX(u32 vd, u32 ra, u32 rb) override; - void LDBRX(u32 rd, u32 ra, u32 rb) override; - void LSWX(u32 rd, u32 ra, u32 rb) override; - void LWBRX(u32 rd, u32 ra, u32 rb) override; - void LFSX(u32 frd, u32 ra, u32 rb) override; - void SRW(u32 ra, u32 rs, u32 rb, bool rc) override; - void SRD(u32 ra, u32 rs, u32 rb, bool rc) override; - void LVRX(u32 vd, u32 ra, u32 rb) override; - void LSWI(u32 rd, u32 ra, u32 nb) override; - void LFSUX(u32 frd, u32 ra, u32 rb) override; - void SYNC(u32 l) override; - void LFDX(u32 frd, u32 ra, u32 rb) override; - void LFDUX(u32 frd, u32 ra, u32 rb) override; - void STVLX(u32 vs, u32 ra, u32 rb) override; - void STSWX(u32 rs, u32 ra, u32 rb) override; - void STWBRX(u32 rs, u32 ra, u32 rb) override; - void STFSX(u32 frs, u32 ra, u32 rb) override; - void STVRX(u32 vs, u32 ra, u32 rb) override; - void STFSUX(u32 frs, u32 ra, u32 rb) override; - void STSWI(u32 rd, u32 ra, u32 nb) override; - void STFDX(u32 frs, u32 ra, u32 rb) override; - void STFDUX(u32 frs, u32 ra, u32 rb) override; - void LVLXL(u32 vd, u32 ra, u32 rb) override; - void LHBRX(u32 rd, u32 ra, u32 rb) override; - void SRAW(u32 ra, u32 rs, u32 rb, bool rc) override; - void SRAD(u32 ra, u32 rs, u32 rb, bool rc) override; - void LVRXL(u32 vd, u32 ra, u32 rb) override; - void DSS(u32 strm, u32 a) override; - void SRAWI(u32 ra, u32 rs, u32 sh, bool rc) override; - void SRADI1(u32 ra, u32 rs, u32 sh, bool rc) override; - void SRADI2(u32 ra, u32 rs, u32 sh, bool rc) override; - void EIEIO() override; - void STVLXL(u32 vs, u32 ra, u32 rb) override; - void STHBRX(u32 rs, u32 ra, u32 rb) override; - void EXTSH(u32 ra, u32 rs, bool rc) override; - void STVRXL(u32 sd, u32 ra, u32 rb) override; - void EXTSB(u32 ra, u32 rs, bool rc) override; - void STFIWX(u32 frs, u32 ra, u32 rb) override; - void EXTSW(u32 ra, u32 rs, bool rc) override; - void ICBI(u32 ra, u32 rb) override; - void DCBZ(u32 ra, u32 rb) override; - void LWZ(u32 rd, u32 ra, s32 d) override; - void LWZU(u32 rd, u32 ra, s32 d) override; - void LBZ(u32 rd, u32 ra, s32 d) override; - void LBZU(u32 rd, u32 ra, s32 d) override; - void STW(u32 rs, u32 ra, s32 d) override; - void STWU(u32 rs, u32 ra, s32 d) override; - void STB(u32 rs, u32 ra, s32 d) override; - void STBU(u32 rs, u32 ra, s32 d) override; - void LHZ(u32 rd, u32 ra, s32 d) override; - void LHZU(u32 rd, u32 ra, s32 d) override; - void LHA(u32 rs, u32 ra, s32 d) override; - void LHAU(u32 rs, u32 ra, s32 d) override; - void STH(u32 rs, u32 ra, s32 d) override; - void STHU(u32 rs, u32 ra, s32 d) override; - void LMW(u32 rd, u32 ra, s32 d) override; - void STMW(u32 rs, u32 ra, s32 d) override; - void LFS(u32 frd, u32 ra, s32 d) override; - void LFSU(u32 frd, u32 ra, s32 d) override; - void LFD(u32 frd, u32 ra, s32 d) override; - void LFDU(u32 frd, u32 ra, s32 d) override; - void STFS(u32 frs, u32 ra, s32 d) override; - void STFSU(u32 frs, u32 ra, s32 d) override; - void STFD(u32 frs, u32 ra, s32 d) override; - void STFDU(u32 frs, u32 ra, s32 d) override; - void LD(u32 rd, u32 ra, s32 ds) override; - void LDU(u32 rd, u32 ra, s32 ds) override; - void LWA(u32 rd, u32 ra, s32 ds) override; - void FDIVS(u32 frd, u32 fra, u32 frb, bool rc) override; - void FSUBS(u32 frd, u32 fra, u32 frb, bool rc) override; - void FADDS(u32 frd, u32 fra, u32 frb, bool rc) override; - void FSQRTS(u32 frd, u32 frb, bool rc) override; - void FRES(u32 frd, u32 frb, bool rc) override; - void FMULS(u32 frd, u32 fra, u32 frc, bool rc) override; - void FMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void FMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void FNMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void FNMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void STD(u32 rs, u32 ra, s32 ds) override; - void STDU(u32 rs, u32 ra, s32 ds) override; - void MTFSB1(u32 bt, bool rc) override; - void MCRFS(u32 bf, u32 bfa) override; - void MTFSB0(u32 bt, bool rc) override; - void MTFSFI(u32 crfd, u32 i, bool rc) override; - void MFFS(u32 frd, bool rc) override; - void MTFSF(u32 flm, u32 frb, bool rc) override; - - void FCMPU(u32 bf, u32 fra, u32 frb) override; - void FRSP(u32 frd, u32 frb, bool rc) override; - void FCTIW(u32 frd, u32 frb, bool rc) override; - void FCTIWZ(u32 frd, u32 frb, bool rc) override; - void FDIV(u32 frd, u32 fra, u32 frb, bool rc) override; - void FSUB(u32 frd, u32 fra, u32 frb, bool rc) override; - void FADD(u32 frd, u32 fra, u32 frb, bool rc) override; - void FSQRT(u32 frd, u32 frb, bool rc) override; - void FSEL(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void FMUL(u32 frd, u32 fra, u32 frc, bool rc) override; - void FRSQRTE(u32 frd, u32 frb, bool rc) override; - void FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void FNMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void FNMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; - void FCMPO(u32 crfd, u32 fra, u32 frb) override; - void FNEG(u32 frd, u32 frb, bool rc) override; - void FMR(u32 frd, u32 frb, bool rc) override; - void FNABS(u32 frd, u32 frb, bool rc) override; - void FABS(u32 frd, u32 frb, bool rc) override; - void FCTID(u32 frd, u32 frb, bool rc) override; - void FCTIDZ(u32 frd, u32 frb, bool rc) override; - void FCFID(u32 frd, u32 frb, bool rc) override; - - void UNK(const u32 code, const u32 opcode, const u32 gcode) override; - -private: - struct ExecutableInfo { - /// Pointer to the executable - Executable executable; - - /// Size of the executable - size_t size; - - /// Number of PPU instructions compiled into this executable - u32 num_instructions; - - /// List of blocks that this executable refers to that have not been hit yet - std::list unhit_blocks_list; - - /// LLVM function corresponding to the executable - llvm::Function * llvm_function; +namespace ppu_recompiler_llvm { + /// Branch type + enum BranchType { + None, + FunctionCall, + Block, + Return, }; - /// Lock for accessing m_compiled_shared - // TODO: Use a RW lock - std::mutex m_compiled_shared_lock; + /// Unique id of a block + union BlockId { + u64 block_id; - /// Sections that have been compiled. This data store is shared with the execution threads. - /// Keys are starting address of the section and ~revision. Data is pointer to the executable and its reference count. - std::map, std::pair> m_compiled_shared; + struct { + /// Address of the block + u32 address; - /// Lock for accessing m_uncompiled_shared - std::mutex m_uncompiled_shared_lock; - - /// Current revision. This is incremented everytime a section is compiled. - std::atomic m_revision; - - /// Sections that have not been compiled yet. This data store is shared with the execution threads. - std::list m_uncompiled_shared; - - /// Set of all blocks that have been hit - std::set m_hit_blocks; - - /// Sections that have been compiled. Keys are starting address of the section and ~revision. - std::map, ExecutableInfo> m_compiled; - - /// LLVM context - llvm::LLVMContext * m_llvm_context; - - /// LLVM IR builder - llvm::IRBuilder<> * m_ir_builder; - - /// Module to which all generated code is output to - llvm::Module * m_module; - - /// JIT execution engine - llvm::ExecutionEngine * m_execution_engine; - - /// Function pass manager - llvm::FunctionPassManager * m_fpm; - - /// A flag used to detect branch instructions. - /// This is set to false at the start of compilation of a block. - /// When a branch instruction is encountered, this is set to true by the decode function. - bool m_hit_branch_instruction; - - /// The function being compiled - llvm::Function * m_current_function; - - /// List of blocks to be compiled in the current function being compiled - std::list m_current_function_uncompiled_blocks_list; - - /// List of blocks that the current function refers to but have not been hit yet - std::list m_current_function_unhit_blocks_list; - - /// Address of the current instruction - u32 m_current_instruction_address; - - /// Number of instructions in this section - u32 m_num_instructions; - - /// Time spent building the LLVM IR - std::chrono::nanoseconds m_ir_build_time; - - /// Time spent optimizing - std::chrono::nanoseconds m_optimizing_time; - - /// Time spent translating LLVM IR to machine code - std::chrono::nanoseconds m_translation_time; - - /// Time spent compiling - std::chrono::nanoseconds m_compilation_time; - - /// Time spent idling - std::chrono::nanoseconds m_idling_time; - - /// Total time - std::chrono::nanoseconds m_total_time; - - /// Contains the number of times the interpreter fallback was used - std::map m_interpreter_fallback_stats; - - /// Get the block in function for the instruction at the specified address. - llvm::BasicBlock * GetBlockInFunction(u32 address, llvm::Function * function, bool create_if_not_exist = false); - - /// Compile the section startin at address - void Compile(u32 address); - - /// Remove old versions of executables that are no longer used by any execution thread - void RemoveUnusedOldVersions(); - - /// Test whether the blocks needs to be compiled - bool NeedsCompiling(u32 address); - - /// Get PPU state pointer - llvm::Value * GetPPUState(); - - /// Get interpreter pointer - llvm::Value * GetInterpreter(); - - /// Get a bit - llvm::Value * GetBit(llvm::Value * val, u32 n); - - /// Clear a bit - llvm::Value * ClrBit(llvm::Value * val, u32 n); - - /// Set a bit - llvm::Value * SetBit(llvm::Value * val, u32 n, llvm::Value * bit, bool doClear = true); - - /// Get a nibble - llvm::Value * GetNibble(llvm::Value * val, u32 n); - - /// Clear a nibble - llvm::Value * ClrNibble(llvm::Value * val, u32 n); - - /// Set a nibble - llvm::Value * SetNibble(llvm::Value * val, u32 n, llvm::Value * nibble, bool doClear = true); - - /// Set a nibble - llvm::Value * SetNibble(llvm::Value * val, u32 n, llvm::Value * b0, llvm::Value * b1, llvm::Value * b2, llvm::Value * b3, bool doClear = true); - - /// Load PC - llvm::Value * GetPc(); - - /// Set PC - void SetPc(llvm::Value * val_ix); - - /// Load GPR - llvm::Value * GetGpr(u32 r, u32 num_bits = 64); - - /// Set GPR - void SetGpr(u32 r, llvm::Value * val_x64); - - /// Load CR - llvm::Value * GetCr(); - - /// Load CR and get field CRn - llvm::Value * GetCrField(u32 n); - - /// Set CR - void SetCr(llvm::Value * val_x32); - - /// Set CR field - void SetCrField(u32 n, llvm::Value * field); - - /// Set CR field - void SetCrField(u32 n, llvm::Value * b0, llvm::Value * b1, llvm::Value * b2, llvm::Value * b3); - - /// Set CR field based on signed comparison - void SetCrFieldSignedCmp(u32 n, llvm::Value * a, llvm::Value * b); - - /// Set CR field based on unsigned comparison - void SetCrFieldUnsignedCmp(u32 n, llvm::Value * a, llvm::Value * b); - - /// Set CR6 based on the result of the vector compare instruction - void SetCr6AfterVectorCompare(u32 vr); - - /// Get LR - llvm::Value * GetLr(); - - /// Set LR - void SetLr(llvm::Value * val_x64); - - /// Get CTR - llvm::Value * GetCtr(); - - /// Set CTR - void SetCtr(llvm::Value * val_x64); - - /// Load XER and convert it to an i64 - llvm::Value * GetXer(); - - /// Load XER and return the CA bit - llvm::Value * GetXerCa(); - - /// Load XER and return the SO bit - llvm::Value * GetXerSo(); - - /// Set XER - void SetXer(llvm::Value * val_x64); - - /// Set the CA bit of XER - void SetXerCa(llvm::Value * ca); - - /// Set the SO bit of XER - void SetXerSo(llvm::Value * so); - - /// Get USPRG0 - llvm::Value * GetUsprg0(); - - /// Set USPRG0 - void SetUsprg0(llvm::Value * val_x64); - - /// Get FPR - llvm::Value * GetFpr(u32 r, u32 bits = 64, bool as_int = false); - - /// Set FPR - void SetFpr(u32 r, llvm::Value * val); - - /// Load VSCR - llvm::Value * GetVscr(); - - /// Set VSCR - void SetVscr(llvm::Value * val_x32); - - /// Load VR - llvm::Value * GetVr(u32 vr); - - /// Load VR and convert it to an integer vector - llvm::Value * GetVrAsIntVec(u32 vr, u32 vec_elt_num_bits); - - /// Load VR and convert it to a float vector with 4 elements - llvm::Value * GetVrAsFloatVec(u32 vr); - - /// Load VR and convert it to a double vector with 2 elements - llvm::Value * GetVrAsDoubleVec(u32 vr); - - /// Set VR to the specified value - void SetVr(u32 vr, llvm::Value * val_x128); - - /// Check condition for branch instructions - llvm::Value * CheckBranchCondition(u32 bo, u32 bi); - - /// Create IR for a branch instruction - void CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool lk); - - /// Read from memory - llvm::Value * ReadMemory(llvm::Value * addr_i64, u32 bits, u32 alignment = 0, bool bswap = true, bool could_be_mmio = true); - - /// Write to memory - void WriteMemory(llvm::Value * addr_i64, llvm::Value * val_ix, u32 alignment = 0, bool bswap = true, bool could_be_mmio = true); - - /// Call an interpreter function - template - llvm::Value * InterpreterCall(const char * name, Func function, Args... args); - - /// Convert a C++ type to an LLVM type - template - llvm::Type * CppToLlvmType(); - - /// Call a function - template - llvm::Value * Call(const char * name, Func function, Args... args); - - /// Test an instruction against the interpreter - template - void VerifyInstructionAgainstInterpreter(const char * name, PPULLVMRecompilerFn recomp_fn, PPUInterpreterFn interp_fn, PPUState & input_state, Args... args); - - /// Excute a test - void RunTest(const char * name, std::function test_case, std::function input, std::function check_result); - - /// A mask used in rotate instructions - static u64 s_rotate_mask[64][64]; - - /// A flag indicating whether s_rotate_mask has been initialised or not - static bool s_rotate_mask_inited; - - /// Initialse s_rotate_mask - static void InitRotateMask(); -}; - -/// PPU emulator that uses LLVM to convert PPU instructions to host CPU instructions -class PPULLVMEmulator : public CPUDecoder { -public: - PPULLVMEmulator(PPUThread & ppu); - PPULLVMEmulator() = delete; - - PPULLVMEmulator(const PPULLVMEmulator & other) = delete; - PPULLVMEmulator(PPULLVMEmulator && other) = delete; - - virtual ~PPULLVMEmulator(); - - PPULLVMEmulator & operator = (const PPULLVMEmulator & other) = delete; - PPULLVMEmulator & operator = (PPULLVMEmulator && other) = delete; - - u8 DecodeMemory(const u32 address) override; - -private: - struct ExecutableInfo { - /// Pointer to the executable - PPULLVMRecompiler::Executable executable; - - /// The revision of the executable - u32 revision; - - /// Number of times the executable was hit - u32 num_hits; + /// The type of the block + BranchType type; + }; }; - /// PPU processor context - PPUThread & m_ppu; + /// An execution trace. + struct ExecutionTrace { + /// The function in which this trace was found + u32 function_address; - /// PPU Interpreter - PPUInterpreter * m_interpreter; + /// Execution trace type + enum { + Linear, + Loop, + } type; - /// PPU instruction Decoder - PPUDecoder m_decoder; + /// Sequence of blocks enountered in this trace + std::vector blocks; + }; - /// Set to true if the last executed instruction was a branch - bool m_last_instr_was_branch; + /// A fragment of PPU code. A list of (block, list of next blocks) pairs. + typedef std::vector>> CodeFragment; - /// The time at which the m_address_to_executable cache was last cleared - std::chrono::high_resolution_clock::time_point m_last_cache_clear_time; + /// Pointer to a function built by compiling a fragment of PPU code + typedef u64(*CompiledCodeFragment)(PPUThread * ppu_state, PPUInterpreter * interpreter); - /// The revision of the recompiler to which this thread is synced - u32 m_recompiler_revision; + struct PPUState; - /// Address to executable map. Key is address. - std::unordered_map m_address_to_executable; + /// PPU compiler that uses LLVM for code generation and optimization + class Compiler : protected PPUOpcodes, protected PPCDecoder { + public: + struct Stats { + /// Time spent building the LLVM IR + std::chrono::nanoseconds ir_build_time; - /// Sections that have not been compiled yet. Key is starting address of the section. - std::unordered_map m_uncompiled; + /// Time spent optimizing + std::chrono::nanoseconds optimization_time; - /// Number of instances of this class - static u32 s_num_instances; + /// Time spent translating LLVM IR to machine code + std::chrono::nanoseconds translation_time; - /// Mutex used prevent multiple instances of the recompiler from being created - static std::mutex s_recompiler_mutex; + /// Total time + std::chrono::nanoseconds total_time; - /// PPU to LLVM recompiler - static PPULLVMRecompiler * s_recompiler; -}; + /// Contains the number of times interpreter fallback was used + std::map interpreter_fallback_stats; + }; + + Compiler(); + + Compiler(const Compiler & other) = delete; + Compiler(Compiler && other) = delete; + + virtual ~Compiler(); + + Compiler & operator = (const Compiler & other) = delete; + Compiler & operator = (Compiler && other) = delete; + + /// Compile a code fragment + CompiledCodeFragment Compile(const std::string & name, const CodeFragment & code_fragment); + + /// Free a compiled code fragment + void FreeCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment); + + /// Retrieve compiler stats + Stats GetStats(); + + /// Execute all tests + void RunAllTests(PPUThread * ppu_state, PPUInterpreter * interpreter); + + protected: + void Decode(const u32 code) override; + + void NULL_OP() override; + void NOP() override; + + void TDI(u32 to, u32 ra, s32 simm16) override; + void TWI(u32 to, u32 ra, s32 simm16) override; + + void MFVSCR(u32 vd) override; + void MTVSCR(u32 vb) override; + void VADDCUW(u32 vd, u32 va, u32 vb) override; + void VADDFP(u32 vd, u32 va, u32 vb) override; + void VADDSBS(u32 vd, u32 va, u32 vb) override; + void VADDSHS(u32 vd, u32 va, u32 vb) override; + void VADDSWS(u32 vd, u32 va, u32 vb) override; + void VADDUBM(u32 vd, u32 va, u32 vb) override; + void VADDUBS(u32 vd, u32 va, u32 vb) override; + void VADDUHM(u32 vd, u32 va, u32 vb) override; + void VADDUHS(u32 vd, u32 va, u32 vb) override; + void VADDUWM(u32 vd, u32 va, u32 vb) override; + void VADDUWS(u32 vd, u32 va, u32 vb) override; + void VAND(u32 vd, u32 va, u32 vb) override; + void VANDC(u32 vd, u32 va, u32 vb) override; + void VAVGSB(u32 vd, u32 va, u32 vb) override; + void VAVGSH(u32 vd, u32 va, u32 vb) override; + void VAVGSW(u32 vd, u32 va, u32 vb) override; + void VAVGUB(u32 vd, u32 va, u32 vb) override; + void VAVGUH(u32 vd, u32 va, u32 vb) override; + void VAVGUW(u32 vd, u32 va, u32 vb) override; + void VCFSX(u32 vd, u32 uimm5, u32 vb) override; + void VCFUX(u32 vd, u32 uimm5, u32 vb) override; + void VCMPBFP(u32 vd, u32 va, u32 vb) override; + void VCMPBFP_(u32 vd, u32 va, u32 vb) override; + void VCMPEQFP(u32 vd, u32 va, u32 vb) override; + void VCMPEQFP_(u32 vd, u32 va, u32 vb) override; + void VCMPEQUB(u32 vd, u32 va, u32 vb) override; + void VCMPEQUB_(u32 vd, u32 va, u32 vb) override; + void VCMPEQUH(u32 vd, u32 va, u32 vb) override; + void VCMPEQUH_(u32 vd, u32 va, u32 vb) override; + void VCMPEQUW(u32 vd, u32 va, u32 vb) override; + void VCMPEQUW_(u32 vd, u32 va, u32 vb) override; + void VCMPGEFP(u32 vd, u32 va, u32 vb) override; + void VCMPGEFP_(u32 vd, u32 va, u32 vb) override; + void VCMPGTFP(u32 vd, u32 va, u32 vb) override; + void VCMPGTFP_(u32 vd, u32 va, u32 vb) override; + void VCMPGTSB(u32 vd, u32 va, u32 vb) override; + void VCMPGTSB_(u32 vd, u32 va, u32 vb) override; + void VCMPGTSH(u32 vd, u32 va, u32 vb) override; + void VCMPGTSH_(u32 vd, u32 va, u32 vb) override; + void VCMPGTSW(u32 vd, u32 va, u32 vb) override; + void VCMPGTSW_(u32 vd, u32 va, u32 vb) override; + void VCMPGTUB(u32 vd, u32 va, u32 vb) override; + void VCMPGTUB_(u32 vd, u32 va, u32 vb) override; + void VCMPGTUH(u32 vd, u32 va, u32 vb) override; + void VCMPGTUH_(u32 vd, u32 va, u32 vb) override; + void VCMPGTUW(u32 vd, u32 va, u32 vb) override; + void VCMPGTUW_(u32 vd, u32 va, u32 vb) override; + void VCTSXS(u32 vd, u32 uimm5, u32 vb) override; + void VCTUXS(u32 vd, u32 uimm5, u32 vb) override; + void VEXPTEFP(u32 vd, u32 vb) override; + void VLOGEFP(u32 vd, u32 vb) override; + void VMADDFP(u32 vd, u32 va, u32 vc, u32 vb) override; + void VMAXFP(u32 vd, u32 va, u32 vb) override; + void VMAXSB(u32 vd, u32 va, u32 vb) override; + void VMAXSH(u32 vd, u32 va, u32 vb) override; + void VMAXSW(u32 vd, u32 va, u32 vb) override; + void VMAXUB(u32 vd, u32 va, u32 vb) override; + void VMAXUH(u32 vd, u32 va, u32 vb) override; + void VMAXUW(u32 vd, u32 va, u32 vb) override; + void VMHADDSHS(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMHRADDSHS(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMINFP(u32 vd, u32 va, u32 vb) override; + void VMINSB(u32 vd, u32 va, u32 vb) override; + void VMINSH(u32 vd, u32 va, u32 vb) override; + void VMINSW(u32 vd, u32 va, u32 vb) override; + void VMINUB(u32 vd, u32 va, u32 vb) override; + void VMINUH(u32 vd, u32 va, u32 vb) override; + void VMINUW(u32 vd, u32 va, u32 vb) override; + void VMLADDUHM(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMRGHB(u32 vd, u32 va, u32 vb) override; + void VMRGHH(u32 vd, u32 va, u32 vb) override; + void VMRGHW(u32 vd, u32 va, u32 vb) override; + void VMRGLB(u32 vd, u32 va, u32 vb) override; + void VMRGLH(u32 vd, u32 va, u32 vb) override; + void VMRGLW(u32 vd, u32 va, u32 vb) override; + void VMSUMMBM(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMSUMSHM(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMSUMSHS(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMSUMUBM(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMSUMUHM(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMSUMUHS(u32 vd, u32 va, u32 vb, u32 vc) override; + void VMULESB(u32 vd, u32 va, u32 vb) override; + void VMULESH(u32 vd, u32 va, u32 vb) override; + void VMULEUB(u32 vd, u32 va, u32 vb) override; + void VMULEUH(u32 vd, u32 va, u32 vb) override; + void VMULOSB(u32 vd, u32 va, u32 vb) override; + void VMULOSH(u32 vd, u32 va, u32 vb) override; + void VMULOUB(u32 vd, u32 va, u32 vb) override; + void VMULOUH(u32 vd, u32 va, u32 vb) override; + void VNMSUBFP(u32 vd, u32 va, u32 vc, u32 vb) override; + void VNOR(u32 vd, u32 va, u32 vb) override; + void VOR(u32 vd, u32 va, u32 vb) override; + void VPERM(u32 vd, u32 va, u32 vb, u32 vc) override; + void VPKPX(u32 vd, u32 va, u32 vb) override; + void VPKSHSS(u32 vd, u32 va, u32 vb) override; + void VPKSHUS(u32 vd, u32 va, u32 vb) override; + void VPKSWSS(u32 vd, u32 va, u32 vb) override; + void VPKSWUS(u32 vd, u32 va, u32 vb) override; + void VPKUHUM(u32 vd, u32 va, u32 vb) override; + void VPKUHUS(u32 vd, u32 va, u32 vb) override; + void VPKUWUM(u32 vd, u32 va, u32 vb) override; + void VPKUWUS(u32 vd, u32 va, u32 vb) override; + void VREFP(u32 vd, u32 vb) override; + void VRFIM(u32 vd, u32 vb) override; + void VRFIN(u32 vd, u32 vb) override; + void VRFIP(u32 vd, u32 vb) override; + void VRFIZ(u32 vd, u32 vb) override; + void VRLB(u32 vd, u32 va, u32 vb) override; + void VRLH(u32 vd, u32 va, u32 vb) override; + void VRLW(u32 vd, u32 va, u32 vb) override; + void VRSQRTEFP(u32 vd, u32 vb) override; + void VSEL(u32 vd, u32 va, u32 vb, u32 vc) override; + void VSL(u32 vd, u32 va, u32 vb) override; + void VSLB(u32 vd, u32 va, u32 vb) override; + void VSLDOI(u32 vd, u32 va, u32 vb, u32 sh) override; + void VSLH(u32 vd, u32 va, u32 vb) override; + void VSLO(u32 vd, u32 va, u32 vb) override; + void VSLW(u32 vd, u32 va, u32 vb) override; + void VSPLTB(u32 vd, u32 uimm5, u32 vb) override; + void VSPLTH(u32 vd, u32 uimm5, u32 vb) override; + void VSPLTISB(u32 vd, s32 simm5) override; + void VSPLTISH(u32 vd, s32 simm5) override; + void VSPLTISW(u32 vd, s32 simm5) override; + void VSPLTW(u32 vd, u32 uimm5, u32 vb) override; + void VSR(u32 vd, u32 va, u32 vb) override; + void VSRAB(u32 vd, u32 va, u32 vb) override; + void VSRAH(u32 vd, u32 va, u32 vb) override; + void VSRAW(u32 vd, u32 va, u32 vb) override; + void VSRB(u32 vd, u32 va, u32 vb) override; + void VSRH(u32 vd, u32 va, u32 vb) override; + void VSRO(u32 vd, u32 va, u32 vb) override; + void VSRW(u32 vd, u32 va, u32 vb) override; + void VSUBCUW(u32 vd, u32 va, u32 vb) override; + void VSUBFP(u32 vd, u32 va, u32 vb) override; + void VSUBSBS(u32 vd, u32 va, u32 vb) override; + void VSUBSHS(u32 vd, u32 va, u32 vb) override; + void VSUBSWS(u32 vd, u32 va, u32 vb) override; + void VSUBUBM(u32 vd, u32 va, u32 vb) override; + void VSUBUBS(u32 vd, u32 va, u32 vb) override; + void VSUBUHM(u32 vd, u32 va, u32 vb) override; + void VSUBUHS(u32 vd, u32 va, u32 vb) override; + void VSUBUWM(u32 vd, u32 va, u32 vb) override; + void VSUBUWS(u32 vd, u32 va, u32 vb) override; + void VSUMSWS(u32 vd, u32 va, u32 vb) override; + void VSUM2SWS(u32 vd, u32 va, u32 vb) override; + void VSUM4SBS(u32 vd, u32 va, u32 vb) override; + void VSUM4SHS(u32 vd, u32 va, u32 vb) override; + void VSUM4UBS(u32 vd, u32 va, u32 vb) override; + void VUPKHPX(u32 vd, u32 vb) override; + void VUPKHSB(u32 vd, u32 vb) override; + void VUPKHSH(u32 vd, u32 vb) override; + void VUPKLPX(u32 vd, u32 vb) override; + void VUPKLSB(u32 vd, u32 vb) override; + void VUPKLSH(u32 vd, u32 vb) override; + void VXOR(u32 vd, u32 va, u32 vb) override; + void MULLI(u32 rd, u32 ra, s32 simm16) override; + void SUBFIC(u32 rd, u32 ra, s32 simm16) override; + void CMPLI(u32 bf, u32 l, u32 ra, u32 uimm16) override; + void CMPI(u32 bf, u32 l, u32 ra, s32 simm16) override; + void ADDIC(u32 rd, u32 ra, s32 simm16) override; + void ADDIC_(u32 rd, u32 ra, s32 simm16) override; + void ADDI(u32 rd, u32 ra, s32 simm16) override; + void ADDIS(u32 rd, u32 ra, s32 simm16) override; + void BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) override; + void SC(u32 sc_code) override; + void B(s32 ll, u32 aa, u32 lk) override; + void MCRF(u32 crfd, u32 crfs) override; + void BCLR(u32 bo, u32 bi, u32 bh, u32 lk) override; + void CRNOR(u32 bt, u32 ba, u32 bb) override; + void CRANDC(u32 bt, u32 ba, u32 bb) override; + void ISYNC() override; + void CRXOR(u32 bt, u32 ba, u32 bb) override; + void CRNAND(u32 bt, u32 ba, u32 bb) override; + void CRAND(u32 bt, u32 ba, u32 bb) override; + void CREQV(u32 bt, u32 ba, u32 bb) override; + void CRORC(u32 bt, u32 ba, u32 bb) override; + void CROR(u32 bt, u32 ba, u32 bb) override; + void BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) override; + void RLWIMI(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) override; + void RLWINM(u32 ra, u32 rs, u32 sh, u32 mb, u32 me, bool rc) override; + void RLWNM(u32 ra, u32 rs, u32 rb, u32 MB, u32 ME, bool rc) override; + void ORI(u32 rs, u32 ra, u32 uimm16) override; + void ORIS(u32 rs, u32 ra, u32 uimm16) override; + void XORI(u32 ra, u32 rs, u32 uimm16) override; + void XORIS(u32 ra, u32 rs, u32 uimm16) override; + void ANDI_(u32 ra, u32 rs, u32 uimm16) override; + void ANDIS_(u32 ra, u32 rs, u32 uimm16) override; + void RLDICL(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) override; + void RLDICR(u32 ra, u32 rs, u32 sh, u32 me, bool rc) override; + void RLDIC(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) override; + void RLDIMI(u32 ra, u32 rs, u32 sh, u32 mb, bool rc) override; + void RLDC_LR(u32 ra, u32 rs, u32 rb, u32 m_eb, bool is_r, bool rc) override; + void CMP(u32 crfd, u32 l, u32 ra, u32 rb) override; + void TW(u32 to, u32 ra, u32 rb) override; + void LVSL(u32 vd, u32 ra, u32 rb) override; + void LVEBX(u32 vd, u32 ra, u32 rb) override; + void SUBFC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void MULHDU(u32 rd, u32 ra, u32 rb, bool rc) override; + void ADDC(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void MULHWU(u32 rd, u32 ra, u32 rb, bool rc) override; + void MFOCRF(u32 a, u32 rd, u32 crm) override; + void LWARX(u32 rd, u32 ra, u32 rb) override; + void LDX(u32 ra, u32 rs, u32 rb) override; + void LWZX(u32 rd, u32 ra, u32 rb) override; + void SLW(u32 ra, u32 rs, u32 rb, bool rc) override; + void CNTLZW(u32 ra, u32 rs, bool rc) override; + void SLD(u32 ra, u32 rs, u32 rb, bool rc) override; + void AND(u32 ra, u32 rs, u32 rb, bool rc) override; + void CMPL(u32 bf, u32 l, u32 ra, u32 rb) override; + void LVSR(u32 vd, u32 ra, u32 rb) override; + void LVEHX(u32 vd, u32 ra, u32 rb) override; + void SUBF(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void LDUX(u32 rd, u32 ra, u32 rb) override; + void DCBST(u32 ra, u32 rb) override; + void LWZUX(u32 rd, u32 ra, u32 rb) override; + void CNTLZD(u32 ra, u32 rs, bool rc) override; + void ANDC(u32 ra, u32 rs, u32 rb, bool rc) override; + void TD(u32 to, u32 ra, u32 rb) override; + void LVEWX(u32 vd, u32 ra, u32 rb) override; + void MULHD(u32 rd, u32 ra, u32 rb, bool rc) override; + void MULHW(u32 rd, u32 ra, u32 rb, bool rc) override; + void LDARX(u32 rd, u32 ra, u32 rb) override; + void DCBF(u32 ra, u32 rb) override; + void LBZX(u32 rd, u32 ra, u32 rb) override; + void LVX(u32 vd, u32 ra, u32 rb) override; + void NEG(u32 rd, u32 ra, u32 oe, bool rc) override; + void LBZUX(u32 rd, u32 ra, u32 rb) override; + void NOR(u32 ra, u32 rs, u32 rb, bool rc) override; + void STVEBX(u32 vs, u32 ra, u32 rb) override; + void SUBFE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void ADDE(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void MTOCRF(u32 l, u32 crm, u32 rs) override; + void STDX(u32 rs, u32 ra, u32 rb) override; + void STWCX_(u32 rs, u32 ra, u32 rb) override; + void STWX(u32 rs, u32 ra, u32 rb) override; + void STVEHX(u32 vs, u32 ra, u32 rb) override; + void STDUX(u32 rs, u32 ra, u32 rb) override; + void STWUX(u32 rs, u32 ra, u32 rb) override; + void STVEWX(u32 vs, u32 ra, u32 rb) override; + void SUBFZE(u32 rd, u32 ra, u32 oe, bool rc) override; + void ADDZE(u32 rd, u32 ra, u32 oe, bool rc) override; + void STDCX_(u32 rs, u32 ra, u32 rb) override; + void STBX(u32 rs, u32 ra, u32 rb) override; + void STVX(u32 vs, u32 ra, u32 rb) override; + void MULLD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void SUBFME(u32 rd, u32 ra, u32 oe, bool rc) override; + void ADDME(u32 rd, u32 ra, u32 oe, bool rc) override; + void MULLW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void DCBTST(u32 ra, u32 rb, u32 th) override; + void STBUX(u32 rs, u32 ra, u32 rb) override; + void ADD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void DCBT(u32 ra, u32 rb, u32 th) override; + void LHZX(u32 rd, u32 ra, u32 rb) override; + void EQV(u32 ra, u32 rs, u32 rb, bool rc) override; + void ECIWX(u32 rd, u32 ra, u32 rb) override; + void LHZUX(u32 rd, u32 ra, u32 rb) override; + void XOR(u32 rs, u32 ra, u32 rb, bool rc) override; + void MFSPR(u32 rd, u32 spr) override; + void LWAX(u32 rd, u32 ra, u32 rb) override; + void DST(u32 ra, u32 rb, u32 strm, u32 t) override; + void LHAX(u32 rd, u32 ra, u32 rb) override; + void LVXL(u32 vd, u32 ra, u32 rb) override; + void MFTB(u32 rd, u32 spr) override; + void LWAUX(u32 rd, u32 ra, u32 rb) override; + void DSTST(u32 ra, u32 rb, u32 strm, u32 t) override; + void LHAUX(u32 rd, u32 ra, u32 rb) override; + void STHX(u32 rs, u32 ra, u32 rb) override; + void ORC(u32 rs, u32 ra, u32 rb, bool rc) override; + void ECOWX(u32 rs, u32 ra, u32 rb) override; + void STHUX(u32 rs, u32 ra, u32 rb) override; + void OR(u32 ra, u32 rs, u32 rb, bool rc) override; + void DIVDU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void DIVWU(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void MTSPR(u32 spr, u32 rs) override; + //DCBI + void NAND(u32 ra, u32 rs, u32 rb, bool rc) override; + void STVXL(u32 vs, u32 ra, u32 rb) override; + void DIVD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void DIVW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) override; + void LVLX(u32 vd, u32 ra, u32 rb) override; + void LDBRX(u32 rd, u32 ra, u32 rb) override; + void LSWX(u32 rd, u32 ra, u32 rb) override; + void LWBRX(u32 rd, u32 ra, u32 rb) override; + void LFSX(u32 frd, u32 ra, u32 rb) override; + void SRW(u32 ra, u32 rs, u32 rb, bool rc) override; + void SRD(u32 ra, u32 rs, u32 rb, bool rc) override; + void LVRX(u32 vd, u32 ra, u32 rb) override; + void LSWI(u32 rd, u32 ra, u32 nb) override; + void LFSUX(u32 frd, u32 ra, u32 rb) override; + void SYNC(u32 l) override; + void LFDX(u32 frd, u32 ra, u32 rb) override; + void LFDUX(u32 frd, u32 ra, u32 rb) override; + void STVLX(u32 vs, u32 ra, u32 rb) override; + void STSWX(u32 rs, u32 ra, u32 rb) override; + void STWBRX(u32 rs, u32 ra, u32 rb) override; + void STFSX(u32 frs, u32 ra, u32 rb) override; + void STVRX(u32 vs, u32 ra, u32 rb) override; + void STFSUX(u32 frs, u32 ra, u32 rb) override; + void STSWI(u32 rd, u32 ra, u32 nb) override; + void STFDX(u32 frs, u32 ra, u32 rb) override; + void STFDUX(u32 frs, u32 ra, u32 rb) override; + void LVLXL(u32 vd, u32 ra, u32 rb) override; + void LHBRX(u32 rd, u32 ra, u32 rb) override; + void SRAW(u32 ra, u32 rs, u32 rb, bool rc) override; + void SRAD(u32 ra, u32 rs, u32 rb, bool rc) override; + void LVRXL(u32 vd, u32 ra, u32 rb) override; + void DSS(u32 strm, u32 a) override; + void SRAWI(u32 ra, u32 rs, u32 sh, bool rc) override; + void SRADI1(u32 ra, u32 rs, u32 sh, bool rc) override; + void SRADI2(u32 ra, u32 rs, u32 sh, bool rc) override; + void EIEIO() override; + void STVLXL(u32 vs, u32 ra, u32 rb) override; + void STHBRX(u32 rs, u32 ra, u32 rb) override; + void EXTSH(u32 ra, u32 rs, bool rc) override; + void STVRXL(u32 sd, u32 ra, u32 rb) override; + void EXTSB(u32 ra, u32 rs, bool rc) override; + void STFIWX(u32 frs, u32 ra, u32 rb) override; + void EXTSW(u32 ra, u32 rs, bool rc) override; + void ICBI(u32 ra, u32 rb) override; + void DCBZ(u32 ra, u32 rb) override; + void LWZ(u32 rd, u32 ra, s32 d) override; + void LWZU(u32 rd, u32 ra, s32 d) override; + void LBZ(u32 rd, u32 ra, s32 d) override; + void LBZU(u32 rd, u32 ra, s32 d) override; + void STW(u32 rs, u32 ra, s32 d) override; + void STWU(u32 rs, u32 ra, s32 d) override; + void STB(u32 rs, u32 ra, s32 d) override; + void STBU(u32 rs, u32 ra, s32 d) override; + void LHZ(u32 rd, u32 ra, s32 d) override; + void LHZU(u32 rd, u32 ra, s32 d) override; + void LHA(u32 rs, u32 ra, s32 d) override; + void LHAU(u32 rs, u32 ra, s32 d) override; + void STH(u32 rs, u32 ra, s32 d) override; + void STHU(u32 rs, u32 ra, s32 d) override; + void LMW(u32 rd, u32 ra, s32 d) override; + void STMW(u32 rs, u32 ra, s32 d) override; + void LFS(u32 frd, u32 ra, s32 d) override; + void LFSU(u32 frd, u32 ra, s32 d) override; + void LFD(u32 frd, u32 ra, s32 d) override; + void LFDU(u32 frd, u32 ra, s32 d) override; + void STFS(u32 frs, u32 ra, s32 d) override; + void STFSU(u32 frs, u32 ra, s32 d) override; + void STFD(u32 frs, u32 ra, s32 d) override; + void STFDU(u32 frs, u32 ra, s32 d) override; + void LD(u32 rd, u32 ra, s32 ds) override; + void LDU(u32 rd, u32 ra, s32 ds) override; + void LWA(u32 rd, u32 ra, s32 ds) override; + void FDIVS(u32 frd, u32 fra, u32 frb, bool rc) override; + void FSUBS(u32 frd, u32 fra, u32 frb, bool rc) override; + void FADDS(u32 frd, u32 fra, u32 frb, bool rc) override; + void FSQRTS(u32 frd, u32 frb, bool rc) override; + void FRES(u32 frd, u32 frb, bool rc) override; + void FMULS(u32 frd, u32 fra, u32 frc, bool rc) override; + void FMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void FMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void FNMSUBS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void FNMADDS(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void STD(u32 rs, u32 ra, s32 ds) override; + void STDU(u32 rs, u32 ra, s32 ds) override; + void MTFSB1(u32 bt, bool rc) override; + void MCRFS(u32 bf, u32 bfa) override; + void MTFSB0(u32 bt, bool rc) override; + void MTFSFI(u32 crfd, u32 i, bool rc) override; + void MFFS(u32 frd, bool rc) override; + void MTFSF(u32 flm, u32 frb, bool rc) override; + + void FCMPU(u32 bf, u32 fra, u32 frb) override; + void FRSP(u32 frd, u32 frb, bool rc) override; + void FCTIW(u32 frd, u32 frb, bool rc) override; + void FCTIWZ(u32 frd, u32 frb, bool rc) override; + void FDIV(u32 frd, u32 fra, u32 frb, bool rc) override; + void FSUB(u32 frd, u32 fra, u32 frb, bool rc) override; + void FADD(u32 frd, u32 fra, u32 frb, bool rc) override; + void FSQRT(u32 frd, u32 frb, bool rc) override; + void FSEL(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void FMUL(u32 frd, u32 fra, u32 frc, bool rc) override; + void FRSQRTE(u32 frd, u32 frb, bool rc) override; + void FMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void FMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void FNMSUB(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void FNMADD(u32 frd, u32 fra, u32 frc, u32 frb, bool rc) override; + void FCMPO(u32 crfd, u32 fra, u32 frb) override; + void FNEG(u32 frd, u32 frb, bool rc) override; + void FMR(u32 frd, u32 frb, bool rc) override; + void FNABS(u32 frd, u32 frb, bool rc) override; + void FABS(u32 frd, u32 frb, bool rc) override; + void FCTID(u32 frd, u32 frb, bool rc) override; + void FCTIDZ(u32 frd, u32 frb, bool rc) override; + void FCFID(u32 frd, u32 frb, bool rc) override; + + void UNK(const u32 code, const u32 opcode, const u32 gcode) override; + + private: + /// Map from compiled code fragment to the LLVM function for the code fragment + std::map m_compiled; + + /// LLVM context + llvm::LLVMContext * m_llvm_context; + + /// LLVM IR builder + llvm::IRBuilder<> * m_ir_builder; + + /// Module to which all generated code is output to + llvm::Module * m_module; + + /// JIT execution engine + llvm::ExecutionEngine * m_execution_engine; + + /// Function pass manager + llvm::FunctionPassManager * m_fpm; + + /// A flag used to detect branch instructions. + /// This is set to false at the start of compilation of a block. + /// When a branch instruction is encountered, this is set to true by the decode function. + bool m_hit_branch_instruction; + + /// The function being compiled + llvm::Function * m_current_function; + + /// The list of next blocks for the current block + const std::vector * m_current_block_next_blocks; + + /// Address of the current instruction + u32 m_current_instruction_address; + + /// Compiler stats + Stats m_stats; + + /// Get the name of the basic block for the specified address + std::string GetBasicBlockNameFromAddress(u32 address); + + /// Get the basic block in for the specified address. + llvm::BasicBlock * GetBasicBlockFromAddress(u32 address, llvm::Function * function, bool create_if_not_exist = false); + + /// Get PPU state pointer argument + llvm::Value * GetPPUStateArg(); + + /// Get interpreter pointer argument + llvm::Value * GetInterpreterArg(); + + /// Get tracer pointer argument + llvm::Value * GetTracerArg(); + + /// Get a bit + llvm::Value * GetBit(llvm::Value * val, u32 n); + + /// Clear a bit + llvm::Value * ClrBit(llvm::Value * val, u32 n); + + /// Set a bit + llvm::Value * SetBit(llvm::Value * val, u32 n, llvm::Value * bit, bool doClear = true); + + /// Get a nibble + llvm::Value * GetNibble(llvm::Value * val, u32 n); + + /// Clear a nibble + llvm::Value * ClrNibble(llvm::Value * val, u32 n); + + /// Set a nibble + llvm::Value * SetNibble(llvm::Value * val, u32 n, llvm::Value * nibble, bool doClear = true); + + /// Set a nibble + llvm::Value * SetNibble(llvm::Value * val, u32 n, llvm::Value * b0, llvm::Value * b1, llvm::Value * b2, llvm::Value * b3, bool doClear = true); + + /// Load PC + llvm::Value * GetPc(); + + /// Set PC + void SetPc(llvm::Value * val_ix); + + /// Load GPR + llvm::Value * GetGpr(u32 r, u32 num_bits = 64); + + /// Set GPR + void SetGpr(u32 r, llvm::Value * val_x64); + + /// Load CR + llvm::Value * GetCr(); + + /// Load CR and get field CRn + llvm::Value * GetCrField(u32 n); + + /// Set CR + void SetCr(llvm::Value * val_x32); + + /// Set CR field + void SetCrField(u32 n, llvm::Value * field); + + /// Set CR field + void SetCrField(u32 n, llvm::Value * b0, llvm::Value * b1, llvm::Value * b2, llvm::Value * b3); + + /// Set CR field based on signed comparison + void SetCrFieldSignedCmp(u32 n, llvm::Value * a, llvm::Value * b); + + /// Set CR field based on unsigned comparison + void SetCrFieldUnsignedCmp(u32 n, llvm::Value * a, llvm::Value * b); + + /// Set CR6 based on the result of the vector compare instruction + void SetCr6AfterVectorCompare(u32 vr); + + /// Get LR + llvm::Value * GetLr(); + + /// Set LR + void SetLr(llvm::Value * val_x64); + + /// Get CTR + llvm::Value * GetCtr(); + + /// Set CTR + void SetCtr(llvm::Value * val_x64); + + /// Load XER and convert it to an i64 + llvm::Value * GetXer(); + + /// Load XER and return the CA bit + llvm::Value * GetXerCa(); + + /// Load XER and return the SO bit + llvm::Value * GetXerSo(); + + /// Set XER + void SetXer(llvm::Value * val_x64); + + /// Set the CA bit of XER + void SetXerCa(llvm::Value * ca); + + /// Set the SO bit of XER + void SetXerSo(llvm::Value * so); + + /// Get USPRG0 + llvm::Value * GetUsprg0(); + + /// Set USPRG0 + void SetUsprg0(llvm::Value * val_x64); + + /// Get FPR + llvm::Value * GetFpr(u32 r, u32 bits = 64, bool as_int = false); + + /// Set FPR + void SetFpr(u32 r, llvm::Value * val); + + /// Load VSCR + llvm::Value * GetVscr(); + + /// Set VSCR + void SetVscr(llvm::Value * val_x32); + + /// Load VR + llvm::Value * GetVr(u32 vr); + + /// Load VR and convert it to an integer vector + llvm::Value * GetVrAsIntVec(u32 vr, u32 vec_elt_num_bits); + + /// Load VR and convert it to a float vector with 4 elements + llvm::Value * GetVrAsFloatVec(u32 vr); + + /// Load VR and convert it to a double vector with 2 elements + llvm::Value * GetVrAsDoubleVec(u32 vr); + + /// Set VR to the specified value + void SetVr(u32 vr, llvm::Value * val_x128); + + /// Check condition for branch instructions + llvm::Value * CheckBranchCondition(u32 bo, u32 bi); + + /// Create IR for a branch instruction + void CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool lk, bool target_is_lr = false); + + /// Read from memory + llvm::Value * ReadMemory(llvm::Value * addr_i64, u32 bits, u32 alignment = 0, bool bswap = true, bool could_be_mmio = true); + + /// Write to memory + void WriteMemory(llvm::Value * addr_i64, llvm::Value * val_ix, u32 alignment = 0, bool bswap = true, bool could_be_mmio = true); + + /// Call an interpreter function + template + llvm::Value * InterpreterCall(const char * name, Func function, Args... args); + + /// Convert a C++ type to an LLVM type + template + llvm::Type * CppToLlvmType(); + + /// Call a function + template + llvm::Value * Call(const char * name, Func function, Args... args); + + /// Tests if the instruction is a branch instruction or not + bool IsBranchInstruction(u32 instruction); + + /// Test an instruction against the interpreter + template + void VerifyInstructionAgainstInterpreter(const char * name, PPULLVMRecompilerFn recomp_fn, PPUInterpreterFn interp_fn, PPUState & input_state, Args... args); + + /// Excute a test + void RunTest(const char * name, std::function test_case, std::function input, std::function check_result); + + /// A mask used in rotate instructions + static u64 s_rotate_mask[64][64]; + + /// A flag indicating whether s_rotate_mask has been initialised or not + static bool s_rotate_mask_inited; + + /// Initialse s_rotate_mask + static void InitRotateMask(); + }; + + /// Analyses execution traces and finds hot paths + class Profiler { + + }; + + class RecompilationEngine { + public: + virtual ~RecompilationEngine() = default; + + /// Get the compiled code fragment for the specified address + CompiledCodeFragment GetCompiledCodeFragment(u32 address); + + /// Release a compiled code fragment earlier obtained through GetCompiledCodeFragment + void ReleaseCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment); + + /// Get the current revision + u32 GetCurrentRevision(); + + /// Get a pointer to the instance of this class + static std::shared_ptr GetInstance(); + + private: + RecompilationEngine() = default; + + RecompilationEngine(const RecompilationEngine & other) = delete; + RecompilationEngine(RecompilationEngine && other) = delete; + + RecompilationEngine & operator = (const RecompilationEngine & other) = delete; + RecompilationEngine & operator = (RecompilationEngine && other) = delete; + + /// Mutex used to prevent multiple creation + static std::mutex s_mutex; + + /// The instance + static std::shared_ptr s_the_instance; + }; + + /// Finds interesting execution sequences + class Tracer { + public: + Tracer(); + + Tracer(const Tracer & other) = delete; + Tracer(Tracer && other) = delete; + + virtual ~Tracer(); + + Tracer & operator = (const Tracer & other) = delete; + Tracer & operator = (Tracer && other) = delete; + + /// Notify the tracer that a branch was encountered + void Trace(BranchType branch_type, u32 address); + + /// Notify the tracer that the execution sequence is being terminated. + void Terminate(); + + private: + /// Current execution trace + std::vector m_trace; + + /// Call stack + std::vector m_stack; + }; + + /// PPU execution engine + class ExecutionEngine : public CPUDecoder { + public: + ExecutionEngine(PPUThread & ppu); + ExecutionEngine() = delete; + + ExecutionEngine(const ExecutionEngine & other) = delete; + ExecutionEngine(ExecutionEngine && other) = delete; + + virtual ~ExecutionEngine(); + + ExecutionEngine & operator = (const ExecutionEngine & other) = delete; + ExecutionEngine & operator = (ExecutionEngine && other) = delete; + + u8 DecodeMemory(const u32 address) override; + + private: + /// PPU processor context + PPUThread & m_ppu; + + /// PPU Interpreter + PPUInterpreter * m_interpreter; + + /// PPU instruction Decoder + PPUDecoder m_decoder; + + /// Execution tracer + Tracer m_tracer; + + /// Set to true if the last executed instruction was a branch + BranchType m_last_branch_type; + + /// The time at which the m_address_to_compiled_code_fragment cache was last cleared + std::chrono::high_resolution_clock::time_point m_last_cache_clear_time; + + /// The revision of the recompiler to which this thread is synced + u32 m_recompiler_revision; + + /// Address to compiled code fragmnet lookup. Key is address. Data is the pair (compiled code fragment, times hit). + std::unordered_map> m_address_to_compiled_code_fragment; + + /// Recompilation engine + std::shared_ptr m_recompilation_engine; + }; + + // Get the branch type from a branch instruction + BranchType GetBranchTypeFromInstruction(u32 instruction); +} #endif // PPU_LLVM_RECOMPILER_H diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index e164e1c531..8f1cc2407c 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -630,7 +630,7 @@ Emu\Cell - Source Files + Emu\Cell From 160b58cf617d6f98038875cf03904ee201bc31c1 Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Sat, 25 Oct 2014 06:56:57 +0530 Subject: [PATCH 02/12] Fixed compilation issues --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 11 ++++------- rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp | 13 +++++++------ rpcs3/Emu/Cell/PPUThread.cpp | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 4dda28cb55..9f37308fc7 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -4748,7 +4748,7 @@ CompiledCodeFragment RecompilationEngine::GetCompiledCodeFragment(u32 address) { return nullptr; } -void ReleaseCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment) { +void RecompilationEngine::ReleaseCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment) { } @@ -4778,7 +4778,6 @@ void Tracer::Trace(BranchType branch_type, u32 address) { ExecutionTrace * execution_trace = nullptr; BlockId block_id; int function; - int start; block_id.address = address; block_id.type = branch_type; @@ -4810,13 +4809,11 @@ void Tracer::Trace(BranchType branch_type, u32 address) { function = m_stack.back(); m_stack.pop_back(); - start = function; - execution_trace = new ExecutionTrace(); execution_trace->function_address = m_trace[function].address; execution_trace->type = ExecutionTrace::Linear; - execution_trace->blocks.insert(execution_trace->blocks.begin(), m_trace.begin() + start, m_trace.end()); - m_trace.erase(m_trace.begin() + start + 1, m_trace.end()); + execution_trace->blocks.insert(execution_trace->blocks.begin(), m_trace.begin() + function, m_trace.end()); + m_trace.erase(m_trace.begin() + function + 1, m_trace.end()); break; case None: break; @@ -4908,7 +4905,7 @@ u8 ppu_recompiler_llvm::ExecutionEngine::DecodeMemory(const u32 address) { return ret; } -BranchType GetBranchTypeFromInstruction(u32 instruction) { +BranchType ppu_recompiler_llvm::GetBranchTypeFromInstruction(u32 instruction) { auto type = BranchType::None; auto field1 = instruction >> 26; auto lk = instruction & 1; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp b/rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp index d601503e1b..2b637c4b4c 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompilerTests.cpp @@ -11,9 +11,10 @@ //#define PPU_LLVM_RECOMPILER_UNIT_TESTS 1 using namespace llvm; +using namespace ppu_recompiler_llvm; #define VERIFY_INSTRUCTION_AGAINST_INTERPRETER(fn, tc, input, ...) \ -VerifyInstructionAgainstInterpreter(fmt::Format("%s.%d", #fn, tc).c_str(), &PPULLVMRecompiler::fn, &PPUInterpreter::fn, input, __VA_ARGS__) +VerifyInstructionAgainstInterpreter(fmt::Format("%s.%d", #fn, tc).c_str(), &Compiler::fn, &PPUInterpreter::fn, input, __VA_ARGS__) #define VERIFY_INSTRUCTION_AGAINST_INTERPRETER_USING_RANDOM_INPUT(fn, s, n, ...) { \ PPUState input; \ @@ -24,7 +25,7 @@ VerifyInstructionAgainstInterpreter(fmt::Format("%s.%d", #fn, tc).c_str(), &PPUL } /// Register state of a PPU -struct PPUState { + struct ppu_recompiler_llvm::PPUState { /// Floating point registers PPCdouble FPR[32]; @@ -201,8 +202,8 @@ static PPUThread * s_ppu_state = nullptr; static PPUInterpreter * s_interpreter = nullptr; #endif // PPU_LLVM_RECOMPILER_UNIT_TESTS -template -void PPULLVMRecompiler::VerifyInstructionAgainstInterpreter(const char * name, PPULLVMRecompilerFn recomp_fn, PPUInterpreterFn interp_fn, PPUState & input_state, Args... args) { +template +void Compiler::VerifyInstructionAgainstInterpreter(const char * name, CompilerFn recomp_fn, PPUInterpreterFn interp_fn, PPUState & input_state, Args... args) { #ifdef PPU_LLVM_RECOMPILER_UNIT_TESTS auto test_case = [&]() { (this->*recomp_fn)(args...); @@ -232,7 +233,7 @@ void PPULLVMRecompiler::VerifyInstructionAgainstInterpreter(const char * name, P #endif // PPU_LLVM_RECOMPILER_UNIT_TESTS } -void PPULLVMRecompiler::RunTest(const char * name, std::function test_case, std::function input, std::function check_result) { +void Compiler::RunTest(const char * name, std::function test_case, std::function input, std::function check_result) { #ifdef PPU_LLVM_RECOMPILER_UNIT_TESTS // Create the unit test function m_current_function = (Function *)m_module->getOrInsertFunction(name, m_ir_builder->getVoidTy(), @@ -311,7 +312,7 @@ void PPULLVMRecompiler::RunTest(const char * name, std::function test_ca #endif // PPU_LLVM_RECOMPILER_UNIT_TESTS } -void PPULLVMRecompiler::RunAllTests(PPUThread * ppu_state, PPUInterpreter * interpreter) { +void Compiler::RunAllTests(PPUThread * ppu_state, PPUInterpreter * interpreter) { #ifdef PPU_LLVM_RECOMPILER_UNIT_TESTS s_ppu_state = ppu_state; s_interpreter = interpreter; diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index f9d01a75a7..df7ce2740b 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -112,7 +112,7 @@ void PPUThread::DoRun() case 2: if (!m_dec) { - m_dec = new PPULLVMEmulator(*this); + m_dec = new ppu_recompiler_llvm::ExecutionEngine(*this); } break; From 7846fa5829da514ba6bef6c042fdd39e2909e11f Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Sat, 1 Nov 2014 01:22:39 +0530 Subject: [PATCH 03/12] Made changes to tracer so that it understand compiled blocks --- rpcs3/Emu/Cell/PPUInterpreter.h | 2 +- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 424 +++++++++++++++++++++------ rpcs3/Emu/Cell/PPULLVMRecompiler.h | 168 +++++++---- 3 files changed, 456 insertions(+), 138 deletions(-) diff --git a/rpcs3/Emu/Cell/PPUInterpreter.h b/rpcs3/Emu/Cell/PPUInterpreter.h index da61d3f1b7..f476d8c870 100644 --- a/rpcs3/Emu/Cell/PPUInterpreter.h +++ b/rpcs3/Emu/Cell/PPUInterpreter.h @@ -61,7 +61,7 @@ namespace ppu_recompiler_llvm { class PPUInterpreter : public PPUOpcodes { - friend class ppu_recompiler_llvm::Compiler; + friend class ppu_recompiler_llvm::Compiler; private: PPUThread& CPU; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 9f37308fc7..952eed981c 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -76,7 +76,7 @@ Compiler::~Compiler() { delete m_llvm_context; } -CompiledCodeFragment Compiler::Compile(const std::string & name, const CodeFragment & code_fragment) { +Executable Compiler::Compile(const std::string & name, const CodeFragment & code_fragment) { assert(!name.empty()); assert(!code_fragment.empty()); @@ -114,7 +114,7 @@ CompiledCodeFragment Compiler::Compile(const std::string & name, const CodeFragm // Add code to notify the tracer about this function and branch to the first instruction m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0, m_current_function)); //Call("Tracer.Trace", &Tracer::Trace, *arg_i, - // m_ir_builder->getIntN(sizeof(Tracer::BranchType) * 8, code_fragment[0].first.type == FunctionStart ? Tracer::BranchType::CompiledFunctionCall : Tracer::BranchType::CompiledBlock), + // m_ir_builder->getInt32(code_fragment[0].first.type == Function ? FunctionCall : Block), // m_ir_builder->getInt32(code_fragment[0].first.address)); m_ir_builder->CreateBr(GetBasicBlockFromAddress(code_fragment[0].first.address, m_current_function)); @@ -123,9 +123,14 @@ CompiledCodeFragment Compiler::Compile(const std::string & name, const CodeFragm m_current_instruction_address = i->first.address; m_current_block_next_blocks = &(i->second); auto block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); - m_hit_branch_instruction = false; m_ir_builder->SetInsertPoint(block); + if (i != code_fragment.begin() && i->first.type == BlockId::Type::FunctionCall) { + auto ordinal = RecompilationEngine::GetInstance()->GetOrdinal(i->first.address); + + } + + m_hit_branch_instruction = false; while (!m_hit_branch_instruction) { if (!block->getInstList().empty()) { break; @@ -143,7 +148,7 @@ CompiledCodeFragment Compiler::Compile(const std::string & name, const CodeFragm } } - // If the function has an unknown block then notify the tracer + // If the function has an unknown block then add code to notify the tracer auto unknown_bb = GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function); if (!unknown_bb) { m_ir_builder->SetInsertPoint(unknown_bb); @@ -177,16 +182,17 @@ CompiledCodeFragment Compiler::Compile(const std::string & name, const CodeFragm auto compilation_end = std::chrono::high_resolution_clock::now(); m_stats.total_time += std::chrono::duration_cast(compilation_end - compilation_start); - m_compiled[(CompiledCodeFragment)mci.address()] = m_current_function; - return (CompiledCodeFragment)mci.address(); + //m_compiled[(CompiledCodeFragment)mci.address()] = m_current_function; + //return (CompiledCodeFragment)mci.address(); + return nullptr; } -void Compiler::FreeCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment) { - auto i = m_compiled.find(compiled_code_fragment); - if (i != m_compiled.end()) { - m_execution_engine->freeMachineCodeForFunction(i->second); - i->second->eraseFromParent(); - } +void Compiler::FreeCompiledCodeFragment(Executable compiled_code_fragment) { + //auto i = m_compiled.find(compiled_code_fragment); + //if (i != m_compiled.end()) { + // m_execution_engine->freeMachineCodeForFunction(i->second); + // i->second->eraseFromParent(); + //} } Compiler::Stats Compiler::GetStats() { @@ -4741,31 +4747,201 @@ void Compiler::InitRotateMask() { } } -std::mutex RecompilationEngine::s_mutex; -std::shared_ptr RecompilationEngine::s_the_instance; +std::mutex RecompilationEngine::s_mutex; +RecompilationEngine * RecompilationEngine::s_the_instance; -CompiledCodeFragment RecompilationEngine::GetCompiledCodeFragment(u32 address) { +RecompilationEngine::BlockEntry::BlockEntry() + : num_hits(0) + , is_compiled(false) { +} + +RecompilationEngine::BlockEntry::~BlockEntry() { + for (auto i = execution_traces.begin(); i != execution_traces.end(); i++) { + delete i->second; + } +} + +RecompilationEngine::RecompilationEngine() + : ThreadBase("PPU Recompilation Engine") { + Start(); +} + +RecompilationEngine::~RecompilationEngine() { + Stop(); +} + +u32 RecompilationEngine::GetOrdinal(u32 address) { + return 0xFFFFFFFF; +} + +Executable * RecompilationEngine::GetExecutableLookup() const { return nullptr; } -void RecompilationEngine::ReleaseCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment) { +void RecompilationEngine::NotifyTrace(ExecutionTrace * execution_trace) { + { + std::lock_guard lock(m_pending_execution_traces_lock); + m_pending_execution_traces.push_back(execution_trace); + } + Notify(); + // TODO: Increase the priority of the recompilation engine thread } -u32 RecompilationEngine::GetCurrentRevision() { - return 0; +void RecompilationEngine::Task() { + std::chrono::nanoseconds idling_time(0); + + auto start = std::chrono::high_resolution_clock::now(); + while (!TestDestroy() && !Emu.IsStopped()) { + // Wait a few ms for something to happen + auto idling_start = std::chrono::high_resolution_clock::now(); + WaitForAnySignal(250); + auto idling_end = std::chrono::high_resolution_clock::now(); + idling_time += std::chrono::duration_cast(idling_end - idling_start); + + u32 num_processed = 0; + while (!TestDestroy() && !Emu.IsStopped()) { + ExecutionTrace * execution_trace; + + { + std::lock_guard lock(m_pending_execution_traces_lock); + + auto i = m_pending_execution_traces.begin(); + if (i != m_pending_execution_traces.end()) { + execution_trace = *i; + m_pending_execution_traces.erase(i); + } else { + break; + } + } + + auto block_i = ProcessExecutionTrace(execution_trace); + if (block_i != m_block_table.end()) { + CompileBlock(block_i); + } + } + + // TODO: Reduce the priority of the recompilation engine thread + + if (num_processed == 0) { + // If we get here, it means the recompilation engine is idling. + // We should use this oppurtunity to optimize the code. + } + } + + std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); + auto total_time = std::chrono::duration_cast(end - start); + auto compiler_stats = m_compiler.GetStats(); + + std::string error; + raw_fd_ostream log_file("PPULLVMRecompiler.log", error, sys::fs::F_Text); + log_file << "Total time = " << total_time.count() / 1000000 << "ms\n"; + log_file << " Time spent compiling = " << compiler_stats.total_time.count() / 1000000 << "ms\n"; + log_file << " Time spent building IR = " << compiler_stats.ir_build_time.count() / 1000000 << "ms\n"; + log_file << " Time spent optimizing = " << compiler_stats.optimization_time.count() / 1000000 << "ms\n"; + log_file << " Time spent translating = " << compiler_stats.translation_time.count() / 1000000 << "ms\n"; + log_file << " Time spent idling = " << idling_time.count() / 1000000 << "ms\n"; + log_file << " Time spent doing misc tasks = " << (total_time.count() - idling_time.count() - compiler_stats.total_time.count()) / 1000000 << "ms\n"; + log_file << "\nInterpreter fallback stats:\n"; + for (auto i = compiler_stats.interpreter_fallback_stats.begin(); i != compiler_stats.interpreter_fallback_stats.end(); i++) { + log_file << i->first << " = " << i->second << "\n"; + } + + //log_file << "\nDisassembly:\n"; + //auto disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr); + //for (auto i = m_compiled.begin(); i != m_compiled.end(); i++) { + // log_file << fmt::Format("%s: Size = %u bytes, Number of instructions = %u\n", i->second.llvm_function->getName().str().c_str(), i->second.size, i->second.num_instructions); + + // uint8_t * fn_ptr = (uint8_t *)i->second.executable; + // for (size_t pc = 0; pc < i->second.size;) { + // char str[1024]; + + // auto size = LLVMDisasmInstruction(disassembler, fn_ptr + pc, i->second.size - pc, (uint64_t)(fn_ptr + pc), str, sizeof(str)); + // log_file << str << '\n'; + // pc += size; + // } + //} + + //LLVMDisasmDispose(disassembler); + + //log_file << "\nLLVM IR:\n" << *m_module; + + LOG_NOTICE(PPU, "PPU LLVM Recompilation thread exiting."); +} + +RecompilationEngine::BlockTable::iterator RecompilationEngine::ProcessExecutionTrace(ExecutionTrace * execution_trace) { + auto block_i = m_block_table.find(execution_trace->blocks[0].address); + if (block_i == m_block_table.end()) { + // New block + block_i = m_block_table.insert(m_block_table.end(), std::make_pair(execution_trace->blocks[0].address, BlockEntry())); + } + + block_i->second.num_hits++; + auto execution_trace_id = GetExecutionTraceId(execution_trace); + auto execution_trace_i = block_i->second.execution_traces.find(execution_trace_id); + if (execution_trace_i == block_i->second.execution_traces.end()) { + block_i->second.execution_traces.insert(std::make_pair(execution_trace_id, execution_trace)); + } + + if (!block_i->second.is_compiled && block_i->second.num_hits > 1000) { // TODO: Make threshold configurable + return block_i; + } + + return m_block_table.end(); +} + +void RecompilationEngine::CompileBlock(BlockTable::iterator block_i) { + auto code_fragment = BuildCodeFragmentFromBlock(block_i->second, false); +} + +CodeFragment RecompilationEngine::BuildCodeFragmentFromBlock(const BlockEntry & block_entry, bool force_inline) { + CodeFragment code_fragment; + //std::vector queue; + + //queue.push_back(&block_entry); + //for (auto q = queue.begin(); q != queue.end(); q++) { + // for (auto i = (*q)->execution_traces.begin(); i != (*q)->execution_traces.end(); i++) { + // for (auto j = i->second->blocks.begin(); j != i->second->blocks.end(); j++) { + // auto k = std::find_if(code_fragment.begin(), code_fragment.end(), + // [&j](const CodeFragment::value_type & v)->bool { return v.first.address == j->address; }); + // if (k == code_fragment.end()) { + // code_fragment.push_back(std::make_pair(*j, std::vector())); + // k = code_fragment.end() - 1; + // } + + // if ((j + 1) != i->second->blocks.end()) { + // auto l = std::find(k->second.begin(), k->second.end(), *(j + 1)); + // if (l == k->second.end()) { + // k->second.push_back(*(j + 1)); + // } + // } + + // if (force_inline && j->type == BlockId::Type::Normal) { + // auto block_i = m_block_table.find(j->address); + // if (block_i != m_block_table.end()) { + // if (std::find(queue.begin(), queue.end(), block_i->second) == queue.end()) { + // queue.push_back(&(block_i->second)); + // } + // } + // } + // } + // } + //} + + return code_fragment; } std::shared_ptr RecompilationEngine::GetInstance() { if (s_the_instance == nullptr) { std::lock_guard lock(s_mutex); - s_the_instance = std::shared_ptr(new RecompilationEngine()); + s_the_instance = new RecompilationEngine(); } - return s_the_instance; + return std::shared_ptr(s_the_instance); } -Tracer::Tracer() { +Tracer::Tracer() + : m_recompilation_engine(RecompilationEngine::GetInstance()) { m_trace.reserve(1000); m_stack.reserve(100); } @@ -4774,27 +4950,60 @@ Tracer::~Tracer() { Terminate(); } -void Tracer::Trace(BranchType branch_type, u32 address) { +void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { ExecutionTrace * execution_trace = nullptr; BlockId block_id; int function; - block_id.address = address; - block_id.type = branch_type; - switch (branch_type) { - case FunctionCall: + switch (trace_type) { + case TraceType::CallFunction: + // arg1 is address of the function + block_id.address = arg1; + block_id.type = BlockId::Type::FunctionCall; + m_trace.push_back(block_id); + break; + case TraceType::EnterFunction: + // arg1 is address. + block_id.address = arg1; + block_id.type = BlockId::Type::Normal; m_stack.push_back((u32)m_trace.size()); m_trace.push_back(block_id); break; - case Block: + case TraceType::ExitFromCompiledFunction: + // arg1 is address of function. + // arg2 is the address of the exit block. + block_id.address = arg1; + block_id.type = BlockId::Type::Normal; + m_stack.push_back((u32)m_trace.size()); + m_trace.push_back(block_id); + + block_id.address = arg2; + block_id.type = BlockId::Type::Exit; + m_trace.push_back(block_id); + break; + case TraceType::Return: + // No args used + function = m_stack.back(); + m_stack.pop_back(); + + execution_trace = new ExecutionTrace(); + execution_trace->type = ExecutionTrace::Type::Linear; + execution_trace->function_address = m_trace[function].address; + execution_trace->previous_block_address = 0; + std::copy(m_trace.begin() + function, m_trace.end(), std::back_inserter(execution_trace->blocks)); + m_trace.erase(m_trace.begin() + function, m_trace.end()); + break; + case TraceType::EnterBlock: + // arg1 is address. Other args are not used. function = m_stack.back(); for (int i = (int)m_trace.size() - 1; i >= function; i--) { - if (m_trace[i].address == address) { + if (m_trace[i].address == arg1 && m_trace[i].type == BlockId::Type::Normal) { // Found a loop within the current function - execution_trace = new ExecutionTrace(); - execution_trace->type = ExecutionTrace::Loop; - execution_trace->function_address = m_trace[function].address; - execution_trace->blocks.insert(execution_trace->blocks.begin(), m_trace.begin() + i, m_trace.end()); + execution_trace = new ExecutionTrace(); + execution_trace->type = ExecutionTrace::Type::Loop; + execution_trace->function_address = m_trace[function].address; + execution_trace->previous_block_address = i == function ? 0 : m_trace[i - 1].address; + std::copy(m_trace.begin() + i, m_trace.end(), std::back_inserter(execution_trace->blocks)); m_trace.erase(m_trace.begin() + i + 1, m_trace.end()); break; } @@ -4802,20 +5011,16 @@ void Tracer::Trace(BranchType branch_type, u32 address) { if (!execution_trace) { // A loop was not found + block_id.address = arg1; + block_id.type = BlockId::Type::Normal; m_trace.push_back(block_id); } break; - case Return: - function = m_stack.back(); - m_stack.pop_back(); - - execution_trace = new ExecutionTrace(); - execution_trace->function_address = m_trace[function].address; - execution_trace->type = ExecutionTrace::Linear; - execution_trace->blocks.insert(execution_trace->blocks.begin(), m_trace.begin() + function, m_trace.end()); - m_trace.erase(m_trace.begin() + function + 1, m_trace.end()); - break; - case None: + case TraceType::ExitFromCompiledBlock: + // arg1 is address of the exit block. + block_id.address = arg1; + block_id.type = BlockId::Type::Exit; + m_trace.push_back(block_id); break; default: assert(0); @@ -4823,14 +5028,17 @@ void Tracer::Trace(BranchType branch_type, u32 address) { } if (execution_trace) { - auto s = fmt::Format("Trace: 0x%08X, %s -> ", execution_trace->function_address, execution_trace->type == ExecutionTrace::Loop ? "Loop" : "Linear"); - for (auto i = 0; i < execution_trace->blocks.size(); i++) { - s += fmt::Format("0x%08X ", execution_trace->blocks[i]); + auto s = fmt::Format("Trace: 0x%08X, 0x%08X, %s -> ", execution_trace->function_address, execution_trace->previous_block_address, + execution_trace->type == ExecutionTrace::Type::Loop ? "Loop" : "Linear"); + for (auto i = 0; i < execution_trace->blocks.size(); i++) {; + s += fmt::Format("%c:0x%08X ", + execution_trace->blocks[i].type == BlockId::Type::Normal ? 'N' : + execution_trace->blocks[i].type == BlockId::Type::FunctionCall ? 'F' : 'E', + execution_trace->blocks[i].address); } LOG_NOTICE(PPU, s.c_str()); - delete execution_trace; - // TODO: Notify recompilation engine + //m_recompilation_engine->NotifyTrace(execution_trace); } } @@ -4842,36 +5050,27 @@ ppu_recompiler_llvm::ExecutionEngine::ExecutionEngine(PPUThread & ppu) : m_ppu(ppu) , m_interpreter(new PPUInterpreter(ppu)) , m_decoder(m_interpreter) - , m_last_branch_type(FunctionCall) , m_last_cache_clear_time(std::chrono::high_resolution_clock::now()) - , m_recompiler_revision(0) , m_recompilation_engine(RecompilationEngine::GetInstance()) { + m_executable_lookup = m_recompilation_engine->GetExecutableLookup(); } ppu_recompiler_llvm::ExecutionEngine::~ExecutionEngine() { - for (auto iter = m_address_to_compiled_code_fragment.begin(); iter != m_address_to_compiled_code_fragment.end(); iter++) { - m_recompilation_engine->ReleaseCompiledCodeFragment(iter->second.first); - } } u8 ppu_recompiler_llvm::ExecutionEngine::DecodeMemory(const u32 address) { + ExecuteFunction(this, &m_ppu, m_interpreter, &m_tracer); + return 0; +} + +void ppu_recompiler_llvm::ExecutionEngine::RemoveUnusedEntriesFromCache() { auto now = std::chrono::high_resolution_clock::now(); - if (std::chrono::duration_cast(now - m_last_cache_clear_time).count() > 10000) { - bool clear_all = false; - - u32 revision = m_recompilation_engine->GetCurrentRevision(); - if (m_recompiler_revision != revision) { - m_recompiler_revision = revision; - clear_all = true; - } - - for (auto i = m_address_to_compiled_code_fragment.begin(); i != m_address_to_compiled_code_fragment.end();) { + for (auto i = m_address_to_ordinal.begin(); i != m_address_to_ordinal.end();) { auto tmp = i; i++; - if (tmp->second.second == 0 || clear_all) { - m_address_to_compiled_code_fragment.erase(tmp); - m_recompilation_engine->ReleaseCompiledCodeFragment(tmp->second.first); + if (tmp->second.second == 0) { + m_address_to_ordinal.erase(tmp); } else { tmp->second.second = 0; } @@ -4879,47 +5078,98 @@ u8 ppu_recompiler_llvm::ExecutionEngine::DecodeMemory(const u32 address) { m_last_cache_clear_time = now; } +} - auto i = m_address_to_compiled_code_fragment.find(address); - if (i == m_address_to_compiled_code_fragment.end()) { - auto compiled_code_fragment = m_recompilation_engine->GetCompiledCodeFragment(address); - if (compiled_code_fragment) { - i = m_address_to_compiled_code_fragment.insert(m_address_to_compiled_code_fragment.end(), std::make_pair(address, std::make_pair(compiled_code_fragment, 0))); +Executable ppu_recompiler_llvm::ExecutionEngine::GetExecutable(u32 address, Executable default_executable) { + // Find the ordinal for the specified address and insert it to the cache + auto i = m_address_to_ordinal.find(address); + if (i == m_address_to_ordinal.end()) { + auto ordinal = m_recompilation_engine->GetOrdinal(address); + if (ordinal != 0xFFFFFFFF) { + i = m_address_to_ordinal.insert(m_address_to_ordinal.end(), std::make_pair(address, std::make_pair(ordinal, 0))); } } - u8 ret = 0; - if (i != m_address_to_compiled_code_fragment.end()) { - m_last_branch_type = None; + Executable executable = default_executable; + if (i != m_address_to_ordinal.end()) { i->second.second++; - i->second.first(&m_ppu, m_interpreter); - } else { - if (m_last_branch_type != None) { - m_tracer.Trace(m_last_branch_type, address); - } - - ret = m_decoder.DecodeMemory(address); - m_last_branch_type = m_ppu.m_is_branch ? GetBranchTypeFromInstruction(vm::read32(address)) : None; + executable = m_executable_lookup[i->second.first]; } - return ret; + RemoveUnusedEntriesFromCache(); + return executable; +} + +u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteFunction(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer) { + tracer->Trace(Tracer::TraceType::EnterFunction, ppu_state->PC, 0); + return ExecuteTillReturn(execution_engine, ppu_state, interpreter, tracer); +} + +u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteTillReturn(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer) { + bool terminate = false; + + while (!terminate) { + auto instruction = re32(vm::get_ref(ppu_state->PC)); + execution_engine->m_decoder.Decode(instruction); + auto is_branch = ppu_state->m_is_branch; + ppu_state->NextPc(4); + + if (is_branch) { + Executable executable; + auto branch_type = GetBranchTypeFromInstruction(instruction); + + switch (branch_type) { + case BranchType::Return: + tracer->Trace(Tracer::TraceType::Return, 0, 0); + terminate = true; + break; + case BranchType::FunctionCall: + tracer->Trace(Tracer::TraceType::CallFunction, ppu_state->PC, 0); + executable = execution_engine->GetExecutable(ppu_state->PC, ExecuteFunction); + executable(execution_engine, ppu_state, interpreter, tracer); + // Fallthrough + case BranchType::LocalBranch: + tracer->Trace(Tracer::TraceType::EnterBlock, ppu_state->PC, 0); + executable = execution_engine->GetExecutable(ppu_state->PC, nullptr); + if (executable != nullptr) { + auto exit_block = executable(execution_engine, ppu_state, interpreter, tracer); + if (exit_block) { + tracer->Trace(Tracer::TraceType::ExitFromCompiledBlock, (u32)exit_block, 0); + } else { + tracer->Trace(Tracer::TraceType::Return, 0, 0); + terminate = true; + } + } + break; + default: + assert(0); + break; + } + } + } + + return 0; } BranchType ppu_recompiler_llvm::GetBranchTypeFromInstruction(u32 instruction) { - auto type = BranchType::None; + auto type = BranchType::NonBranch; auto field1 = instruction >> 26; auto lk = instruction & 1; if (field1 == 16 || field1 == 18) { - type = lk ? FunctionCall : Block; + type = lk ? BranchType::FunctionCall : BranchType::LocalBranch; } else if (field1 == 19) { u32 field2 = (instruction >> 1) & 0x3FF; if (field2 == 16) { - type = lk ? FunctionCall : Return; + type = lk ? BranchType::FunctionCall : BranchType::Return; } else if (field2 == 528) { - type = lk ? FunctionCall : Block; + type = lk ? BranchType::FunctionCall : BranchType::LocalBranch; } } return type; } + +ExecutionTraceId ppu_recompiler_llvm::GetExecutionTraceId(const ExecutionTrace * execution_trace) { + return 0; +} diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index a93da8021b..c3e4511da2 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -12,34 +12,44 @@ #include "llvm/PassManager.h" namespace ppu_recompiler_llvm { - /// Branch type - enum BranchType { - None, + class Compiler; + class RecompilationEngine; + class Tracer; + class ExecutionEngine; + + enum class BranchType { + NonBranch, + LocalBranch, FunctionCall, - Block, Return, }; /// Unique id of a block - union BlockId { - u64 block_id; + struct BlockId { + /// Address of the block + u32 address; - struct { - /// Address of the block - u32 address; - - /// The type of the block - BranchType type; - }; + /// The type of the block + enum class Type { + FunctionCall, + Normal, + Exit, + } type; }; + /// Uniquely identifies an execution trace + typedef u64 ExecutionTraceId; + /// An execution trace. struct ExecutionTrace { - /// The function in which this trace was found + /// The function to which this trace belongs u32 function_address; + /// The address of the block that came before this trace + u32 previous_block_address; + /// Execution trace type - enum { + enum class Type { Linear, Loop, } type; @@ -51,8 +61,8 @@ namespace ppu_recompiler_llvm { /// A fragment of PPU code. A list of (block, list of next blocks) pairs. typedef std::vector>> CodeFragment; - /// Pointer to a function built by compiling a fragment of PPU code - typedef u64(*CompiledCodeFragment)(PPUThread * ppu_state, PPUInterpreter * interpreter); + /// Pointer to an executable + typedef u64(*Executable)(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer); struct PPUState; @@ -86,11 +96,11 @@ namespace ppu_recompiler_llvm { Compiler & operator = (const Compiler & other) = delete; Compiler & operator = (Compiler && other) = delete; - /// Compile a code fragment - CompiledCodeFragment Compile(const std::string & name, const CodeFragment & code_fragment); + /// Compile a code fragment and obtain an executable + Executable Compile(const std::string & name, const CodeFragment & code_fragment); - /// Free a compiled code fragment - void FreeCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment); + /// Free an executable earilier obtained from the Compile function + void FreeCompiledCodeFragment(Executable executable); /// Retrieve compiler stats Stats GetStats(); @@ -502,9 +512,6 @@ namespace ppu_recompiler_llvm { void UNK(const u32 code, const u32 opcode, const u32 gcode) override; private: - /// Map from compiled code fragment to the LLVM function for the code fragment - std::map m_compiled; - /// LLVM context llvm::LLVMContext * m_llvm_context; @@ -716,29 +723,44 @@ namespace ppu_recompiler_llvm { static void InitRotateMask(); }; - /// Analyses execution traces and finds hot paths - class Profiler { - - }; - - class RecompilationEngine { + class RecompilationEngine : public ThreadBase { public: - virtual ~RecompilationEngine() = default; + virtual ~RecompilationEngine(); - /// Get the compiled code fragment for the specified address - CompiledCodeFragment GetCompiledCodeFragment(u32 address); + /// Get the ordinal for the specified address + u32 GetOrdinal(u32 address); - /// Release a compiled code fragment earlier obtained through GetCompiledCodeFragment - void ReleaseCompiledCodeFragment(CompiledCodeFragment compiled_code_fragment); + /// Get the executable lookup table + Executable * GetExecutableLookup() const; - /// Get the current revision - u32 GetCurrentRevision(); + /// Notify the recompilation engine about a newly detected trace. It takes ownership of the trace. + void NotifyTrace(ExecutionTrace * execution_trace); + + void Task() override; /// Get a pointer to the instance of this class static std::shared_ptr GetInstance(); private: - RecompilationEngine() = default; + /// An entry in the block table + struct BlockEntry { + BlockEntry(); + ~BlockEntry(); + + /// Number of times this block was hit + u32 num_hits; + + /// Execution traces starting at this block + std::unordered_map execution_traces; + + /// Indicates whether the block has been compiled or not + bool is_compiled; + }; + + /// Block table type. Key is block address. + typedef std::unordered_map BlockTable; + + RecompilationEngine(); RecompilationEngine(const RecompilationEngine & other) = delete; RecompilationEngine(RecompilationEngine && other) = delete; @@ -746,16 +768,47 @@ namespace ppu_recompiler_llvm { RecompilationEngine & operator = (const RecompilationEngine & other) = delete; RecompilationEngine & operator = (RecompilationEngine && other) = delete; + /// Process an execution trace. Returns an iterator to a block table entry if the block should be compiled. + BlockTable::iterator ProcessExecutionTrace(ExecutionTrace * execution_trace); + + /// Compile a block + void CompileBlock(BlockTable::iterator block_i); + + /// Build code fragment from a block + CodeFragment BuildCodeFragmentFromBlock(const BlockEntry & block_entry, bool force_inline); + + /// Lock for accessing m_pending_execution_traces. TODO: Eliminate this and use a lock-free queue. + std::mutex m_pending_execution_traces_lock; + + /// Queue of execution traces pending prcessing + std::list m_pending_execution_traces; + + /// Block table + BlockTable m_block_table; + + /// PPU Compiler + Compiler m_compiler; + /// Mutex used to prevent multiple creation static std::mutex s_mutex; /// The instance - static std::shared_ptr s_the_instance; + static RecompilationEngine * s_the_instance; }; /// Finds interesting execution sequences class Tracer { public: + /// Trace type + enum class TraceType { + CallFunction, + EnterFunction, + ExitFromCompiledFunction, + Return, + EnterBlock, + ExitFromCompiledBlock, + }; + Tracer(); Tracer(const Tracer & other) = delete; @@ -766,8 +819,8 @@ namespace ppu_recompiler_llvm { Tracer & operator = (const Tracer & other) = delete; Tracer & operator = (Tracer && other) = delete; - /// Notify the tracer that a branch was encountered - void Trace(BranchType branch_type, u32 address); + /// Notify the tracer + void Trace(TraceType trace_type, u32 arg1, u32 arg2); /// Notify the tracer that the execution sequence is being terminated. void Terminate(); @@ -778,6 +831,9 @@ namespace ppu_recompiler_llvm { /// Call stack std::vector m_stack; + + /// Recompilation engine + std::shared_ptr m_recompilation_engine; }; /// PPU execution engine @@ -809,24 +865,36 @@ namespace ppu_recompiler_llvm { /// Execution tracer Tracer m_tracer; - /// Set to true if the last executed instruction was a branch - BranchType m_last_branch_type; + /// Executable lookup table + Executable * m_executable_lookup; - /// The time at which the m_address_to_compiled_code_fragment cache was last cleared + /// The time at which the m_address_to_ordinal cache was last cleared std::chrono::high_resolution_clock::time_point m_last_cache_clear_time; - /// The revision of the recompiler to which this thread is synced - u32 m_recompiler_revision; - - /// Address to compiled code fragmnet lookup. Key is address. Data is the pair (compiled code fragment, times hit). - std::unordered_map> m_address_to_compiled_code_fragment; + /// Address to ordinal lookup. Key is address. Data is the pair (ordinal, times hit). + std::unordered_map> m_address_to_ordinal; /// Recompilation engine std::shared_ptr m_recompilation_engine; + + /// Remove unused entries from the m_address_to_ordinal cache + void RemoveUnusedEntriesFromCache(); + + /// Get the executable for the specified address + Executable GetExecutable(u32 address, Executable default_executable); + + /// Execute a function + static u64 ExecuteFunction(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer); + + /// Execute till the current function returns + static u64 ExecuteTillReturn(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer); }; - // Get the branch type from a branch instruction + /// Get the branch type from a branch instruction BranchType GetBranchTypeFromInstruction(u32 instruction); + + /// Get the execution trace id of an execution trace + ExecutionTraceId GetExecutionTraceId(const ExecutionTrace * execution_trace); } #endif // PPU_LLVM_RECOMPILER_H From 7c3c5ae08ed02f22f22c8ddf5b7ee93c6c53c78b Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Wed, 5 Nov 2014 01:01:20 +0530 Subject: [PATCH 04/12] Construct CFG from execution traces --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 522 ++++++++++++++------------- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 110 ++++-- rpcs3/stdafx.h | 1 + 3 files changed, 354 insertions(+), 279 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 952eed981c..6adfdaed00 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -76,124 +76,124 @@ Compiler::~Compiler() { delete m_llvm_context; } -Executable Compiler::Compile(const std::string & name, const CodeFragment & code_fragment) { - assert(!name.empty()); - assert(!code_fragment.empty()); +//Executable Compiler::Compile(const std::string & name, const CodeFragment & code_fragment) { +// assert(!name.empty()); +// assert(!code_fragment.empty()); +// +// auto compilation_start = std::chrono::high_resolution_clock::now(); +// +// // Create the function +// m_current_function = (Function *)m_module->getOrInsertFunction(name, m_ir_builder->getVoidTy(), +// m_ir_builder->getInt8PtrTy() /*ppu_state*/, +// m_ir_builder->getInt8PtrTy() /*interpreter*/, +// m_ir_builder->getInt8PtrTy() /*tracer*/, nullptr); +// m_current_function->setCallingConv(CallingConv::X86_64_Win64); +// auto arg_i = m_current_function->arg_begin(); +// arg_i->setName("ppu_state"); +// (++arg_i)->setName("interpreter"); +// (++arg_i)->setName("tracer"); +// +// // Create the entry block +// GetBasicBlockFromAddress(0, m_current_function, true); +// +// // Create basic blocks for each instruction +// for (auto i = code_fragment.begin(); i != code_fragment.end(); i++) { +// u32 address = i->first.address; +// while (1) { +// GetBasicBlockFromAddress(address, m_current_function, true); +// +// u32 instr = vm::read32(address); +// if (IsBranchInstruction(instr)) { +// break; +// } +// +// address += 4; +// } +// } +// +// // Add code to notify the tracer about this function and branch to the first instruction +// m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0, m_current_function)); +// //Call("Tracer.Trace", &Tracer::Trace, *arg_i, +// // m_ir_builder->getInt32(code_fragment[0].first.type == Function ? FunctionCall : Block), +// // m_ir_builder->getInt32(code_fragment[0].first.address)); +// m_ir_builder->CreateBr(GetBasicBlockFromAddress(code_fragment[0].first.address, m_current_function)); +// +// // Convert each block in this code fragment to LLVM IR +// for (auto i = code_fragment.begin(); i != code_fragment.end(); i++) { +// m_current_instruction_address = i->first.address; +// m_current_block_next_blocks = &(i->second); +// auto block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); +// m_ir_builder->SetInsertPoint(block); +// +// if (i != code_fragment.begin() && i->first.type == BlockId::Type::FunctionCall) { +// auto ordinal = RecompilationEngine::GetInstance()->GetOrdinal(i->first.address); +// +// } +// +// m_hit_branch_instruction = false; +// while (!m_hit_branch_instruction) { +// if (!block->getInstList().empty()) { +// break; +// } +// +// u32 instr = vm::read32(m_current_instruction_address); +// Decode(instr); +// +// m_current_instruction_address += 4; +// if (!m_hit_branch_instruction) { +// block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); +// m_ir_builder->CreateBr(block); +// m_ir_builder->SetInsertPoint(block); +// } +// } +// } +// +// // If the function has an unknown block then add code to notify the tracer +// auto unknown_bb = GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function); +// if (!unknown_bb) { +// m_ir_builder->SetInsertPoint(unknown_bb); +// auto branch_type_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 1); +// for (auto i = pred_begin(unknown_bb); i != pred_end(unknown_bb); i++) { +// // We assume that the last but one instruction of the predecessor sets the branch type +// auto j = (*i)->rbegin(); +// j--; +// branch_type_i32->addIncoming(&(*j), *i); +// } +// +// //Call("NotifyBranch", &Tracer::NotifyBranch, *arg_i, +// // m_ir_builder->CreateZExtOrTrunc(branch_type_i32, m_ir_builder->getIntNTy(sizeof(Tracer::BranchType) * 8)), GetPc()); +// m_ir_builder->CreateRetVoid(); +// } +// +// auto ir_build_end = std::chrono::high_resolution_clock::now(); +// m_stats.ir_build_time += std::chrono::duration_cast(ir_build_end - compilation_start); +// +// // Optimize this function +// m_fpm->run(*m_current_function); +// auto optimize_end = std::chrono::high_resolution_clock::now(); +// m_stats.optimization_time += std::chrono::duration_cast(optimize_end - ir_build_end); +// +// // Translate to machine code +// MachineCodeInfo mci; +// m_execution_engine->runJITOnFunction(m_current_function, &mci); +// auto translate_end = std::chrono::high_resolution_clock::now(); +// m_stats.translation_time += std::chrono::duration_cast(translate_end - optimize_end); +// +// auto compilation_end = std::chrono::high_resolution_clock::now(); +// m_stats.total_time += std::chrono::duration_cast(compilation_end - compilation_start); +// +// //m_compiled[(CompiledCodeFragment)mci.address()] = m_current_function; +// //return (CompiledCodeFragment)mci.address(); +// return nullptr; +//} - auto compilation_start = std::chrono::high_resolution_clock::now(); - - // Create the function - m_current_function = (Function *)m_module->getOrInsertFunction(name, m_ir_builder->getVoidTy(), - m_ir_builder->getInt8PtrTy() /*ppu_state*/, - m_ir_builder->getInt8PtrTy() /*interpreter*/, - m_ir_builder->getInt8PtrTy() /*tracer*/, nullptr); - m_current_function->setCallingConv(CallingConv::X86_64_Win64); - auto arg_i = m_current_function->arg_begin(); - arg_i->setName("ppu_state"); - (++arg_i)->setName("interpreter"); - (++arg_i)->setName("tracer"); - - // Create the entry block - GetBasicBlockFromAddress(0, m_current_function, true); - - // Create basic blocks for each instruction - for (auto i = code_fragment.begin(); i != code_fragment.end(); i++) { - u32 address = i->first.address; - while (1) { - GetBasicBlockFromAddress(address, m_current_function, true); - - u32 instr = vm::read32(address); - if (IsBranchInstruction(instr)) { - break; - } - - address += 4; - } - } - - // Add code to notify the tracer about this function and branch to the first instruction - m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0, m_current_function)); - //Call("Tracer.Trace", &Tracer::Trace, *arg_i, - // m_ir_builder->getInt32(code_fragment[0].first.type == Function ? FunctionCall : Block), - // m_ir_builder->getInt32(code_fragment[0].first.address)); - m_ir_builder->CreateBr(GetBasicBlockFromAddress(code_fragment[0].first.address, m_current_function)); - - // Convert each block in this code fragment to LLVM IR - for (auto i = code_fragment.begin(); i != code_fragment.end(); i++) { - m_current_instruction_address = i->first.address; - m_current_block_next_blocks = &(i->second); - auto block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); - m_ir_builder->SetInsertPoint(block); - - if (i != code_fragment.begin() && i->first.type == BlockId::Type::FunctionCall) { - auto ordinal = RecompilationEngine::GetInstance()->GetOrdinal(i->first.address); - - } - - m_hit_branch_instruction = false; - while (!m_hit_branch_instruction) { - if (!block->getInstList().empty()) { - break; - } - - u32 instr = vm::read32(m_current_instruction_address); - Decode(instr); - - m_current_instruction_address += 4; - if (!m_hit_branch_instruction) { - block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); - m_ir_builder->CreateBr(block); - m_ir_builder->SetInsertPoint(block); - } - } - } - - // If the function has an unknown block then add code to notify the tracer - auto unknown_bb = GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function); - if (!unknown_bb) { - m_ir_builder->SetInsertPoint(unknown_bb); - auto branch_type_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 1); - for (auto i = pred_begin(unknown_bb); i != pred_end(unknown_bb); i++) { - // We assume that the last but one instruction of the predecessor sets the branch type - auto j = (*i)->rbegin(); - j--; - branch_type_i32->addIncoming(&(*j), *i); - } - - //Call("NotifyBranch", &Tracer::NotifyBranch, *arg_i, - // m_ir_builder->CreateZExtOrTrunc(branch_type_i32, m_ir_builder->getIntNTy(sizeof(Tracer::BranchType) * 8)), GetPc()); - m_ir_builder->CreateRetVoid(); - } - - auto ir_build_end = std::chrono::high_resolution_clock::now(); - m_stats.ir_build_time += std::chrono::duration_cast(ir_build_end - compilation_start); - - // Optimize this function - m_fpm->run(*m_current_function); - auto optimize_end = std::chrono::high_resolution_clock::now(); - m_stats.optimization_time += std::chrono::duration_cast(optimize_end - ir_build_end); - - // Translate to machine code - MachineCodeInfo mci; - m_execution_engine->runJITOnFunction(m_current_function, &mci); - auto translate_end = std::chrono::high_resolution_clock::now(); - m_stats.translation_time += std::chrono::duration_cast(translate_end - optimize_end); - - auto compilation_end = std::chrono::high_resolution_clock::now(); - m_stats.total_time += std::chrono::duration_cast(compilation_end - compilation_start); - - //m_compiled[(CompiledCodeFragment)mci.address()] = m_current_function; - //return (CompiledCodeFragment)mci.address(); - return nullptr; -} - -void Compiler::FreeCompiledCodeFragment(Executable compiled_code_fragment) { - //auto i = m_compiled.find(compiled_code_fragment); - //if (i != m_compiled.end()) { - // m_execution_engine->freeMachineCodeForFunction(i->second); - // i->second->eraseFromParent(); - //} -} +//void Compiler::FreeCompiledCodeFragment(Executable compiled_code_fragment) { +// //auto i = m_compiled.find(compiled_code_fragment); +// //if (i != m_compiled.end()) { +// // m_execution_engine->freeMachineCodeForFunction(i->second); +// // i->second->eraseFromParent(); +// //} +//} Compiler::Stats Compiler::GetStats() { return m_stats; @@ -4747,19 +4747,8 @@ void Compiler::InitRotateMask() { } } -std::mutex RecompilationEngine::s_mutex; -RecompilationEngine * RecompilationEngine::s_the_instance; - -RecompilationEngine::BlockEntry::BlockEntry() - : num_hits(0) - , is_compiled(false) { -} - -RecompilationEngine::BlockEntry::~BlockEntry() { - for (auto i = execution_traces.begin(); i != execution_traces.end(); i++) { - delete i->second; - } -} +std::mutex RecompilationEngine::s_mutex; +std::shared_ptr RecompilationEngine::s_the_instance = nullptr; RecompilationEngine::RecompilationEngine() : ThreadBase("PPU Recompilation Engine") { @@ -4815,10 +4804,7 @@ void RecompilationEngine::Task() { } } - auto block_i = ProcessExecutionTrace(execution_trace); - if (block_i != m_block_table.end()) { - CompileBlock(block_i); - } + ProcessExecutionTrace(*execution_trace); } // TODO: Reduce the priority of the recompilation engine thread @@ -4867,77 +4853,112 @@ void RecompilationEngine::Task() { //log_file << "\nLLVM IR:\n" << *m_module; LOG_NOTICE(PPU, "PPU LLVM Recompilation thread exiting."); + s_the_instance = nullptr; // Can cause deadlock if this is the last instance. Need to fix this. } -RecompilationEngine::BlockTable::iterator RecompilationEngine::ProcessExecutionTrace(ExecutionTrace * execution_trace) { - auto block_i = m_block_table.find(execution_trace->blocks[0].address); - if (block_i == m_block_table.end()) { - // New block - block_i = m_block_table.insert(m_block_table.end(), std::make_pair(execution_trace->blocks[0].address, BlockEntry())); +void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution_trace) { + auto execution_trace_id = GetExecutionTraceId(execution_trace); + auto processed_execution_trace_i = m_processed_execution_traces.find(execution_trace_id); + if (processed_execution_trace_i == m_processed_execution_traces.end()) { + std::vector tmp_block_list; + + auto split_trace = false; + auto block_i = m_block_table.end(); + auto trace_block_i = execution_trace.blocks.begin(); + for (; trace_block_i != execution_trace.blocks.end(); trace_block_i++) { + if (trace_block_i->type == BlockId::Type::Exit) { + block_i = m_block_table.end(); + split_trace = true; + } else if (block_i == m_block_table.end()) { + BlockEntry key(trace_block_i->address); + + block_i = m_block_table.find(&key); + if (block_i == m_block_table.end()) { + block_i = m_block_table.insert(m_block_table.end(), new BlockEntry(key.address)); + } + + (*block_i)->is_function_start = key.address == execution_trace.function_address; + tmp_block_list.push_back(*block_i); + } + + if (block_i != m_block_table.end()) { + BlockId next_block; + if (trace_block_i + 1 != execution_trace.blocks.end()) { + next_block = *(trace_block_i + 1); + } else { + if (!split_trace && execution_trace.type == ExecutionTrace::Type::Loop) { + next_block = *(execution_trace.blocks.begin()); + } else { + next_block.address = 0; + next_block.type = BlockId::Type::Exit; + } + } + + UpdateControlFlowGraph((*block_i)->cfg, *trace_block_i, next_block); + } + } + + processed_execution_trace_i = m_processed_execution_traces.insert(m_processed_execution_traces.end(), std::make_pair(execution_trace_id, std::move(tmp_block_list))); } - block_i->second.num_hits++; - auto execution_trace_id = GetExecutionTraceId(execution_trace); - auto execution_trace_i = block_i->second.execution_traces.find(execution_trace_id); - if (execution_trace_i == block_i->second.execution_traces.end()) { - block_i->second.execution_traces.insert(std::make_pair(execution_trace_id, execution_trace)); + for (auto i = processed_execution_trace_i->second.begin(); i != processed_execution_trace_i->second.end(); i++) { + if (!(*i)->is_compiled) { + (*i)->num_hits++; + if ((*i)->num_hits >= 1) { // TODO: Make this configurable + CompileBlock(*(*i), false); + (*i)->is_compiled = true; + } + } } - if (!block_i->second.is_compiled && block_i->second.num_hits > 1000) { // TODO: Make threshold configurable - return block_i; - } - - return m_block_table.end(); + std::remove_if(processed_execution_trace_i->second.begin(), processed_execution_trace_i->second.end(), [](const BlockEntry * b)->bool { return b->is_compiled; }); } -void RecompilationEngine::CompileBlock(BlockTable::iterator block_i) { - auto code_fragment = BuildCodeFragmentFromBlock(block_i->second, false); +void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, BlockId block, BlockId next_block) { + if (block.type == BlockId::Type::Exit && next_block.type == BlockId::Type::Exit) { + return; + } + + if (block.type == BlockId::Type::FunctionCall) { + return; + } + + auto block_i = std::find_if(cfg.begin(), cfg.end(), [&block](const ControlFlowGraph::value_type & v)->bool { return v.first == block.address; }); + if (block.type == BlockId::Type::Normal && block_i == cfg.end()) { + block_i = cfg.insert(cfg.end(), std::make_pair(block.address, std::vector())); + } + + if (block_i != cfg.end() && next_block.address && next_block.type != BlockId::Type::Exit) { + auto next_block_i = std::find(block_i->second.begin(), block_i->second.end(), next_block); + if (next_block_i == block_i->second.end()) { + block_i->second.push_back(next_block); + } + } } -CodeFragment RecompilationEngine::BuildCodeFragmentFromBlock(const BlockEntry & block_entry, bool force_inline) { - CodeFragment code_fragment; - //std::vector queue; +void RecompilationEngine::CompileBlock(const BlockEntry & block_entry, bool inline_referenced_blocks) { + std::string cfg_str; + for (auto i = block_entry.cfg.begin(); i != block_entry.cfg.end(); i++) { + cfg_str += fmt::Format("0x%08X ->", i->first); + for (auto j = i->second.begin(); j != i->second.end(); j++) { + cfg_str += " " + j->ToString(); + } - //queue.push_back(&block_entry); - //for (auto q = queue.begin(); q != queue.end(); q++) { - // for (auto i = (*q)->execution_traces.begin(); i != (*q)->execution_traces.end(); i++) { - // for (auto j = i->second->blocks.begin(); j != i->second->blocks.end(); j++) { - // auto k = std::find_if(code_fragment.begin(), code_fragment.end(), - // [&j](const CodeFragment::value_type & v)->bool { return v.first.address == j->address; }); - // if (k == code_fragment.end()) { - // code_fragment.push_back(std::make_pair(*j, std::vector())); - // k = code_fragment.end() - 1; - // } - - // if ((j + 1) != i->second->blocks.end()) { - // auto l = std::find(k->second.begin(), k->second.end(), *(j + 1)); - // if (l == k->second.end()) { - // k->second.push_back(*(j + 1)); - // } - // } - - // if (force_inline && j->type == BlockId::Type::Normal) { - // auto block_i = m_block_table.find(j->address); - // if (block_i != m_block_table.end()) { - // if (std::find(queue.begin(), queue.end(), block_i->second) == queue.end()) { - // queue.push_back(&(block_i->second)); - // } - // } - // } - // } - // } - //} - - return code_fragment; + if (i != (block_entry.cfg.end() - 1)) { + cfg_str += "\n"; + } + } + LOG_NOTICE(PPU, "Compile: %c:0x%08X, NumHits=%u\n%s", block_entry.is_function_start ? 'F' : 'N', block_entry.address, block_entry.num_hits, cfg_str.c_str()); } std::shared_ptr RecompilationEngine::GetInstance() { + std::lock_guard lock(s_mutex); + if (s_the_instance == nullptr) { - std::lock_guard lock(s_mutex); - s_the_instance = new RecompilationEngine(); + s_the_instance = std::shared_ptr(new RecompilationEngine()); } - return std::shared_ptr(s_the_instance); + return s_the_instance; } Tracer::Tracer() @@ -4986,10 +5007,9 @@ void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { function = m_stack.back(); m_stack.pop_back(); - execution_trace = new ExecutionTrace(); - execution_trace->type = ExecutionTrace::Type::Linear; - execution_trace->function_address = m_trace[function].address; - execution_trace->previous_block_address = 0; + execution_trace = new ExecutionTrace(); + execution_trace->type = ExecutionTrace::Type::Linear; + execution_trace->function_address = m_trace[function].address; std::copy(m_trace.begin() + function, m_trace.end(), std::back_inserter(execution_trace->blocks)); m_trace.erase(m_trace.begin() + function, m_trace.end()); break; @@ -4999,10 +5019,9 @@ void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { for (int i = (int)m_trace.size() - 1; i >= function; i--) { if (m_trace[i].address == arg1 && m_trace[i].type == BlockId::Type::Normal) { // Found a loop within the current function - execution_trace = new ExecutionTrace(); - execution_trace->type = ExecutionTrace::Type::Loop; - execution_trace->function_address = m_trace[function].address; - execution_trace->previous_block_address = i == function ? 0 : m_trace[i - 1].address; + execution_trace = new ExecutionTrace(); + execution_trace->type = ExecutionTrace::Type::Loop; + execution_trace->function_address = m_trace[function].address; std::copy(m_trace.begin() + i, m_trace.end(), std::back_inserter(execution_trace->blocks)); m_trace.erase(m_trace.begin() + i + 1, m_trace.end()); break; @@ -5021,6 +5040,18 @@ void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { block_id.address = arg1; block_id.type = BlockId::Type::Exit; m_trace.push_back(block_id); + + if (arg1 == 0) { + // Return from function + function = m_stack.back(); + m_stack.pop_back(); + + execution_trace = new ExecutionTrace(); + execution_trace->type = ExecutionTrace::Type::Linear; + execution_trace->function_address = m_trace[function].address; + std::copy(m_trace.begin() + function, m_trace.end(), std::back_inserter(execution_trace->blocks)); + m_trace.erase(m_trace.begin() + function, m_trace.end()); + } break; default: assert(0); @@ -5028,17 +5059,8 @@ void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { } if (execution_trace) { - auto s = fmt::Format("Trace: 0x%08X, 0x%08X, %s -> ", execution_trace->function_address, execution_trace->previous_block_address, - execution_trace->type == ExecutionTrace::Type::Loop ? "Loop" : "Linear"); - for (auto i = 0; i < execution_trace->blocks.size(); i++) {; - s += fmt::Format("%c:0x%08X ", - execution_trace->blocks[i].type == BlockId::Type::Normal ? 'N' : - execution_trace->blocks[i].type == BlockId::Type::FunctionCall ? 'F' : 'E', - execution_trace->blocks[i].address); - } - - LOG_NOTICE(PPU, s.c_str()); - //m_recompilation_engine->NotifyTrace(execution_trace); + LOG_NOTICE(PPU, "Trace: %s", execution_trace->ToString().c_str()); + m_recompilation_engine->NotifyTrace(execution_trace); } } @@ -5056,6 +5078,7 @@ ppu_recompiler_llvm::ExecutionEngine::ExecutionEngine(PPUThread & ppu) } ppu_recompiler_llvm::ExecutionEngine::~ExecutionEngine() { + } u8 ppu_recompiler_llvm::ExecutionEngine::DecodeMemory(const u32 address) { @@ -5107,44 +5130,53 @@ u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteFunction(ExecutionEngine * exec u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteTillReturn(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer) { bool terminate = false; + bool returned = false; - while (!terminate) { - auto instruction = re32(vm::get_ref(ppu_state->PC)); - execution_engine->m_decoder.Decode(instruction); - auto is_branch = ppu_state->m_is_branch; - ppu_state->NextPc(4); + while (!terminate && !Emu.IsStopped()) { + if (Emu.IsPaused()) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + continue; + } - if (is_branch) { - Executable executable; - auto branch_type = GetBranchTypeFromInstruction(instruction); + BranchType branch_type; + if (!returned) { + auto instruction = re32(vm::get_ref(ppu_state->PC)); + execution_engine->m_decoder.Decode(instruction); + branch_type = ppu_state->m_is_branch ? GetBranchTypeFromInstruction(instruction) : BranchType::NonBranch; + ppu_state->NextPc(4); + } else { + returned = false; + branch_type = BranchType::LocalBranch; + } - switch (branch_type) { - case BranchType::Return: - tracer->Trace(Tracer::TraceType::Return, 0, 0); - terminate = true; - break; - case BranchType::FunctionCall: - tracer->Trace(Tracer::TraceType::CallFunction, ppu_state->PC, 0); - executable = execution_engine->GetExecutable(ppu_state->PC, ExecuteFunction); - executable(execution_engine, ppu_state, interpreter, tracer); - // Fallthrough - case BranchType::LocalBranch: - tracer->Trace(Tracer::TraceType::EnterBlock, ppu_state->PC, 0); - executable = execution_engine->GetExecutable(ppu_state->PC, nullptr); - if (executable != nullptr) { - auto exit_block = executable(execution_engine, ppu_state, interpreter, tracer); - if (exit_block) { - tracer->Trace(Tracer::TraceType::ExitFromCompiledBlock, (u32)exit_block, 0); - } else { - tracer->Trace(Tracer::TraceType::Return, 0, 0); - terminate = true; - } + Executable executable; + switch (branch_type) { + case BranchType::Return: + tracer->Trace(Tracer::TraceType::Return, 0, 0); + terminate = true; + break; + case BranchType::FunctionCall: + tracer->Trace(Tracer::TraceType::CallFunction, ppu_state->PC, 0); + executable = execution_engine->GetExecutable(ppu_state->PC, ExecuteFunction); + executable(execution_engine, ppu_state, interpreter, tracer); + returned = true; + break; + case BranchType::LocalBranch: + tracer->Trace(Tracer::TraceType::EnterBlock, ppu_state->PC, 0); + executable = execution_engine->GetExecutable(ppu_state->PC, nullptr); + if (executable != nullptr) { + auto exit_block = executable(execution_engine, ppu_state, interpreter, tracer); + tracer->Trace(Tracer::TraceType::ExitFromCompiledBlock, (u32)exit_block, 0); + if (exit_block == 0) { + terminate = true; } - break; - default: - assert(0); - break; } + break; + case BranchType::NonBranch: + break; + default: + assert(0); + break; } } @@ -5170,6 +5202,12 @@ BranchType ppu_recompiler_llvm::GetBranchTypeFromInstruction(u32 instruction) { return type; } -ExecutionTraceId ppu_recompiler_llvm::GetExecutionTraceId(const ExecutionTrace * execution_trace) { - return 0; +ExecutionTraceId ppu_recompiler_llvm::GetExecutionTraceId(const ExecutionTrace & execution_trace) { + ExecutionTraceId id = 0; + + for (auto i = execution_trace.blocks.begin(); i != execution_trace.blocks.end(); i++) { + id = (id << 8) ^ ((u64)i->address << 32 | _byteswap_ulong((u64)i->address)); + } + + return id; } diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index c3e4511da2..5f27cd540e 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -16,6 +16,7 @@ namespace ppu_recompiler_llvm { class RecompilationEngine; class Tracer; class ExecutionEngine; + struct PPUState; enum class BranchType { NonBranch, @@ -35,8 +36,19 @@ namespace ppu_recompiler_llvm { Normal, Exit, } type; + + bool operator == (const BlockId & other) const { + return (address == other.address && type == other.type); + } + + std::string ToString() const { + return fmt::Format("%c:0x%08X", type == BlockId::Type::Normal ? 'N' : type == BlockId::Type::FunctionCall ? 'F' : 'E', address); + } }; + /// Control flow graph of a block. A list of (block address, list of next blocks) pairs. + typedef std::vector>> ControlFlowGraph; + /// Uniquely identifies an execution trace typedef u64 ExecutionTraceId; @@ -45,9 +57,6 @@ namespace ppu_recompiler_llvm { /// The function to which this trace belongs u32 function_address; - /// The address of the block that came before this trace - u32 previous_block_address; - /// Execution trace type enum class Type { Linear, @@ -56,16 +65,58 @@ namespace ppu_recompiler_llvm { /// Sequence of blocks enountered in this trace std::vector blocks; - }; - /// A fragment of PPU code. A list of (block, list of next blocks) pairs. - typedef std::vector>> CodeFragment; + std::string ToString() const { + auto s = fmt::Format("0x%08X %s ->", function_address, type == ExecutionTrace::Type::Loop ? "Loop" : "Linear"); + for (auto i = 0; i < blocks.size(); i++) { + s += " " + blocks[i].ToString(); + } + + return s; + } + }; /// Pointer to an executable typedef u64(*Executable)(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer); - struct PPUState; + /// An entry in the block table + struct BlockEntry { + /// Address of the block + u32 address; + /// Number of times this block was hit + u32 num_hits; + + /// The CFG for this block + ControlFlowGraph cfg; + + /// Indicates whether the block has been compiled or not + bool is_compiled; + + /// Indicates whether the block is the first block of a function or not + bool is_function_start; + + BlockEntry(u32 addr) + : address(addr) + , num_hits(0) + , is_compiled(false) { + } + + bool operator == (const BlockEntry & other) const { + return address == other.address; + } + }; +} + +namespace std { + template<> struct hash { + size_t operator()(const ppu_recompiler_llvm::BlockEntry * e) const { + return e->address; + } + }; +} + +namespace ppu_recompiler_llvm { /// PPU compiler that uses LLVM for code generation and optimization class Compiler : protected PPUOpcodes, protected PPCDecoder { public: @@ -97,10 +148,10 @@ namespace ppu_recompiler_llvm { Compiler & operator = (Compiler && other) = delete; /// Compile a code fragment and obtain an executable - Executable Compile(const std::string & name, const CodeFragment & code_fragment); + //Executable Compile(const std::string & name, const CodeFragment & code_fragment); /// Free an executable earilier obtained from the Compile function - void FreeCompiledCodeFragment(Executable executable); + //void FreeCompiledCodeFragment(Executable executable); /// Retrieve compiler stats Stats GetStats(); @@ -742,24 +793,6 @@ namespace ppu_recompiler_llvm { static std::shared_ptr GetInstance(); private: - /// An entry in the block table - struct BlockEntry { - BlockEntry(); - ~BlockEntry(); - - /// Number of times this block was hit - u32 num_hits; - - /// Execution traces starting at this block - std::unordered_map execution_traces; - - /// Indicates whether the block has been compiled or not - bool is_compiled; - }; - - /// Block table type. Key is block address. - typedef std::unordered_map BlockTable; - RecompilationEngine(); RecompilationEngine(const RecompilationEngine & other) = delete; @@ -768,23 +801,26 @@ namespace ppu_recompiler_llvm { RecompilationEngine & operator = (const RecompilationEngine & other) = delete; RecompilationEngine & operator = (RecompilationEngine && other) = delete; - /// Process an execution trace. Returns an iterator to a block table entry if the block should be compiled. - BlockTable::iterator ProcessExecutionTrace(ExecutionTrace * execution_trace); + /// Process an execution trace. + void ProcessExecutionTrace(const ExecutionTrace & execution_trace); + + /// Update a CFG + void UpdateControlFlowGraph(ControlFlowGraph & cfg, BlockId block, BlockId next_block); /// Compile a block - void CompileBlock(BlockTable::iterator block_i); - - /// Build code fragment from a block - CodeFragment BuildCodeFragmentFromBlock(const BlockEntry & block_entry, bool force_inline); + void CompileBlock(const BlockEntry & block_entry, bool inline_referenced_blocks); /// Lock for accessing m_pending_execution_traces. TODO: Eliminate this and use a lock-free queue. std::mutex m_pending_execution_traces_lock; - /// Queue of execution traces pending prcessing + /// Queue of execution traces pending processing std::list m_pending_execution_traces; /// Block table - BlockTable m_block_table; + std::unordered_set m_block_table; + + /// Execution traces that have been already encountered. Data is the list of all blocks that this trace includes. + std::unordered_map> m_processed_execution_traces; /// PPU Compiler Compiler m_compiler; @@ -793,7 +829,7 @@ namespace ppu_recompiler_llvm { static std::mutex s_mutex; /// The instance - static RecompilationEngine * s_the_instance; + static std::shared_ptr s_the_instance; }; /// Finds interesting execution sequences @@ -894,7 +930,7 @@ namespace ppu_recompiler_llvm { BranchType GetBranchTypeFromInstruction(u32 instruction); /// Get the execution trace id of an execution trace - ExecutionTraceId GetExecutionTraceId(const ExecutionTrace * execution_trace); + ExecutionTraceId GetExecutionTraceId(const ExecutionTrace & execution_trace); } #endif // PPU_LLVM_RECOMPILER_H diff --git a/rpcs3/stdafx.h b/rpcs3/stdafx.h index 7e67a2723a..88e422e2a3 100644 --- a/rpcs3/stdafx.h +++ b/rpcs3/stdafx.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "Utilities/GNU.h" From ee6a239679eade4c1f4211c5489d31c30e0fade7 Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Fri, 7 Nov 2014 16:24:59 +0530 Subject: [PATCH 05/12] Generate code from a CFG --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 657 ++++++++++++++++----------- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 192 +++++--- 2 files changed, 531 insertions(+), 318 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 6adfdaed00..912d457a1b 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -5,7 +5,6 @@ #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Host.h" #include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/raw_ostream.h" #include "llvm/CodeGen/MachineCodeInfo.h" #include "llvm/ExecutionEngine/GenericValue.h" #include "llvm/IR/Intrinsics.h" @@ -19,6 +18,7 @@ #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Vectorize.h" #include "llvm/MC/MCDisassembler.h" +#include "llvm/IR/Verifier.h" using namespace llvm; using namespace ppu_recompiler_llvm; @@ -26,7 +26,10 @@ using namespace ppu_recompiler_llvm; u64 Compiler::s_rotate_mask[64][64]; bool Compiler::s_rotate_mask_inited = false; -Compiler::Compiler() { +Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable default_function_executable, const Executable default_block_executable) + : m_recompilation_engine(recompilation_engine) + , m_default_function_executable(default_function_executable) + , m_default_block_executable(default_block_executable) { InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); InitializeNativeTargetDisassembler(); @@ -63,6 +66,13 @@ Compiler::Compiler() { m_fpm->add(createCFGSimplificationPass()); m_fpm->doInitialization(); + std::vector arg_types; + arg_types.push_back(m_ir_builder->getInt64Ty()->getPointerTo()); + arg_types.push_back(m_ir_builder->getInt64Ty()->getPointerTo()); + arg_types.push_back(m_ir_builder->getInt64Ty()->getPointerTo()); + arg_types.push_back(m_ir_builder->getInt64Ty()->getPointerTo()); + m_compiled_function_type = FunctionType::get(m_ir_builder->getInt64Ty(), arg_types, false); + if (!s_rotate_mask_inited) { InitRotateMask(); s_rotate_mask_inited = true; @@ -76,124 +86,188 @@ Compiler::~Compiler() { delete m_llvm_context; } -//Executable Compiler::Compile(const std::string & name, const CodeFragment & code_fragment) { -// assert(!name.empty()); -// assert(!code_fragment.empty()); -// -// auto compilation_start = std::chrono::high_resolution_clock::now(); -// -// // Create the function -// m_current_function = (Function *)m_module->getOrInsertFunction(name, m_ir_builder->getVoidTy(), -// m_ir_builder->getInt8PtrTy() /*ppu_state*/, -// m_ir_builder->getInt8PtrTy() /*interpreter*/, -// m_ir_builder->getInt8PtrTy() /*tracer*/, nullptr); -// m_current_function->setCallingConv(CallingConv::X86_64_Win64); -// auto arg_i = m_current_function->arg_begin(); -// arg_i->setName("ppu_state"); -// (++arg_i)->setName("interpreter"); -// (++arg_i)->setName("tracer"); -// -// // Create the entry block -// GetBasicBlockFromAddress(0, m_current_function, true); -// -// // Create basic blocks for each instruction -// for (auto i = code_fragment.begin(); i != code_fragment.end(); i++) { -// u32 address = i->first.address; -// while (1) { -// GetBasicBlockFromAddress(address, m_current_function, true); -// -// u32 instr = vm::read32(address); -// if (IsBranchInstruction(instr)) { -// break; -// } -// -// address += 4; -// } -// } -// -// // Add code to notify the tracer about this function and branch to the first instruction -// m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0, m_current_function)); -// //Call("Tracer.Trace", &Tracer::Trace, *arg_i, -// // m_ir_builder->getInt32(code_fragment[0].first.type == Function ? FunctionCall : Block), -// // m_ir_builder->getInt32(code_fragment[0].first.address)); -// m_ir_builder->CreateBr(GetBasicBlockFromAddress(code_fragment[0].first.address, m_current_function)); -// -// // Convert each block in this code fragment to LLVM IR -// for (auto i = code_fragment.begin(); i != code_fragment.end(); i++) { -// m_current_instruction_address = i->first.address; -// m_current_block_next_blocks = &(i->second); -// auto block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); -// m_ir_builder->SetInsertPoint(block); -// -// if (i != code_fragment.begin() && i->first.type == BlockId::Type::FunctionCall) { -// auto ordinal = RecompilationEngine::GetInstance()->GetOrdinal(i->first.address); -// -// } -// -// m_hit_branch_instruction = false; -// while (!m_hit_branch_instruction) { -// if (!block->getInstList().empty()) { -// break; -// } -// -// u32 instr = vm::read32(m_current_instruction_address); -// Decode(instr); -// -// m_current_instruction_address += 4; -// if (!m_hit_branch_instruction) { -// block = GetBasicBlockFromAddress(m_current_instruction_address, m_current_function); -// m_ir_builder->CreateBr(block); -// m_ir_builder->SetInsertPoint(block); -// } -// } -// } -// -// // If the function has an unknown block then add code to notify the tracer -// auto unknown_bb = GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function); -// if (!unknown_bb) { -// m_ir_builder->SetInsertPoint(unknown_bb); -// auto branch_type_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 1); -// for (auto i = pred_begin(unknown_bb); i != pred_end(unknown_bb); i++) { -// // We assume that the last but one instruction of the predecessor sets the branch type -// auto j = (*i)->rbegin(); -// j--; -// branch_type_i32->addIncoming(&(*j), *i); -// } -// -// //Call("NotifyBranch", &Tracer::NotifyBranch, *arg_i, -// // m_ir_builder->CreateZExtOrTrunc(branch_type_i32, m_ir_builder->getIntNTy(sizeof(Tracer::BranchType) * 8)), GetPc()); -// m_ir_builder->CreateRetVoid(); -// } -// -// auto ir_build_end = std::chrono::high_resolution_clock::now(); -// m_stats.ir_build_time += std::chrono::duration_cast(ir_build_end - compilation_start); -// -// // Optimize this function -// m_fpm->run(*m_current_function); -// auto optimize_end = std::chrono::high_resolution_clock::now(); -// m_stats.optimization_time += std::chrono::duration_cast(optimize_end - ir_build_end); -// -// // Translate to machine code -// MachineCodeInfo mci; -// m_execution_engine->runJITOnFunction(m_current_function, &mci); -// auto translate_end = std::chrono::high_resolution_clock::now(); -// m_stats.translation_time += std::chrono::duration_cast(translate_end - optimize_end); -// -// auto compilation_end = std::chrono::high_resolution_clock::now(); -// m_stats.total_time += std::chrono::duration_cast(compilation_end - compilation_start); -// -// //m_compiled[(CompiledCodeFragment)mci.address()] = m_current_function; -// //return (CompiledCodeFragment)mci.address(); -// return nullptr; -//} +Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & cfg, bool inline_all_blocks, bool generate_linkable_exits, bool generate_trace) { + assert(!name.empty()); + assert(!cfg.empty()); -//void Compiler::FreeCompiledCodeFragment(Executable compiled_code_fragment) { -// //auto i = m_compiled.find(compiled_code_fragment); -// //if (i != m_compiled.end()) { -// // m_execution_engine->freeMachineCodeForFunction(i->second); -// // i->second->eraseFromParent(); -// //} -//} + auto compilation_start = std::chrono::high_resolution_clock::now(); + + m_state.cfg = &cfg; + m_state.inline_all_blocks = inline_all_blocks; + m_state.generate_linkable_exits = generate_linkable_exits; + m_state.generate_trace = generate_trace; + m_state.address_to_block.clear(); + + // Create the function + m_state.function = (Function *)m_module->getOrInsertFunction(name, m_compiled_function_type); + m_state.function->setCallingConv(CallingConv::X86_64_Win64); + auto arg_i = m_state.function->arg_begin(); + arg_i->setName("execution_engine"); + m_state.args[CompileTaskState::Args::ExecutionEngine] = arg_i; + (++arg_i)->setName("state"); + m_state.args[CompileTaskState::Args::State] = arg_i; + (++arg_i)->setName("interpreter"); + m_state.args[CompileTaskState::Args::Interpreter] = arg_i; + (++arg_i)->setName("tracer"); + m_state.args[CompileTaskState::Args::Tracer] = arg_i; + + // Create the entry block and add code to branch to the first instruction + m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0)); + m_ir_builder->CreateBr(GetBasicBlockFromAddress(cfg[0].first)); + + // Convert each block in this CFG to LLVM IR + for (m_state.cfg_entry = cfg.begin(); m_state.cfg_entry != cfg.end(); m_state.cfg_entry++) { + m_state.current_instruction_address = m_state.cfg_entry->first; + auto block = GetBasicBlockFromAddress(m_state.current_instruction_address); + m_ir_builder->SetInsertPoint(block); + + m_state.hit_branch_instruction = false; + if (!inline_all_blocks && m_state.cfg_entry != cfg.begin()) { + // Use an already compiled implementation of this block if available + auto ordinal = m_recompilation_engine.GetOrdinal(m_state.cfg_entry->first); + if (ordinal != 0xFFFFFFFF) { + auto ret_i64 = IndirectCall(m_state.cfg_entry->first, false); + auto switch_instr = m_ir_builder->CreateSwitch(ret_i64, GetBasicBlockFromAddress(0xFFFFFFFF)); + for (auto i = m_state.cfg_entry->second.begin(); i != m_state.cfg_entry->second.end(); i++) { + switch_instr->addCase(m_ir_builder->getInt64(i->address), GetBasicBlockFromAddress(i->address)); + } + + m_state.hit_branch_instruction = true; + } + } + + while (!m_state.hit_branch_instruction) { + if (!block->getInstList().empty()) { + break; + } + + u32 instr = re32(vm::get_ref(m_state.current_instruction_address)); + Decode(instr); + + if (!m_state.hit_branch_instruction) { + m_state.current_instruction_address += 4; + block = GetBasicBlockFromAddress(m_state.current_instruction_address); + m_ir_builder->CreateBr(block); + m_ir_builder->SetInsertPoint(block); + } + } + } + + m_recompilation_engine.Log() << *m_state.function; + + auto default_exit_block_name = GetBasicBlockNameFromAddress(0xFFFFFFFF); + for (auto block_i = m_state.function->begin(); block_i != m_state.function->end(); block_i++) { + if (!block_i->getInstList().empty() || block_i->getName() == default_exit_block_name) { + continue; + } + + // An empty block. Generate exit logic. + m_recompilation_engine.Log() << "Empty block: " << block_i->getName() << "\n"; + + m_ir_builder->SetInsertPoint(block_i); + auto exit_block_i64 = m_ir_builder->CreatePHI(m_ir_builder->getInt64Ty(), 0); + for (auto i = pred_begin(block_i); i != pred_end(block_i); i++) { + auto pred_address = GetAddressFromBasicBlockName(block_i->getName()); + exit_block_i64->addIncoming(m_ir_builder->getInt64(m_state.address_to_block[pred_address]), *i); + } + + auto block_address = GetAddressFromBasicBlockName(block_i->getName()); + SetPc(m_ir_builder->getInt32(block_address)); + + if (generate_linkable_exits) { + if (generate_trace) { + Call("Tracer.Trace", &Tracer::Trace, m_ir_builder->getInt32((uint32_t)Tracer::TraceType::ExitFromCompiledFunction), + m_ir_builder->getInt32(cfg[0].first), m_ir_builder->CreateTrunc(exit_block_i64, m_ir_builder->getInt32Ty())); + } + + auto ret_i64 = IndirectCall(block_address, false); + auto cmp_i1 = m_ir_builder->CreateICmpNE(ret_i64, m_ir_builder->getInt64(0)); + auto then_bb = BasicBlock::Create(m_ir_builder->getContext()); + auto merge_bb = BasicBlock::Create(m_ir_builder->getContext()); + m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb); + + m_ir_builder->SetInsertPoint(then_bb); + IndirectCall(1, false); + m_ir_builder->CreateBr(merge_bb); + + m_ir_builder->SetInsertPoint(merge_bb); + m_ir_builder->CreateRet(m_ir_builder->getInt64(0)); + } else { + m_ir_builder->CreateRet(exit_block_i64); + } + } + + m_recompilation_engine.Log() << *m_state.function; + + // If the function has a default exit block then generate code for it + auto default_exit_bb = GetBasicBlockFromAddress(0xFFFFFFFF, false); + if (default_exit_bb) { + m_ir_builder->SetInsertPoint(default_exit_bb); + auto exit_block_i64 = m_ir_builder->CreatePHI(m_ir_builder->getInt64Ty(), 1); + for (auto i = pred_begin(default_exit_bb); i != pred_end(default_exit_bb); i++) { + // the last but one instruction of the predecessor sets the exit block address + auto j = (*i)->rbegin(); + j++; + exit_block_i64->addIncoming(&(*j), *i); + } + + if (generate_linkable_exits) { + auto cmp_i1 = m_ir_builder->CreateICmpNE(exit_block_i64, m_ir_builder->getInt64(0)); + auto then_bb = BasicBlock::Create(m_ir_builder->getContext()); + auto merge_bb = BasicBlock::Create(m_ir_builder->getContext()); + m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb); + + m_ir_builder->SetInsertPoint(then_bb); + if (generate_trace) { + Call("Tracer.Trace", &Tracer::Trace, m_ir_builder->getInt32((uint32_t)Tracer::TraceType::ExitFromCompiledFunction), + m_ir_builder->getInt32(cfg[0].first), m_ir_builder->CreateTrunc(exit_block_i64, m_ir_builder->getInt32Ty())); + } + + IndirectCall(1, false); + m_ir_builder->CreateBr(merge_bb); + + m_ir_builder->SetInsertPoint(merge_bb); + m_ir_builder->CreateRet(m_ir_builder->getInt64(0)); + } else { + m_ir_builder->CreateRet(exit_block_i64); + } + } + + m_recompilation_engine.Log() << *m_state.function; + + std::string verify; + raw_string_ostream verify_ostream(verify); + if (verifyFunction(*m_state.function, &verify_ostream)) { + m_recompilation_engine.Log() << "Verification failed: " << verify << "\n"; + } + + auto ir_build_end = std::chrono::high_resolution_clock::now(); + m_stats.ir_build_time += std::chrono::duration_cast(ir_build_end - compilation_start); + + // Optimize this function + //m_fpm->run(*m_state.function); + auto optimize_end = std::chrono::high_resolution_clock::now(); + m_stats.optimization_time += std::chrono::duration_cast(optimize_end - ir_build_end); + + // Translate to machine code + MachineCodeInfo mci; + m_execution_engine->runJITOnFunction(m_state.function, &mci); + auto translate_end = std::chrono::high_resolution_clock::now(); + m_stats.translation_time += std::chrono::duration_cast(translate_end - optimize_end); + + auto compilation_end = std::chrono::high_resolution_clock::now(); + m_stats.total_time += std::chrono::duration_cast(compilation_end - compilation_start); + + return (Executable)mci.address(); +} + +void Compiler::FreeExecutable(const std::string & name) { + auto function = m_module->getFunction(name); + if (function) { + m_execution_engine->freeMachineCodeForFunction(function); + function->eraseFromParent(); + } +} Compiler::Stats Compiler::GetStats() { return m_stats; @@ -1500,7 +1574,7 @@ void Compiler::ADDIS(u32 rd, u32 ra, s32 simm16) { } void Compiler::BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) { - auto target_i64 = m_ir_builder->getInt64(branchTarget(aa ? 0 : m_current_instruction_address, bd)); + auto target_i64 = m_ir_builder->getInt64(branchTarget(aa ? 0 : m_state.current_instruction_address, bd)); CreateBranch(CheckBranchCondition(bo, bi), target_i64, lk ? true : false); //m_hit_branch_instruction = true; //SetPc(m_ir_builder->getInt32(m_current_instruction_address)); @@ -1514,7 +1588,7 @@ void Compiler::SC(u32 sc_code) { } void Compiler::B(s32 ll, u32 aa, u32 lk) { - auto target_i64 = m_ir_builder->getInt64(branchTarget(aa ? 0 : m_current_instruction_address, ll)); + auto target_i64 = m_ir_builder->getInt64(branchTarget(aa ? 0 : m_state.current_instruction_address, ll)); CreateBranch(nullptr, target_i64, lk ? true : false); //m_hit_branch_instruction = true; //SetPc(m_ir_builder->getInt32(m_current_instruction_address)); @@ -2009,13 +2083,13 @@ void Compiler::LWARX(u32 rd, u32 ra, u32 rb) { addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); } - auto resv_addr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, R_ADDR)); + auto resv_addr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, R_ADDR)); auto resv_addr_i64_ptr = m_ir_builder->CreateBitCast(resv_addr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(addr_i64, resv_addr_i64_ptr, 8); auto resv_val_i32 = ReadMemory(addr_i64, 32, 4, false, false); auto resv_val_i64 = m_ir_builder->CreateZExt(resv_val_i32, m_ir_builder->getInt64Ty()); - auto resv_val_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, R_VALUE)); + auto resv_val_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, R_VALUE)); auto resv_val_i64_ptr = m_ir_builder->CreateBitCast(resv_val_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(resv_val_i64, resv_val_i64_ptr, 8); @@ -2208,6 +2282,7 @@ void Compiler::LDUX(u32 rd, u32 ra, u32 rb) { void Compiler::DCBST(u32 ra, u32 rb) { // TODO: Implement this + m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::donothing)); //InterpreterCall("DCBST", &PPUInterpreter::DCBST, ra, rb); } @@ -2301,12 +2376,12 @@ void Compiler::LDARX(u32 rd, u32 ra, u32 rb) { addr_i64 = m_ir_builder->CreateAdd(ra_i64, addr_i64); } - auto resv_addr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, R_ADDR)); + auto resv_addr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, R_ADDR)); auto resv_addr_i64_ptr = m_ir_builder->CreateBitCast(resv_addr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(addr_i64, resv_addr_i64_ptr, 8); auto resv_val_i64 = ReadMemory(addr_i64, 64, 8, false); - auto resv_val_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, R_VALUE)); + auto resv_val_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, R_VALUE)); auto resv_val_i64_ptr = m_ir_builder->CreateBitCast(resv_val_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(resv_val_i64, resv_val_i64_ptr, 8); @@ -2317,6 +2392,7 @@ void Compiler::LDARX(u32 rd, u32 ra, u32 rb) { void Compiler::DCBF(u32 ra, u32 rb) { // TODO: Implement this + m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::donothing)); //InterpreterCall("DCBF", &PPUInterpreter::DCBF, ra, rb); } @@ -2596,6 +2672,7 @@ void Compiler::MULLW(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { void Compiler::DCBTST(u32 ra, u32 rb, u32 th) { // TODO: Implement this + m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::donothing)); //InterpreterCall("DCBTST", &PPUInterpreter::DCBTST, ra, rb, th); } @@ -2627,6 +2704,7 @@ void Compiler::ADD(u32 rd, u32 ra, u32 rb, u32 oe, bool rc) { void Compiler::DCBT(u32 ra, u32 rb, u32 th) { // TODO: Implement this using prefetch + m_ir_builder->CreateCall(Intrinsic::getDeclaration(m_module, Intrinsic::donothing)); //InterpreterCall("DCBT", &PPUInterpreter::DCBT, ra, rb, th); } @@ -4070,24 +4148,40 @@ void Compiler::UNK(const u32 code, const u32 opcode, const u32 gcode) { //InterpreterCall("UNK", &PPUInterpreter::UNK, code, opcode, gcode); } -std::string Compiler::GetBasicBlockNameFromAddress(u32 address) { +std::string Compiler::GetBasicBlockNameFromAddress(u32 address, const std::string & suffix) { std::string name; if (address == 0) { name = "entry"; } else if (address == 0xFFFFFFFF) { - name = "unknown"; + name = "default_exit"; } else { - name = fmt::Format("instr_0x%X", address); + name = fmt::Format("instr_0x%08X", address); + } + + if (suffix != "") { + name += "_" + suffix; } return name; } -BasicBlock * Compiler::GetBasicBlockFromAddress(u32 address, Function * function, bool create_if_not_exist) { - auto block_name = GetBasicBlockNameFromAddress(address); +u32 Compiler::GetAddressFromBasicBlockName(const std::string & name) { + if (name.compare(0, 6, "instr_") == 0) { + return strtoul(name.c_str() + 6, nullptr, 0); + } else if (name == GetBasicBlockNameFromAddress(0)) { + return 0; + } else if (name == GetBasicBlockNameFromAddress(0xFFFFFFFF)) { + return 0xFFFFFFFF; + } + + return 0; +} + +BasicBlock * Compiler::GetBasicBlockFromAddress(u32 address, const std::string & suffix, bool create_if_not_exist) { + auto block_name = GetBasicBlockNameFromAddress(address, suffix); BasicBlock * block = nullptr; - for (auto i = function->getBasicBlockList().begin(); i != function->getBasicBlockList().end(); i++) { + for (auto i = m_state.function->getBasicBlockList().begin(); i != m_state.function->getBasicBlockList().end(); i++) { if (i->getName() == block_name) { block = &(*i); break; @@ -4095,29 +4189,12 @@ BasicBlock * Compiler::GetBasicBlockFromAddress(u32 address, Function * function } if (!block && create_if_not_exist) { - block = BasicBlock::Create(m_ir_builder->getContext(), block_name, function); + block = BasicBlock::Create(m_ir_builder->getContext(), block_name, m_state.function); } return block; } -Value * Compiler::GetPPUStateArg() { - return m_current_function->arg_begin(); -} - -Value * Compiler::GetInterpreterArg() { - auto i = m_current_function->arg_begin(); - i++; - return i; -} - -Value * Compiler::GetTracerArg() { - auto i = m_current_function->arg_begin(); - i++; - i++; - return i; -} - Value * Compiler::GetBit(Value * val, u32 n) { Value * bit; @@ -4231,33 +4308,33 @@ Value * Compiler::SetNibble(Value * val, u32 n, Value * b0, Value * b1, Value * } Value * Compiler::GetPc() { - auto pc_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, PC)); + auto pc_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, PC)); auto pc_i32_ptr = m_ir_builder->CreateBitCast(pc_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(pc_i32_ptr, 4); } void Compiler::SetPc(Value * val_ix) { - auto pc_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, PC)); + auto pc_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, PC)); auto pc_i32_ptr = m_ir_builder->CreateBitCast(pc_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); auto val_i32 = m_ir_builder->CreateZExtOrTrunc(val_ix, m_ir_builder->getInt32Ty()); m_ir_builder->CreateAlignedStore(val_i32, pc_i32_ptr, 4); } Value * Compiler::GetGpr(u32 r, u32 num_bits) { - auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, GPR[r])); + auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, GPR[r])); auto r_ix_ptr = m_ir_builder->CreateBitCast(r_i8_ptr, m_ir_builder->getIntNTy(num_bits)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(r_ix_ptr, 8); } void Compiler::SetGpr(u32 r, Value * val_x64) { - auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, GPR[r])); + auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, GPR[r])); auto r_i64_ptr = m_ir_builder->CreateBitCast(r_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); m_ir_builder->CreateAlignedStore(val_i64, r_i64_ptr, 8); } Value * Compiler::GetCr() { - auto cr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, CR)); + auto cr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, CR)); auto cr_i32_ptr = m_ir_builder->CreateBitCast(cr_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(cr_i32_ptr, 4); } @@ -4268,7 +4345,7 @@ Value * Compiler::GetCrField(u32 n) { void Compiler::SetCr(Value * val_x32) { auto val_i32 = m_ir_builder->CreateBitCast(val_x32, m_ir_builder->getInt32Ty()); - auto cr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, CR)); + auto cr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, CR)); auto cr_i32_ptr = m_ir_builder->CreateBitCast(cr_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i32, cr_i32_ptr, 4); } @@ -4310,33 +4387,33 @@ void Compiler::SetCr6AfterVectorCompare(u32 vr) { } Value * Compiler::GetLr() { - auto lr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, LR)); + auto lr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, LR)); auto lr_i64_ptr = m_ir_builder->CreateBitCast(lr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(lr_i64_ptr, 8); } void Compiler::SetLr(Value * val_x64) { auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); - auto lr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, LR)); + auto lr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, LR)); auto lr_i64_ptr = m_ir_builder->CreateBitCast(lr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i64, lr_i64_ptr, 8); } Value * Compiler::GetCtr() { - auto ctr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, CTR)); + auto ctr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, CTR)); auto ctr_i64_ptr = m_ir_builder->CreateBitCast(ctr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(ctr_i64_ptr, 8); } void Compiler::SetCtr(Value * val_x64) { auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); - auto ctr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, CTR)); + auto ctr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, CTR)); auto ctr_i64_ptr = m_ir_builder->CreateBitCast(ctr_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i64, ctr_i64_ptr, 8); } Value * Compiler::GetXer() { - auto xer_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, XER)); + auto xer_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, XER)); auto xer_i64_ptr = m_ir_builder->CreateBitCast(xer_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(xer_i64_ptr, 8); } @@ -4351,7 +4428,7 @@ Value * Compiler::GetXerSo() { void Compiler::SetXer(Value * val_x64) { auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); - auto xer_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, XER)); + auto xer_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, XER)); auto xer_i64_ptr = m_ir_builder->CreateBitCast(xer_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i64, xer_i64_ptr, 8); } @@ -4369,20 +4446,20 @@ void Compiler::SetXerSo(Value * so) { } Value * Compiler::GetUsprg0() { - auto usrpg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, USPRG0)); + auto usrpg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, USPRG0)); auto usprg0_i64_ptr = m_ir_builder->CreateBitCast(usrpg0_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(usprg0_i64_ptr, 8); } void Compiler::SetUsprg0(Value * val_x64) { auto val_i64 = m_ir_builder->CreateBitCast(val_x64, m_ir_builder->getInt64Ty()); - auto usprg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, USPRG0)); + auto usprg0_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, USPRG0)); auto usprg0_i64_ptr = m_ir_builder->CreateBitCast(usprg0_i8_ptr, m_ir_builder->getInt64Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i64, usprg0_i64_ptr, 8); } Value * Compiler::GetFpr(u32 r, u32 bits, bool as_int) { - auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, FPR[r])); + auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, FPR[r])); if (!as_int) { auto r_f64_ptr = m_ir_builder->CreateBitCast(r_i8_ptr, m_ir_builder->getDoubleTy()->getPointerTo()); auto r_f64 = m_ir_builder->CreateAlignedLoad(r_f64_ptr, 8); @@ -4403,7 +4480,7 @@ Value * Compiler::GetFpr(u32 r, u32 bits, bool as_int) { } void Compiler::SetFpr(u32 r, Value * val) { - auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, FPR[r])); + auto r_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, FPR[r])); auto r_f64_ptr = m_ir_builder->CreateBitCast(r_i8_ptr, m_ir_builder->getDoubleTy()->getPointerTo()); Value* val_f64; @@ -4420,47 +4497,47 @@ void Compiler::SetFpr(u32 r, Value * val) { } Value * Compiler::GetVscr() { - auto vscr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VSCR)); + auto vscr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VSCR)); auto vscr_i32_ptr = m_ir_builder->CreateBitCast(vscr_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vscr_i32_ptr, 4); } void Compiler::SetVscr(Value * val_x32) { auto val_i32 = m_ir_builder->CreateBitCast(val_x32, m_ir_builder->getInt32Ty()); - auto vscr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VSCR)); + auto vscr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VSCR)); auto vscr_i32_ptr = m_ir_builder->CreateBitCast(vscr_i8_ptr, m_ir_builder->getInt32Ty()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_i32, vscr_i32_ptr, 4); } Value * Compiler::GetVr(u32 vr) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vr_i128_ptr, 16); } Value * Compiler::GetVrAsIntVec(u32 vr, u32 vec_elt_num_bits) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); auto vr_vec_ptr = m_ir_builder->CreateBitCast(vr_i128_ptr, VectorType::get(m_ir_builder->getIntNTy(vec_elt_num_bits), 128 / vec_elt_num_bits)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vr_vec_ptr, 16); } Value * Compiler::GetVrAsFloatVec(u32 vr) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); auto vr_v4f32_ptr = m_ir_builder->CreateBitCast(vr_i128_ptr, VectorType::get(m_ir_builder->getFloatTy(), 4)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vr_v4f32_ptr, 16); } Value * Compiler::GetVrAsDoubleVec(u32 vr) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); auto vr_v2f64_ptr = m_ir_builder->CreateBitCast(vr_i128_ptr, VectorType::get(m_ir_builder->getDoubleTy(), 2)->getPointerTo()); return m_ir_builder->CreateAlignedLoad(vr_v2f64_ptr, 16); } void Compiler::SetVr(u32 vr, Value * val_x128) { - auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(GetPPUStateArg(), (unsigned int)offsetof(PPUThread, VPR[vr])); + auto vr_i8_ptr = m_ir_builder->CreateConstGEP1_32(m_state.args[CompileTaskState::Args::State], (unsigned int)offsetof(PPUThread, VPR[vr])); auto vr_i128_ptr = m_ir_builder->CreateBitCast(vr_i8_ptr, m_ir_builder->getIntNTy(128)->getPointerTo()); auto val_i128 = m_ir_builder->CreateBitCast(val_x128, m_ir_builder->getIntNTy(128)); m_ir_builder->CreateAlignedStore(val_i128, vr_i128_ptr, 16); @@ -4510,7 +4587,7 @@ Value * Compiler::CheckBranchCondition(u32 bo, u32 bi) { void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool lk, bool target_is_lr) { if (lk) { - SetLr(m_ir_builder->getInt64(m_current_instruction_address + 4)); + SetLr(m_ir_builder->getInt64(m_state.current_instruction_address + 4)); } auto current_block = m_ir_builder->GetInsertBlock(); @@ -4519,49 +4596,69 @@ void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool if (dyn_cast(target_i64)) { // Target address is an immediate value. u32 target_address = (u32)(dyn_cast(target_i64)->getLimitedValue()); - target_block = GetBasicBlockFromAddress(target_address, m_current_function); - if (!target_block) { - target_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function); - m_ir_builder->SetInsertPoint(target_block); + if (lk) { + // Function call + if (cmp_i1) { // There is no need to create a new block for an unconditional jump + target_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function); + m_ir_builder->SetInsertPoint(target_block); + } + SetPc(target_i64); - m_ir_builder->CreateBr(GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function, true)); + IndirectCall(target_address, true); + m_ir_builder->CreateBr(GetBasicBlockFromAddress(m_state.current_instruction_address + 4)); + } else { + // Local branch + target_block = GetBasicBlockFromAddress(target_address); } } else { // Target address is in a register - target_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function); - m_ir_builder->SetInsertPoint(target_block); + if (cmp_i1) { // There is no need to create a new block for an unconditional jump + target_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function); + m_ir_builder->SetInsertPoint(target_block); + } + SetPc(target_i64); if (target_is_lr && !lk) { // Return from function call - m_ir_builder->CreateRetVoid(); + m_ir_builder->CreateRet(m_ir_builder->getInt64(0)); + } else if (lk) { + auto next_block = GetBasicBlockFromAddress(m_state.current_instruction_address + 4); + auto call_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function); + m_ir_builder->CreateOr(m_ir_builder->getInt64(m_state.cfg_entry->first), (uint64_t)0); + auto switch_instr = m_ir_builder->CreateSwitch(target_i64, call_block); + m_ir_builder->SetInsertPoint(call_block); + IndirectCall(0, true); + m_ir_builder->CreateBr(next_block); + for (auto i = m_state.cfg_entry->second.begin(); i != m_state.cfg_entry->second.end(); i++) { + call_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function); + m_ir_builder->SetInsertPoint(call_block); + IndirectCall(i->address, true); + m_ir_builder->CreateBr(next_block); + switch_instr->addCase(m_ir_builder->getInt32(i->address), call_block); + } } else { - auto switch_instr = m_ir_builder->CreateSwitch(target_i64, GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function, true)); - for (auto i = m_current_block_next_blocks->begin(); i != m_current_block_next_blocks->end(); i++) { - switch_instr->addCase(m_ir_builder->getInt32(i->address), GetBasicBlockFromAddress(i->address, m_current_function)); + auto switch_instr = m_ir_builder->CreateSwitch(target_i64, GetBasicBlockFromAddress(0xFFFFFFFF)); + for (auto i = m_state.cfg_entry->second.begin(); i != m_state.cfg_entry->second.end(); i++) { + switch_instr->addCase(m_ir_builder->getInt64(i->address), GetBasicBlockFromAddress(i->address)); } } } if (cmp_i1) { // Conditional branch - auto next_block = GetBasicBlockFromAddress(m_current_instruction_address + 4, m_current_function); - if (!next_block) { - next_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function); - m_ir_builder->SetInsertPoint(next_block); - SetPc(m_ir_builder->getInt32(m_current_instruction_address + 4)); - m_ir_builder->CreateBr(GetBasicBlockFromAddress(0xFFFFFFFF, m_current_function, true)); - } - + auto next_block = GetBasicBlockFromAddress(m_state.current_instruction_address + 4); m_ir_builder->SetInsertPoint(current_block); m_ir_builder->CreateCondBr(cmp_i1, target_block, next_block); } else { // Unconditional branch - m_ir_builder->SetInsertPoint(current_block); - m_ir_builder->CreateBr(target_block); + if (target_block) { + m_ir_builder->SetInsertPoint(current_block); + m_ir_builder->CreateBr(target_block); + } } - m_hit_branch_instruction = true; + m_state.hit_branch_instruction = true; } Value * Compiler::ReadMemory(Value * addr_i64, u32 bits, u32 alignment, bool bswap, bool could_be_mmio) { @@ -4576,10 +4673,10 @@ Value * Compiler::ReadMemory(Value * addr_i64, u32 bits, u32 alignment, bool bsw return val_ix; } else { BasicBlock * next_block = nullptr; - for (auto i = m_current_function->begin(); i != m_current_function->end(); i++) { + for (auto i = m_state.function->begin(); i != m_state.function->end(); i++) { if (&(*i) == m_ir_builder->GetInsertBlock()) { i++; - if (i != m_current_function->end()) { + if (i != m_state.function->end()) { next_block = &(*i); } @@ -4588,9 +4685,9 @@ Value * Compiler::ReadMemory(Value * addr_i64, u32 bits, u32 alignment, bool bsw } auto cmp_i1 = m_ir_builder->CreateICmpULT(addr_i64, m_ir_builder->getInt64(RAW_SPU_BASE_ADDR)); - auto then_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function, next_block); - auto else_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function, next_block); - auto merge_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function, next_block); + auto then_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); + auto else_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); + auto merge_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); m_ir_builder->CreateCondBr(cmp_i1, then_bb, else_bb); m_ir_builder->SetInsertPoint(then_bb); @@ -4630,10 +4727,10 @@ void Compiler::WriteMemory(Value * addr_i64, Value * val_ix, u32 alignment, bool m_ir_builder->CreateAlignedStore(val_ix, eaddr_ix_ptr, alignment); } else { BasicBlock * next_block = nullptr; - for (auto i = m_current_function->begin(); i != m_current_function->end(); i++) { + for (auto i = m_state.function->begin(); i != m_state.function->end(); i++) { if (&(*i) == m_ir_builder->GetInsertBlock()) { i++; - if (i != m_current_function->end()) { + if (i != m_state.function->end()) { next_block = &(*i); } @@ -4642,9 +4739,9 @@ void Compiler::WriteMemory(Value * addr_i64, Value * val_ix, u32 alignment, bool } auto cmp_i1 = m_ir_builder->CreateICmpULT(addr_i64, m_ir_builder->getInt64(RAW_SPU_BASE_ADDR)); - auto then_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function, next_block); - auto else_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function, next_block); - auto merge_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_current_function, next_block); + auto then_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); + auto else_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); + auto merge_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); m_ir_builder->CreateCondBr(cmp_i1, then_bb, else_bb); m_ir_builder->SetInsertPoint(then_bb); @@ -4680,7 +4777,7 @@ Value * Compiler::InterpreterCall(const char * name, Func function, Args... args i->second++; - return Call(name, function, GetInterpreterArg(), m_ir_builder->getInt32(args)...); + return Call(name, function, m_state.args[CompileTaskState::Args::Interpreter], m_ir_builder->getInt32(args)...); } template @@ -4723,19 +4820,14 @@ Value * Compiler::Call(const char * name, Func function, Args... args) { return m_ir_builder->CreateCall(fn, fn_args); } -bool Compiler::IsBranchInstruction(u32 instruction) { - bool is_branch = false; - u32 field1 = instruction >> 26; - if (field1 == 16 || field1 == 18) { - is_branch = true; - } else if (field1 == 19) { - u32 field2 = (instruction >> 1) & 0x3FF; - if (field2 == 16 || field2 == 528) { - is_branch = true; - } - } - - return is_branch; +llvm::Value * Compiler::IndirectCall(u32 address, bool is_function) { + auto ordinal = m_recompilation_engine.AllocateOrdinal(address, is_function); + auto executable_addr_i64 = m_ir_builder->getInt64(m_recompilation_engine.GetAddressOfExecutableLookup() + (ordinal * sizeof(u64))); + auto executable_ptr = m_ir_builder->CreateIntToPtr(executable_addr_i64, m_compiled_function_type); + return m_ir_builder->CreateCall4(executable_ptr, m_state.args[CompileTaskState::Args::ExecutionEngine], + m_state.args[CompileTaskState::Args::State], + m_state.args[CompileTaskState::Args::Interpreter], + m_state.args[CompileTaskState::Args::Tracer]); } void Compiler::InitRotateMask() { @@ -4751,20 +4843,50 @@ std::mutex RecompilationEngine::s_mutex; std::shared_ptr RecompilationEngine::s_the_instance = nullptr; RecompilationEngine::RecompilationEngine() - : ThreadBase("PPU Recompilation Engine") { - Start(); + : ThreadBase("PPU Recompilation Engine") + , m_next_ordinal(0) + , m_compiler(*this, ExecutionEngine::ExecuteFunction, ExecutionEngine::ExecuteTillReturn) + , m_log("PPULLVMRecompiler.log", std::string(), sys::fs::F_Text) { + m_log.SetUnbuffered(); } RecompilationEngine::~RecompilationEngine() { Stop(); } -u32 RecompilationEngine::GetOrdinal(u32 address) { - return 0xFFFFFFFF; +u32 RecompilationEngine::AllocateOrdinal(u32 address, bool is_function) { + std::lock_guard lock(m_address_to_ordinal_lock); + + auto i = m_address_to_ordinal.find(address); + if (i == m_address_to_ordinal.end()) { + assert(m_next_ordinal < (sizeof(m_executable_lookup) / sizeof(m_executable_lookup[0]))); + + m_executable_lookup[m_next_ordinal] = is_function ? ExecutionEngine::ExecuteFunction : ExecutionEngine::ExecuteTillReturn; + std::atomic_thread_fence(std::memory_order_release); + i = m_address_to_ordinal.insert(m_address_to_ordinal.end(), std::make_pair(address, m_next_ordinal++)); + } + + return i->second; } -Executable * RecompilationEngine::GetExecutableLookup() const { - return nullptr; +u32 RecompilationEngine::GetOrdinal(u32 address) const { + std::lock_guard lock(m_address_to_ordinal_lock); + + auto i = m_address_to_ordinal.find(address); + if (i != m_address_to_ordinal.end()) { + return i->second; + } else { + return 0xFFFFFFFF; + } +} + +const Executable RecompilationEngine::GetExecutable(u32 ordinal) const { + std::atomic_thread_fence(std::memory_order_acquire); + return m_executable_lookup[ordinal]; +} + +u64 RecompilationEngine::GetAddressOfExecutableLookup() const { + return (u64)m_executable_lookup; } void RecompilationEngine::NotifyTrace(ExecutionTrace * execution_trace) { @@ -4773,10 +4895,18 @@ void RecompilationEngine::NotifyTrace(ExecutionTrace * execution_trace) { m_pending_execution_traces.push_back(execution_trace); } + if (!IsAlive()) { + Start(); + } + Notify(); // TODO: Increase the priority of the recompilation engine thread } +raw_fd_ostream & RecompilationEngine::Log() { + return m_log; +} + void RecompilationEngine::Task() { std::chrono::nanoseconds idling_time(0); @@ -4805,6 +4935,7 @@ void RecompilationEngine::Task() { } ProcessExecutionTrace(*execution_trace); + delete execution_trace; } // TODO: Reduce the priority of the recompilation engine thread @@ -4819,18 +4950,16 @@ void RecompilationEngine::Task() { auto total_time = std::chrono::duration_cast(end - start); auto compiler_stats = m_compiler.GetStats(); - std::string error; - raw_fd_ostream log_file("PPULLVMRecompiler.log", error, sys::fs::F_Text); - log_file << "Total time = " << total_time.count() / 1000000 << "ms\n"; - log_file << " Time spent compiling = " << compiler_stats.total_time.count() / 1000000 << "ms\n"; - log_file << " Time spent building IR = " << compiler_stats.ir_build_time.count() / 1000000 << "ms\n"; - log_file << " Time spent optimizing = " << compiler_stats.optimization_time.count() / 1000000 << "ms\n"; - log_file << " Time spent translating = " << compiler_stats.translation_time.count() / 1000000 << "ms\n"; - log_file << " Time spent idling = " << idling_time.count() / 1000000 << "ms\n"; - log_file << " Time spent doing misc tasks = " << (total_time.count() - idling_time.count() - compiler_stats.total_time.count()) / 1000000 << "ms\n"; - log_file << "\nInterpreter fallback stats:\n"; + Log() << "Total time = " << total_time.count() / 1000000 << "ms\n"; + Log() << " Time spent compiling = " << compiler_stats.total_time.count() / 1000000 << "ms\n"; + Log() << " Time spent building IR = " << compiler_stats.ir_build_time.count() / 1000000 << "ms\n"; + Log() << " Time spent optimizing = " << compiler_stats.optimization_time.count() / 1000000 << "ms\n"; + Log() << " Time spent translating = " << compiler_stats.translation_time.count() / 1000000 << "ms\n"; + Log() << " Time spent idling = " << idling_time.count() / 1000000 << "ms\n"; + Log() << " Time spent doing misc tasks = " << (total_time.count() - idling_time.count() - compiler_stats.total_time.count()) / 1000000 << "ms\n"; + Log() << "\nInterpreter fallback stats:\n"; for (auto i = compiler_stats.interpreter_fallback_stats.begin(); i != compiler_stats.interpreter_fallback_stats.end(); i++) { - log_file << i->first << " = " << i->second << "\n"; + Log() << i->first << " = " << i->second << "\n"; } //log_file << "\nDisassembly:\n"; @@ -4860,6 +4989,8 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution auto execution_trace_id = GetExecutionTraceId(execution_trace); auto processed_execution_trace_i = m_processed_execution_traces.find(execution_trace_id); if (processed_execution_trace_i == m_processed_execution_traces.end()) { + Log() << "Trace: " << execution_trace.ToString() << "\n"; + std::vector tmp_block_list; auto split_trace = false; @@ -4936,19 +5067,15 @@ void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, BlockId } } -void RecompilationEngine::CompileBlock(const BlockEntry & block_entry, bool inline_referenced_blocks) { - std::string cfg_str; - for (auto i = block_entry.cfg.begin(); i != block_entry.cfg.end(); i++) { - cfg_str += fmt::Format("0x%08X ->", i->first); - for (auto j = i->second.begin(); j != i->second.end(); j++) { - cfg_str += " " + j->ToString(); - } +void RecompilationEngine::CompileBlock(BlockEntry & block_entry, bool inline_referenced_blocks) { + Log() << "Compile: " << block_entry.ToString() << "\n"; - if (i != (block_entry.cfg.end() - 1)) { - cfg_str += "\n"; - } - } - LOG_NOTICE(PPU, "Compile: %c:0x%08X, NumHits=%u\n%s", block_entry.is_function_start ? 'F' : 'N', block_entry.address, block_entry.num_hits, cfg_str.c_str()); + auto ordinal = AllocateOrdinal(block_entry.address, block_entry.is_function_start); + auto executable = m_compiler.Compile(fmt::Format("fn_0x%08X_%u", block_entry.address, block_entry.revision++), block_entry.cfg, + block_entry.is_function_start ? false : true /*inline_all_blocks*/, + block_entry.is_function_start ? true : false /*generate_linkable_exits*/, + block_entry.is_function_start ? true : false /*generate_trace*/); + m_executable_lookup[ordinal] = executable; } std::shared_ptr RecompilationEngine::GetInstance() { @@ -4984,11 +5111,8 @@ void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { m_trace.push_back(block_id); break; case TraceType::EnterFunction: - // arg1 is address. - block_id.address = arg1; - block_id.type = BlockId::Type::Normal; + // No args used m_stack.push_back((u32)m_trace.size()); - m_trace.push_back(block_id); break; case TraceType::ExitFromCompiledFunction: // arg1 is address of function. @@ -5059,7 +5183,6 @@ void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { } if (execution_trace) { - LOG_NOTICE(PPU, "Trace: %s", execution_trace->ToString().c_str()); m_recompilation_engine->NotifyTrace(execution_trace); } } @@ -5074,7 +5197,6 @@ ppu_recompiler_llvm::ExecutionEngine::ExecutionEngine(PPUThread & ppu) , m_decoder(m_interpreter) , m_last_cache_clear_time(std::chrono::high_resolution_clock::now()) , m_recompilation_engine(RecompilationEngine::GetInstance()) { - m_executable_lookup = m_recompilation_engine->GetExecutableLookup(); } ppu_recompiler_llvm::ExecutionEngine::~ExecutionEngine() { @@ -5086,7 +5208,7 @@ u8 ppu_recompiler_llvm::ExecutionEngine::DecodeMemory(const u32 address) { return 0; } -void ppu_recompiler_llvm::ExecutionEngine::RemoveUnusedEntriesFromCache() { +void ppu_recompiler_llvm::ExecutionEngine::RemoveUnusedEntriesFromCache() const { auto now = std::chrono::high_resolution_clock::now(); if (std::chrono::duration_cast(now - m_last_cache_clear_time).count() > 10000) { for (auto i = m_address_to_ordinal.begin(); i != m_address_to_ordinal.end();) { @@ -5103,7 +5225,7 @@ void ppu_recompiler_llvm::ExecutionEngine::RemoveUnusedEntriesFromCache() { } } -Executable ppu_recompiler_llvm::ExecutionEngine::GetExecutable(u32 address, Executable default_executable) { +Executable ppu_recompiler_llvm::ExecutionEngine::GetExecutable(u32 address, Executable default_executable) const { // Find the ordinal for the specified address and insert it to the cache auto i = m_address_to_ordinal.find(address); if (i == m_address_to_ordinal.end()) { @@ -5116,7 +5238,7 @@ Executable ppu_recompiler_llvm::ExecutionEngine::GetExecutable(u32 address, Exec Executable executable = default_executable; if (i != m_address_to_ordinal.end()) { i->second.second++; - executable = m_executable_lookup[i->second.first]; + executable = m_recompilation_engine->GetExecutable(i->second.first); } RemoveUnusedEntriesFromCache(); @@ -5124,13 +5246,13 @@ Executable ppu_recompiler_llvm::ExecutionEngine::GetExecutable(u32 address, Exec } u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteFunction(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer) { - tracer->Trace(Tracer::TraceType::EnterFunction, ppu_state->PC, 0); + tracer->Trace(Tracer::TraceType::EnterFunction, 0, 0); return ExecuteTillReturn(execution_engine, ppu_state, interpreter, tracer); } u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteTillReturn(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer) { bool terminate = false; - bool returned = false; + bool returned = true; while (!terminate && !Emu.IsStopped()) { if (Emu.IsPaused()) { @@ -5183,6 +5305,23 @@ u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteTillReturn(ExecutionEngine * ex return 0; } +std::string ppu_recompiler_llvm::ControlFlowGraphToString(const ControlFlowGraph & cfg) { + std::string s; + + for (auto i = cfg.begin(); i != cfg.end(); i++) { + s += fmt::Format("0x%08X ->", i->first); + for (auto j = i->second.begin(); j != i->second.end(); j++) { + s += " " + j->ToString(); + } + + if (i != (cfg.end() - 1)) { + s += "\n"; + } + } + + return s; +} + BranchType ppu_recompiler_llvm::GetBranchTypeFromInstruction(u32 instruction) { auto type = BranchType::NonBranch; auto field1 = instruction >> 26; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index 5f27cd540e..856ecddc72 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -4,6 +4,7 @@ #include "Emu/Cell/PPUDecoder.h" #include "Emu/Cell/PPUThread.h" #include "Emu/Cell/PPUInterpreter.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" @@ -49,6 +50,9 @@ namespace ppu_recompiler_llvm { /// Control flow graph of a block. A list of (block address, list of next blocks) pairs. typedef std::vector>> ControlFlowGraph; + /// Get a string representation of a ControlFlowGraph + std::string ControlFlowGraphToString(const ControlFlowGraph & cfg); + /// Uniquely identifies an execution trace typedef u64 ExecutionTraceId; @@ -87,6 +91,9 @@ namespace ppu_recompiler_llvm { /// Number of times this block was hit u32 num_hits; + /// The current revision number of this function + u32 revision; + /// The CFG for this block ControlFlowGraph cfg; @@ -99,9 +106,15 @@ namespace ppu_recompiler_llvm { BlockEntry(u32 addr) : address(addr) , num_hits(0) + , revision(0) , is_compiled(false) { } + std::string ToString() const { + return fmt::Format("%c:0x%08X, NumHits=%u, IsCompiled=%c\n%s", is_function_start ? 'F' : 'N', address, num_hits, + is_compiled ? 'Y' : 'N', ControlFlowGraphToString(cfg).c_str()); + } + bool operator == (const BlockEntry & other) const { return address == other.address; } @@ -137,7 +150,7 @@ namespace ppu_recompiler_llvm { std::map interpreter_fallback_stats; }; - Compiler(); + Compiler(RecompilationEngine & recompilation_engine, const Executable default_function_executable, const Executable default_block_executable); Compiler(const Compiler & other) = delete; Compiler(Compiler && other) = delete; @@ -147,11 +160,11 @@ namespace ppu_recompiler_llvm { Compiler & operator = (const Compiler & other) = delete; Compiler & operator = (Compiler && other) = delete; - /// Compile a code fragment and obtain an executable - //Executable Compile(const std::string & name, const CodeFragment & code_fragment); + /// Compile a code fragment described by a cfg and return an executable + Executable Compile(const std::string & name, const ControlFlowGraph & cfg, bool inline_all_blocks, bool generate_linkable_exits, bool generate_trace); - /// Free an executable earilier obtained from the Compile function - //void FreeCompiledCodeFragment(Executable executable); + /// Free an executable earilier obtained via a call to Compile + void FreeExecutable(const std::string & name); /// Retrieve compiler stats Stats GetStats(); @@ -563,6 +576,58 @@ namespace ppu_recompiler_llvm { void UNK(const u32 code, const u32 opcode, const u32 gcode) override; private: + /// State of a compilation task + struct CompileTaskState { + enum Args { + ExecutionEngine, + State, + Interpreter, + Tracer, + MaxArgs, + }; + + /// The LLVM function for the compilation task + llvm::Function * function; + + /// Args of the LLVM function + llvm::Value * args[MaxArgs]; + + /// The CFG being compiled + const ControlFlowGraph * cfg; + + /// The current entry of the CFG being compiled + ControlFlowGraph::const_iterator cfg_entry; + + /// Address of the current instruction being compiled + u32 current_instruction_address; + + /// Map from an address to the address of the block that it belongs to + std::unordered_map address_to_block; + + /// A flag used to detect branch instructions. + /// This is set to false at the start of compilation of a block. + /// When a branch instruction is encountered, this is set to true by the decode function. + bool hit_branch_instruction; + + /// Indicates whether a block should be inlined even if an already compiled version of the block exists + bool inline_all_blocks; + + /// Create code such that exit points can be linked to other blocks + bool generate_linkable_exits; + + /// Notify the tracer upon exit + bool generate_trace; + }; + + /// Recompilation engine + RecompilationEngine & m_recompilation_engine; + + /// The executable that will be called to process unknown functions + const Executable m_default_function_executable; + + /// The executable that will be called to process unknown blocks + const Executable m_default_block_executable; + /// LLVM context llvm::LLVMContext * m_llvm_context; @@ -578,37 +643,23 @@ namespace ppu_recompiler_llvm { /// Function pass manager llvm::FunctionPassManager * m_fpm; - /// A flag used to detect branch instructions. - /// This is set to false at the start of compilation of a block. - /// When a branch instruction is encountered, this is set to true by the decode function. - bool m_hit_branch_instruction; + /// LLVM type of the functions genreated by the compiler + llvm::FunctionType * m_compiled_function_type; - /// The function being compiled - llvm::Function * m_current_function; - - /// The list of next blocks for the current block - const std::vector * m_current_block_next_blocks; - - /// Address of the current instruction - u32 m_current_instruction_address; + /// State of the current compilation task + CompileTaskState m_state; /// Compiler stats Stats m_stats; /// Get the name of the basic block for the specified address - std::string GetBasicBlockNameFromAddress(u32 address); + std::string GetBasicBlockNameFromAddress(u32 address, const std::string & suffix = ""); + + /// Get the address of a basic block from its name + u32 GetAddressFromBasicBlockName(const std::string & name); /// Get the basic block in for the specified address. - llvm::BasicBlock * GetBasicBlockFromAddress(u32 address, llvm::Function * function, bool create_if_not_exist = false); - - /// Get PPU state pointer argument - llvm::Value * GetPPUStateArg(); - - /// Get interpreter pointer argument - llvm::Value * GetInterpreterArg(); - - /// Get tracer pointer argument - llvm::Value * GetTracerArg(); + llvm::BasicBlock * GetBasicBlockFromAddress(u32 address, const std::string & suffix = "", bool create_if_not_exist = true); /// Get a bit llvm::Value * GetBit(llvm::Value * val, u32 n); @@ -754,8 +805,8 @@ namespace ppu_recompiler_llvm { template llvm::Value * Call(const char * name, Func function, Args... args); - /// Tests if the instruction is a branch instruction or not - bool IsBranchInstruction(u32 instruction); + /// Indirect call + llvm::Value * IndirectCall(u32 address, bool is_function); /// Test an instruction against the interpreter template @@ -778,21 +829,61 @@ namespace ppu_recompiler_llvm { public: virtual ~RecompilationEngine(); - /// Get the ordinal for the specified address - u32 GetOrdinal(u32 address); + /// Allocate an ordinal + u32 AllocateOrdinal(u32 address, bool is_function); - /// Get the executable lookup table - Executable * GetExecutableLookup() const; + /// Get the ordinal for the specified address + u32 GetOrdinal(u32 address) const; + + /// Get the executable specified by the ordinal + const Executable GetExecutable(u32 ordinal) const; + + /// Get the address of the executable lookup + u64 GetAddressOfExecutableLookup() const; /// Notify the recompilation engine about a newly detected trace. It takes ownership of the trace. void NotifyTrace(ExecutionTrace * execution_trace); + /// Log + llvm::raw_fd_ostream & Log(); + void Task() override; /// Get a pointer to the instance of this class static std::shared_ptr GetInstance(); private: + /// Lock for accessing m_pending_execution_traces. TODO: Eliminate this and use a lock-free queue. + std::mutex m_pending_execution_traces_lock; + + /// Queue of execution traces pending processing + std::list m_pending_execution_traces; + + /// Block table + std::unordered_set m_block_table; + + /// Execution traces that have been already encountered. Data is the list of all blocks that this trace includes. + std::unordered_map> m_processed_execution_traces; + + /// Lock for accessing m_address_to_ordinal. + // TODO: Make this a RW lock + mutable std::mutex m_address_to_ordinal_lock; + + /// Mapping from address to ordinal + std::unordered_map m_address_to_ordinal; + + /// Next ordinal to allocate + u32 m_next_ordinal; + + /// PPU Compiler + Compiler m_compiler; + + /// Log + llvm::raw_fd_ostream m_log; + + /// Executable lookup table + Executable m_executable_lookup[10000]; // TODO: Adjust size + RecompilationEngine(); RecompilationEngine(const RecompilationEngine & other) = delete; @@ -808,22 +899,7 @@ namespace ppu_recompiler_llvm { void UpdateControlFlowGraph(ControlFlowGraph & cfg, BlockId block, BlockId next_block); /// Compile a block - void CompileBlock(const BlockEntry & block_entry, bool inline_referenced_blocks); - - /// Lock for accessing m_pending_execution_traces. TODO: Eliminate this and use a lock-free queue. - std::mutex m_pending_execution_traces_lock; - - /// Queue of execution traces pending processing - std::list m_pending_execution_traces; - - /// Block table - std::unordered_set m_block_table; - - /// Execution traces that have been already encountered. Data is the list of all blocks that this trace includes. - std::unordered_map> m_processed_execution_traces; - - /// PPU Compiler - Compiler m_compiler; + void CompileBlock(BlockEntry & block_entry, bool inline_referenced_blocks); /// Mutex used to prevent multiple creation static std::mutex s_mutex; @@ -836,7 +912,7 @@ namespace ppu_recompiler_llvm { class Tracer { public: /// Trace type - enum class TraceType { + enum class TraceType : u32 { CallFunction, EnterFunction, ExitFromCompiledFunction, @@ -874,6 +950,7 @@ namespace ppu_recompiler_llvm { /// PPU execution engine class ExecutionEngine : public CPUDecoder { + friend class RecompilationEngine; public: ExecutionEngine(PPUThread & ppu); ExecutionEngine() = delete; @@ -901,23 +978,20 @@ namespace ppu_recompiler_llvm { /// Execution tracer Tracer m_tracer; - /// Executable lookup table - Executable * m_executable_lookup; - /// The time at which the m_address_to_ordinal cache was last cleared - std::chrono::high_resolution_clock::time_point m_last_cache_clear_time; + mutable std::chrono::high_resolution_clock::time_point m_last_cache_clear_time; - /// Address to ordinal lookup. Key is address. Data is the pair (ordinal, times hit). - std::unordered_map> m_address_to_ordinal; + /// Address to ordinal cahce. Key is address. Data is the pair (ordinal, times hit). + mutable std::unordered_map> m_address_to_ordinal; /// Recompilation engine std::shared_ptr m_recompilation_engine; /// Remove unused entries from the m_address_to_ordinal cache - void RemoveUnusedEntriesFromCache(); + void RemoveUnusedEntriesFromCache() const; /// Get the executable for the specified address - Executable GetExecutable(u32 address, Executable default_executable); + Executable GetExecutable(u32 address, Executable default_executable) const; /// Execute a function static u64 ExecuteFunction(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer); From 34e34910fd4cac5340396c6d8d1ee3fe7fd11f3c Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Sat, 8 Nov 2014 09:52:23 +0530 Subject: [PATCH 06/12] Integrated execution engine, tracer, recompilation engine and compiler --- rpcs3/Emu/CPU/CPUThread.h | 2 + rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 504 ++++++++++++--------------- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 270 +++++++++----- 3 files changed, 425 insertions(+), 351 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index 3acb2c12b3..d15af1431d 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -113,6 +113,8 @@ public: return temp; } + CPUDecoder * GetDecoder() { return m_dec; }; + public: u32 entry; u32 PC; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 912d457a1b..1805a9a095 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -26,10 +26,8 @@ using namespace ppu_recompiler_llvm; u64 Compiler::s_rotate_mask[64][64]; bool Compiler::s_rotate_mask_inited = false; -Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable default_function_executable, const Executable default_block_executable) - : m_recompilation_engine(recompilation_engine) - , m_default_function_executable(default_function_executable) - , m_default_block_executable(default_block_executable) { +Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable unknown_function, const Executable unknown_block) + : m_recompilation_engine(recompilation_engine) { InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); InitializeNativeTargetDisassembler(); @@ -67,11 +65,18 @@ Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable m_fpm->doInitialization(); std::vector arg_types; - arg_types.push_back(m_ir_builder->getInt64Ty()->getPointerTo()); - arg_types.push_back(m_ir_builder->getInt64Ty()->getPointerTo()); - arg_types.push_back(m_ir_builder->getInt64Ty()->getPointerTo()); - arg_types.push_back(m_ir_builder->getInt64Ty()->getPointerTo()); - m_compiled_function_type = FunctionType::get(m_ir_builder->getInt64Ty(), arg_types, false); + arg_types.push_back(m_ir_builder->getInt8PtrTy()); + arg_types.push_back(m_ir_builder->getInt8PtrTy()); + arg_types.push_back(m_ir_builder->getInt64Ty()); + m_compiled_function_type = FunctionType::get(m_ir_builder->getInt32Ty(), arg_types, false); + + m_unknown_function = (Function *)m_module->getOrInsertFunction("unknown_function", m_compiled_function_type); + m_unknown_function->setCallingConv(CallingConv::X86_64_Win64); + m_execution_engine->addGlobalMapping(m_unknown_function, unknown_function); + + m_unknown_block = (Function *)m_module->getOrInsertFunction("unknown_block", m_compiled_function_type); + m_unknown_block->setCallingConv(CallingConv::X86_64_Win64); + m_execution_engine->addGlobalMapping(m_unknown_block, unknown_block); if (!s_rotate_mask_inited) { InitRotateMask(); @@ -86,50 +91,53 @@ Compiler::~Compiler() { delete m_llvm_context; } -Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & cfg, bool inline_all_blocks, bool generate_linkable_exits, bool generate_trace) { - assert(!name.empty()); - assert(!cfg.empty()); - +Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & cfg, bool inline_all, bool generate_linkable_exits) { auto compilation_start = std::chrono::high_resolution_clock::now(); m_state.cfg = &cfg; - m_state.inline_all_blocks = inline_all_blocks; + m_state.inline_all = inline_all; m_state.generate_linkable_exits = generate_linkable_exits; - m_state.generate_trace = generate_trace; - m_state.address_to_block.clear(); // Create the function m_state.function = (Function *)m_module->getOrInsertFunction(name, m_compiled_function_type); m_state.function->setCallingConv(CallingConv::X86_64_Win64); auto arg_i = m_state.function->arg_begin(); - arg_i->setName("execution_engine"); - m_state.args[CompileTaskState::Args::ExecutionEngine] = arg_i; - (++arg_i)->setName("state"); + arg_i->setName("ppu_state"); m_state.args[CompileTaskState::Args::State] = arg_i; (++arg_i)->setName("interpreter"); m_state.args[CompileTaskState::Args::Interpreter] = arg_i; - (++arg_i)->setName("tracer"); - m_state.args[CompileTaskState::Args::Tracer] = arg_i; + (++arg_i)->setName("context"); + m_state.args[CompileTaskState::Args::Context] = arg_i; // Create the entry block and add code to branch to the first instruction m_ir_builder->SetInsertPoint(GetBasicBlockFromAddress(0)); - m_ir_builder->CreateBr(GetBasicBlockFromAddress(cfg[0].first)); + m_ir_builder->CreateBr(GetBasicBlockFromAddress(cfg.start_address)); - // Convert each block in this CFG to LLVM IR - for (m_state.cfg_entry = cfg.begin(); m_state.cfg_entry != cfg.end(); m_state.cfg_entry++) { - m_state.current_instruction_address = m_state.cfg_entry->first; - auto block = GetBasicBlockFromAddress(m_state.current_instruction_address); - m_ir_builder->SetInsertPoint(block); + // Convert each instruction in the CFG to LLVM IR + std::vector exit_instr_list; + for (auto instr_i = cfg.instruction_addresses.begin(); instr_i != cfg.instruction_addresses.end(); instr_i++) { + m_state.current_instruction_address = *instr_i; + auto instr_bb = GetBasicBlockFromAddress(m_state.current_instruction_address); + m_ir_builder->SetInsertPoint(instr_bb); m_state.hit_branch_instruction = false; - if (!inline_all_blocks && m_state.cfg_entry != cfg.begin()) { + if (!inline_all && instr_i != cfg.instruction_addresses.begin()) { // Use an already compiled implementation of this block if available - auto ordinal = m_recompilation_engine.GetOrdinal(m_state.cfg_entry->first); + auto ordinal = m_recompilation_engine.GetOrdinal(*instr_i); if (ordinal != 0xFFFFFFFF) { - auto ret_i64 = IndirectCall(m_state.cfg_entry->first, false); - auto switch_instr = m_ir_builder->CreateSwitch(ret_i64, GetBasicBlockFromAddress(0xFFFFFFFF)); - for (auto i = m_state.cfg_entry->second.begin(); i != m_state.cfg_entry->second.end(); i++) { - switch_instr->addCase(m_ir_builder->getInt64(i->address), GetBasicBlockFromAddress(i->address)); + auto exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0); + exit_instr_list.push_back(exit_instr_i32); + + auto context_i64 = m_ir_builder->CreateZExt(exit_instr_i32, m_ir_builder->getInt64Ty()); + context_i64 = m_ir_builder->CreateOr(context_i64, (u64)cfg.function_address << 32); + auto ret_i32 = IndirectCall(*instr_i, context_i64, false); + + auto switch_instr = m_ir_builder->CreateSwitch(ret_i32, GetBasicBlockFromAddress(0xFFFFFFFF)); + auto branch_i = cfg.branches.find(*instr_i); + if (branch_i != cfg.branches.end()) { + for (auto next_instr_i = branch_i->second.begin(); next_instr_i != branch_i->second.end(); next_instr_i++) { + switch_instr->addCase(m_ir_builder->getInt32(*next_instr_i), GetBasicBlockFromAddress(*next_instr_i)); + } } m_state.hit_branch_instruction = true; @@ -137,7 +145,7 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & } while (!m_state.hit_branch_instruction) { - if (!block->getInstList().empty()) { + if (!instr_bb->getInstList().empty()) { break; } @@ -146,54 +154,51 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & if (!m_state.hit_branch_instruction) { m_state.current_instruction_address += 4; - block = GetBasicBlockFromAddress(m_state.current_instruction_address); - m_ir_builder->CreateBr(block); - m_ir_builder->SetInsertPoint(block); + instr_bb = GetBasicBlockFromAddress(m_state.current_instruction_address); + m_ir_builder->CreateBr(instr_bb); + m_ir_builder->SetInsertPoint(instr_bb); } } } m_recompilation_engine.Log() << *m_state.function; + // Generate exit logic for all empty blocks auto default_exit_block_name = GetBasicBlockNameFromAddress(0xFFFFFFFF); for (auto block_i = m_state.function->begin(); block_i != m_state.function->end(); block_i++) { if (!block_i->getInstList().empty() || block_i->getName() == default_exit_block_name) { continue; } - // An empty block. Generate exit logic. + // Found an empty block m_recompilation_engine.Log() << "Empty block: " << block_i->getName() << "\n"; m_ir_builder->SetInsertPoint(block_i); - auto exit_block_i64 = m_ir_builder->CreatePHI(m_ir_builder->getInt64Ty(), 0); - for (auto i = pred_begin(block_i); i != pred_end(block_i); i++) { - auto pred_address = GetAddressFromBasicBlockName(block_i->getName()); - exit_block_i64->addIncoming(m_ir_builder->getInt64(m_state.address_to_block[pred_address]), *i); - } + auto exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0); + exit_instr_list.push_back(exit_instr_i32); - auto block_address = GetAddressFromBasicBlockName(block_i->getName()); - SetPc(m_ir_builder->getInt32(block_address)); + auto instr_address = GetAddressFromBasicBlockName(block_i->getName()); + SetPc(m_ir_builder->getInt32(instr_address)); if (generate_linkable_exits) { - if (generate_trace) { - Call("Tracer.Trace", &Tracer::Trace, m_ir_builder->getInt32((uint32_t)Tracer::TraceType::ExitFromCompiledFunction), - m_ir_builder->getInt32(cfg[0].first), m_ir_builder->CreateTrunc(exit_block_i64, m_ir_builder->getInt32Ty())); - } - - auto ret_i64 = IndirectCall(block_address, false); - auto cmp_i1 = m_ir_builder->CreateICmpNE(ret_i64, m_ir_builder->getInt64(0)); - auto then_bb = BasicBlock::Create(m_ir_builder->getContext()); - auto merge_bb = BasicBlock::Create(m_ir_builder->getContext()); + auto context_i64 = m_ir_builder->CreateZExt(exit_instr_i32, m_ir_builder->getInt64Ty()); + context_i64 = m_ir_builder->CreateOr(context_i64, (u64)cfg.function_address << 32); + auto ret_i32 = IndirectCall(instr_address, context_i64, false); + auto cmp_i1 = m_ir_builder->CreateICmpNE(ret_i32, m_ir_builder->getInt32(0)); + auto then_bb = GetBasicBlockFromAddress(instr_address, "then"); + auto merge_bb = GetBasicBlockFromAddress(instr_address, "merge"); m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb); m_ir_builder->SetInsertPoint(then_bb); - IndirectCall(1, false); + context_i64 = m_ir_builder->CreateZExt(ret_i32, m_ir_builder->getInt64Ty()); + context_i64 = m_ir_builder->CreateOr(context_i64, (u64)cfg.function_address << 32); + m_ir_builder->CreateCall3(m_unknown_block, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], context_i64); m_ir_builder->CreateBr(merge_bb); m_ir_builder->SetInsertPoint(merge_bb); - m_ir_builder->CreateRet(m_ir_builder->getInt64(0)); + m_ir_builder->CreateRet(m_ir_builder->getInt32(0)); } else { - m_ir_builder->CreateRet(exit_block_i64); + m_ir_builder->CreateRet(exit_instr_i32); } } @@ -203,33 +208,34 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & auto default_exit_bb = GetBasicBlockFromAddress(0xFFFFFFFF, false); if (default_exit_bb) { m_ir_builder->SetInsertPoint(default_exit_bb); - auto exit_block_i64 = m_ir_builder->CreatePHI(m_ir_builder->getInt64Ty(), 1); - for (auto i = pred_begin(default_exit_bb); i != pred_end(default_exit_bb); i++) { - // the last but one instruction of the predecessor sets the exit block address - auto j = (*i)->rbegin(); - j++; - exit_block_i64->addIncoming(&(*j), *i); - } + auto exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0); + exit_instr_list.push_back(exit_instr_i32); if (generate_linkable_exits) { - auto cmp_i1 = m_ir_builder->CreateICmpNE(exit_block_i64, m_ir_builder->getInt64(0)); - auto then_bb = BasicBlock::Create(m_ir_builder->getContext()); - auto merge_bb = BasicBlock::Create(m_ir_builder->getContext()); + auto cmp_i1 = m_ir_builder->CreateICmpNE(exit_instr_i32, m_ir_builder->getInt32(0)); + auto then_bb = GetBasicBlockFromAddress(0xFFFFFFFF, "then"); + auto merge_bb = GetBasicBlockFromAddress(0xFFFFFFFF, "merge"); m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb); m_ir_builder->SetInsertPoint(then_bb); - if (generate_trace) { - Call("Tracer.Trace", &Tracer::Trace, m_ir_builder->getInt32((uint32_t)Tracer::TraceType::ExitFromCompiledFunction), - m_ir_builder->getInt32(cfg[0].first), m_ir_builder->CreateTrunc(exit_block_i64, m_ir_builder->getInt32Ty())); - } - - IndirectCall(1, false); + auto context_i64 = m_ir_builder->CreateZExt(exit_instr_i32, m_ir_builder->getInt64Ty()); + context_i64 = m_ir_builder->CreateOr(context_i64, (u64)cfg.function_address << 32); + m_ir_builder->CreateCall3(m_unknown_block, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], context_i64); m_ir_builder->CreateBr(merge_bb); m_ir_builder->SetInsertPoint(merge_bb); - m_ir_builder->CreateRet(m_ir_builder->getInt64(0)); + m_ir_builder->CreateRet(m_ir_builder->getInt32(0)); } else { - m_ir_builder->CreateRet(exit_block_i64); + m_ir_builder->CreateRet(exit_instr_i32); + } + } + + // Add incoming values for all exit instr PHI nodes + for (auto exit_instr_i = exit_instr_list.begin(); exit_instr_i != exit_instr_list.end(); exit_instr_i++) { + auto block = (*exit_instr_i)->getParent(); + for (auto pred_i = pred_begin(block); pred_i != pred_end(block); pred_i++) { + auto pred_address = GetAddressFromBasicBlockName((*pred_i)->getName()); + (*exit_instr_i)->addIncoming(m_ir_builder->getInt32(pred_address), *pred_i); } } @@ -1575,7 +1581,8 @@ void Compiler::ADDIS(u32 rd, u32 ra, s32 simm16) { void Compiler::BC(u32 bo, u32 bi, s32 bd, u32 aa, u32 lk) { auto target_i64 = m_ir_builder->getInt64(branchTarget(aa ? 0 : m_state.current_instruction_address, bd)); - CreateBranch(CheckBranchCondition(bo, bi), target_i64, lk ? true : false); + auto target_i32 = m_ir_builder->CreateTrunc(target_i64, m_ir_builder->getInt32Ty()); + CreateBranch(CheckBranchCondition(bo, bi), target_i32, lk ? true : false); //m_hit_branch_instruction = true; //SetPc(m_ir_builder->getInt32(m_current_instruction_address)); //InterpreterCall("BC", &PPUInterpreter::BC, bo, bi, bd, aa, lk); @@ -1589,7 +1596,8 @@ void Compiler::SC(u32 sc_code) { void Compiler::B(s32 ll, u32 aa, u32 lk) { auto target_i64 = m_ir_builder->getInt64(branchTarget(aa ? 0 : m_state.current_instruction_address, ll)); - CreateBranch(nullptr, target_i64, lk ? true : false); + auto target_i32 = m_ir_builder->CreateTrunc(target_i64, m_ir_builder->getInt32Ty()); + CreateBranch(nullptr, target_i32, lk ? true : false); //m_hit_branch_instruction = true; //SetPc(m_ir_builder->getInt32(m_current_instruction_address)); //InterpreterCall("B", &PPUInterpreter::B, ll, aa, lk); @@ -1609,7 +1617,8 @@ void Compiler::MCRF(u32 crfd, u32 crfs) { void Compiler::BCLR(u32 bo, u32 bi, u32 bh, u32 lk) { auto lr_i64 = GetLr(); lr_i64 = m_ir_builder->CreateAnd(lr_i64, ~0x3ULL); - CreateBranch(CheckBranchCondition(bo, bi), lr_i64, lk ? true : false, true); + auto lr_i32 = m_ir_builder->CreateTrunc(lr_i64, m_ir_builder->getInt32Ty()); + CreateBranch(CheckBranchCondition(bo, bi), lr_i32, lk ? true : false, true); //m_hit_branch_instruction = true; //SetPc(m_ir_builder->getInt32(m_current_instruction_address)); //InterpreterCall("BCLR", &PPUInterpreter::BCLR, bo, bi, bh, lk); @@ -1710,7 +1719,8 @@ void Compiler::CROR(u32 crbd, u32 crba, u32 crbb) { void Compiler::BCCTR(u32 bo, u32 bi, u32 bh, u32 lk) { auto ctr_i64 = GetCtr(); ctr_i64 = m_ir_builder->CreateAnd(ctr_i64, ~0x3ULL); - CreateBranch(CheckBranchCondition(bo, bi), ctr_i64, lk ? true : false); + auto ctr_i32 = m_ir_builder->CreateTrunc(ctr_i64, m_ir_builder->getInt32Ty()); + CreateBranch(CheckBranchCondition(bo, bi), ctr_i32, lk ? true : false); //m_hit_branch_instruction = true; //SetPc(m_ir_builder->getInt32(m_current_instruction_address)); //InterpreterCall("BCCTR", &PPUInterpreter::BCCTR, bo, bi, bh, lk); @@ -4148,7 +4158,7 @@ void Compiler::UNK(const u32 code, const u32 opcode, const u32 gcode) { //InterpreterCall("UNK", &PPUInterpreter::UNK, code, opcode, gcode); } -std::string Compiler::GetBasicBlockNameFromAddress(u32 address, const std::string & suffix) { +std::string Compiler::GetBasicBlockNameFromAddress(u32 address, const std::string & suffix) const { std::string name; if (address == 0) { @@ -4166,7 +4176,7 @@ std::string Compiler::GetBasicBlockNameFromAddress(u32 address, const std::strin return name; } -u32 Compiler::GetAddressFromBasicBlockName(const std::string & name) { +u32 Compiler::GetAddressFromBasicBlockName(const std::string & name) const { if (name.compare(0, 6, "instr_") == 0) { return strtoul(name.c_str() + 6, nullptr, 0); } else if (name == GetBasicBlockNameFromAddress(0)) { @@ -4585,7 +4595,7 @@ Value * Compiler::CheckBranchCondition(u32 bo, u32 bi) { return cmp_i1; } -void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool lk, bool target_is_lr) { +void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i32, bool lk, bool target_is_lr) { if (lk) { SetLr(m_ir_builder->getInt64(m_state.current_instruction_address + 4)); } @@ -4593,18 +4603,18 @@ void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool auto current_block = m_ir_builder->GetInsertBlock(); BasicBlock * target_block = nullptr; - if (dyn_cast(target_i64)) { + if (dyn_cast(target_i32)) { // Target address is an immediate value. - u32 target_address = (u32)(dyn_cast(target_i64)->getLimitedValue()); + u32 target_address = (u32)(dyn_cast(target_i32)->getLimitedValue()); if (lk) { // Function call if (cmp_i1) { // There is no need to create a new block for an unconditional jump - target_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function); + target_block = GetBasicBlockFromAddress(m_state.current_instruction_address, "target"); m_ir_builder->SetInsertPoint(target_block); } - SetPc(target_i64); - IndirectCall(target_address, true); + SetPc(target_i32); + IndirectCall(target_address, m_ir_builder->getInt64(0), true); m_ir_builder->CreateBr(GetBasicBlockFromAddress(m_state.current_instruction_address + 4)); } else { // Local branch @@ -4613,34 +4623,40 @@ void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool } else { // Target address is in a register if (cmp_i1) { // There is no need to create a new block for an unconditional jump - target_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function); + target_block = GetBasicBlockFromAddress(m_state.current_instruction_address, "target"); m_ir_builder->SetInsertPoint(target_block); } - SetPc(target_i64); - + SetPc(target_i32); if (target_is_lr && !lk) { - // Return from function call - m_ir_builder->CreateRet(m_ir_builder->getInt64(0)); + // Return from this function + m_ir_builder->CreateRet(m_ir_builder->getInt32(0)); } else if (lk) { - auto next_block = GetBasicBlockFromAddress(m_state.current_instruction_address + 4); - auto call_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function); - m_ir_builder->CreateOr(m_ir_builder->getInt64(m_state.cfg_entry->first), (uint64_t)0); - auto switch_instr = m_ir_builder->CreateSwitch(target_i64, call_block); - m_ir_builder->SetInsertPoint(call_block); - IndirectCall(0, true); + auto next_block = GetBasicBlockFromAddress(m_state.current_instruction_address + 4); + auto unknown_function_block = GetBasicBlockFromAddress(m_state.current_instruction_address, "unknown_function"); + + auto switch_instr = m_ir_builder->CreateSwitch(target_i32, unknown_function_block); + m_ir_builder->SetInsertPoint(unknown_function_block); + m_ir_builder->CreateCall3(m_unknown_function, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], m_ir_builder->getInt64(0)); m_ir_builder->CreateBr(next_block); - for (auto i = m_state.cfg_entry->second.begin(); i != m_state.cfg_entry->second.end(); i++) { - call_block = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function); - m_ir_builder->SetInsertPoint(call_block); - IndirectCall(i->address, true); - m_ir_builder->CreateBr(next_block); - switch_instr->addCase(m_ir_builder->getInt32(i->address), call_block); + + auto call_i = m_state.cfg->calls.find(m_state.current_instruction_address); + if (call_i != m_state.cfg->calls.end()) { + for (auto function_i = call_i->second.begin(); function_i != call_i->second.end(); function_i++) { + auto block = GetBasicBlockFromAddress(m_state.current_instruction_address, fmt::Format("0x%08X", *function_i)); + m_ir_builder->SetInsertPoint(block); + IndirectCall(*function_i, m_ir_builder->getInt64(0), true); + m_ir_builder->CreateBr(next_block); + switch_instr->addCase(m_ir_builder->getInt32(*function_i), block); + } } } else { - auto switch_instr = m_ir_builder->CreateSwitch(target_i64, GetBasicBlockFromAddress(0xFFFFFFFF)); - for (auto i = m_state.cfg_entry->second.begin(); i != m_state.cfg_entry->second.end(); i++) { - switch_instr->addCase(m_ir_builder->getInt64(i->address), GetBasicBlockFromAddress(i->address)); + auto switch_instr = m_ir_builder->CreateSwitch(target_i32, GetBasicBlockFromAddress(0xFFFFFFFF)); + auto branch_i = m_state.cfg->branches.find(m_state.current_instruction_address); + if (branch_i != m_state.cfg->branches.end()) { + for (auto next_instr_i = branch_i->second.begin(); next_instr_i != branch_i->second.end(); next_instr_i++) { + switch_instr->addCase(m_ir_builder->getInt32(*next_instr_i), GetBasicBlockFromAddress(*next_instr_i)); + } } } } @@ -4820,14 +4836,11 @@ Value * Compiler::Call(const char * name, Func function, Args... args) { return m_ir_builder->CreateCall(fn, fn_args); } -llvm::Value * Compiler::IndirectCall(u32 address, bool is_function) { +llvm::Value * Compiler::IndirectCall(u32 address, Value * context_i64, bool is_function) { auto ordinal = m_recompilation_engine.AllocateOrdinal(address, is_function); auto executable_addr_i64 = m_ir_builder->getInt64(m_recompilation_engine.GetAddressOfExecutableLookup() + (ordinal * sizeof(u64))); auto executable_ptr = m_ir_builder->CreateIntToPtr(executable_addr_i64, m_compiled_function_type); - return m_ir_builder->CreateCall4(executable_ptr, m_state.args[CompileTaskState::Args::ExecutionEngine], - m_state.args[CompileTaskState::Args::State], - m_state.args[CompileTaskState::Args::Interpreter], - m_state.args[CompileTaskState::Args::Tracer]); + return m_ir_builder->CreateCall3(executable_ptr, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], context_i64); } void Compiler::InitRotateMask() { @@ -4986,7 +4999,7 @@ void RecompilationEngine::Task() { } void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution_trace) { - auto execution_trace_id = GetExecutionTraceId(execution_trace); + auto execution_trace_id = execution_trace.GetId(); auto processed_execution_trace_i = m_processed_execution_traces.find(execution_trace_id); if (processed_execution_trace_i == m_processed_execution_traces.end()) { Log() << "Trace: " << execution_trace.ToString() << "\n"; @@ -4995,38 +5008,33 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution auto split_trace = false; auto block_i = m_block_table.end(); - auto trace_block_i = execution_trace.blocks.begin(); - for (; trace_block_i != execution_trace.blocks.end(); trace_block_i++) { - if (trace_block_i->type == BlockId::Type::Exit) { + for (auto trace_i = execution_trace.entries.begin(); trace_i != execution_trace.entries.end(); trace_i++) { + if (trace_i->type == ExecutionTraceEntry::Type::CompiledBlock) { block_i = m_block_table.end(); split_trace = true; - } else if (block_i == m_block_table.end()) { - BlockEntry key(trace_block_i->address); + } + + if (block_i == m_block_table.end()) { + BlockEntry key(trace_i->GetPrimaryAddress(), execution_trace.function_address); block_i = m_block_table.find(&key); if (block_i == m_block_table.end()) { - block_i = m_block_table.insert(m_block_table.end(), new BlockEntry(key.address)); + block_i = m_block_table.insert(m_block_table.end(), new BlockEntry(key.cfg.start_address, key.cfg.function_address)); } - (*block_i)->is_function_start = key.address == execution_trace.function_address; tmp_block_list.push_back(*block_i); } - if (block_i != m_block_table.end()) { - BlockId next_block; - if (trace_block_i + 1 != execution_trace.blocks.end()) { - next_block = *(trace_block_i + 1); - } else { - if (!split_trace && execution_trace.type == ExecutionTrace::Type::Loop) { - next_block = *(execution_trace.blocks.begin()); - } else { - next_block.address = 0; - next_block.type = BlockId::Type::Exit; - } + const ExecutionTraceEntry * next_trace = nullptr; + if (trace_i + 1 != execution_trace.entries.end()) { + next_trace = &(*(trace_i + 1)); + } else if (!split_trace && execution_trace.type == ExecutionTrace::Type::Loop) { + if (!split_trace && execution_trace.type == ExecutionTrace::Type::Loop) { + next_trace = &(*(execution_trace.entries.begin())); } - - UpdateControlFlowGraph((*block_i)->cfg, *trace_block_i, next_block); } + + UpdateControlFlowGraph((*block_i)->cfg, *trace_i, next_trace); } processed_execution_trace_i = m_processed_execution_traces.insert(m_processed_execution_traces.end(), std::make_pair(execution_trace_id, std::move(tmp_block_list))); @@ -5045,24 +5053,26 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution std::remove_if(processed_execution_trace_i->second.begin(), processed_execution_trace_i->second.end(), [](const BlockEntry * b)->bool { return b->is_compiled; }); } -void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, BlockId block, BlockId next_block) { - if (block.type == BlockId::Type::Exit && next_block.type == BlockId::Type::Exit) { - return; - } +void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, const ExecutionTraceEntry & this_entry, const ExecutionTraceEntry * next_entry) { + if (this_entry.type == ExecutionTraceEntry::Type::Instruction) { + cfg.instruction_addresses.insert(this_entry.GetPrimaryAddress()); - if (block.type == BlockId::Type::FunctionCall) { - return; - } - - auto block_i = std::find_if(cfg.begin(), cfg.end(), [&block](const ControlFlowGraph::value_type & v)->bool { return v.first == block.address; }); - if (block.type == BlockId::Type::Normal && block_i == cfg.end()) { - block_i = cfg.insert(cfg.end(), std::make_pair(block.address, std::vector())); - } - - if (block_i != cfg.end() && next_block.address && next_block.type != BlockId::Type::Exit) { - auto next_block_i = std::find(block_i->second.begin(), block_i->second.end(), next_block); - if (next_block_i == block_i->second.end()) { - block_i->second.push_back(next_block); + if (next_entry) { + if (next_entry->type == ExecutionTraceEntry::Type::Instruction || next_entry->type == ExecutionTraceEntry::Type::CompiledBlock) { + if (next_entry->GetPrimaryAddress() != (this_entry.GetPrimaryAddress() + 4)) { + cfg.branches[this_entry.GetPrimaryAddress()].insert(next_entry->GetPrimaryAddress()); + } + } else if (next_entry->type == ExecutionTraceEntry::Type::FunctionCall) { + cfg.calls[this_entry.instruction.address].insert(next_entry->GetPrimaryAddress()); + } + } + } else if (this_entry.type == ExecutionTraceEntry::Type::CompiledBlock) { + if (next_entry) { + if (next_entry->type == ExecutionTraceEntry::Type::Instruction || next_entry->type == ExecutionTraceEntry::Type::CompiledBlock) { + cfg.branches[this_entry.compiled_block.exit_address].insert(next_entry->GetPrimaryAddress()); + } else if (next_entry->type == ExecutionTraceEntry::Type::FunctionCall) { + cfg.calls[this_entry.compiled_block.exit_address].insert(next_entry->GetPrimaryAddress()); + } } } } @@ -5070,11 +5080,11 @@ void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, BlockId void RecompilationEngine::CompileBlock(BlockEntry & block_entry, bool inline_referenced_blocks) { Log() << "Compile: " << block_entry.ToString() << "\n"; - auto ordinal = AllocateOrdinal(block_entry.address, block_entry.is_function_start); - auto executable = m_compiler.Compile(fmt::Format("fn_0x%08X_%u", block_entry.address, block_entry.revision++), block_entry.cfg, - block_entry.is_function_start ? false : true /*inline_all_blocks*/, - block_entry.is_function_start ? true : false /*generate_linkable_exits*/, - block_entry.is_function_start ? true : false /*generate_trace*/); + auto is_funciton = block_entry.cfg.start_address == block_entry.cfg.function_address; + auto ordinal = AllocateOrdinal(block_entry.cfg.start_address, is_funciton); + auto executable = m_compiler.Compile(fmt::Format("fn_0x%08X_%u", block_entry.cfg.start_address, block_entry.revision++), block_entry.cfg, + is_funciton ? false : true /*inline_all*/, + is_funciton ? true : false /*generate_linkable_exits*/); m_executable_lookup[ordinal] = executable; } @@ -5090,7 +5100,6 @@ std::shared_ptr RecompilationEngine::GetInstance() { Tracer::Tracer() : m_recompilation_engine(RecompilationEngine::GetInstance()) { - m_trace.reserve(1000); m_stack.reserve(100); } @@ -5100,81 +5109,59 @@ Tracer::~Tracer() { void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { ExecutionTrace * execution_trace = nullptr; - BlockId block_id; - int function; switch (trace_type) { case TraceType::CallFunction: // arg1 is address of the function - block_id.address = arg1; - block_id.type = BlockId::Type::FunctionCall; - m_trace.push_back(block_id); + m_stack.back()->entries.push_back(ExecutionTraceEntry(ExecutionTraceEntry::Type::FunctionCall, arg1)); break; case TraceType::EnterFunction: - // No args used - m_stack.push_back((u32)m_trace.size()); + // arg1 is address of the function + m_stack.push_back(new ExecutionTrace(arg1)); break; case TraceType::ExitFromCompiledFunction: // arg1 is address of function. - // arg2 is the address of the exit block. - block_id.address = arg1; - block_id.type = BlockId::Type::Normal; - m_stack.push_back((u32)m_trace.size()); - m_trace.push_back(block_id); - - block_id.address = arg2; - block_id.type = BlockId::Type::Exit; - m_trace.push_back(block_id); + // arg2 is the address of the exit instruction. + if (arg2) { + m_stack.push_back(new ExecutionTrace(arg1)); + m_stack.back()->entries.push_back(ExecutionTraceEntry(ExecutionTraceEntry::Type::CompiledBlock, arg1, arg2)); + } break; case TraceType::Return: // No args used - function = m_stack.back(); + execution_trace = m_stack.back(); + execution_trace->type = ExecutionTrace::Type::Linear; m_stack.pop_back(); - - execution_trace = new ExecutionTrace(); - execution_trace->type = ExecutionTrace::Type::Linear; - execution_trace->function_address = m_trace[function].address; - std::copy(m_trace.begin() + function, m_trace.end(), std::back_inserter(execution_trace->blocks)); - m_trace.erase(m_trace.begin() + function, m_trace.end()); break; - case TraceType::EnterBlock: - // arg1 is address. Other args are not used. - function = m_stack.back(); - for (int i = (int)m_trace.size() - 1; i >= function; i--) { - if (m_trace[i].address == arg1 && m_trace[i].type == BlockId::Type::Normal) { - // Found a loop within the current function - execution_trace = new ExecutionTrace(); - execution_trace->type = ExecutionTrace::Type::Loop; - execution_trace->function_address = m_trace[function].address; - std::copy(m_trace.begin() + i, m_trace.end(), std::back_inserter(execution_trace->blocks)); - m_trace.erase(m_trace.begin() + i + 1, m_trace.end()); + case TraceType::Instruction: + // arg1 is the address of the instruction + for (int i = (int)m_stack.back()->entries.size() - 1; i >= 0; i--) { + if ((m_stack.back()->entries[i].type == ExecutionTraceEntry::Type::Instruction && m_stack.back()->entries[i].instruction.address == arg1) || + (m_stack.back()->entries[i].type == ExecutionTraceEntry::Type::CompiledBlock && m_stack.back()->entries[i].compiled_block.entry_address == arg1)) { + // Found a loop + execution_trace = new ExecutionTrace(m_stack.back()->function_address); + execution_trace->type = ExecutionTrace::Type::Loop; + std::copy(m_stack.back()->entries.begin() + i, m_stack.back()->entries.end(), std::back_inserter(execution_trace->entries)); + m_stack.back()->entries.erase(m_stack.back()->entries.begin() + i + 1, m_stack.back()->entries.end()); break; } } if (!execution_trace) { // A loop was not found - block_id.address = arg1; - block_id.type = BlockId::Type::Normal; - m_trace.push_back(block_id); + m_stack.back()->entries.push_back(ExecutionTraceEntry(ExecutionTraceEntry::Type::Instruction, arg1)); } break; case TraceType::ExitFromCompiledBlock: - // arg1 is address of the exit block. - block_id.address = arg1; - block_id.type = BlockId::Type::Exit; - m_trace.push_back(block_id); + // arg1 is address of the compiled block. + // arg2 is the address of the exit instruction. + m_stack.back()->entries.push_back(ExecutionTraceEntry(ExecutionTraceEntry::Type::CompiledBlock, arg1, arg2)); - if (arg1 == 0) { + if (arg2 == 0) { // Return from function - function = m_stack.back(); + execution_trace = m_stack.back(); + execution_trace->type = ExecutionTrace::Type::Linear; m_stack.pop_back(); - - execution_trace = new ExecutionTrace(); - execution_trace->type = ExecutionTrace::Type::Linear; - execution_trace->function_address = m_trace[function].address; - std::copy(m_trace.begin() + function, m_trace.end(), std::back_inserter(execution_trace->blocks)); - m_trace.erase(m_trace.begin() + function, m_trace.end()); } break; default: @@ -5204,7 +5191,7 @@ ppu_recompiler_llvm::ExecutionEngine::~ExecutionEngine() { } u8 ppu_recompiler_llvm::ExecutionEngine::DecodeMemory(const u32 address) { - ExecuteFunction(this, &m_ppu, m_interpreter, &m_tracer); + ExecuteFunction(&m_ppu, m_interpreter, 0); return 0; } @@ -5245,14 +5232,20 @@ Executable ppu_recompiler_llvm::ExecutionEngine::GetExecutable(u32 address, Exec return executable; } -u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteFunction(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer) { - tracer->Trace(Tracer::TraceType::EnterFunction, 0, 0); - return ExecuteTillReturn(execution_engine, ppu_state, interpreter, tracer); +u32 ppu_recompiler_llvm::ExecutionEngine::ExecuteFunction(PPUThread * ppu_state, PPUInterpreter * interpreter, u64 context) { + auto execution_engine = (ExecutionEngine *)ppu_state->GetDecoder(); + execution_engine->m_tracer.Trace(Tracer::TraceType::EnterFunction, ppu_state->PC, 0); + return ExecuteTillReturn(ppu_state, interpreter, 0); } -u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteTillReturn(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer) { - bool terminate = false; - bool returned = true; +u32 ppu_recompiler_llvm::ExecutionEngine::ExecuteTillReturn(PPUThread * ppu_state, PPUInterpreter * interpreter, u64 context) { + auto execution_engine = (ExecutionEngine *)ppu_state->GetDecoder(); + auto terminate = false; + auto branch_type = BranchType::NonBranch; + + if (context) { + execution_engine->m_tracer.Trace(Tracer::TraceType::ExitFromCompiledFunction, context >> 32, context & 0xFFFFFFFF); + } while (!terminate && !Emu.IsStopped()) { if (Emu.IsPaused()) { @@ -5260,68 +5253,45 @@ u64 ppu_recompiler_llvm::ExecutionEngine::ExecuteTillReturn(ExecutionEngine * ex continue; } - BranchType branch_type; - if (!returned) { + auto executable = execution_engine->GetExecutable(ppu_state->PC, ExecuteTillReturn); + if (executable != ExecuteTillReturn) { + auto entry = ppu_state->PC; + auto exit = (u32)executable(ppu_state, interpreter, 0); + execution_engine->m_tracer.Trace(Tracer::TraceType::ExitFromCompiledBlock, entry, exit); + if (exit == 0) { + terminate = true; + } + } else { + execution_engine->m_tracer.Trace(Tracer::TraceType::Instruction, ppu_state->PC, 0); auto instruction = re32(vm::get_ref(ppu_state->PC)); execution_engine->m_decoder.Decode(instruction); branch_type = ppu_state->m_is_branch ? GetBranchTypeFromInstruction(instruction) : BranchType::NonBranch; ppu_state->NextPc(4); - } else { - returned = false; - branch_type = BranchType::LocalBranch; - } - Executable executable; - switch (branch_type) { - case BranchType::Return: - tracer->Trace(Tracer::TraceType::Return, 0, 0); - terminate = true; - break; - case BranchType::FunctionCall: - tracer->Trace(Tracer::TraceType::CallFunction, ppu_state->PC, 0); - executable = execution_engine->GetExecutable(ppu_state->PC, ExecuteFunction); - executable(execution_engine, ppu_state, interpreter, tracer); - returned = true; - break; - case BranchType::LocalBranch: - tracer->Trace(Tracer::TraceType::EnterBlock, ppu_state->PC, 0); - executable = execution_engine->GetExecutable(ppu_state->PC, nullptr); - if (executable != nullptr) { - auto exit_block = executable(execution_engine, ppu_state, interpreter, tracer); - tracer->Trace(Tracer::TraceType::ExitFromCompiledBlock, (u32)exit_block, 0); - if (exit_block == 0) { - terminate = true; - } + switch (branch_type) { + case BranchType::Return: + execution_engine->m_tracer.Trace(Tracer::TraceType::Return, 0, 0); + terminate = true; + break; + case BranchType::FunctionCall: + execution_engine->m_tracer.Trace(Tracer::TraceType::CallFunction, ppu_state->PC, 0); + executable = execution_engine->GetExecutable(ppu_state->PC, ExecuteFunction); + executable(ppu_state, interpreter, 0); + break; + case BranchType::LocalBranch: + break; + case BranchType::NonBranch: + break; + default: + assert(0); + break; } - break; - case BranchType::NonBranch: - break; - default: - assert(0); - break; } } return 0; } -std::string ppu_recompiler_llvm::ControlFlowGraphToString(const ControlFlowGraph & cfg) { - std::string s; - - for (auto i = cfg.begin(); i != cfg.end(); i++) { - s += fmt::Format("0x%08X ->", i->first); - for (auto j = i->second.begin(); j != i->second.end(); j++) { - s += " " + j->ToString(); - } - - if (i != (cfg.end() - 1)) { - s += "\n"; - } - } - - return s; -} - BranchType ppu_recompiler_llvm::GetBranchTypeFromInstruction(u32 instruction) { auto type = BranchType::NonBranch; auto field1 = instruction >> 26; @@ -5340,13 +5310,3 @@ BranchType ppu_recompiler_llvm::GetBranchTypeFromInstruction(u32 instruction) { return type; } - -ExecutionTraceId ppu_recompiler_llvm::GetExecutionTraceId(const ExecutionTrace & execution_trace) { - ExecutionTraceId id = 0; - - for (auto i = execution_trace.blocks.begin(); i != execution_trace.blocks.end(); i++) { - id = (id << 8) ^ ((u64)i->address << 32 | _byteswap_ulong((u64)i->address)); - } - - return id; -} diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index 856ecddc72..963e22c8af 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -19,45 +19,106 @@ namespace ppu_recompiler_llvm { class ExecutionEngine; struct PPUState; - enum class BranchType { - NonBranch, - LocalBranch, - FunctionCall, - Return, - }; + /// An entry in an execution trace + struct ExecutionTraceEntry { + /// Data associated with the entry. This is discriminated by type. + union { + struct Instruction { + u32 address; + } instruction; - /// Unique id of a block - struct BlockId { - /// Address of the block - u32 address; + struct FunctionCall { + u32 address; + } function_call; - /// The type of the block + struct CompiledBlock { + u32 entry_address; + u32 exit_address; + } compiled_block; + }; + + /// The type of the entry enum class Type { FunctionCall, - Normal, - Exit, + Instruction, + CompiledBlock, } type; - bool operator == (const BlockId & other) const { - return (address == other.address && type == other.type); + ExecutionTraceEntry(Type type, u32 arg1, u32 arg2 = 0) + : type(type) { + switch (type) { + case Type::Instruction: + instruction.address = arg1; + break; + case Type::FunctionCall: + function_call.address = arg1; + break; + case Type::CompiledBlock: + compiled_block.entry_address = arg1; + compiled_block.exit_address = arg2; + break; + default: + assert(0); + break; + } + } + + u32 GetPrimaryAddress() const { + switch (type) { + case Type::Instruction: + return instruction.address; + case Type::FunctionCall: + return function_call.address; + case Type::CompiledBlock: + return compiled_block.entry_address; + default: + assert(0); + return 0; + } } std::string ToString() const { - return fmt::Format("%c:0x%08X", type == BlockId::Type::Normal ? 'N' : type == BlockId::Type::FunctionCall ? 'F' : 'E', address); + switch (type) { + case Type::Instruction: + return fmt::Format("I:0x%08X", instruction.address); + case Type::FunctionCall: + return fmt::Format("F:0x%08X", function_call.address); + case Type::CompiledBlock: + return fmt::Format("C:0x%08X-0x%08X", compiled_block.entry_address, compiled_block.exit_address); + default: + assert(0); + return ""; + } + } + + u64 hash() const { + u64 hash = ((u64)type << 32); + switch (type) { + case Type::Instruction: + hash |= instruction.address; + break; + case Type::FunctionCall: + hash |= function_call.address; + break; + case Type::CompiledBlock: + hash = compiled_block.exit_address; + hash <<= 32; + hash |= compiled_block.entry_address; + break; + default: + assert(0); + break; + } + + return hash; } }; - /// Control flow graph of a block. A list of (block address, list of next blocks) pairs. - typedef std::vector>> ControlFlowGraph; - - /// Get a string representation of a ControlFlowGraph - std::string ControlFlowGraphToString(const ControlFlowGraph & cfg); - - /// Uniquely identifies an execution trace - typedef u64 ExecutionTraceId; - /// An execution trace. struct ExecutionTrace { + /// Unique id of an execution trace; + typedef u64 Id; + /// The function to which this trace belongs u32 function_address; @@ -67,27 +128,98 @@ namespace ppu_recompiler_llvm { Loop, } type; - /// Sequence of blocks enountered in this trace - std::vector blocks; + /// entries in the trace + std::vector entries; + + ExecutionTrace(u32 address) + : function_address(address) { + } std::string ToString() const { auto s = fmt::Format("0x%08X %s ->", function_address, type == ExecutionTrace::Type::Loop ? "Loop" : "Linear"); - for (auto i = 0; i < blocks.size(); i++) { - s += " " + blocks[i].ToString(); + for (auto i = 0; i < entries.size(); i++) { + s += " " + entries[i].ToString(); + } + + return s; + } + + Id GetId() const { + Id id = 0; + + for (auto i = entries.begin(); i != entries.end(); i++) { + id ^= i->hash(); + id <<= 1; + } + + return id; + } + }; + + /// A control flow graph + struct ControlFlowGraph { + /// Address of the first instruction + u32 start_address; + + /// Address of the function to which this CFG belongs to + u32 function_address; + + /// Set of addresses of the instructions in the CFG + std::set instruction_addresses; + + /// Branches in the CFG. + /// Key is the address of an instruction + /// Data is the set of all instructions to which this instruction branches to. + std::map> branches; + + /// Function calls in the CFG + /// Key is the address of an instruction + /// Data is the set of all functions which this instruction invokes. + std::map> calls; + + ControlFlowGraph(u32 start_address, u32 function_address) + : start_address(start_address) + , function_address(function_address) { + } + + std::string ToString() const { + auto s = fmt::Format("0x%08X (0x%08X):", start_address, function_address); + for (auto i = instruction_addresses.begin(); i != instruction_addresses.end(); i++) { + s += fmt::Format(" 0x%08X", *i); + } + + s += "\nBranches:"; + for (auto i = branches.begin(); i != branches.end(); i++) { + s += fmt::Format("\n0x%08X ->", i->first); + for (auto j = i->second.begin(); j != i->second.end(); j++) { + s += fmt::Format(" 0x%08X", *j); + } + } + + s += "\nCalls:"; + for (auto i = calls.begin(); i != calls.end(); i++) { + s += fmt::Format("\n0x%08X ->", i->first); + for (auto j = i->second.begin(); j != i->second.end(); j++) { + s += fmt::Format(" 0x%08X", *j); + } } return s; } }; + enum class BranchType { + NonBranch, + LocalBranch, + FunctionCall, + Return, + }; + /// Pointer to an executable - typedef u64(*Executable)(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer); + typedef u32(*Executable)(PPUThread * ppu_state, PPUInterpreter * interpreter, u64 context); /// An entry in the block table struct BlockEntry { - /// Address of the block - u32 address; - /// Number of times this block was hit u32 num_hits; @@ -100,23 +232,19 @@ namespace ppu_recompiler_llvm { /// Indicates whether the block has been compiled or not bool is_compiled; - /// Indicates whether the block is the first block of a function or not - bool is_function_start; - - BlockEntry(u32 addr) - : address(addr) - , num_hits(0) + BlockEntry(u32 start_address, u32 function_address) + : num_hits(0) , revision(0) - , is_compiled(false) { + , is_compiled(false) + , cfg(start_address, function_address) { } std::string ToString() const { - return fmt::Format("%c:0x%08X, NumHits=%u, IsCompiled=%c\n%s", is_function_start ? 'F' : 'N', address, num_hits, - is_compiled ? 'Y' : 'N', ControlFlowGraphToString(cfg).c_str()); + return fmt::Format("%s\nNumHits=%u, Revision=%u, IsCompiled=%c\n", cfg.ToString().c_str(), num_hits, revision, is_compiled ? 'Y' : 'N'); } bool operator == (const BlockEntry & other) const { - return address == other.address; + return cfg.start_address == other.cfg.start_address; } }; } @@ -124,7 +252,7 @@ namespace ppu_recompiler_llvm { namespace std { template<> struct hash { size_t operator()(const ppu_recompiler_llvm::BlockEntry * e) const { - return e->address; + return e->cfg.start_address; } }; } @@ -150,7 +278,7 @@ namespace ppu_recompiler_llvm { std::map interpreter_fallback_stats; }; - Compiler(RecompilationEngine & recompilation_engine, const Executable default_function_executable, const Executable default_block_executable); + Compiler(RecompilationEngine & recompilation_engine, const Executable unknown_function, const Executable unknown_block); Compiler(const Compiler & other) = delete; Compiler(Compiler && other) = delete; @@ -161,7 +289,7 @@ namespace ppu_recompiler_llvm { Compiler & operator = (Compiler && other) = delete; /// Compile a code fragment described by a cfg and return an executable - Executable Compile(const std::string & name, const ControlFlowGraph & cfg, bool inline_all_blocks, bool generate_linkable_exits, bool generate_trace); + Executable Compile(const std::string & name, const ControlFlowGraph & cfg, bool inline_all_blocks, bool generate_linkable_exits); /// Free an executable earilier obtained via a call to Compile void FreeExecutable(const std::string & name); @@ -579,10 +707,9 @@ namespace ppu_recompiler_llvm { /// State of a compilation task struct CompileTaskState { enum Args { - ExecutionEngine, State, Interpreter, - Tracer, + Context, MaxArgs, }; @@ -595,38 +722,29 @@ namespace ppu_recompiler_llvm { /// The CFG being compiled const ControlFlowGraph * cfg; - /// The current entry of the CFG being compiled - ControlFlowGraph::const_iterator cfg_entry; - /// Address of the current instruction being compiled u32 current_instruction_address; - /// Map from an address to the address of the block that it belongs to - std::unordered_map address_to_block; - /// A flag used to detect branch instructions. - /// This is set to false at the start of compilation of a block. - /// When a branch instruction is encountered, this is set to true by the decode function. + /// This is set to false at the start of compilation of an instruction. + /// If a branch instruction is encountered, this is set to true by the decode function. bool hit_branch_instruction; /// Indicates whether a block should be inlined even if an already compiled version of the block exists - bool inline_all_blocks; + bool inline_all; /// Create code such that exit points can be linked to other blocks bool generate_linkable_exits; - - /// Notify the tracer upon exit - bool generate_trace; }; /// Recompilation engine RecompilationEngine & m_recompilation_engine; - /// The executable that will be called to process unknown functions - const Executable m_default_function_executable; + /// The function that will be called to process unknown functions + llvm::Function * m_unknown_function; /// The executable that will be called to process unknown blocks - const Executable m_default_block_executable; + llvm::Function * m_unknown_block; /// LLVM context llvm::LLVMContext * m_llvm_context; @@ -653,10 +771,10 @@ namespace ppu_recompiler_llvm { Stats m_stats; /// Get the name of the basic block for the specified address - std::string GetBasicBlockNameFromAddress(u32 address, const std::string & suffix = ""); + std::string GetBasicBlockNameFromAddress(u32 address, const std::string & suffix = "") const; /// Get the address of a basic block from its name - u32 GetAddressFromBasicBlockName(const std::string & name); + u32 GetAddressFromBasicBlockName(const std::string & name) const; /// Get the basic block in for the specified address. llvm::BasicBlock * GetBasicBlockFromAddress(u32 address, const std::string & suffix = "", bool create_if_not_exist = true); @@ -785,7 +903,7 @@ namespace ppu_recompiler_llvm { llvm::Value * CheckBranchCondition(u32 bo, u32 bi); /// Create IR for a branch instruction - void CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i64, bool lk, bool target_is_lr = false); + void CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i32, bool lk, bool target_is_lr = false); /// Read from memory llvm::Value * ReadMemory(llvm::Value * addr_i64, u32 bits, u32 alignment = 0, bool bswap = true, bool could_be_mmio = true); @@ -806,7 +924,7 @@ namespace ppu_recompiler_llvm { llvm::Value * Call(const char * name, Func function, Args... args); /// Indirect call - llvm::Value * IndirectCall(u32 address, bool is_function); + llvm::Value * IndirectCall(u32 address, llvm::Value * context_i64, bool is_function); /// Test an instruction against the interpreter template @@ -863,7 +981,7 @@ namespace ppu_recompiler_llvm { std::unordered_set m_block_table; /// Execution traces that have been already encountered. Data is the list of all blocks that this trace includes. - std::unordered_map> m_processed_execution_traces; + std::unordered_map> m_processed_execution_traces; /// Lock for accessing m_address_to_ordinal. // TODO: Make this a RW lock @@ -896,7 +1014,7 @@ namespace ppu_recompiler_llvm { void ProcessExecutionTrace(const ExecutionTrace & execution_trace); /// Update a CFG - void UpdateControlFlowGraph(ControlFlowGraph & cfg, BlockId block, BlockId next_block); + void UpdateControlFlowGraph(ControlFlowGraph & cfg, const ExecutionTraceEntry & this_entry, const ExecutionTraceEntry * next_entry); /// Compile a block void CompileBlock(BlockEntry & block_entry, bool inline_referenced_blocks); @@ -917,7 +1035,7 @@ namespace ppu_recompiler_llvm { EnterFunction, ExitFromCompiledFunction, Return, - EnterBlock, + Instruction, ExitFromCompiledBlock, }; @@ -938,11 +1056,8 @@ namespace ppu_recompiler_llvm { void Terminate(); private: - /// Current execution trace - std::vector m_trace; - /// Call stack - std::vector m_stack; + std::vector m_stack; /// Recompilation engine std::shared_ptr m_recompilation_engine; @@ -994,17 +1109,14 @@ namespace ppu_recompiler_llvm { Executable GetExecutable(u32 address, Executable default_executable) const; /// Execute a function - static u64 ExecuteFunction(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer); + static u32 ExecuteFunction(PPUThread * ppu_state, PPUInterpreter * interpreter, u64 context); /// Execute till the current function returns - static u64 ExecuteTillReturn(ExecutionEngine * execution_engine, PPUThread * ppu_state, PPUInterpreter * interpreter, Tracer * tracer); + static u32 ExecuteTillReturn(PPUThread * ppu_state, PPUInterpreter * interpreter, u64 context); }; /// Get the branch type from a branch instruction BranchType GetBranchTypeFromInstruction(u32 instruction); - - /// Get the execution trace id of an execution trace - ExecutionTraceId GetExecutionTraceId(const ExecutionTrace & execution_trace); } #endif // PPU_LLVM_RECOMPILER_H From 80294e1034195be8ce13894884690150e8793371 Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Sun, 9 Nov 2014 02:00:19 +0530 Subject: [PATCH 07/12] Fixed some bugs --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 143 +++++++++++---------------- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 22 +++-- rpcs3/rpcs3.vcxproj | 2 +- 3 files changed, 71 insertions(+), 96 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 1805a9a095..11f83eb8ca 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -116,12 +116,12 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & // Convert each instruction in the CFG to LLVM IR std::vector exit_instr_list; for (auto instr_i = cfg.instruction_addresses.begin(); instr_i != cfg.instruction_addresses.end(); instr_i++) { + m_state.hit_branch_instruction = false; m_state.current_instruction_address = *instr_i; auto instr_bb = GetBasicBlockFromAddress(m_state.current_instruction_address); m_ir_builder->SetInsertPoint(instr_bb); - m_state.hit_branch_instruction = false; - if (!inline_all && instr_i != cfg.instruction_addresses.begin()) { + if (!inline_all && *instr_i != cfg.start_address) { // Use an already compiled implementation of this block if available auto ordinal = m_recompilation_engine.GetOrdinal(*instr_i); if (ordinal != 0xFFFFFFFF) { @@ -139,30 +139,18 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & switch_instr->addCase(m_ir_builder->getInt32(*next_instr_i), GetBasicBlockFromAddress(*next_instr_i)); } } - - m_state.hit_branch_instruction = true; } } - while (!m_state.hit_branch_instruction) { - if (!instr_bb->getInstList().empty()) { - break; - } - + if (instr_bb->empty()) { u32 instr = re32(vm::get_ref(m_state.current_instruction_address)); Decode(instr); - if (!m_state.hit_branch_instruction) { - m_state.current_instruction_address += 4; - instr_bb = GetBasicBlockFromAddress(m_state.current_instruction_address); - m_ir_builder->CreateBr(instr_bb); - m_ir_builder->SetInsertPoint(instr_bb); + m_ir_builder->CreateBr(GetBasicBlockFromAddress(m_state.current_instruction_address + 4)); } } } - m_recompilation_engine.Log() << *m_state.function; - // Generate exit logic for all empty blocks auto default_exit_block_name = GetBasicBlockNameFromAddress(0xFFFFFFFF); for (auto block_i = m_state.function->begin(); block_i != m_state.function->end(); block_i++) { @@ -171,8 +159,6 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & } // Found an empty block - m_recompilation_engine.Log() << "Empty block: " << block_i->getName() << "\n"; - m_ir_builder->SetInsertPoint(block_i); auto exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0); exit_instr_list.push_back(exit_instr_i32); @@ -202,10 +188,8 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & } } - m_recompilation_engine.Log() << *m_state.function; - // If the function has a default exit block then generate code for it - auto default_exit_bb = GetBasicBlockFromAddress(0xFFFFFFFF, false); + auto default_exit_bb = GetBasicBlockFromAddress(0xFFFFFFFF, "", false); if (default_exit_bb) { m_ir_builder->SetInsertPoint(default_exit_bb); auto exit_instr_i32 = m_ir_builder->CreatePHI(m_ir_builder->getInt32Ty(), 0); @@ -213,8 +197,8 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & if (generate_linkable_exits) { auto cmp_i1 = m_ir_builder->CreateICmpNE(exit_instr_i32, m_ir_builder->getInt32(0)); - auto then_bb = GetBasicBlockFromAddress(0xFFFFFFFF, "then"); - auto merge_bb = GetBasicBlockFromAddress(0xFFFFFFFF, "merge"); + auto then_bb = GetBasicBlockFromAddress(0xFFFFFFFF, "then"); + auto merge_bb = GetBasicBlockFromAddress(0xFFFFFFFF, "merge"); m_ir_builder->CreateCondBr(cmp_i1, then_bb, merge_bb); m_ir_builder->SetInsertPoint(then_bb); @@ -239,6 +223,7 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & } } +#ifdef _DEBUG m_recompilation_engine.Log() << *m_state.function; std::string verify; @@ -246,6 +231,7 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & if (verifyFunction(*m_state.function, &verify_ostream)) { m_recompilation_engine.Log() << "Verification failed: " << verify << "\n"; } +#endif auto ir_build_end = std::chrono::high_resolution_clock::now(); m_stats.ir_build_time += std::chrono::duration_cast(ir_build_end - compilation_start); @@ -261,6 +247,20 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & auto translate_end = std::chrono::high_resolution_clock::now(); m_stats.translation_time += std::chrono::duration_cast(translate_end - optimize_end); +#ifdef _DEBUG + m_recompilation_engine.Log() << "\nDisassembly:\n"; + auto disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr); + for (size_t pc = 0; pc < mci.size();) { + char str[1024]; + + auto size = LLVMDisasmInstruction(disassembler, ((u8 *)mci.address()) + pc, mci.size() - pc, (uint64_t)(((u8 *)mci.address()) + pc), str, sizeof(str)); + m_recompilation_engine.Log() << fmt::Format("0x%08X: ", (u64)(((u8 *)mci.address()) + pc)) << str << '\n'; + pc += size; + } + + LLVMDisasmDispose(disassembler); +#endif + auto compilation_end = std::chrono::high_resolution_clock::now(); m_stats.total_time += std::chrono::duration_cast(compilation_end - compilation_start); @@ -4191,15 +4191,24 @@ u32 Compiler::GetAddressFromBasicBlockName(const std::string & name) const { BasicBlock * Compiler::GetBasicBlockFromAddress(u32 address, const std::string & suffix, bool create_if_not_exist) { auto block_name = GetBasicBlockNameFromAddress(address, suffix); BasicBlock * block = nullptr; - for (auto i = m_state.function->getBasicBlockList().begin(); i != m_state.function->getBasicBlockList().end(); i++) { + BasicBlock * next_block = nullptr; + for (auto i = m_state.function->begin(); i != m_state.function->end(); i++) { if (i->getName() == block_name) { block = &(*i); break; } + +#ifdef _DEBUG + auto block_address = GetAddressFromBasicBlockName(i->getName()); + if (block_address > address) { + next_block = &(*i); + break; + } +#endif } if (!block && create_if_not_exist) { - block = BasicBlock::Create(m_ir_builder->getContext(), block_name, m_state.function); + block = BasicBlock::Create(m_ir_builder->getContext(), block_name, m_state.function, next_block); } return block; @@ -4688,22 +4697,10 @@ Value * Compiler::ReadMemory(Value * addr_i64, u32 bits, u32 alignment, bool bsw return val_ix; } else { - BasicBlock * next_block = nullptr; - for (auto i = m_state.function->begin(); i != m_state.function->end(); i++) { - if (&(*i) == m_ir_builder->GetInsertBlock()) { - i++; - if (i != m_state.function->end()) { - next_block = &(*i); - } - - break; - } - } - auto cmp_i1 = m_ir_builder->CreateICmpULT(addr_i64, m_ir_builder->getInt64(RAW_SPU_BASE_ADDR)); - auto then_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); - auto else_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); - auto merge_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); + auto then_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "then"); + auto else_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "else"); + auto merge_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "merge"); m_ir_builder->CreateCondBr(cmp_i1, then_bb, else_bb); m_ir_builder->SetInsertPoint(then_bb); @@ -4742,22 +4739,10 @@ void Compiler::WriteMemory(Value * addr_i64, Value * val_ix, u32 alignment, bool auto eaddr_ix_ptr = m_ir_builder->CreateIntToPtr(eaddr_i64, val_ix->getType()->getPointerTo()); m_ir_builder->CreateAlignedStore(val_ix, eaddr_ix_ptr, alignment); } else { - BasicBlock * next_block = nullptr; - for (auto i = m_state.function->begin(); i != m_state.function->end(); i++) { - if (&(*i) == m_ir_builder->GetInsertBlock()) { - i++; - if (i != m_state.function->end()) { - next_block = &(*i); - } - - break; - } - } - auto cmp_i1 = m_ir_builder->CreateICmpULT(addr_i64, m_ir_builder->getInt64(RAW_SPU_BASE_ADDR)); - auto then_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); - auto else_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); - auto merge_bb = BasicBlock::Create(m_ir_builder->getContext(), "", m_state.function, next_block); + auto then_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "then"); + auto else_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "else"); + auto merge_bb = GetBasicBlockFromAddress(m_state.current_instruction_address, "merge"); m_ir_builder->CreateCondBr(cmp_i1, then_bb, else_bb); m_ir_builder->SetInsertPoint(then_bb); @@ -4837,9 +4822,11 @@ Value * Compiler::Call(const char * name, Func function, Args... args) { } llvm::Value * Compiler::IndirectCall(u32 address, Value * context_i64, bool is_function) { - auto ordinal = m_recompilation_engine.AllocateOrdinal(address, is_function); - auto executable_addr_i64 = m_ir_builder->getInt64(m_recompilation_engine.GetAddressOfExecutableLookup() + (ordinal * sizeof(u64))); - auto executable_ptr = m_ir_builder->CreateIntToPtr(executable_addr_i64, m_compiled_function_type); + auto ordinal = m_recompilation_engine.AllocateOrdinal(address, is_function); + auto location_i64 = m_ir_builder->getInt64(m_recompilation_engine.GetAddressOfExecutableLookup() + (ordinal * sizeof(u64))); + auto location_i64_ptr = m_ir_builder->CreateIntToPtr(location_i64, m_ir_builder->getInt64Ty()->getPointerTo()); + auto executable_i64 = m_ir_builder->CreateLoad(location_i64_ptr); + auto executable_ptr = m_ir_builder->CreateIntToPtr(executable_i64, m_compiled_function_type->getPointerTo()); return m_ir_builder->CreateCall3(executable_ptr, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], context_i64); } @@ -4970,30 +4957,12 @@ void RecompilationEngine::Task() { Log() << " Time spent translating = " << compiler_stats.translation_time.count() / 1000000 << "ms\n"; Log() << " Time spent idling = " << idling_time.count() / 1000000 << "ms\n"; Log() << " Time spent doing misc tasks = " << (total_time.count() - idling_time.count() - compiler_stats.total_time.count()) / 1000000 << "ms\n"; + Log() << "Ordinals allocated = " << m_next_ordinal << "\n"; Log() << "\nInterpreter fallback stats:\n"; for (auto i = compiler_stats.interpreter_fallback_stats.begin(); i != compiler_stats.interpreter_fallback_stats.end(); i++) { Log() << i->first << " = " << i->second << "\n"; } - //log_file << "\nDisassembly:\n"; - //auto disassembler = LLVMCreateDisasm(sys::getProcessTriple().c_str(), nullptr, 0, nullptr, nullptr); - //for (auto i = m_compiled.begin(); i != m_compiled.end(); i++) { - // log_file << fmt::Format("%s: Size = %u bytes, Number of instructions = %u\n", i->second.llvm_function->getName().str().c_str(), i->second.size, i->second.num_instructions); - - // uint8_t * fn_ptr = (uint8_t *)i->second.executable; - // for (size_t pc = 0; pc < i->second.size;) { - // char str[1024]; - - // auto size = LLVMDisasmInstruction(disassembler, fn_ptr + pc, i->second.size - pc, (uint64_t)(fn_ptr + pc), str, sizeof(str)); - // log_file << str << '\n'; - // pc += size; - // } - //} - - //LLVMDisasmDispose(disassembler); - - //log_file << "\nLLVM IR:\n" << *m_module; - LOG_NOTICE(PPU, "PPU LLVM Recompilation thread exiting."); s_the_instance = nullptr; // Can cause deadlock if this is the last instance. Need to fix this. } @@ -5002,7 +4971,9 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution auto execution_trace_id = execution_trace.GetId(); auto processed_execution_trace_i = m_processed_execution_traces.find(execution_trace_id); if (processed_execution_trace_i == m_processed_execution_traces.end()) { +#ifdef _DEBUG Log() << "Trace: " << execution_trace.ToString() << "\n"; +#endif std::vector tmp_block_list; @@ -5029,9 +5000,7 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution if (trace_i + 1 != execution_trace.entries.end()) { next_trace = &(*(trace_i + 1)); } else if (!split_trace && execution_trace.type == ExecutionTrace::Type::Loop) { - if (!split_trace && execution_trace.type == ExecutionTrace::Type::Loop) { - next_trace = &(*(execution_trace.entries.begin())); - } + next_trace = &(*(execution_trace.entries.begin())); } UpdateControlFlowGraph((*block_i)->cfg, *trace_i, next_trace); @@ -5043,7 +5012,7 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution for (auto i = processed_execution_trace_i->second.begin(); i != processed_execution_trace_i->second.end(); i++) { if (!(*i)->is_compiled) { (*i)->num_hits++; - if ((*i)->num_hits >= 1) { // TODO: Make this configurable + if ((*i)->num_hits >= 1000) { // TODO: Make this configurable CompileBlock(*(*i), false); (*i)->is_compiled = true; } @@ -5078,13 +5047,15 @@ void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, const E } void RecompilationEngine::CompileBlock(BlockEntry & block_entry, bool inline_referenced_blocks) { +#ifdef _DEBUG Log() << "Compile: " << block_entry.ToString() << "\n"; +#endif - auto is_funciton = block_entry.cfg.start_address == block_entry.cfg.function_address; - auto ordinal = AllocateOrdinal(block_entry.cfg.start_address, is_funciton); + auto is_function = block_entry.cfg.start_address == block_entry.cfg.function_address; + auto ordinal = AllocateOrdinal(block_entry.cfg.start_address, is_function); auto executable = m_compiler.Compile(fmt::Format("fn_0x%08X_%u", block_entry.cfg.start_address, block_entry.revision++), block_entry.cfg, - is_funciton ? false : true /*inline_all*/, - is_funciton ? true : false /*generate_linkable_exits*/); + is_function ? false : true /*inline_all*/, + is_function ? true : false /*generate_linkable_exits*/); m_executable_lookup[ordinal] = executable; } @@ -5254,7 +5225,7 @@ u32 ppu_recompiler_llvm::ExecutionEngine::ExecuteTillReturn(PPUThread * ppu_stat } auto executable = execution_engine->GetExecutable(ppu_state->PC, ExecuteTillReturn); - if (executable != ExecuteTillReturn) { + if (executable != ExecuteTillReturn && executable != ExecuteFunction) { auto entry = ppu_state->PC; auto exit = (u32)executable(ppu_state, interpreter, 0); execution_engine->m_tracer.Trace(Tracer::TraceType::ExitFromCompiledBlock, entry, exit); diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index 963e22c8af..8c3fd6c699 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -240,20 +240,24 @@ namespace ppu_recompiler_llvm { } std::string ToString() const { - return fmt::Format("%s\nNumHits=%u, Revision=%u, IsCompiled=%c\n", cfg.ToString().c_str(), num_hits, revision, is_compiled ? 'Y' : 'N'); + return fmt::Format("%s\nNumHits=%u, Revision=%u, IsCompiled=%c", cfg.ToString().c_str(), num_hits, revision, is_compiled ? 'Y' : 'N'); } bool operator == (const BlockEntry & other) const { return cfg.start_address == other.cfg.start_address; } - }; -} -namespace std { - template<> struct hash { - size_t operator()(const ppu_recompiler_llvm::BlockEntry * e) const { - return e->cfg.start_address; - } + struct hash { + size_t operator()(const BlockEntry * e) const { + return e->cfg.start_address; + } + }; + + struct equal_to { + bool operator()(const BlockEntry * lhs, const BlockEntry * rhs) const { + return *lhs == *rhs; + } + }; }; } @@ -978,7 +982,7 @@ namespace ppu_recompiler_llvm { std::list m_pending_execution_traces; /// Block table - std::unordered_set m_block_table; + std::unordered_set m_block_table; /// Execution traces that have been already encountered. Data is the list of all blocks that this trace includes. std::unordered_map> m_processed_execution_traces; diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index d1c76abf35..c96bfad05e 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -87,7 +87,7 @@ true - $(SolutionDir)$(Platform)\$(Configuration)\emucore.lib;wxmsw31ud_adv.lib;wxbase31ud.lib;wxmsw31ud_core.lib;wxmsw31ud_aui.lib;wxtiffd.lib;wxjpegd.lib;wxpngd.lib;wxzlibd.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;libOpenAL32.dll.a;asmjit.lib;LLVMJIT.lib;LLVMVectorize.lib;LLVMX86CodeGen.lib;LLVMX86Disassembler.lib;LLVMExecutionEngine.lib;LLVMAsmPrinter.lib;LLVMSelectionDAG.lib;LLVMCodeGen.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMTransformUtils.lib;LLVMipa.lib;LLVMAnalysis.lib;LLVMTarget.lib;LLVMX86Desc.lib;LLVMX86AsmPrinter.lib;LLVMObject.lib;LLVMMCParser.lib;LLVMBitReader.lib;LLVMCore.lib;LLVMX86Utils.lib;LLVMMC.lib;LLVMX86Info.lib;LLVMSupport.lib; ;%(AdditionalDependencies) + $(SolutionDir)$(Platform)\$(Configuration)\emucore.lib;wxmsw31ud_adv.lib;wxbase31ud.lib;wxmsw31ud_core.lib;wxmsw31ud_aui.lib;wxtiffd.lib;wxjpegd.lib;wxpngd.lib;wxzlibd.lib;odbc32.lib;odbccp32.lib;comctl32.lib;ws2_32.lib;shlwapi.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;rpcrt4.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;libOpenAL32.dll.a;asmjit.lib;LLVMJIT.lib;LLVMVectorize.lib;LLVMX86CodeGen.lib;LLVMX86Disassembler.lib;LLVMExecutionEngine.lib;LLVMAsmPrinter.lib;LLVMSelectionDAG.lib;LLVMCodeGen.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMTransformUtils.lib;LLVMipa.lib;LLVMAnalysis.lib;LLVMTarget.lib;LLVMX86Desc.lib;LLVMX86AsmPrinter.lib;LLVMObject.lib;LLVMMCParser.lib;LLVMBitReader.lib;LLVMCore.lib;LLVMX86Utils.lib;LLVMMC.lib;LLVMX86Info.lib;LLVMSupport.lib; LLVMMCDisassembler.lib;%(AdditionalDependencies) %(IgnoreSpecificDefaultLibraries) false ..\wxWidgets\lib\vc_x64_lib;..\ffmpeg\Windows\x86_64\lib;..\OpenAL\Win64;..\llvm_build\Debug\lib From 5c468d7591f60f719bcb60b8be4f23e7e16be103 Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Sun, 9 Nov 2014 11:50:01 +0530 Subject: [PATCH 08/12] Disabled call stack tracing when using the PPU LLVM recompiler --- rpcs3/Emu/CPU/CPUThread.cpp | 3 ++- rpcs3/Emu/CPU/CPUThread.h | 4 ++++ rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 2 +- rpcs3/Emu/Cell/PPUThread.cpp | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index fea5e15400..5990228a29 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -29,6 +29,7 @@ CPUThread::CPUThread(CPUThreadType type) , m_status(Stopped) , m_last_syscall(0) , m_trace_enabled(false) + , m_trace_call_stack(true) { } @@ -155,7 +156,7 @@ void CPUThread::SetBranch(const u32 pc, bool record_branch) m_is_branch = true; nPC = pc; - if(record_branch) + if(m_trace_call_stack && record_branch) CallStackBranch(pc); } diff --git a/rpcs3/Emu/CPU/CPUThread.h b/rpcs3/Emu/CPU/CPUThread.h index d15af1431d..35d48dd990 100644 --- a/rpcs3/Emu/CPU/CPUThread.h +++ b/rpcs3/Emu/CPU/CPUThread.h @@ -43,6 +43,8 @@ protected: CPUDecoder* m_dec; + bool m_trace_call_stack; + public: virtual void InitRegs()=0; @@ -177,6 +179,8 @@ public: u32 GetId() const { return m_id; } CPUThreadType GetType() const { return m_type; } + void SetCallStackTracing(bool trace_call_stack) { m_trace_call_stack = trace_call_stack; } + void Reset(); void Close(); void Run(); diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 11f83eb8ca..eb7ad0fa8d 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -237,7 +237,7 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & m_stats.ir_build_time += std::chrono::duration_cast(ir_build_end - compilation_start); // Optimize this function - //m_fpm->run(*m_state.function); + m_fpm->run(*m_state.function); auto optimize_end = std::chrono::high_resolution_clock::now(); m_stats.optimization_time += std::chrono::duration_cast(optimize_end - ir_build_end); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index df7ce2740b..a74a64fa2d 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -111,6 +111,7 @@ void PPUThread::DoRun() break; case 2: + SetCallStackTracing(false); if (!m_dec) { m_dec = new ppu_recompiler_llvm::ExecutionEngine(*this); } From 71c12360c7ef8232cf157680cf08046077ef10a3 Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Sun, 9 Nov 2014 14:03:36 +0530 Subject: [PATCH 09/12] Always inline functions --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 41 +++++++++++++++++++++++----- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 40 +++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index eb7ad0fa8d..5e48bb5548 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -4991,6 +4991,17 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution block_i = m_block_table.find(&key); if (block_i == m_block_table.end()) { block_i = m_block_table.insert(m_block_table.end(), new BlockEntry(key.cfg.start_address, key.cfg.function_address)); + + // Update the function to block map + auto function_to_block_i = m_function_to_blocks.find(execution_trace.function_address); + if (function_to_block_i == m_function_to_blocks.end()) { + function_to_block_i = m_function_to_blocks.insert(m_function_to_blocks.end(), std::make_pair(execution_trace.function_address, std::vector())); + } + + auto i = std::find(function_to_block_i->second.begin(), function_to_block_i->second.end(), *block_i); + if (i == function_to_block_i->second.end()) { + function_to_block_i->second.push_back(*block_i); + } } tmp_block_list.push_back(*block_i); @@ -5013,7 +5024,7 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution if (!(*i)->is_compiled) { (*i)->num_hits++; if ((*i)->num_hits >= 1000) { // TODO: Make this configurable - CompileBlock(*(*i), false); + CompileBlock(*(*i)); (*i)->is_compiled = true; } } @@ -5046,16 +5057,32 @@ void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, const E } } -void RecompilationEngine::CompileBlock(BlockEntry & block_entry, bool inline_referenced_blocks) { +void RecompilationEngine::CompileBlock(BlockEntry & block_entry) { #ifdef _DEBUG Log() << "Compile: " << block_entry.ToString() << "\n"; #endif - auto is_function = block_entry.cfg.start_address == block_entry.cfg.function_address; - auto ordinal = AllocateOrdinal(block_entry.cfg.start_address, is_function); - auto executable = m_compiler.Compile(fmt::Format("fn_0x%08X_%u", block_entry.cfg.start_address, block_entry.revision++), block_entry.cfg, - is_function ? false : true /*inline_all*/, - is_function ? true : false /*generate_linkable_exits*/); + ControlFlowGraph * cfg; + ControlFlowGraph temp_cfg(block_entry.cfg.start_address, block_entry.cfg.function_address); + if (block_entry.IsFunction()) { + // Form a CFG by merging all the blocks in this function + auto function_to_block_i = m_function_to_blocks.find(block_entry.cfg.function_address); + for (auto block_i = function_to_block_i->second.begin(); block_i != function_to_block_i->second.end(); block_i++) { + temp_cfg += (*block_i)->cfg; + } + + cfg = &temp_cfg; + } else { + cfg = &block_entry.cfg; + } + +#ifdef _DEBUG + Log() << "CFG: " << cfg->ToString() << "\n"; +#endif + + auto ordinal = AllocateOrdinal(block_entry.cfg.start_address, block_entry.IsFunction()); + auto executable = m_compiler.Compile(fmt::Format("fn_0x%08X_%u", block_entry.cfg.start_address, block_entry.revision++), *cfg, true, + block_entry.IsFunction() ? true : false /*generate_linkable_exits*/); m_executable_lookup[ordinal] = executable; } diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index 8c3fd6c699..20523aa5a4 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -182,6 +182,34 @@ namespace ppu_recompiler_llvm { , function_address(function_address) { } + void operator += (const ControlFlowGraph & other) { + for (auto i = other.instruction_addresses.begin(); i != other.instruction_addresses.end(); i++) { + instruction_addresses.insert(*i); + } + + for (auto i = other.branches.begin(); i != other.branches.end(); i++) { + auto j = branches.find(i->first); + if (j == branches.end()) { + j = branches.insert(branches.begin(), std::make_pair(i->first, std::set())); + } + + for (auto k = i->second.begin(); k != i->second.end(); k++) { + j->second.insert(*k); + } + } + + for (auto i = other.calls.begin(); i != other.calls.end(); i++) { + auto j = calls.find(i->first); + if (j == calls.end()) { + j = calls.insert(calls.begin(), std::make_pair(i->first, std::set())); + } + + for (auto k = i->second.begin(); k != i->second.end(); k++) { + j->second.insert(*k); + } + } + } + std::string ToString() const { auto s = fmt::Format("0x%08X (0x%08X):", start_address, function_address); for (auto i = instruction_addresses.begin(); i != instruction_addresses.end(); i++) { @@ -240,13 +268,18 @@ namespace ppu_recompiler_llvm { } std::string ToString() const { - return fmt::Format("%s\nNumHits=%u, Revision=%u, IsCompiled=%c", cfg.ToString().c_str(), num_hits, revision, is_compiled ? 'Y' : 'N'); + return fmt::Format("0x%08X (0x%08X): NumHits=%u, Revision=%u, IsCompiled=%c", + cfg.start_address, cfg.function_address, num_hits, revision, is_compiled ? 'Y' : 'N'); } bool operator == (const BlockEntry & other) const { return cfg.start_address == other.cfg.start_address; } + bool IsFunction() const { + return cfg.function_address == cfg.start_address; + } + struct hash { size_t operator()(const BlockEntry * e) const { return e->cfg.start_address; @@ -984,6 +1017,9 @@ namespace ppu_recompiler_llvm { /// Block table std::unordered_set m_block_table; + /// Maps a function to the set of all blocks in the function. Key is the address of the function. + std::unordered_map> m_function_to_blocks; + /// Execution traces that have been already encountered. Data is the list of all blocks that this trace includes. std::unordered_map> m_processed_execution_traces; @@ -1021,7 +1057,7 @@ namespace ppu_recompiler_llvm { void UpdateControlFlowGraph(ControlFlowGraph & cfg, const ExecutionTraceEntry & this_entry, const ExecutionTraceEntry * next_entry); /// Compile a block - void CompileBlock(BlockEntry & block_entry, bool inline_referenced_blocks); + void CompileBlock(BlockEntry & block_entry); /// Mutex used to prevent multiple creation static std::mutex s_mutex; From c12a98510c8efb435ec17fce2b726a70f5c1ef5c Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Mon, 10 Nov 2014 01:04:21 +0530 Subject: [PATCH 10/12] Utilize idle time to combine blocks --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 114 +++++++++++++------- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 151 ++++++++++++++++++--------- 2 files changed, 177 insertions(+), 88 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 5e48bb5548..afcfc66f15 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -4908,41 +4908,70 @@ raw_fd_ostream & RecompilationEngine::Log() { } void RecompilationEngine::Task() { + bool work_done_this_iteration = false; + bool work_done_last_iteration = false; std::chrono::nanoseconds idling_time(0); + std::chrono::nanoseconds recompiling_time(0); auto start = std::chrono::high_resolution_clock::now(); while (!TestDestroy() && !Emu.IsStopped()) { - // Wait a few ms for something to happen - auto idling_start = std::chrono::high_resolution_clock::now(); - WaitForAnySignal(250); - auto idling_end = std::chrono::high_resolution_clock::now(); - idling_time += std::chrono::duration_cast(idling_end - idling_start); + work_done_last_iteration = work_done_this_iteration; + work_done_this_iteration = false; + ExecutionTrace * execution_trace = nullptr; - u32 num_processed = 0; - while (!TestDestroy() && !Emu.IsStopped()) { - ExecutionTrace * execution_trace; + { + std::lock_guard lock(m_pending_execution_traces_lock); - { - std::lock_guard lock(m_pending_execution_traces_lock); + auto i = m_pending_execution_traces.begin(); + if (i != m_pending_execution_traces.end()) { + execution_trace = *i; + m_pending_execution_traces.erase(i); + } + } - auto i = m_pending_execution_traces.begin(); - if (i != m_pending_execution_traces.end()) { - execution_trace = *i; - m_pending_execution_traces.erase(i); - } else { - break; + if (execution_trace) { + ProcessExecutionTrace(*execution_trace); + delete execution_trace; + work_done_this_iteration = true; + } + + if (!work_done_this_iteration) { + // TODO: Reduce the priority of the recompilation engine thread if its set to high priority + } + + if (!work_done_this_iteration && !work_done_last_iteration) { + auto recompiling_start = std::chrono::high_resolution_clock::now(); + + // Recompile the function with the most number of compiled fragments + auto candidate = m_function_table.end(); + for (auto function_i = m_function_table.begin(); function_i != m_function_table.end(); function_i++) { + if ((*function_i)->num_compiled_fragments && (*function_i)->blocks.front()->IsFunction() && (*function_i)->blocks.front()->is_compiled) { + if (candidate != m_function_table.end()) { + if ((*function_i)->num_compiled_fragments > (*candidate)->num_compiled_fragments) { + candidate = function_i; + } + } else { + candidate = function_i; + } } } - ProcessExecutionTrace(*execution_trace); - delete execution_trace; + if (candidate != m_function_table.end()) { + Log() << "Recompiling: " << (*candidate)->ToString() << "\n"; + CompileBlock(*(*candidate), *((*candidate)->blocks.front())); + work_done_this_iteration = true; + } + + auto recompiling_end = std::chrono::high_resolution_clock::now(); + recompiling_time += std::chrono::duration_cast(recompiling_end - recompiling_start); } - // TODO: Reduce the priority of the recompilation engine thread - - if (num_processed == 0) { - // If we get here, it means the recompilation engine is idling. - // We should use this oppurtunity to optimize the code. + if (!work_done_this_iteration) { + // Wait a few ms for something to happen + auto idling_start = std::chrono::high_resolution_clock::now(); + WaitForAnySignal(250); + auto idling_end = std::chrono::high_resolution_clock::now(); + idling_time += std::chrono::duration_cast(idling_end - idling_start); } } @@ -4955,6 +4984,7 @@ void RecompilationEngine::Task() { Log() << " Time spent building IR = " << compiler_stats.ir_build_time.count() / 1000000 << "ms\n"; Log() << " Time spent optimizing = " << compiler_stats.optimization_time.count() / 1000000 << "ms\n"; Log() << " Time spent translating = " << compiler_stats.translation_time.count() / 1000000 << "ms\n"; + Log() << " Time spent recompiling = " << recompiling_time.count() / 1000000 << "ms\n"; Log() << " Time spent idling = " << idling_time.count() / 1000000 << "ms\n"; Log() << " Time spent doing misc tasks = " << (total_time.count() - idling_time.count() - compiler_stats.total_time.count()) / 1000000 << "ms\n"; Log() << "Ordinals allocated = " << m_next_ordinal << "\n"; @@ -4968,6 +4998,8 @@ void RecompilationEngine::Task() { } void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution_trace) { + auto function_i = m_function_table.end(); + auto execution_trace_id = execution_trace.GetId(); auto processed_execution_trace_i = m_processed_execution_traces.find(execution_trace_id); if (processed_execution_trace_i == m_processed_execution_traces.end()) { @@ -4992,16 +5024,16 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution if (block_i == m_block_table.end()) { block_i = m_block_table.insert(m_block_table.end(), new BlockEntry(key.cfg.start_address, key.cfg.function_address)); - // Update the function to block map - auto function_to_block_i = m_function_to_blocks.find(execution_trace.function_address); - if (function_to_block_i == m_function_to_blocks.end()) { - function_to_block_i = m_function_to_blocks.insert(m_function_to_blocks.end(), std::make_pair(execution_trace.function_address, std::vector())); + if (function_i == m_function_table.end()) { + FunctionEntry key(execution_trace.function_address); + function_i = m_function_table.find(&key); + if (function_i == m_function_table.end()) { + function_i = m_function_table.insert(m_function_table.end(), new FunctionEntry(key.address)); + } } - auto i = std::find(function_to_block_i->second.begin(), function_to_block_i->second.end(), *block_i); - if (i == function_to_block_i->second.end()) { - function_to_block_i->second.push_back(*block_i); - } + // Update the function table + (*function_i)->AddBlock(*block_i); } tmp_block_list.push_back(*block_i); @@ -5024,7 +5056,12 @@ void RecompilationEngine::ProcessExecutionTrace(const ExecutionTrace & execution if (!(*i)->is_compiled) { (*i)->num_hits++; if ((*i)->num_hits >= 1000) { // TODO: Make this configurable - CompileBlock(*(*i)); + if (function_i == m_function_table.end()) { + FunctionEntry key(execution_trace.function_address); + function_i = m_function_table.find(&key); + } + + CompileBlock(*(*function_i), *(*i)); (*i)->is_compiled = true; } } @@ -5057,17 +5094,16 @@ void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, const E } } -void RecompilationEngine::CompileBlock(BlockEntry & block_entry) { +void RecompilationEngine::CompileBlock(FunctionEntry & function_entry, BlockEntry & block_entry) { #ifdef _DEBUG Log() << "Compile: " << block_entry.ToString() << "\n"; #endif - ControlFlowGraph * cfg; ControlFlowGraph temp_cfg(block_entry.cfg.start_address, block_entry.cfg.function_address); + ControlFlowGraph * cfg; if (block_entry.IsFunction()) { // Form a CFG by merging all the blocks in this function - auto function_to_block_i = m_function_to_blocks.find(block_entry.cfg.function_address); - for (auto block_i = function_to_block_i->second.begin(); block_i != function_to_block_i->second.end(); block_i++) { + for (auto block_i = function_entry.blocks.begin(); block_i != function_entry.blocks.end(); block_i++) { temp_cfg += (*block_i)->cfg; } @@ -5084,6 +5120,12 @@ void RecompilationEngine::CompileBlock(BlockEntry & block_entry) { auto executable = m_compiler.Compile(fmt::Format("fn_0x%08X_%u", block_entry.cfg.start_address, block_entry.revision++), *cfg, true, block_entry.IsFunction() ? true : false /*generate_linkable_exits*/); m_executable_lookup[ordinal] = executable; + + if (block_entry.IsFunction()) { + function_entry.num_compiled_fragments = 0; + } else { + function_entry.num_compiled_fragments++; + } } std::shared_ptr RecompilationEngine::GetInstance() { diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index 20523aa5a4..c5de00940e 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -246,55 +246,6 @@ namespace ppu_recompiler_llvm { /// Pointer to an executable typedef u32(*Executable)(PPUThread * ppu_state, PPUInterpreter * interpreter, u64 context); - /// An entry in the block table - struct BlockEntry { - /// Number of times this block was hit - u32 num_hits; - - /// The current revision number of this function - u32 revision; - - /// The CFG for this block - ControlFlowGraph cfg; - - /// Indicates whether the block has been compiled or not - bool is_compiled; - - BlockEntry(u32 start_address, u32 function_address) - : num_hits(0) - , revision(0) - , is_compiled(false) - , cfg(start_address, function_address) { - } - - std::string ToString() const { - return fmt::Format("0x%08X (0x%08X): NumHits=%u, Revision=%u, IsCompiled=%c", - cfg.start_address, cfg.function_address, num_hits, revision, is_compiled ? 'Y' : 'N'); - } - - bool operator == (const BlockEntry & other) const { - return cfg.start_address == other.cfg.start_address; - } - - bool IsFunction() const { - return cfg.function_address == cfg.start_address; - } - - struct hash { - size_t operator()(const BlockEntry * e) const { - return e->cfg.start_address; - } - }; - - struct equal_to { - bool operator()(const BlockEntry * lhs, const BlockEntry * rhs) const { - return *lhs == *rhs; - } - }; - }; -} - -namespace ppu_recompiler_llvm { /// PPU compiler that uses LLVM for code generation and optimization class Compiler : protected PPUOpcodes, protected PPCDecoder { public: @@ -1008,6 +959,102 @@ namespace ppu_recompiler_llvm { static std::shared_ptr GetInstance(); private: + /// An entry in the block table + struct BlockEntry { + /// Number of times this block was hit + u32 num_hits; + + /// The current revision number of this function + u32 revision; + + /// The CFG for this block + ControlFlowGraph cfg; + + /// Indicates whether the block has been compiled or not + bool is_compiled; + + BlockEntry(u32 start_address, u32 function_address) + : num_hits(0) + , revision(0) + , is_compiled(false) + , cfg(start_address, function_address) { + } + + std::string ToString() const { + return fmt::Format("0x%08X (0x%08X): NumHits=%u, Revision=%u, IsCompiled=%c", + cfg.start_address, cfg.function_address, num_hits, revision, is_compiled ? 'Y' : 'N'); + } + + bool operator == (const BlockEntry & other) const { + return cfg.start_address == other.cfg.start_address; + } + + bool IsFunction() const { + return cfg.function_address == cfg.start_address; + } + + struct hash { + size_t operator()(const BlockEntry * e) const { + return e->cfg.start_address; + } + }; + + struct equal_to { + bool operator()(const BlockEntry * lhs, const BlockEntry * rhs) const { + return *lhs == *rhs; + } + }; + }; + + /// An entry in the function table + struct FunctionEntry { + /// Address of the function + u32 address; + + /// Number of compiled fragments + u32 num_compiled_fragments; + + /// Blocks in the function + std::list blocks; + + FunctionEntry(u32 address) + : address(address) + , num_compiled_fragments(0) { + } + + void AddBlock(BlockEntry * block_entry) { + auto i = std::find(blocks.begin(), blocks.end(), block_entry); + if (i == blocks.end()) { + if (block_entry->IsFunction()) { + // The first block must be the starting block of the function + blocks.push_front(block_entry); + } else { + blocks.push_back(block_entry); + } + } + } + + std::string ToString() const { + return fmt::Format("0x%08X: NumCompiledFragments=%u, NumBlocks=%u", address, num_compiled_fragments, blocks.size()); + } + + bool operator == (const FunctionEntry & other) const { + return address == other.address; + } + + struct hash { + size_t operator()(const FunctionEntry * f) const { + return f->address; + } + }; + + struct equal_to { + bool operator()(const FunctionEntry * lhs, const FunctionEntry * rhs) const { + return *lhs == *rhs; + } + }; + }; + /// Lock for accessing m_pending_execution_traces. TODO: Eliminate this and use a lock-free queue. std::mutex m_pending_execution_traces_lock; @@ -1017,8 +1064,8 @@ namespace ppu_recompiler_llvm { /// Block table std::unordered_set m_block_table; - /// Maps a function to the set of all blocks in the function. Key is the address of the function. - std::unordered_map> m_function_to_blocks; + /// Function table + std::unordered_set m_function_table; /// Execution traces that have been already encountered. Data is the list of all blocks that this trace includes. std::unordered_map> m_processed_execution_traces; @@ -1057,7 +1104,7 @@ namespace ppu_recompiler_llvm { void UpdateControlFlowGraph(ControlFlowGraph & cfg, const ExecutionTraceEntry & this_entry, const ExecutionTraceEntry * next_entry); /// Compile a block - void CompileBlock(BlockEntry & block_entry); + void CompileBlock(FunctionEntry & function_entry, BlockEntry & block_entry); /// Mutex used to prevent multiple creation static std::mutex s_mutex; From 87accc624ff6513f90f5bedc3aeb1f10fef1c9a1 Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Mon, 10 Nov 2014 12:49:48 +0530 Subject: [PATCH 11/12] Fixed some errors thrown by gcc/clang --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 10 +++++----- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 30 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 85d616d633..0ed3301b29 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -5094,15 +5094,15 @@ void RecompilationEngine::UpdateControlFlowGraph(ControlFlowGraph & cfg, const E cfg.branches[this_entry.GetPrimaryAddress()].insert(next_entry->GetPrimaryAddress()); } } else if (next_entry->type == ExecutionTraceEntry::Type::FunctionCall) { - cfg.calls[this_entry.instruction.address].insert(next_entry->GetPrimaryAddress()); + cfg.calls[this_entry.data.instruction.address].insert(next_entry->GetPrimaryAddress()); } } } else if (this_entry.type == ExecutionTraceEntry::Type::CompiledBlock) { if (next_entry) { if (next_entry->type == ExecutionTraceEntry::Type::Instruction || next_entry->type == ExecutionTraceEntry::Type::CompiledBlock) { - cfg.branches[this_entry.compiled_block.exit_address].insert(next_entry->GetPrimaryAddress()); + cfg.branches[this_entry.data.compiled_block.exit_address].insert(next_entry->GetPrimaryAddress()); } else if (next_entry->type == ExecutionTraceEntry::Type::FunctionCall) { - cfg.calls[this_entry.compiled_block.exit_address].insert(next_entry->GetPrimaryAddress()); + cfg.calls[this_entry.data.compiled_block.exit_address].insert(next_entry->GetPrimaryAddress()); } } } @@ -5190,8 +5190,8 @@ void Tracer::Trace(TraceType trace_type, u32 arg1, u32 arg2) { case TraceType::Instruction: // arg1 is the address of the instruction for (int i = (int)m_stack.back()->entries.size() - 1; i >= 0; i--) { - if ((m_stack.back()->entries[i].type == ExecutionTraceEntry::Type::Instruction && m_stack.back()->entries[i].instruction.address == arg1) || - (m_stack.back()->entries[i].type == ExecutionTraceEntry::Type::CompiledBlock && m_stack.back()->entries[i].compiled_block.entry_address == arg1)) { + if ((m_stack.back()->entries[i].type == ExecutionTraceEntry::Type::Instruction && m_stack.back()->entries[i].data.instruction.address == arg1) || + (m_stack.back()->entries[i].type == ExecutionTraceEntry::Type::CompiledBlock && m_stack.back()->entries[i].data.compiled_block.entry_address == arg1)) { // Found a loop execution_trace = new ExecutionTrace(m_stack.back()->function_address); execution_trace->type = ExecutionTrace::Type::Loop; diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index 680a8988df..edee34d2dd 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -38,7 +38,7 @@ namespace ppu_recompiler_llvm { u32 entry_address; u32 exit_address; } compiled_block; - }; + } data; /// The type of the entry enum class Type { @@ -51,14 +51,14 @@ namespace ppu_recompiler_llvm { : type(type) { switch (type) { case Type::Instruction: - instruction.address = arg1; + data.instruction.address = arg1; break; case Type::FunctionCall: - function_call.address = arg1; + data.function_call.address = arg1; break; case Type::CompiledBlock: - compiled_block.entry_address = arg1; - compiled_block.exit_address = arg2; + data.compiled_block.entry_address = arg1; + data.compiled_block.exit_address = arg2; break; default: assert(0); @@ -69,11 +69,11 @@ namespace ppu_recompiler_llvm { u32 GetPrimaryAddress() const { switch (type) { case Type::Instruction: - return instruction.address; + return data.instruction.address; case Type::FunctionCall: - return function_call.address; + return data.function_call.address; case Type::CompiledBlock: - return compiled_block.entry_address; + return data.compiled_block.entry_address; default: assert(0); return 0; @@ -83,11 +83,11 @@ namespace ppu_recompiler_llvm { std::string ToString() const { switch (type) { case Type::Instruction: - return fmt::Format("I:0x%08X", instruction.address); + return fmt::Format("I:0x%08X", data.instruction.address); case Type::FunctionCall: - return fmt::Format("F:0x%08X", function_call.address); + return fmt::Format("F:0x%08X", data.function_call.address); case Type::CompiledBlock: - return fmt::Format("C:0x%08X-0x%08X", compiled_block.entry_address, compiled_block.exit_address); + return fmt::Format("C:0x%08X-0x%08X", data.compiled_block.entry_address, data.compiled_block.exit_address); default: assert(0); return ""; @@ -98,15 +98,15 @@ namespace ppu_recompiler_llvm { u64 hash = ((u64)type << 32); switch (type) { case Type::Instruction: - hash |= instruction.address; + hash |= data.instruction.address; break; case Type::FunctionCall: - hash |= function_call.address; + hash |= data.function_call.address; break; case Type::CompiledBlock: - hash = compiled_block.exit_address; + hash = data.compiled_block.exit_address; hash <<= 32; - hash |= compiled_block.entry_address; + hash |= data.compiled_block.entry_address; break; default: assert(0); From 1568d2d602b8febb0b78740166eff3594e157018 Mon Sep 17 00:00:00 2001 From: S Gopal Rajagopal Date: Mon, 10 Nov 2014 14:32:41 +0530 Subject: [PATCH 12/12] Fixed some more gcc/clang compilation errors --- rpcs3/Emu/Cell/PPULLVMRecompiler.cpp | 29 ++++++++++++++-------------- rpcs3/Emu/Cell/PPULLVMRecompiler.h | 12 ++++++------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp index 0ed3301b29..aad5ca6d0f 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.cpp @@ -26,7 +26,7 @@ using namespace ppu_recompiler_llvm; u64 Compiler::s_rotate_mask[64][64]; bool Compiler::s_rotate_mask_inited = false; -Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable unknown_function, const Executable unknown_block) +Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable execute_unknown_function, const Executable execute_unknown_block) : m_recompilation_engine(recompilation_engine) { InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); @@ -70,13 +70,13 @@ Compiler::Compiler(RecompilationEngine & recompilation_engine, const Executable arg_types.push_back(m_ir_builder->getInt64Ty()); m_compiled_function_type = FunctionType::get(m_ir_builder->getInt32Ty(), arg_types, false); - m_unknown_function = (Function *)m_module->getOrInsertFunction("unknown_function", m_compiled_function_type); - m_unknown_function->setCallingConv(CallingConv::X86_64_Win64); - m_execution_engine->addGlobalMapping(m_unknown_function, unknown_function); + m_execute_unknown_function = (Function *)m_module->getOrInsertFunction("execute_unknown_function", m_compiled_function_type); + m_execute_unknown_function->setCallingConv(CallingConv::X86_64_Win64); + m_execution_engine->addGlobalMapping(m_execute_unknown_function, (void *)execute_unknown_function); - m_unknown_block = (Function *)m_module->getOrInsertFunction("unknown_block", m_compiled_function_type); - m_unknown_block->setCallingConv(CallingConv::X86_64_Win64); - m_execution_engine->addGlobalMapping(m_unknown_block, unknown_block); + m_execute_unknown_block = (Function *)m_module->getOrInsertFunction("execute_unknown_block", m_compiled_function_type); + m_execute_unknown_block->setCallingConv(CallingConv::X86_64_Win64); + m_execution_engine->addGlobalMapping(m_execute_unknown_block, (void *)execute_unknown_block); if (!s_rotate_mask_inited) { InitRotateMask(); @@ -178,7 +178,7 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & m_ir_builder->SetInsertPoint(then_bb); context_i64 = m_ir_builder->CreateZExt(ret_i32, m_ir_builder->getInt64Ty()); context_i64 = m_ir_builder->CreateOr(context_i64, (u64)cfg.function_address << 32); - m_ir_builder->CreateCall3(m_unknown_block, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], context_i64); + m_ir_builder->CreateCall3(m_execute_unknown_block, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], context_i64); m_ir_builder->CreateBr(merge_bb); m_ir_builder->SetInsertPoint(merge_bb); @@ -204,7 +204,7 @@ Executable Compiler::Compile(const std::string & name, const ControlFlowGraph & m_ir_builder->SetInsertPoint(then_bb); auto context_i64 = m_ir_builder->CreateZExt(exit_instr_i32, m_ir_builder->getInt64Ty()); context_i64 = m_ir_builder->CreateOr(context_i64, (u64)cfg.function_address << 32); - m_ir_builder->CreateCall3(m_unknown_block, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], context_i64); + m_ir_builder->CreateCall3(m_execute_unknown_block, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], context_i64); m_ir_builder->CreateBr(merge_bb); m_ir_builder->SetInsertPoint(merge_bb); @@ -4660,7 +4660,7 @@ void Compiler::CreateBranch(llvm::Value * cmp_i1, llvm::Value * target_i32, bool auto switch_instr = m_ir_builder->CreateSwitch(target_i32, unknown_function_block); m_ir_builder->SetInsertPoint(unknown_function_block); - m_ir_builder->CreateCall3(m_unknown_function, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], m_ir_builder->getInt64(0)); + m_ir_builder->CreateCall3(m_execute_unknown_function, m_state.args[CompileTaskState::Args::State], m_state.args[CompileTaskState::Args::Interpreter], m_ir_builder->getInt64(0)); m_ir_builder->CreateBr(next_block); auto call_i = m_state.cfg->calls.find(m_state.current_instruction_address); @@ -4859,9 +4859,10 @@ std::shared_ptr RecompilationEngine::s_the_instance = nullp RecompilationEngine::RecompilationEngine() : ThreadBase("PPU Recompilation Engine") , m_next_ordinal(0) - , m_compiler(*this, ExecutionEngine::ExecuteFunction, ExecutionEngine::ExecuteTillReturn) - , m_log("PPULLVMRecompiler.log", std::string(), sys::fs::F_Text) { - m_log.SetUnbuffered(); + , m_compiler(*this, ExecutionEngine::ExecuteFunction, ExecutionEngine::ExecuteTillReturn) { + std::string error; + m_log = new raw_fd_ostream("PPULLVMRecompiler.log", error, sys::fs::F_Text); + m_log->SetUnbuffered(); } RecompilationEngine::~RecompilationEngine() { @@ -4918,7 +4919,7 @@ void RecompilationEngine::NotifyTrace(ExecutionTrace * execution_trace) { } raw_fd_ostream & RecompilationEngine::Log() { - return m_log; + return *m_log; } void RecompilationEngine::Task() { diff --git a/rpcs3/Emu/Cell/PPULLVMRecompiler.h b/rpcs3/Emu/Cell/PPULLVMRecompiler.h index edee34d2dd..0da0c1835e 100644 --- a/rpcs3/Emu/Cell/PPULLVMRecompiler.h +++ b/rpcs3/Emu/Cell/PPULLVMRecompiler.h @@ -269,7 +269,7 @@ namespace ppu_recompiler_llvm { std::map interpreter_fallback_stats; }; - Compiler(RecompilationEngine & recompilation_engine, const Executable unknown_function, const Executable unknown_block); + Compiler(RecompilationEngine & recompilation_engine, const Executable execute_unknown_function, const Executable execute_unknown_block); Compiler(const Compiler & other) = delete; Compiler(Compiler && other) = delete; @@ -731,11 +731,11 @@ namespace ppu_recompiler_llvm { /// Recompilation engine RecompilationEngine & m_recompilation_engine; - /// The function that will be called to process unknown functions - llvm::Function * m_unknown_function; + /// The function that will be called to execute unknown functions + llvm::Function * m_execute_unknown_function; - /// The executable that will be called to process unknown blocks - llvm::Function * m_unknown_block; + /// The executable that will be called to execute unknown blocks + llvm::Function * m_execute_unknown_block; /// LLVM context llvm::LLVMContext * m_llvm_context; @@ -1087,7 +1087,7 @@ namespace ppu_recompiler_llvm { Compiler m_compiler; /// Log - llvm::raw_fd_ostream m_log; + llvm::raw_fd_ostream * m_log; /// Executable lookup table Executable m_executable_lookup[10000]; // TODO: Adjust size