JitX64: Implement branch instructions

This commit is contained in:
MerryMage 2016-03-28 00:26:45 +01:00
parent 578e5881cc
commit c5dc3fedd5
13 changed files with 426 additions and 199 deletions

View File

@ -315,6 +315,8 @@ public:
virtual void SRS() = 0; virtual void SRS() = 0;
// Thumb specific instructions // 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_prefix(Imm11 imm11) = 0;
virtual void thumb_BLX_suffix(bool L, Imm11 imm11) = 0; virtual void thumb_BLX_suffix(bool L, Imm11 imm11) = 0;
}; };

View File

@ -414,7 +414,7 @@ static const std::array<Instruction, 27> thumb_instruction_table = { {
// B<cond> <PC + #offset*2> // B<cond> <PC + #offset*2>
Cond cond = bits<8, 11>(instruction); Cond cond = bits<8, 11>(instruction);
s32 offset = bits<0, 7>(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", MakeMatcher("11011111xxxxxxxx", [](Visitor* v, u32 instruction) {
// SWI #imm8 // SWI #imm8
@ -424,7 +424,7 @@ static const std::array<Instruction, 27> thumb_instruction_table = { {
{ "B", MakeMatcher("11100xxxxxxxxxxx", [](Visitor* v, u32 instruction) { { "B", MakeMatcher("11100xxxxxxxxxxx", [](Visitor* v, u32 instruction) {
// B <PC + #offset*2> // B <PC + #offset*2>
Imm11 imm11 = bits<0, 10>(instruction); 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) { { "BLX (suffix)", MakeMatcher("11101xxxxxxxxxx0", [](Visitor* v, u32 instruction) {
Imm11 imm11 = bits<0, 10>(instruction); Imm11 imm11 = bits<0, 10>(instruction);

View File

@ -2,15 +2,120 @@
// 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/math_util.h"
#include "core/arm/jit_x64/jit_x64.h" #include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 { namespace JitX64 {
void JitX64::B(Cond cond, ArmImm24 imm24) { CompileInterpretInstruction(); } using namespace Gen;
void JitX64::BL(Cond cond, ArmImm24 imm24) { CompileInterpretInstruction(); }
void JitX64::BLX_imm(bool H, ArmImm24 imm24) { CompileInterpretInstruction(); } void JitX64::B(Cond cond, ArmImm24 imm24) {
void JitX64::BLX_reg(Cond cond, ArmReg Rm) { CompileInterpretInstruction(); } cond_manager.CompileCond((ConditionCode)cond);
void JitX64::BX(Cond cond, ArmReg Rm) { CompileInterpretInstruction(); }
void JitX64::BXJ(Cond cond, ArmReg Rm) { CompileInterpretInstruction(); } 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);
}
} }

View File

@ -2,10 +2,40 @@
// 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/math_util.h"
#include "core/arm/jit_x64/jit_x64.h" #include "core/arm/jit_x64/jit_x64.h"
namespace JitX64 { 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_prefix(ArmImm11 imm11) { CompileInterpretInstruction(); }
void JitX64::thumb_BLX_suffix(bool L, ArmImm11 imm11) { CompileInterpretInstruction(); } void JitX64::thumb_BLX_suffix(bool L, ArmImm11 imm11) { CompileInterpretInstruction(); }

View File

@ -103,8 +103,6 @@ void JitX64::CompileReturnToDispatch() {
} }
void JitX64::CompileJumpToBB(u32 new_pc) { void JitX64::CompileJumpToBB(u32 new_pc) {
ASSERT(instructions_compiled == 0);
reg_alloc.FlushEverything(); reg_alloc.FlushEverything();
code->CMP(32, MJitStateCycleCount(), Imm8(0)); code->CMP(32, MJitStateCycleCount(), Imm8(0));
@ -146,7 +144,7 @@ void JitX64::CompileSingleArmInstruction() {
void JitX64::CompileSingleThumbInstruction() { void JitX64::CompileSingleThumbInstruction() {
u32 inst_u32 = Memory::Read32(current.arm_pc & 0xFFFFFFFC); 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 >>= 16;
} }
inst_u32 &= 0xFFFFF; 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)); 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<decltype(JitState::cpu_state), ARMul_State>::value, "JitState::cpu_state must be ARMul_State");
static_assert(std::is_same<decltype(ARMul_State::TFlag), u32>::value, "TFlag must be u32");
return Gen::MDisp(reg_alloc.JitStateReg(), offsetof(JitState, cpu_state) + offsetof(ARMul_State, TFlag));
}
Gen::OpArg JitX64::MJitStateHostReturnRIP() { Gen::OpArg JitX64::MJitStateHostReturnRIP() {
static_assert(std::is_same<decltype(JitState::return_RIP), u64>::value, "JitState::return_RIP must be u64"); static_assert(std::is_same<decltype(JitState::return_RIP), u64>::value, "JitState::return_RIP must be u64");

View File

@ -84,6 +84,7 @@ private:
Gen::OpArg MJitStateCycleCount(); Gen::OpArg MJitStateCycleCount();
Gen::OpArg MJitStateArmPC(); Gen::OpArg MJitStateArmPC();
Gen::OpArg MJitStateTFlag();
Gen::OpArg MJitStateHostReturnRIP(); Gen::OpArg MJitStateHostReturnRIP();
Gen::OpArg MJitStateHostReturnRSP(); Gen::OpArg MJitStateHostReturnRSP();
Gen::OpArg MJitStateZFlag(); Gen::OpArg MJitStateZFlag();
@ -394,6 +395,8 @@ private:
void SRS() override; void SRS() override;
// Thumb specific instructions // 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_prefix(ArmImm11 imm11) override;
void thumb_BLX_suffix(bool L, ArmImm11 imm11) override; void thumb_BLX_suffix(bool L, ArmImm11 imm11) override;
}; };

View File

@ -7,10 +7,16 @@ set(HEADERS
if(ARCHITECTURE_x86_64) if(ARCHITECTURE_x86_64)
set(SRCS ${SRCS} 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_arm_data_processing.cpp
core/arm/jit_x64/fuzz_thumb.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() endif()
create_directory_groups(${SRCS} ${HEADERS}) create_directory_groups(${SRCS} ${HEADERS})

View File

@ -0,0 +1,35 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <catch.hpp>
#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<std::pair<u32, u32>, 6> instructions = {{
FromBitString32("1111101hvvvvvvvvvvvvvvvvvvvvvvvv"),
FromBitString32("cccc000100101111111111110011mmmm"),
FromBitString32("cccc1010vvvvvvvvvvvvvvvvvvvvvvvv"),
FromBitString32("cccc1011vvvvvvvvvvvvvvvvvvvvvvvv"),
FromBitString32("cccc000100101111111111110001mmmm"),
FromBitString32("cccc000100101111111111110010mmmm"),
}};
auto instruction_select = [&]() -> u32 {
size_t inst_index = RandInt<size_t>(0, instructions.size() - 1);
u32 random = RandInt<u32>(0, 0xFFFFFFFF);
return instructions[inst_index].first | (random & (~instructions[inst_index].second));
};
SECTION("R15") {
FuzzJit(1, 1, 10000, instruction_select);
}
}

View File

@ -0,0 +1,143 @@
// Copyright 2016 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstdio>
#include <cstring>
#include <catch.hpp>
#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<u32, u32> 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<u32()> instruction_generator) {
// Init core
Core::Init();
SCOPE_EXIT({ Core::Shutdown(); });
// Prepare memory
constexpr size_t MEMORY_SIZE = 4096 * 2;
std::array<u8, MEMORY_SIZE> 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<u32>(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);
}
}
}

View File

@ -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 <functional>
#include <utility>
#include "common/common_types.h"
std::pair<u32, u32> 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<u32()> instruction_generator);

View File

@ -2,194 +2,65 @@
// 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 <cstdio> #include <array>
#include <cstring>
#include <catch.hpp> #include <catch.hpp>
#include "common/common_types.h" #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" #include "tests/core/arm/jit_x64/rand_int.h"
#include "tests/core/arm/jit_x64/fuzz_arm_common.h"
std::pair<u32, u32> 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<u32()> instruction_generator) {
// Init core
Core::Init();
SCOPE_EXIT({ Core::Shutdown(); });
// Prepare memory
constexpr size_t MEMORY_SIZE = 4096 * 2;
std::array<u8, MEMORY_SIZE> 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<u32>(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);
}
}
}
TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") { TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") {
const std::array<std::pair<u32, u32>, 48> instructions = {{ const std::array<std::pair<u32, u32>, 48> instructions = {{
FromBitString("cccc0010101Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0010101Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0000101Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0000101Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0000101Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0000101Snnnnddddssss0rr1mmmm"),
FromBitString("cccc0010100Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0010100Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0000100Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0000100Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0000100Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0000100Snnnnddddssss0rr1mmmm"),
FromBitString("cccc0010000Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0010000Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0000000Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0000000Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0000000Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0000000Snnnnddddssss0rr1mmmm"),
FromBitString("cccc0011110Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0011110Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0001110Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0001110Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0001110Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0001110Snnnnddddssss0rr1mmmm"),
FromBitString("cccc00110111nnnn0000rrrrvvvvvvvv"), FromBitString32("cccc00110111nnnn0000rrrrvvvvvvvv"),
FromBitString("cccc00010111nnnn0000vvvvvrr0mmmm"), FromBitString32("cccc00010111nnnn0000vvvvvrr0mmmm"),
FromBitString("cccc00010111nnnn0000ssss0rr1mmmm"), FromBitString32("cccc00010111nnnn0000ssss0rr1mmmm"),
FromBitString("cccc00110101nnnn0000rrrrvvvvvvvv"), FromBitString32("cccc00110101nnnn0000rrrrvvvvvvvv"),
FromBitString("cccc00010101nnnn0000vvvvvrr0mmmm"), FromBitString32("cccc00010101nnnn0000vvvvvrr0mmmm"),
FromBitString("cccc00010101nnnn0000ssss0rr1mmmm"), FromBitString32("cccc00010101nnnn0000ssss0rr1mmmm"),
FromBitString("cccc0010001Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0010001Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0000001Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0000001Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0000001Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0000001Snnnnddddssss0rr1mmmm"),
FromBitString("cccc0011101S0000ddddrrrrvvvvvvvv"), FromBitString32("cccc0011101S0000ddddrrrrvvvvvvvv"),
FromBitString("cccc0001101S0000ddddvvvvvrr0mmmm"), FromBitString32("cccc0001101S0000ddddvvvvvrr0mmmm"),
FromBitString("cccc0001101S0000ddddssss0rr1mmmm"), FromBitString32("cccc0001101S0000ddddssss0rr1mmmm"),
FromBitString("cccc0011111S0000ddddrrrrvvvvvvvv"), FromBitString32("cccc0011111S0000ddddrrrrvvvvvvvv"),
FromBitString("cccc0001111S0000ddddvvvvvrr0mmmm"), FromBitString32("cccc0001111S0000ddddvvvvvrr0mmmm"),
FromBitString("cccc0001111S0000ddddssss0rr1mmmm"), FromBitString32("cccc0001111S0000ddddssss0rr1mmmm"),
FromBitString("cccc0011100Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0011100Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0001100Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0001100Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0001100Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0001100Snnnnddddssss0rr1mmmm"),
FromBitString("cccc0010011Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0010011Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0000011Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0000011Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0000011Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0000011Snnnnddddssss0rr1mmmm"),
FromBitString("cccc0010111Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0010111Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0000111Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0000111Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0000111Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0000111Snnnnddddssss0rr1mmmm"),
FromBitString("cccc0010110Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0010110Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0000110Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0000110Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0000110Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0000110Snnnnddddssss0rr1mmmm"),
FromBitString("cccc0010010Snnnnddddrrrrvvvvvvvv"), FromBitString32("cccc0010010Snnnnddddrrrrvvvvvvvv"),
FromBitString("cccc0000010Snnnnddddvvvvvrr0mmmm"), FromBitString32("cccc0000010Snnnnddddvvvvvrr0mmmm"),
FromBitString("cccc0000010Snnnnddddssss0rr1mmmm"), FromBitString32("cccc0000010Snnnnddddssss0rr1mmmm"),
FromBitString("cccc00110011nnnn0000rrrrvvvvvvvv"), FromBitString32("cccc00110011nnnn0000rrrrvvvvvvvv"),
FromBitString("cccc00010011nnnn0000vvvvvrr0mmmm"), FromBitString32("cccc00010011nnnn0000vvvvvrr0mmmm"),
FromBitString("cccc00010011nnnn0000ssss0rr1mmmm"), FromBitString32("cccc00010011nnnn0000ssss0rr1mmmm"),
FromBitString("cccc00110001nnnn0000rrrrvvvvvvvv"), FromBitString32("cccc00110001nnnn0000rrrrvvvvvvvv"),
FromBitString("cccc00010001nnnn0000vvvvvrr0mmmm"), FromBitString32("cccc00010001nnnn0000vvvvvrr0mmmm"),
FromBitString("cccc00010001nnnn0000ssss0rr1mmmm"), FromBitString32("cccc00010001nnnn0000ssss0rr1mmmm"),
}}; }};
auto instruction_select_without_R15 = [&]() -> u32 { auto instruction_select_without_R15 = [&]() -> u32 {
@ -212,11 +83,11 @@ TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") {
}; };
SECTION("short blocks") { SECTION("short blocks") {
FuzzJit(5, 5000, instruction_select_without_R15); FuzzJit(5, 6, 5000, instruction_select_without_R15);
} }
SECTION("long blocks") { SECTION("long blocks") {
FuzzJit(1024, 15, instruction_select_without_R15); FuzzJit(1024, 1025, 15, instruction_select_without_R15);
} }
auto instruction_select_only_R15 = [&]() -> u32 { auto instruction_select_only_R15 = [&]() -> u32 {
@ -239,6 +110,6 @@ TEST_CASE("Fuzz ARM data processing instructions", "[JitX64]") {
}; };
SECTION("R15") { SECTION("R15") {
FuzzJit(1, 10000, instruction_select_only_R15); FuzzJit(1, 1, 10000, instruction_select_only_R15);
} }
} }

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) { void FuzzJitThumb(const int instruction_count, const int run_count, const std::function<u16(int)> instruction_generator, bool run_plus_1 = true) {
// 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(instruction_count + 1); interp.ExecuteInstructions(run_plus_1 ? instruction_count + 2 : instruction_count + 1);
jit.ExecuteInstructions(instruction_count + 1); jit.ExecuteInstructions(run_plus_1 ? instruction_count + 2 : instruction_count + 1);
bool pass = true; 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); 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]") { 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); FuzzJitThumb(1, 1000, instruction_select, false);
} }

View File

@ -5,9 +5,14 @@
#pragma once #pragma once
#include <random> #include <random>
#include <type_traits>
template <typename T> template <typename T>
T RandInt(T min, T max) { T RandInt(T min, T max) {
static_assert(std::is_integral<T>::value, "T must be an integral type.");
static_assert(!std::is_same<T, signed char>::value && !std::is_same<T, unsigned char>::value,
"Using char with uniform_int_distribution is undefined behavior.");
static std::random_device rd; static std::random_device rd;
static std::mt19937 mt(rd()); static std::mt19937 mt(rd());
std::uniform_int_distribution<T> rand(min, max); std::uniform_int_distribution<T> rand(min, max);