diff --git a/src/core/arm/decoder/decoder.h b/src/core/arm/decoder/decoder.h index d9cd0b5df..4316a16fe 100644 --- a/src/core/arm/decoder/decoder.h +++ b/src/core/arm/decoder/decoder.h @@ -318,7 +318,7 @@ public: 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; + virtual void thumb_BLX_suffix(bool X, Imm11 imm11) = 0; }; }; diff --git a/src/core/arm/decoder/thumb.cpp b/src/core/arm/decoder/thumb.cpp index b0a8fa6eb..239f30891 100644 --- a/src/core/arm/decoder/thumb.cpp +++ b/src/core/arm/decoder/thumb.cpp @@ -63,11 +63,6 @@ static constexpr T bits(T s){ return ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1)); } -template -s32 sign_extend(s32 x) { - return x << (32 - num_bits) >> (32 - num_bits); -} - static const std::array thumb_instruction_table = { { { "LSL/LSR/ASR", MakeMatcher("000ooxxxxxxxxxxx", [](Visitor* v, u32 instruction) { u32 opcode = bits<11, 12>(instruction); @@ -428,7 +423,7 @@ static const std::array thumb_instruction_table = { { })}, { "BLX (suffix)", MakeMatcher("11101xxxxxxxxxx0", [](Visitor* v, u32 instruction) { Imm11 imm11 = bits<0, 10>(instruction); - v->thumb_BLX_suffix(/*L=*/true, imm11); + v->thumb_BLX_suffix(/*X=*/true, imm11); })}, { "BL/BLX (prefix)", MakeMatcher("11110xxxxxxxxxxx", [](Visitor* v, u32 instruction) { Imm11 imm11 = bits<0, 10>(instruction); @@ -436,7 +431,7 @@ static const std::array thumb_instruction_table = { { })}, { "BL (suffix)", MakeMatcher("11111xxxxxxxxxxx", [](Visitor* v, u32 instruction) { Imm11 imm11 = bits<0, 10>(instruction); - v->thumb_BLX_suffix(/*L=*/false, imm11); + v->thumb_BLX_suffix(/*X=*/false, imm11); })} }}; diff --git a/src/core/arm/jit_x64/instructions/thumb.cpp b/src/core/arm/jit_x64/instructions/thumb.cpp index 1c4f041d1..1fd18dfd8 100644 --- a/src/core/arm/jit_x64/instructions/thumb.cpp +++ b/src/core/arm/jit_x64/instructions/thumb.cpp @@ -2,15 +2,20 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" #include "common/math_util.h" #include "core/arm/jit_x64/jit_x64.h" namespace JitX64 { +using namespace Gen; + void JitX64::thumb_B(Cond cond, ArmImm8 imm8) { cond_manager.CompileCond((ConditionCode)cond); + ASSERT_MSG(current.TFlag, "thumb_B may only be called in thumb mode"); + const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<9, s32>(imm8 << 1); reg_alloc.FlushEverything(); @@ -26,6 +31,8 @@ void JitX64::thumb_B(Cond cond, ArmImm8 imm8) { void JitX64::thumb_B(ArmImm11 imm11) { cond_manager.Always(); + ASSERT_MSG(current.TFlag, "thumb_B may only be called in thumb mode"); + const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<12, s32>(imm11 << 1); reg_alloc.FlushEverything(); @@ -36,7 +43,58 @@ void JitX64::thumb_B(ArmImm11 imm11) { stop_compilation = true; } -void JitX64::thumb_BLX_prefix(ArmImm11 imm11) { CompileInterpretInstruction(); } -void JitX64::thumb_BLX_suffix(bool L, ArmImm11 imm11) { CompileInterpretInstruction(); } +void JitX64::thumb_BLX_prefix(ArmImm11 imm11) { + cond_manager.Always(); + + ASSERT_MSG(!thumb_BLX_prefix_executed, "two thumb BLX prefixes cannot occur in a row, pc = %u", current.arm_pc); + ASSERT_MSG(!thumb_BLX_suffix_executed, "thumb_BLX_prefix invalid state, pc = %u", current.arm_pc); + ASSERT_MSG(current.TFlag, "thumb_BLX_prefix should only be called in thumb mode, pc = %u", current.arm_pc); + + thumb_BLX_prefix_imm11 = imm11; + thumb_BLX_prefix_executed = true; + + current.arm_pc += GetInstSize(); + + // Compile the suffix, and make sure that it's compiled. + CompileSingleThumbInstruction(); + ASSERT_MSG(thumb_BLX_suffix_executed, "thumb BLX suffix did not come after thumb BLX prefix, pc = %u", current.arm_pc); + + thumb_BLX_prefix_executed = false; + thumb_BLX_suffix_executed = false; +} + +void JitX64::thumb_BLX_suffix(bool X, ArmImm11 imm11) { + cond_manager.Always(); + + // Must only be ever called right after the prefix. + ASSERT_MSG(thumb_BLX_prefix_executed, "thumb BLX suffix may only come after a thumb BLX prefix, pc = %u", current.arm_pc); + ASSERT_MSG(!thumb_BLX_suffix_executed, "thumb_BLX_suffix invalid state, pc = %u", current.arm_pc); + ASSERT_MSG(current.TFlag, "thumb_BLX_suffix should only be called in thumb mode, pc = %u", current.arm_pc); + + u32 new_pc = (current.arm_pc + 2) + + MathUtil::SignExtend<23, s32>(thumb_BLX_prefix_imm11 << 12) + + (imm11 << 1); + u32 new_lr = (current.arm_pc + 2) | 1; + + Gen::OpArg LR = reg_alloc.LockArmForWrite(14); + code->MOV(32, LR, Imm32(new_lr)); + reg_alloc.UnlockArm(14); + + if (X) { + new_pc &= 0xFFFFFFFC; + } else { + current.TFlag = false; + code->MOV(32, MJitStateTFlag(), Imm32(0)); + } + + reg_alloc.FlushEverything(); + current.arm_pc += GetInstSize(); + CompileUpdateCycles(false); + CompileJumpToBB(new_pc); + + stop_compilation = true; + + thumb_BLX_suffix_executed = true; +} } diff --git a/src/core/arm/jit_x64/jit_x64.h b/src/core/arm/jit_x64/jit_x64.h index 5fb98522a..68e28665f 100644 --- a/src/core/arm/jit_x64/jit_x64.h +++ b/src/core/arm/jit_x64/jit_x64.h @@ -397,6 +397,10 @@ private: // Thumb specific instructions void thumb_B(Cond cond, ArmImm8 imm8) override; void thumb_B(ArmImm11 imm11) override; + + ArmImm11 thumb_BLX_prefix_imm11 = 0; + bool thumb_BLX_prefix_executed = false; + bool thumb_BLX_suffix_executed = false; void thumb_BLX_prefix(ArmImm11 imm11) override; void thumb_BLX_suffix(bool L, ArmImm11 imm11) override; }; diff --git a/src/tests/core/arm/jit_x64/fuzz_thumb.cpp b/src/tests/core/arm/jit_x64/fuzz_thumb.cpp index 1f1eb0973..fcf6033d9 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, bool run_plus_1 = true) { +void FuzzJitThumb(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(); }); @@ -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(run_plus_1 ? instruction_count + 2 : instruction_count + 1); - jit.ExecuteInstructions(run_plus_1 ? instruction_count + 2 : instruction_count + 1); + interp.ExecuteInstructions(instructions_to_execute_count); + jit.ExecuteInstructions(instructions_to_execute_count); bool pass = true; @@ -183,11 +183,11 @@ TEST_CASE("Fuzz Thumb instructions set 1 (pure computation)", "[JitX64][Thumb]") }; SECTION("short blocks") { - FuzzJitThumb(5, 10000, instruction_select); + FuzzJitThumb(5, 6, 10000, instruction_select); } SECTION("long blocks") { - FuzzJitThumb(1024, 15, instruction_select); + FuzzJitThumb(1024, 1025, 15, instruction_select); } } @@ -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, false); + FuzzJitThumb(1, 1, 10000, instruction_select); } 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, false); + FuzzJitThumb(2, 1, 1000, instruction_select); } \ No newline at end of file