JitX64: Implement 32-bit thumb BL(X) instruction

This commit is contained in:
MerryMage 2016-03-29 17:05:28 +01:00
parent c5dc3fedd5
commit 1e57d34c0b
5 changed files with 74 additions and 17 deletions

View File

@ -318,7 +318,7 @@ public:
virtual void thumb_B(Cond cond, Imm8 imm8) = 0; virtual void thumb_B(Cond cond, Imm8 imm8) = 0;
virtual void thumb_B(Imm11 imm11) = 0; virtual void thumb_B(Imm11 imm11) = 0;
virtual void thumb_BLX_prefix(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;
}; };
}; };

View File

@ -63,11 +63,6 @@ static constexpr T bits(T s){
return ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1)); return ((s << ((sizeof(s) * 8 - 1) - b)) >> (sizeof(s) * 8 - b + a - 1));
} }
template <size_t num_bits>
s32 sign_extend(s32 x) {
return x << (32 - num_bits) >> (32 - num_bits);
}
static const std::array<Instruction, 27> thumb_instruction_table = { { static const std::array<Instruction, 27> thumb_instruction_table = { {
{ "LSL/LSR/ASR", MakeMatcher("000ooxxxxxxxxxxx", [](Visitor* v, u32 instruction) { { "LSL/LSR/ASR", MakeMatcher("000ooxxxxxxxxxxx", [](Visitor* v, u32 instruction) {
u32 opcode = bits<11, 12>(instruction); u32 opcode = bits<11, 12>(instruction);
@ -428,7 +423,7 @@ static const std::array<Instruction, 27> thumb_instruction_table = { {
})}, })},
{ "BLX (suffix)", MakeMatcher("11101xxxxxxxxxx0", [](Visitor* v, u32 instruction) { { "BLX (suffix)", MakeMatcher("11101xxxxxxxxxx0", [](Visitor* v, u32 instruction) {
Imm11 imm11 = bits<0, 10>(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) { { "BL/BLX (prefix)", MakeMatcher("11110xxxxxxxxxxx", [](Visitor* v, u32 instruction) {
Imm11 imm11 = bits<0, 10>(instruction); Imm11 imm11 = bits<0, 10>(instruction);
@ -436,7 +431,7 @@ static const std::array<Instruction, 27> thumb_instruction_table = { {
})}, })},
{ "BL (suffix)", MakeMatcher("11111xxxxxxxxxxx", [](Visitor* v, u32 instruction) { { "BL (suffix)", MakeMatcher("11111xxxxxxxxxxx", [](Visitor* v, u32 instruction) {
Imm11 imm11 = bits<0, 10>(instruction); Imm11 imm11 = bits<0, 10>(instruction);
v->thumb_BLX_suffix(/*L=*/false, imm11); v->thumb_BLX_suffix(/*X=*/false, imm11);
})} })}
}}; }};

View File

@ -2,15 +2,20 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include "common/assert.h"
#include "common/math_util.h" #include "common/math_util.h"
#include "core/arm/jit_x64/jit_x64.h" #include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 { namespace JitX64 {
using namespace Gen;
void JitX64::thumb_B(Cond cond, ArmImm8 imm8) { void JitX64::thumb_B(Cond cond, ArmImm8 imm8) {
cond_manager.CompileCond((ConditionCode)cond); 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); const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<9, s32>(imm8 << 1);
reg_alloc.FlushEverything(); reg_alloc.FlushEverything();
@ -26,6 +31,8 @@ void JitX64::thumb_B(Cond cond, ArmImm8 imm8) {
void JitX64::thumb_B(ArmImm11 imm11) { void JitX64::thumb_B(ArmImm11 imm11) {
cond_manager.Always(); 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); const u32 new_pc = GetReg15Value() + MathUtil::SignExtend<12, s32>(imm11 << 1);
reg_alloc.FlushEverything(); reg_alloc.FlushEverything();
@ -36,7 +43,58 @@ void JitX64::thumb_B(ArmImm11 imm11) {
stop_compilation = true; stop_compilation = true;
} }
void JitX64::thumb_BLX_prefix(ArmImm11 imm11) { CompileInterpretInstruction(); } void JitX64::thumb_BLX_prefix(ArmImm11 imm11) {
void JitX64::thumb_BLX_suffix(bool L, ArmImm11 imm11) { CompileInterpretInstruction(); } 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;
}
} }

View File

@ -397,6 +397,10 @@ private:
// Thumb specific instructions // Thumb specific instructions
void thumb_B(Cond cond, ArmImm8 imm8) override; void thumb_B(Cond cond, ArmImm8 imm8) override;
void thumb_B(ArmImm11 imm11) 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_prefix(ArmImm11 imm11) override;
void thumb_BLX_suffix(bool L, ArmImm11 imm11) override; void thumb_BLX_suffix(bool L, ArmImm11 imm11) override;
}; };

View File

@ -40,7 +40,7 @@ std::pair<u16, u16> FromBitString16(const char* str) {
return{ bits, mask }; return{ bits, mask };
} }
void FuzzJitThumb(const int instruction_count, const int run_count, const std::function<u16(int)> 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<u16(int)> instruction_generator) {
// Init core // Init core
Core::Init(); Core::Init();
SCOPE_EXIT({ Core::Shutdown(); }); 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 Memory::Write16(4 + instruction_count * 2, 0xE7FE); // b +#0 // busy wait loop
interp.ExecuteInstructions(run_plus_1 ? instruction_count + 2 : instruction_count + 1); interp.ExecuteInstructions(instructions_to_execute_count);
jit.ExecuteInstructions(run_plus_1 ? instruction_count + 2 : instruction_count + 1); jit.ExecuteInstructions(instructions_to_execute_count);
bool pass = true; bool pass = true;
@ -183,11 +183,11 @@ TEST_CASE("Fuzz Thumb instructions set 1 (pure computation)", "[JitX64][Thumb]")
}; };
SECTION("short blocks") { SECTION("short blocks") {
FuzzJitThumb(5, 10000, instruction_select); FuzzJitThumb(5, 6, 10000, instruction_select);
} }
SECTION("long blocks") { 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); 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]") { 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); return inst_info.first | (random &~ inst_info.second);
}; };
FuzzJitThumb(1, 1000, instruction_select, false); FuzzJitThumb(2, 1, 1000, instruction_select);
} }