diff --git a/tests/A64/fuzz_with_unicorn.cpp b/tests/A64/fuzz_with_unicorn.cpp index a421d2a2..90cd3afa 100644 --- a/tests/A64/fuzz_with_unicorn.cpp +++ b/tests/A64/fuzz_with_unicorn.cpp @@ -11,6 +11,7 @@ #include +#include "common/common_types.h" #include "common/fp/fpcr.h" #include "common/fp/fpsr.h" #include "common/llvm_disassemble.h" @@ -20,35 +21,17 @@ #include "frontend/A64/translate/translate.h" #include "frontend/ir/basic_block.h" #include "frontend/ir/opcodes.h" -#include "inst_gen.h" +#include "fuzz_util.h" #include "rand_int.h" #include "testenv.h" #include "unicorn_emu/a64_unicorn.h" -// Needs to be declaerd before -static std::ostream& operator<<(std::ostream& o, const Dynarmic::A64::Vector& vec) { - return o << fmt::format("{:016x}'{:016x}", vec[1], vec[0]); -} - +// Must be declared last for all necessary operator<< to be declared prior to this. #include #include using namespace Dynarmic; -static Vector RandomVector() { - return {RandInt(0, ~u64(0)), RandInt(0, ~u64(0))}; -} - -static u32 RandomFpcr() { - FP::FPCR fpcr; - fpcr.AHP(RandInt(0, 1) == 0); - fpcr.DN(RandInt(0, 1) == 0); - fpcr.FZ(RandInt(0, 1) == 0); - fpcr.RMode(static_cast(RandInt(0, 3))); - fpcr.FZ16(RandInt(0, 1) == 0); - return fpcr.Value(); -} - static bool ShouldTestInst(u32 instruction, u64 pc, bool is_last_inst) { const A64::LocationDescriptor location{pc, {}}; IR::Block block{location}; @@ -72,14 +55,18 @@ static bool ShouldTestInst(u32 instruction, u64 pc, bool is_last_inst) { } static u32 GenRandomInst(u64 pc, bool is_last_inst) { - static const std::vector instruction_generators = []{ + static const struct InstructionGeneratorInfo { + std::vector generators; + std::vector invalid; + } instructions = []{ const std::vector> list { #define INST(fn, name, bitstring) {#fn, bitstring}, #include "frontend/A64/decoder/a64.inc" #undef INST }; - std::vector result; + std::vector generators; + std::vector invalid; // List of instructions not to test const std::vector do_not_test { @@ -98,20 +85,23 @@ static u32 GenRandomInst(u64 pc, bool is_last_inst) { continue; } if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) { - InstructionGenerator::AddInvalidInstruction(bitstring); + invalid.emplace_back(InstructionGenerator{bitstring}); continue; } - result.emplace_back(InstructionGenerator{bitstring}); + generators.emplace_back(InstructionGenerator{bitstring}); } - return result; + return InstructionGeneratorInfo{generators, invalid}; }(); while (true) { - const size_t index = RandInt(0, instruction_generators.size() - 1); - const u32 instruction = instruction_generators[index].Generate(); + const size_t index = RandInt(0, instructions.generators.size() - 1); + const u32 inst = instructions.generators[index].Generate(); - if (ShouldTestInst(instruction, pc, is_last_inst)) { - return instruction; + if (std::any_of(instructions.invalid.begin(), instructions.invalid.end(), [inst](const auto& invalid) { return invalid.Match(inst); })) { + continue; + } + if (ShouldTestInst(inst, pc, is_last_inst)) { + return inst; } } } @@ -156,11 +146,17 @@ static u32 GenFloatInst(u64 pc, bool is_last_inst) { } } -static void RunTestInstance(const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start, - const std::vector& instructions, const u32 pstate, const u32 fpcr) { - static A64TestEnv jit_env{}; - static A64TestEnv uni_env{}; +static Dynarmic::A64::UserConfig GetUserConfig(A64TestEnv& jit_env) { + Dynarmic::A64::UserConfig jit_user_config{&jit_env}; + // The below corresponds to the settings for qemu's aarch64_max_initfn + jit_user_config.dczid_el0 = 7; + jit_user_config.ctr_el0 = 0x80038003; + return jit_user_config; +} +static void RunTestInstance(Dynarmic::A64::Jit& jit, A64Unicorn& uni, A64TestEnv& jit_env, A64TestEnv& uni_env, + const A64Unicorn::RegisterArray& regs, const A64Unicorn::VectorArray& vecs, const size_t instructions_start, + const std::vector& instructions, const u32 pstate, const u32 fpcr) { jit_env.code_mem = instructions; uni_env.code_mem = instructions; jit_env.code_mem.emplace_back(0x14000000); // B . @@ -172,14 +168,6 @@ static void RunTestInstance(const A64Unicorn::RegisterArray& regs, const A64Unic jit_env.interrupts.clear(); uni_env.interrupts.clear(); - Dynarmic::A64::UserConfig jit_user_config{&jit_env}; - // The below corresponds to the settings for qemu's aarch64_max_initfn - jit_user_config.dczid_el0 = 7; - jit_user_config.ctr_el0 = 0x80038003; - - static Dynarmic::A64::Jit jit{jit_user_config}; - static A64Unicorn uni{uni_env}; - const u64 initial_sp = RandInt(0x30'0000'0000, 0x40'0000'0000) * 4; jit.SetRegisters(regs); @@ -283,6 +271,12 @@ static void RunTestInstance(const A64Unicorn::RegisterArray& regs, const A64Unic } TEST_CASE("A64: Single random instruction", "[a64]") { + A64TestEnv jit_env{}; + A64TestEnv uni_env{}; + + Dynarmic::A64::Jit jit{GetUserConfig(jit_env)}; + A64Unicorn uni{uni_env}; + A64Unicorn::RegisterArray regs; A64Unicorn::VectorArray vecs; std::vector instructions(1); @@ -299,11 +293,17 @@ TEST_CASE("A64: Single random instruction", "[a64]") { INFO("Instruction: 0x" << std::hex << instructions[0]); - RunTestInstance(regs, vecs, start_address, instructions, pstate, fpcr); + RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr); } } TEST_CASE("A64: Floating point instructions", "[a64]") { + A64TestEnv jit_env{}; + A64TestEnv uni_env{}; + + Dynarmic::A64::Jit jit{GetUserConfig(jit_env)}; + A64Unicorn uni{uni_env}; + static constexpr std::array float_numbers { 0x00000000, // positive zero 0x00000001, // smallest positive denormal @@ -415,11 +415,17 @@ TEST_CASE("A64: Floating point instructions", "[a64]") { INFO("Instruction: 0x" << std::hex << instructions[0]); - RunTestInstance(regs, vecs, start_address, instructions, pstate, fpcr); + RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr); } } TEST_CASE("A64: Small random block", "[a64]") { + A64TestEnv jit_env{}; + A64TestEnv uni_env{}; + + Dynarmic::A64::Jit jit{GetUserConfig(jit_env)}; + A64Unicorn uni{uni_env}; + A64Unicorn::RegisterArray regs; A64Unicorn::VectorArray vecs; std::vector instructions(5); @@ -444,6 +450,6 @@ TEST_CASE("A64: Small random block", "[a64]") { INFO("Instruction 4: 0x" << std::hex << instructions[3]); INFO("Instruction 5: 0x" << std::hex << instructions[4]); - RunTestInstance(regs, vecs, start_address, instructions, pstate, fpcr); + RunTestInstance(jit, uni, jit_env, uni_env, regs, vecs, start_address, instructions, pstate, fpcr); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 768611cb..6e645862 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,8 +26,6 @@ add_executable(dynarmic_tests A32/test_thumb_instructions.cpp A32/testenv.h A64/a64.cpp - A64/inst_gen.cpp - A64/inst_gen.h A64/testenv.h cpu_info.cpp fp/FPToFixed.cpp @@ -42,6 +40,8 @@ if (DYNARMIC_TESTS_USE_UNICORN) target_sources(dynarmic_tests PRIVATE A64/fuzz_with_unicorn.cpp A64/verify_unicorn.cpp + fuzz_util.cpp + fuzz_util.h unicorn_emu/a32_unicorn.cpp unicorn_emu/a32_unicorn.h unicorn_emu/a64_unicorn.cpp diff --git a/tests/A64/inst_gen.cpp b/tests/fuzz_util.cpp similarity index 51% rename from tests/A64/inst_gen.cpp rename to tests/fuzz_util.cpp index 8b212173..31e78043 100644 --- a/tests/A64/inst_gen.cpp +++ b/tests/fuzz_util.cpp @@ -4,10 +4,37 @@ * General Public License version 2 or any later version. */ +#include + +#include +#include + #include "common/assert.h" -#include "inst_gen.h" +#include "common/fp/fpcr.h" +#include "common/fp/rounding_mode.h" +#include "fuzz_util.h" #include "rand_int.h" +using namespace Dynarmic; + +std::ostream& operator<<(std::ostream& o, Vector vec) { + return o << fmt::format("{:016x}'{:016x}", vec[1], vec[0]); +} + +Vector RandomVector() { + return {RandInt(0, ~u64(0)), RandInt(0, ~u64(0))}; +} + +u32 RandomFpcr() { + FP::FPCR fpcr; + fpcr.AHP(RandInt(0, 1) == 0); + fpcr.DN(RandInt(0, 1) == 0); + fpcr.FZ(RandInt(0, 1) == 0); + fpcr.RMode(static_cast(RandInt(0, 3))); + fpcr.FZ16(RandInt(0, 1) == 0); + return fpcr.Value(); +} + InstructionGenerator::InstructionGenerator(const char* format){ ASSERT(std::strlen(format) == 32); @@ -30,9 +57,7 @@ InstructionGenerator::InstructionGenerator(const char* format){ u32 InstructionGenerator::Generate() const { u32 inst; - do { - u32 random = RandInt(0, 0xFFFFFFFF); - inst = bits | (random & ~mask); - } while (IsInvalidInstruction(inst)); + u32 random = RandInt(0, 0xFFFFFFFF); + inst = bits | (random & ~mask); return inst; } diff --git a/tests/A64/inst_gen.h b/tests/fuzz_util.h similarity index 53% rename from tests/A64/inst_gen.h rename to tests/fuzz_util.h index b3a9928f..bdd3d5e9 100644 --- a/tests/A64/inst_gen.h +++ b/tests/fuzz_util.h @@ -4,12 +4,19 @@ * General Public License version 2 or any later version. */ -#include -#include -#include +#pragma once + +#include +#include #include "common/common_types.h" +using Vector = std::array; + +std::ostream& operator<<(std::ostream& o, Vector vec); +Vector RandomVector(); +u32 RandomFpcr(); + struct InstructionGenerator final { public: explicit InstructionGenerator(const char* format); @@ -19,18 +26,7 @@ public: u32 Mask() const { return mask; } bool Match(u32 inst) const { return (inst & mask) == bits; } - static void AddInvalidInstruction(const char* format) { - invalid_instructions.emplace_back(InstructionGenerator{format}); - } - - static bool IsInvalidInstruction(u32 inst) { - return std::any_of(invalid_instructions.begin(), invalid_instructions.end(), - [inst](const auto& invalid) { return invalid.Match(inst); }); - } - private: - static inline std::vector invalid_instructions; - u32 bits = 0; u32 mask = 0; };