From c5dc3fedd52df62c637bf55018b4e6ddc2c6b6d8 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Mon, 28 Mar 2016 00:26:45 +0100 Subject: [PATCH] JitX64: Implement branch instructions --- src/core/arm/decoder/decoder.h | 2 + src/core/arm/decoder/thumb.cpp | 4 +- src/core/arm/jit_x64/instructions/branch.cpp | 117 ++++++++- src/core/arm/jit_x64/instructions/thumb.cpp | 30 +++ src/core/arm/jit_x64/jit_x64.cpp | 11 +- src/core/arm/jit_x64/jit_x64.h | 3 + src/tests/CMakeLists.txt | 8 +- .../core/arm/jit_x64/fuzz_arm_branch.cpp | 35 +++ .../core/arm/jit_x64/fuzz_arm_common.cpp | 143 +++++++++++ src/tests/core/arm/jit_x64/fuzz_arm_common.h | 22 ++ .../arm/jit_x64/fuzz_arm_data_processing.cpp | 235 ++++-------------- src/tests/core/arm/jit_x64/fuzz_thumb.cpp | 10 +- src/tests/core/arm/jit_x64/rand_int.h | 5 + 13 files changed, 426 insertions(+), 199 deletions(-) create mode 100644 src/tests/core/arm/jit_x64/fuzz_arm_branch.cpp create mode 100644 src/tests/core/arm/jit_x64/fuzz_arm_common.cpp create mode 100644 src/tests/core/arm/jit_x64/fuzz_arm_common.h diff --git a/src/core/arm/decoder/decoder.h b/src/core/arm/decoder/decoder.h index 8b5080d99..d9cd0b5df 100644 --- a/src/core/arm/decoder/decoder.h +++ b/src/core/arm/decoder/decoder.h @@ -315,6 +315,8 @@ public: virtual void SRS() = 0; // Thumb specific instructions + virtual void thumb_B(Cond cond, Imm8 imm8) = 0; + virtual void thumb_B(Imm11 imm11) = 0; virtual void thumb_BLX_prefix(Imm11 imm11) = 0; virtual void thumb_BLX_suffix(bool L, Imm11 imm11) = 0; }; diff --git a/src/core/arm/decoder/thumb.cpp b/src/core/arm/decoder/thumb.cpp index 0a7a65e63..b0a8fa6eb 100644 --- a/src/core/arm/decoder/thumb.cpp +++ b/src/core/arm/decoder/thumb.cpp @@ -414,7 +414,7 @@ static const std::array thumb_instruction_table = { { // B Cond cond = bits<8, 11>(instruction); s32 offset = bits<0, 7>(instruction); - v->B(cond, sign_extend<8>(offset)); + v->thumb_B(cond, offset); })}, { "SWI", MakeMatcher("11011111xxxxxxxx", [](Visitor* v, u32 instruction) { // SWI #imm8 @@ -424,7 +424,7 @@ static const std::array thumb_instruction_table = { { { "B", MakeMatcher("11100xxxxxxxxxxx", [](Visitor* v, u32 instruction) { // B Imm11 imm11 = bits<0, 10>(instruction); - v->B(0xE, sign_extend<11>(imm11)); + v->thumb_B(imm11); })}, { "BLX (suffix)", MakeMatcher("11101xxxxxxxxxx0", [](Visitor* v, u32 instruction) { Imm11 imm11 = bits<0, 10>(instruction); diff --git a/src/core/arm/jit_x64/instructions/branch.cpp b/src/core/arm/jit_x64/instructions/branch.cpp index f08128da6..783ba796f 100644 --- a/src/core/arm/jit_x64/instructions/branch.cpp +++ b/src/core/arm/jit_x64/instructions/branch.cpp @@ -2,15 +2,120 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/math_util.h" + #include "core/arm/jit_x64/jit_x64.h" namespace JitX64 { -void JitX64::B(Cond cond, ArmImm24 imm24) { CompileInterpretInstruction(); } -void JitX64::BL(Cond cond, ArmImm24 imm24) { CompileInterpretInstruction(); } -void JitX64::BLX_imm(bool H, ArmImm24 imm24) { CompileInterpretInstruction(); } -void JitX64::BLX_reg(Cond cond, ArmReg Rm) { CompileInterpretInstruction(); } -void JitX64::BX(Cond cond, ArmReg Rm) { CompileInterpretInstruction(); } -void JitX64::BXJ(Cond cond, ArmReg Rm) { CompileInterpretInstruction(); } +using namespace Gen; + +void JitX64::B(Cond cond, ArmImm24 imm24) { + cond_manager.CompileCond((ConditionCode)cond); + + const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<26, s32>(imm24 << 2); + + reg_alloc.FlushEverything(); + current.arm_pc += GetInstSize(); + CompileUpdateCycles(false); + CompileJumpToBB(new_pc); + + if (cond == ConditionCode::AL) { + stop_compilation = true; + } +} + +void JitX64::BL(Cond cond, ArmImm24 imm24) { + cond_manager.CompileCond((ConditionCode)cond); + + const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<26, s32>(imm24 << 2); + + ASSERT(!current.TFlag); + const u32 link_pc = current.arm_pc + GetInstSize(); + Gen::OpArg LR = reg_alloc.LockArmForWrite(14); + code->MOV(32, LR, Imm32(link_pc)); + reg_alloc.UnlockArm(14); + + reg_alloc.FlushEverything(); + current.arm_pc += GetInstSize(); + CompileUpdateCycles(false); + CompileJumpToBB(new_pc); + + if (cond == ConditionCode::AL) { + stop_compilation = true; + } +} + +void JitX64::BLX_imm(bool H, ArmImm24 imm24) { + cond_manager.Always(); + + const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<26, s32>(imm24 << 2) + (H ? 2 : 0); + + ASSERT(!current.TFlag); + const u32 link_pc = current.arm_pc + GetInstSize(); + Gen::OpArg LR = reg_alloc.LockArmForWrite(14); + code->MOV(32, LR, Imm32(link_pc)); + reg_alloc.UnlockArm(14); + + current.TFlag = true; + code->MOV(32, MJitStateTFlag(), Imm32(1)); + + reg_alloc.FlushEverything(); + current.arm_pc += GetInstSize(); + CompileUpdateCycles(false); + CompileJumpToBB(new_pc); + + stop_compilation = true; +} + +void JitX64::BLX_reg(Cond cond, ArmReg Rm_index) { + cond_manager.CompileCond((ConditionCode)cond); + + const u32 link_pc = current.arm_pc + GetInstSize() + (current.TFlag ? 1 : 0); + Gen::OpArg LR = reg_alloc.LockArmForWrite(14); + code->MOV(32, LR, Imm32(link_pc)); + reg_alloc.UnlockArm(14); + + if (Rm_index == 15) { + // This is what the interpreter does. This effectively hangs the cpu. + // blx r15 is marked as UNPREDICTABLE in ARM ARM. + code->MOV(32, MJitStateArmPC(), Imm32(current.arm_pc)); + code->MOV(32, MJitStateTFlag(), Imm32(0)); + } else { + Gen::X64Reg Rm = reg_alloc.BindArmForRead(Rm_index); + code->MOV(32, MJitStateArmPC(), R(Rm)); + code->AND(32, MJitStateArmPC(), Imm32(0xFFFFFFFE)); + code->BT(32, R(Rm), Imm8(0)); + code->SETcc(CC_C, MJitStateTFlag()); // NOTE: current.TFlag is now inaccurate + reg_alloc.UnlockArm(Rm_index); + } + + current.arm_pc += GetInstSize(); + CompileReturnToDispatch(); +} + +void JitX64::BX(Cond cond, ArmReg Rm_index) { + cond_manager.CompileCond((ConditionCode)cond); + + if (Rm_index == 15) { + code->MOV(32, MJitStateArmPC(), Imm32(GetReg15Value())); + code->MOV(32, MJitStateTFlag(), Imm32(0)); + } else { + Gen::X64Reg Rm = reg_alloc.BindArmForRead(Rm_index); + code->MOV(32, MJitStateArmPC(), R(Rm)); + code->AND(32, MJitStateArmPC(), Imm32(0xFFFFFFFE)); + code->BT(32, R(Rm), Imm8(0)); + code->SETcc(CC_C, MJitStateTFlag()); // NOTE: current.TFlag is now inaccurate + reg_alloc.UnlockArm(Rm_index); + } + + current.arm_pc += GetInstSize(); + CompileReturnToDispatch(); +} + +void JitX64::BXJ(Cond cond, ArmReg Rm) { + // BXJ behaves exactly as BX since Jazelle is not supported + BX(cond, Rm); +} } diff --git a/src/core/arm/jit_x64/instructions/thumb.cpp b/src/core/arm/jit_x64/instructions/thumb.cpp index 27a81e04b..1c4f041d1 100644 --- a/src/core/arm/jit_x64/instructions/thumb.cpp +++ b/src/core/arm/jit_x64/instructions/thumb.cpp @@ -2,10 +2,40 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/math_util.h" + #include "core/arm/jit_x64/jit_x64.h" namespace JitX64 { +void JitX64::thumb_B(Cond cond, ArmImm8 imm8) { + cond_manager.CompileCond((ConditionCode)cond); + + const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<9, s32>(imm8 << 1); + + reg_alloc.FlushEverything(); + current.arm_pc += GetInstSize(); + CompileUpdateCycles(false); + CompileJumpToBB(new_pc); + + if (cond == ConditionCode::AL) { + stop_compilation = true; + } +} + +void JitX64::thumb_B(ArmImm11 imm11) { + cond_manager.Always(); + + const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<12, s32>(imm11 << 1); + + reg_alloc.FlushEverything(); + current.arm_pc += GetInstSize(); + CompileUpdateCycles(false); + CompileJumpToBB(new_pc); + + stop_compilation = true; +} + void JitX64::thumb_BLX_prefix(ArmImm11 imm11) { CompileInterpretInstruction(); } void JitX64::thumb_BLX_suffix(bool L, ArmImm11 imm11) { CompileInterpretInstruction(); } diff --git a/src/core/arm/jit_x64/jit_x64.cpp b/src/core/arm/jit_x64/jit_x64.cpp index b8e5aea64..a0b892aa3 100644 --- a/src/core/arm/jit_x64/jit_x64.cpp +++ b/src/core/arm/jit_x64/jit_x64.cpp @@ -103,8 +103,6 @@ void JitX64::CompileReturnToDispatch() { } void JitX64::CompileJumpToBB(u32 new_pc) { - ASSERT(instructions_compiled == 0); - reg_alloc.FlushEverything(); code->CMP(32, MJitStateCycleCount(), Imm8(0)); @@ -146,7 +144,7 @@ void JitX64::CompileSingleArmInstruction() { void JitX64::CompileSingleThumbInstruction() { u32 inst_u32 = Memory::Read32(current.arm_pc & 0xFFFFFFFC); - if ((current.arm_pc & 0x3) != 0) { + if ((current.arm_pc & 0x2) != 0) { inst_u32 >>= 16; } inst_u32 &= 0xFFFFF; @@ -178,6 +176,13 @@ Gen::OpArg JitX64::MJitStateArmPC() { return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, Reg) + 15 * sizeof(u32)); } +Gen::OpArg JitX64::MJitStateTFlag() { + static_assert(std::is_same::value, "JitState::cpu_state must be ARMul_State"); + static_assert(std::is_same::value, "TFlag must be u32"); + + return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, TFlag)); +} + Gen::OpArg JitX64::MJitStateHostReturnRIP() { static_assert(std::is_same::value, "JitState::return_RIP must be u64"); diff --git a/src/core/arm/jit_x64/jit_x64.h b/src/core/arm/jit_x64/jit_x64.h index 1d7bd8088..5fb98522a 100644 --- a/src/core/arm/jit_x64/jit_x64.h +++ b/src/core/arm/jit_x64/jit_x64.h @@ -84,6 +84,7 @@ private: Gen::OpArg MJitStateCycleCount(); Gen::OpArg MJitStateArmPC(); + Gen::OpArg MJitStateTFlag(); Gen::OpArg MJitStateHostReturnRIP(); Gen::OpArg MJitStateHostReturnRSP(); Gen::OpArg MJitStateZFlag(); @@ -394,6 +395,8 @@ private: void SRS() override; // Thumb specific instructions + void thumb_B(Cond cond, ArmImm8 imm8) override; + void thumb_B(ArmImm11 imm11) override; void thumb_BLX_prefix(ArmImm11 imm11) override; void thumb_BLX_suffix(bool L, ArmImm11 imm11) override; }; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index ac2b096cc..71d59f1aa 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -7,10 +7,16 @@ set(HEADERS if(ARCHITECTURE_x86_64) set(SRCS ${SRCS} - core/arm/jit_x64/rand_int.h + core/arm/jit_x64/fuzz_arm_branch.cpp + core/arm/jit_x64/fuzz_arm_common.cpp core/arm/jit_x64/fuzz_arm_data_processing.cpp core/arm/jit_x64/fuzz_thumb.cpp ) + + set(HEADERS ${HEADERS} + core/arm/jit_x64/fuzz_arm_common.h + core/arm/jit_x64/rand_int.h + ) endif() create_directory_groups(${SRCS} ${HEADERS}) diff --git a/src/tests/core/arm/jit_x64/fuzz_arm_branch.cpp b/src/tests/core/arm/jit_x64/fuzz_arm_branch.cpp new file mode 100644 index 000000000..47586527e --- /dev/null +++ b/src/tests/core/arm/jit_x64/fuzz_arm_branch.cpp @@ -0,0 +1,35 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include + +#include "common/common_types.h" + +#include "tests/core/arm/jit_x64/rand_int.h" +#include "tests/core/arm/jit_x64/fuzz_arm_common.h" + +TEST_CASE("Fuzz ARM branch instructions", "[JitX64]") { + const std::array, 6> instructions = {{ + FromBitString32("1111101hvvvvvvvvvvvvvvvvvvvvvvvv"), + FromBitString32("cccc000100101111111111110011mmmm"), + FromBitString32("cccc1010vvvvvvvvvvvvvvvvvvvvvvvv"), + FromBitString32("cccc1011vvvvvvvvvvvvvvvvvvvvvvvv"), + FromBitString32("cccc000100101111111111110001mmmm"), + FromBitString32("cccc000100101111111111110010mmmm"), + }}; + + auto instruction_select = [&]() -> u32 { + size_t inst_index = RandInt(0, instructions.size() - 1); + + u32 random = RandInt(0, 0xFFFFFFFF); + + return instructions[inst_index].first | (random & (~instructions[inst_index].second)); + }; + + SECTION("R15") { + FuzzJit(1, 1, 10000, instruction_select); + } +} \ No newline at end of file diff --git a/src/tests/core/arm/jit_x64/fuzz_arm_common.cpp b/src/tests/core/arm/jit_x64/fuzz_arm_common.cpp new file mode 100644 index 000000000..13c6fb8ff --- /dev/null +++ b/src/tests/core/arm/jit_x64/fuzz_arm_common.cpp @@ -0,0 +1,143 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include + +#include + +#include "common/common_types.h" +#include "common/scope_exit.h" + +#include "core/arm/disassembler/arm_disasm.h" +#include "core/arm/dyncom/arm_dyncom.h" +#include "core/arm/jit_x64/interface.h" +#include "core/core.h" +#include "core/memory_setup.h" + +#include "tests/core/arm/jit_x64/fuzz_arm_common.h" +#include "tests/core/arm/jit_x64/rand_int.h" + +std::pair FromBitString32(const char* str) { + REQUIRE(strlen(str) == 32); + + u32 bits = 0; + u32 mask = 0; + for (int i = 0; i < 32; i++) { + const u32 bit = 1 << (31 - i); + switch (str[i]) { + case '0': + mask |= bit; + break; + case '1': + bits |= bit; + mask |= bit; + break; + default: + // Do nothing + break; + } + } + return{ bits, mask }; +} + +void FuzzJit(const int instruction_count, const int instructions_to_execute_count, const int run_count, const std::function instruction_generator) { + // Init core + Core::Init(); + SCOPE_EXIT({ Core::Shutdown(); }); + + // Prepare memory + constexpr size_t MEMORY_SIZE = 4096 * 2; + std::array test_mem{}; + Memory::MapMemoryRegion(0, MEMORY_SIZE, test_mem.data()); + SCOPE_EXIT({ Memory::UnmapRegion(0, MEMORY_SIZE); }); + + // Prepare test subjects + JitX64::ARM_Jit jit(PrivilegeMode::USER32MODE); + ARM_DynCom interp(PrivilegeMode::USER32MODE); + SCOPE_EXIT({ + jit.FastClearCache(); + interp.ClearCache(); + }); + + for (int run_number = 0; run_number < run_count; run_number++) { + jit.FastClearCache(); + interp.ClearCache(); + + u32 initial_regs[15]; + for (int i = 0; i < 15; i++) { + u32 val = RandInt(0, 0xFFFFFFFF); + interp.SetReg(i, val); + jit.SetReg(i, val); + initial_regs[i] = val; + } + + interp.SetCPSR(0x000001d0); + jit.SetCPSR(0x000001d0); + + interp.SetPC(0); + jit.SetPC(0); + + for (int i = 0; i < instruction_count; i++) { + u32 inst = instruction_generator(); + + Memory::Write32(i * 4, inst); + } + + Memory::Write32(instruction_count * 4, 0xEAFFFFFE); // b +#0 // busy wait loop + + interp.ExecuteInstructions(instructions_to_execute_count); + jit.ExecuteInstructions(instructions_to_execute_count); + + bool pass = true; + + if (interp.GetCPSR() != jit.GetCPSR()) pass = false; + for (int i = 0; i <= 15; i++) { + if (interp.GetReg(i) != jit.GetReg(i)) pass = false; + } + + if (!pass) { + printf("Failed at execution number %i\n", run_number); + + printf("\nInstruction Listing: \n"); + for (int i = 0; i < instruction_count; i++) { + printf("%s\n", ARM_Disasm::Disassemble(i * 4, Memory::Read32(i * 4)).c_str()); + } + + printf("\nFinal Register Listing: \n"); + printf(" R interp jit\n"); + for (int i = 0; i <= 15; i++) { + printf("%4i: %08x %08x %s\n", i, interp.GetReg(i), jit.GetReg(i), interp.GetReg(i) != jit.GetReg(i) ? "*" : ""); + } + printf("CPSR: %08x %08x %s\n", interp.GetCPSR(), jit.GetCPSR(), interp.GetCPSR() != jit.GetCPSR() ? "*" : ""); + + printf("\nInterpreter walkthrough:\n"); + interp.ClearCache(); + interp.SetPC(0); + interp.SetCPSR(0x000001d0); + for (int i = 0; i < 15; i++) { + interp.SetReg(i, initial_regs[i]); + printf("%4i: %08x\n", i, interp.GetReg(i)); + } + for (int inst = 0; inst < instruction_count; inst++) { + printf("%s\n", ARM_Disasm::Disassemble(inst * 4, Memory::Read32(inst * 4)).c_str()); + interp.Step(); + for (int i = 0; i <= 15; i++) { + printf("%4i: %08x\n", i, interp.GetReg(i)); + } + printf("CPSR: %08x\n", interp.GetCPSR()); + } + +#ifdef _MSC_VER + DebugBreak(); +#endif + FAIL(); + } + + printf("%i\r", run_number); + if (run_number % 50 == 0) { + fflush(stdout); + } + } +} \ No newline at end of file diff --git a/src/tests/core/arm/jit_x64/fuzz_arm_common.h b/src/tests/core/arm/jit_x64/fuzz_arm_common.h new file mode 100644 index 000000000..05c03d556 --- /dev/null +++ b/src/tests/core/arm/jit_x64/fuzz_arm_common.h @@ -0,0 +1,22 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "common/common_types.h" + +std::pair FromBitString32(const char* str); + +/** + * For run_count times: + * 1. Generates instruction_count instructions using instruction_generator + * 2. Requests JitX64 and interpreter to execute them for instructions_to_execute_count instructions + * 3. Verifies that the register state are equivalent + * + * Note: A b #+0 instruction is appended to the list of instructions. + */ +void FuzzJit(const int instruction_count, const int instructions_to_execute_count, const int run_count, const std::function instruction_generator); diff --git a/src/tests/core/arm/jit_x64/fuzz_arm_data_processing.cpp b/src/tests/core/arm/jit_x64/fuzz_arm_data_processing.cpp index 906f88232..bccc3c203 100644 --- a/src/tests/core/arm/jit_x64/fuzz_arm_data_processing.cpp +++ b/src/tests/core/arm/jit_x64/fuzz_arm_data_processing.cpp @@ -2,194 +2,65 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include -#include +#include #include #include "common/common_types.h" -#include "common/scope_exit.h" - -#include "core/arm/disassembler/arm_disasm.h" -#include "core/arm/dyncom/arm_dyncom.h" -#include "core/arm/jit_x64/interface.h" -#include "core/core.h" -#include "core/memory_setup.h" #include "tests/core/arm/jit_x64/rand_int.h" - -std::pair FromBitString(const char* str) { - REQUIRE(strlen(str) == 32); - - u32 bits = 0; - u32 mask = 0; - for (int i = 0; i < 32; i++) { - const u32 bit = 1 << (31 - i); - switch (str[i]) { - case '0': - mask |= bit; - break; - case '1': - bits |= bit; - mask |= bit; - break; - default: - // Do nothing - break; - } - } - return { bits, mask }; -} - -void FuzzJit(const int instruction_count, const int run_count, const std::function instruction_generator) { - // Init core - Core::Init(); - SCOPE_EXIT({ Core::Shutdown(); }); - - // Prepare memory - constexpr size_t MEMORY_SIZE = 4096 * 2; - std::array test_mem{}; - Memory::MapMemoryRegion(0, MEMORY_SIZE, test_mem.data()); - SCOPE_EXIT({ Memory::UnmapRegion(0, MEMORY_SIZE); }); - - // Prepare test subjects - JitX64::ARM_Jit jit(PrivilegeMode::USER32MODE); - ARM_DynCom interp(PrivilegeMode::USER32MODE); - SCOPE_EXIT({ - jit.FastClearCache(); - interp.ClearCache(); - }); - - for (int run_number = 0; run_number < run_count; run_number++) { - jit.FastClearCache(); - interp.ClearCache(); - - u32 initial_regs[15]; - for (int i = 0; i < 15; i++) { - u32 val = RandInt(0, 0xFFFFFFFF); - interp.SetReg(i, val); - jit.SetReg(i, val); - initial_regs[i] = val; - } - - interp.SetCPSR(0x000001d0); - jit.SetCPSR(0x000001d0); - - interp.SetPC(0); - jit.SetPC(0); - - for (int i = 0; i < instruction_count; i++) { - u32 inst = instruction_generator(); - - Memory::Write32(i * 4, inst); - } - - Memory::Write32(instruction_count * 4, 0xEAFFFFFE); // b +#0 // busy wait loop - - interp.ExecuteInstructions(instruction_count); - jit.ExecuteInstructions(instruction_count); - - bool pass = true; - - if (interp.GetCPSR() != jit.GetCPSR()) pass = false; - for (int i = 0; i <= 15; i++) { - if (interp.GetReg(i) != jit.GetReg(i)) pass = false; - } - - if (!pass) { - printf("Failed at execution number %i\n", run_number); - - printf("\nInstruction Listing: \n"); - for (int i = 0; i < instruction_count; i++) { - printf("%s\n", ARM_Disasm::Disassemble(i * 4, Memory::Read32(i * 4)).c_str()); - } - - printf("\nFinal Register Listing: \n"); - for (int i = 0; i <= 15; i++) { - printf("%4i: %08x %08x %s\n", i, interp.GetReg(i), jit.GetReg(i), interp.GetReg(i) != jit.GetReg(i) ? "*" : ""); - } - printf("CPSR: %08x %08x %s\n", interp.GetCPSR(), jit.GetCPSR(), interp.GetCPSR() != jit.GetCPSR() ? "*" : ""); - - printf("\nInterpreter walkthrough:\n"); - interp.ClearCache(); - interp.SetPC(0); - interp.SetCPSR(0x000001d0); - for (int i = 0; i < 15; i++) { - interp.SetReg(i, initial_regs[i]); - printf("%4i: %08x\n", i, interp.GetReg(i)); - } - for (int inst = 0; inst < instruction_count; inst++) { - printf("%s\n", ARM_Disasm::Disassemble(inst * 4, Memory::Read32(inst * 4)).c_str()); - interp.Step(); - for (int i = 0; i <= 15; i++) { - printf("%4i: %08x\n", i, interp.GetReg(i)); - } - printf("CPSR: %08x\n", interp.GetCPSR()); - } - -#ifdef _MSC_VER - DebugBreak(); -#endif - FAIL(); - } - - printf("%i\r", run_number); - if (run_number % 50 == 0) { - fflush(stdout); - } - } -} +#include "tests/core/arm/jit_x64/fuzz_arm_common.h" TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") { const std::array, 48> instructions = {{ - FromBitString("cccc0010101Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0000101Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0000101Snnnnddddssss0rr1mmmm"), - FromBitString("cccc0010100Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0000100Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0000100Snnnnddddssss0rr1mmmm"), - FromBitString("cccc0010000Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0000000Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0000000Snnnnddddssss0rr1mmmm"), - FromBitString("cccc0011110Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0001110Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0001110Snnnnddddssss0rr1mmmm"), - FromBitString("cccc00110111nnnn0000rrrrvvvvvvvv"), - FromBitString("cccc00010111nnnn0000vvvvvrr0mmmm"), - FromBitString("cccc00010111nnnn0000ssss0rr1mmmm"), - FromBitString("cccc00110101nnnn0000rrrrvvvvvvvv"), - FromBitString("cccc00010101nnnn0000vvvvvrr0mmmm"), - FromBitString("cccc00010101nnnn0000ssss0rr1mmmm"), - FromBitString("cccc0010001Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0000001Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0000001Snnnnddddssss0rr1mmmm"), - FromBitString("cccc0011101S0000ddddrrrrvvvvvvvv"), - FromBitString("cccc0001101S0000ddddvvvvvrr0mmmm"), - FromBitString("cccc0001101S0000ddddssss0rr1mmmm"), - FromBitString("cccc0011111S0000ddddrrrrvvvvvvvv"), - FromBitString("cccc0001111S0000ddddvvvvvrr0mmmm"), - FromBitString("cccc0001111S0000ddddssss0rr1mmmm"), - FromBitString("cccc0011100Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0001100Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0001100Snnnnddddssss0rr1mmmm"), - FromBitString("cccc0010011Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0000011Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0000011Snnnnddddssss0rr1mmmm"), - FromBitString("cccc0010111Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0000111Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0000111Snnnnddddssss0rr1mmmm"), - FromBitString("cccc0010110Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0000110Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0000110Snnnnddddssss0rr1mmmm"), - FromBitString("cccc0010010Snnnnddddrrrrvvvvvvvv"), - FromBitString("cccc0000010Snnnnddddvvvvvrr0mmmm"), - FromBitString("cccc0000010Snnnnddddssss0rr1mmmm"), - FromBitString("cccc00110011nnnn0000rrrrvvvvvvvv"), - FromBitString("cccc00010011nnnn0000vvvvvrr0mmmm"), - FromBitString("cccc00010011nnnn0000ssss0rr1mmmm"), - FromBitString("cccc00110001nnnn0000rrrrvvvvvvvv"), - FromBitString("cccc00010001nnnn0000vvvvvrr0mmmm"), - FromBitString("cccc00010001nnnn0000ssss0rr1mmmm"), + FromBitString32("cccc0010101Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0000101Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0000101Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc0010100Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0000100Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0000100Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc0010000Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0000000Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0000000Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc0011110Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0001110Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0001110Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc00110111nnnn0000rrrrvvvvvvvv"), + FromBitString32("cccc00010111nnnn0000vvvvvrr0mmmm"), + FromBitString32("cccc00010111nnnn0000ssss0rr1mmmm"), + FromBitString32("cccc00110101nnnn0000rrrrvvvvvvvv"), + FromBitString32("cccc00010101nnnn0000vvvvvrr0mmmm"), + FromBitString32("cccc00010101nnnn0000ssss0rr1mmmm"), + FromBitString32("cccc0010001Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0000001Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0000001Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc0011101S0000ddddrrrrvvvvvvvv"), + FromBitString32("cccc0001101S0000ddddvvvvvrr0mmmm"), + FromBitString32("cccc0001101S0000ddddssss0rr1mmmm"), + FromBitString32("cccc0011111S0000ddddrrrrvvvvvvvv"), + FromBitString32("cccc0001111S0000ddddvvvvvrr0mmmm"), + FromBitString32("cccc0001111S0000ddddssss0rr1mmmm"), + FromBitString32("cccc0011100Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0001100Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0001100Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc0010011Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0000011Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0000011Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc0010111Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0000111Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0000111Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc0010110Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0000110Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0000110Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc0010010Snnnnddddrrrrvvvvvvvv"), + FromBitString32("cccc0000010Snnnnddddvvvvvrr0mmmm"), + FromBitString32("cccc0000010Snnnnddddssss0rr1mmmm"), + FromBitString32("cccc00110011nnnn0000rrrrvvvvvvvv"), + FromBitString32("cccc00010011nnnn0000vvvvvrr0mmmm"), + FromBitString32("cccc00010011nnnn0000ssss0rr1mmmm"), + FromBitString32("cccc00110001nnnn0000rrrrvvvvvvvv"), + FromBitString32("cccc00010001nnnn0000vvvvvrr0mmmm"), + FromBitString32("cccc00010001nnnn0000ssss0rr1mmmm"), }}; auto instruction_select_without_R15 = [&]() -> u32 { @@ -212,11 +83,11 @@ TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") { }; SECTION("short blocks") { - FuzzJit(5, 5000, instruction_select_without_R15); + FuzzJit(5, 6, 5000, instruction_select_without_R15); } SECTION("long blocks") { - FuzzJit(1024, 15, instruction_select_without_R15); + FuzzJit(1024, 1025, 15, instruction_select_without_R15); } auto instruction_select_only_R15 = [&]() -> u32 { @@ -239,6 +110,6 @@ TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") { }; SECTION("R15") { - FuzzJit(1, 10000, instruction_select_only_R15); + FuzzJit(1, 1, 10000, instruction_select_only_R15); } } \ No newline at end of file diff --git a/src/tests/core/arm/jit_x64/fuzz_thumb.cpp b/src/tests/core/arm/jit_x64/fuzz_thumb.cpp index 292a4c24a..1f1eb0973 100644 --- a/src/tests/core/arm/jit_x64/fuzz_thumb.cpp +++ b/src/tests/core/arm/jit_x64/fuzz_thumb.cpp @@ -40,7 +40,7 @@ std::pair FromBitString16(const char* str) { return{ bits, mask }; } -void FuzzJitThumb(const int instruction_count, const int run_count, const std::function instruction_generator) { +void FuzzJitThumb(const int instruction_count, const int run_count, const std::function instruction_generator, bool run_plus_1 = true) { // Init core Core::Init(); SCOPE_EXIT({ Core::Shutdown(); }); @@ -87,8 +87,8 @@ void FuzzJitThumb(const int instruction_count, const int run_count, const std::f Memory::Write16(4 + instruction_count * 2, 0xE7FE); // b +#0 // busy wait loop - interp.ExecuteInstructions(instruction_count + 1); - jit.ExecuteInstructions(instruction_count + 1); + interp.ExecuteInstructions(run_plus_1 ? instruction_count + 2 : instruction_count + 1); + jit.ExecuteInstructions(run_plus_1 ? instruction_count + 2 : instruction_count + 1); bool pass = true; @@ -208,7 +208,7 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") { return instructions[inst_index].first | (random &~ instructions[inst_index].second); }; - FuzzJitThumb(1, 10000, instruction_select); + FuzzJitThumb(1, 10000, instruction_select, false); } TEST_CASE("Fuzz Thumb instructions set 3 (32-bit BL/BLX)", "[JitX64][Thumb]") { @@ -227,5 +227,5 @@ TEST_CASE("Fuzz Thumb instructions set 3 (32-bit BL/BLX)", "[JitX64][Thumb]") { return inst_info.first | (random &~ inst_info.second); }; - FuzzJitThumb(1, 1000, instruction_select); + FuzzJitThumb(1, 1000, instruction_select, false); } \ No newline at end of file diff --git a/src/tests/core/arm/jit_x64/rand_int.h b/src/tests/core/arm/jit_x64/rand_int.h index 4612b8c9c..5fd431452 100644 --- a/src/tests/core/arm/jit_x64/rand_int.h +++ b/src/tests/core/arm/jit_x64/rand_int.h @@ -5,9 +5,14 @@ #pragma once #include +#include template T RandInt(T min, T max) { + static_assert(std::is_integral::value, "T must be an integral type."); + static_assert(!std::is_same::value && !std::is_same::value, + "Using char with uniform_int_distribution is undefined behavior."); + static std::random_device rd; static std::mt19937 mt(rd()); std::uniform_int_distribution rand(min, max);