diff --git a/src/core/cpu_code_cache.cpp b/src/core/cpu_code_cache.cpp index 7d102759c..0a01c527d 100644 --- a/src/core/cpu_code_cache.cpp +++ b/src/core/cpu_code_cache.cpp @@ -936,8 +936,12 @@ bool CPU::CodeCache::ReadBlockInstructions(u32 start_pc, BlockInstructionList* i } Instruction instruction; - if (!SafeReadInstruction(pc, &instruction.bits) || !IsInvalidInstruction(instruction)) + if (!SafeReadInstruction(pc, &instruction.bits) || !IsValidInstruction(instruction)) + { + // Away to the int you go! + ERROR_LOG("Instruction read failed at PC=0x{:08X}, truncating block.", pc); break; + } InstructionInfo info; std::memset(&info, 0, sizeof(info)); @@ -951,8 +955,6 @@ bool CPU::CodeCache::ReadBlockInstructions(u32 start_pc, BlockInstructionList* i info.is_load_instruction = IsMemoryLoadInstruction(instruction); info.is_store_instruction = IsMemoryStoreInstruction(instruction); info.has_load_delay = InstructionHasLoadDelay(instruction); - info.can_trap = CanInstructionTrap(instruction, false /*InUserMode()*/); - info.is_direct_branch_instruction = IsDirectBranchInstruction(instruction); if (g_settings.cpu_recompiler_icache) { diff --git a/src/core/cpu_code_cache_private.h b/src/core/cpu_code_cache_private.h index 4a5e7f596..190ffe62e 100644 --- a/src/core/cpu_code_cache_private.h +++ b/src/core/cpu_code_cache_private.h @@ -53,7 +53,6 @@ struct InstructionInfo bool is_load_delay_slot : 1; bool is_last_instruction : 1; bool has_load_delay : 1; - bool can_trap : 1; u8 reg_flags[static_cast(Reg::count)]; // Reg write_reg[3]; diff --git a/src/core/cpu_newrec_compiler_aarch32.cpp b/src/core/cpu_newrec_compiler_aarch32.cpp index 30af0cc9b..376d88e07 100644 --- a/src/core/cpu_newrec_compiler_aarch32.cpp +++ b/src/core/cpu_newrec_compiler_aarch32.cpp @@ -721,6 +721,8 @@ void CPU::NewRec::AArch32Compiler::Flush(u32 flags) void CPU::NewRec::AArch32Compiler::Compile_Fallback() { + WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", iinfo->pc, inst->bits); + Flush(FLUSH_FOR_INTERPRETER); EmitCall(reinterpret_cast(&CPU::Recompiler::Thunks::InterpretInstruction)); diff --git a/src/core/cpu_newrec_compiler_aarch64.cpp b/src/core/cpu_newrec_compiler_aarch64.cpp index 6544135cc..3a34099de 100644 --- a/src/core/cpu_newrec_compiler_aarch64.cpp +++ b/src/core/cpu_newrec_compiler_aarch64.cpp @@ -693,6 +693,8 @@ void CPU::NewRec::AArch64Compiler::Flush(u32 flags) void CPU::NewRec::AArch64Compiler::Compile_Fallback() { + WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", iinfo->pc, inst->bits); + Flush(FLUSH_FOR_INTERPRETER); EmitCall(reinterpret_cast(&CPU::Recompiler::Thunks::InterpretInstruction)); diff --git a/src/core/cpu_newrec_compiler_riscv64.cpp b/src/core/cpu_newrec_compiler_riscv64.cpp index 759f48968..ec878a7f3 100644 --- a/src/core/cpu_newrec_compiler_riscv64.cpp +++ b/src/core/cpu_newrec_compiler_riscv64.cpp @@ -970,6 +970,8 @@ void CPU::NewRec::RISCV64Compiler::Flush(u32 flags) void CPU::NewRec::RISCV64Compiler::Compile_Fallback() { + WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", iinfo->pc, inst->bits); + Flush(FLUSH_FOR_INTERPRETER); #if 0 diff --git a/src/core/cpu_newrec_compiler_x64.cpp b/src/core/cpu_newrec_compiler_x64.cpp index 58b862b73..08940d3f9 100644 --- a/src/core/cpu_newrec_compiler_x64.cpp +++ b/src/core/cpu_newrec_compiler_x64.cpp @@ -610,6 +610,8 @@ void CPU::NewRec::X64Compiler::Flush(u32 flags) void CPU::NewRec::X64Compiler::Compile_Fallback() { + WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", iinfo->pc, inst->bits); + Flush(FLUSH_FOR_INTERPRETER); cg->call(&CPU::Recompiler::Thunks::InterpretInstruction); diff --git a/src/core/cpu_recompiler_code_generator.cpp b/src/core/cpu_recompiler_code_generator.cpp index fe2bb27f0..af340634b 100644 --- a/src/core/cpu_recompiler_code_generator.cpp +++ b/src/core/cpu_recompiler_code_generator.cpp @@ -1191,6 +1191,8 @@ void CodeGenerator::WriteNewPC(const Value& value, bool commit) bool CodeGenerator::Compile_Fallback(Instruction instruction, const CodeCache::InstructionInfo& info) { + WARNING_LOG("Compiling instruction fallback at PC=0x{:08X}, instruction=0x{:08X}", info.pc, instruction.bits); + InstructionPrologue(instruction, info, 1, true); // flush and invalidate all guest registers, since the fallback could change any of them @@ -1204,20 +1206,11 @@ bool CodeGenerator::Compile_Fallback(Instruction instruction, const CodeCache::I EmitStoreCPUStructField(OFFSETOF(State, current_instruction_pc), Value::FromConstantU32(info.pc)); EmitStoreCPUStructField(OFFSETOF(State, current_instruction.bits), Value::FromConstantU32(instruction.bits)); - // emit the function call - if (CanInstructionTrap(instruction, false /*m_block->key.user_mode*/)) - { - // TODO: Use carry flag or something here too - Value return_value = m_register_cache.AllocateScratch(RegSize_8); - EmitFunctionCall(&return_value, - g_settings.gpu_pgxp_enable ? &Thunks::InterpretInstructionPGXP : &Thunks::InterpretInstruction); - EmitExceptionExitOnBool(return_value); - } - else - { - EmitFunctionCall(nullptr, - g_settings.gpu_pgxp_enable ? &Thunks::InterpretInstructionPGXP : &Thunks::InterpretInstruction); - } + // TODO: Use carry flag or something here too + Value return_value = m_register_cache.AllocateScratch(RegSize_8); + EmitFunctionCall(&return_value, + g_settings.gpu_pgxp_enable ? &Thunks::InterpretInstructionPGXP : &Thunks::InterpretInstruction); + EmitExceptionExitOnBool(return_value); m_current_instruction_in_branch_delay_slot_dirty = info.is_branch_instruction; m_branch_was_taken_dirty = info.is_branch_instruction; diff --git a/src/core/cpu_types.cpp b/src/core/cpu_types.cpp index 3ebc31d77..9b4312d5a 100644 --- a/src/core/cpu_types.cpp +++ b/src/core/cpu_types.cpp @@ -186,7 +186,8 @@ bool CPU::IsMemoryStoreInstruction(const Instruction instruction) } } -std::optional CPU::GetLoadStoreEffectiveAddress(const Instruction instruction, const Registers* regs) +std::optional CPU::GetLoadStoreEffectiveAddress(const Instruction instruction, + const Registers* regs) { switch (instruction.op) { @@ -265,109 +266,107 @@ bool CPU::IsExitBlockInstruction(const Instruction instruction) } } -bool CPU::CanInstructionTrap(const Instruction instruction, bool in_user_mode) +bool CPU::IsValidInstruction(const Instruction instruction) { - switch (instruction.op) - { - case InstructionOp::lui: - case InstructionOp::andi: - case InstructionOp::ori: - case InstructionOp::xori: - case InstructionOp::addiu: - case InstructionOp::slti: - case InstructionOp::sltiu: - return false; + // No constexpr std::bitset until C++23 :( + static constexpr const std::array valid_op_map = []() constexpr { + std::array ret = {}; - case InstructionOp::cop0: - case InstructionOp::cop2: - case InstructionOp::lwc2: - case InstructionOp::swc2: - return in_user_mode; +#define SET(op) ret[static_cast(op) / 32] |= (1u << (static_cast(op) % 32)); - // swc0/lwc0/cop1/cop3 are essentially no-ops - case InstructionOp::cop1: - case InstructionOp::cop3: - case InstructionOp::lwc0: - case InstructionOp::lwc1: - case InstructionOp::lwc3: - case InstructionOp::swc0: - case InstructionOp::swc1: - case InstructionOp::swc3: - return false; + SET(InstructionOp::b); + SET(InstructionOp::j); + SET(InstructionOp::jal); + SET(InstructionOp::beq); + SET(InstructionOp::bne); + SET(InstructionOp::blez); + SET(InstructionOp::bgtz); + SET(InstructionOp::addi); + SET(InstructionOp::addiu); + SET(InstructionOp::slti); + SET(InstructionOp::sltiu); + SET(InstructionOp::andi); + SET(InstructionOp::ori); + SET(InstructionOp::xori); + SET(InstructionOp::lui); - case InstructionOp::addi: - case InstructionOp::lb: - case InstructionOp::lh: - case InstructionOp::lw: - case InstructionOp::lbu: - case InstructionOp::lhu: - case InstructionOp::lwl: - case InstructionOp::lwr: - case InstructionOp::sb: - case InstructionOp::sh: - case InstructionOp::sw: - case InstructionOp::swl: - case InstructionOp::swr: - return true; + // Invalid COP0-3 ops don't raise #RI? + SET(InstructionOp::cop0); + SET(InstructionOp::cop1); + SET(InstructionOp::cop2); + SET(InstructionOp::cop3); - // These can fault on the branch address. Perhaps we should move this to the next instruction? - case InstructionOp::j: - case InstructionOp::jal: - case InstructionOp::b: - case InstructionOp::beq: - case InstructionOp::bgtz: - case InstructionOp::blez: - case InstructionOp::bne: - return false; + SET(InstructionOp::lb); + SET(InstructionOp::lh); + SET(InstructionOp::lwl); + SET(InstructionOp::lw); + SET(InstructionOp::lbu); + SET(InstructionOp::lhu); + SET(InstructionOp::lwr); + SET(InstructionOp::sb); + SET(InstructionOp::sh); + SET(InstructionOp::swl); + SET(InstructionOp::sw); + SET(InstructionOp::swr); + SET(InstructionOp::lwc0); + SET(InstructionOp::lwc1); + SET(InstructionOp::lwc2); + SET(InstructionOp::lwc3); + SET(InstructionOp::swc0); + SET(InstructionOp::swc1); + SET(InstructionOp::swc2); + SET(InstructionOp::swc3); - case InstructionOp::funct: - { - switch (instruction.r.funct) - { - case InstructionFunct::sll: - case InstructionFunct::srl: - case InstructionFunct::sra: - case InstructionFunct::sllv: - case InstructionFunct::srlv: - case InstructionFunct::srav: - case InstructionFunct::and_: - case InstructionFunct::or_: - case InstructionFunct::xor_: - case InstructionFunct::nor: - case InstructionFunct::addu: - case InstructionFunct::subu: - case InstructionFunct::slt: - case InstructionFunct::sltu: - case InstructionFunct::mfhi: - case InstructionFunct::mthi: - case InstructionFunct::mflo: - case InstructionFunct::mtlo: - case InstructionFunct::mult: - case InstructionFunct::multu: - case InstructionFunct::div: - case InstructionFunct::divu: - return false; +#undef SET - case InstructionFunct::jr: - case InstructionFunct::jalr: - return true; + return ret; + }(); - case InstructionFunct::add: - case InstructionFunct::sub: - case InstructionFunct::syscall: - case InstructionFunct::break_: - default: - return true; - } - } + static constexpr const std::array valid_func_map = []() constexpr { + std::array ret = {}; - default: - return true; - } -} - -bool CPU::IsInvalidInstruction(const Instruction instruction) -{ - // TODO - return true; +#define SET(op) ret[static_cast(op) / 32] |= (1u << (static_cast(op) % 32)); + + SET(InstructionFunct::sll); + SET(InstructionFunct::srl); + SET(InstructionFunct::sra); + SET(InstructionFunct::sllv); + SET(InstructionFunct::srlv); + SET(InstructionFunct::srav); + SET(InstructionFunct::jr); + SET(InstructionFunct::jalr); + SET(InstructionFunct::syscall); + SET(InstructionFunct::break_); + SET(InstructionFunct::mfhi); + SET(InstructionFunct::mthi); + SET(InstructionFunct::mflo); + SET(InstructionFunct::mtlo); + SET(InstructionFunct::mult); + SET(InstructionFunct::multu); + SET(InstructionFunct::div); + SET(InstructionFunct::divu); + SET(InstructionFunct::add); + SET(InstructionFunct::addu); + SET(InstructionFunct::sub); + SET(InstructionFunct::subu); + SET(InstructionFunct::and_); + SET(InstructionFunct::or_); + SET(InstructionFunct::xor_); + SET(InstructionFunct::nor); + SET(InstructionFunct::slt); + SET(InstructionFunct::sltu); + +#undef SET + + return ret; + }(); + +#define CHECK(arr, val) ((arr[static_cast(val) / 32] & (1u << (static_cast(val) % 32))) != 0u) + + if (instruction.op == InstructionOp::funct) + return CHECK(valid_func_map, instruction.r.funct.GetValue()); + else + return CHECK(valid_op_map, instruction.op.GetValue()); + +#undef CHECK } diff --git a/src/core/cpu_types.h b/src/core/cpu_types.h index 96524ff67..a2c944225 100644 --- a/src/core/cpu_types.h +++ b/src/core/cpu_types.h @@ -231,8 +231,7 @@ bool IsMemoryLoadInstruction(const Instruction instruction); bool IsMemoryStoreInstruction(const Instruction instruction); bool InstructionHasLoadDelay(const Instruction instruction); bool IsExitBlockInstruction(const Instruction instruction); -bool CanInstructionTrap(const Instruction instruction, bool in_user_mode); -bool IsInvalidInstruction(const Instruction instruction); +bool IsValidInstruction(const Instruction instruction); struct Registers {