fuzz_with_unicorn: Split utility functions into fuzz_util
This commit is contained in:
parent
ac51c2547d
commit
c96c534615
4 changed files with 92 additions and 65 deletions
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
#include "common/fp/fpcr.h"
|
#include "common/fp/fpcr.h"
|
||||||
#include "common/fp/fpsr.h"
|
#include "common/fp/fpsr.h"
|
||||||
#include "common/llvm_disassemble.h"
|
#include "common/llvm_disassemble.h"
|
||||||
|
@ -20,35 +21,17 @@
|
||||||
#include "frontend/A64/translate/translate.h"
|
#include "frontend/A64/translate/translate.h"
|
||||||
#include "frontend/ir/basic_block.h"
|
#include "frontend/ir/basic_block.h"
|
||||||
#include "frontend/ir/opcodes.h"
|
#include "frontend/ir/opcodes.h"
|
||||||
#include "inst_gen.h"
|
#include "fuzz_util.h"
|
||||||
#include "rand_int.h"
|
#include "rand_int.h"
|
||||||
#include "testenv.h"
|
#include "testenv.h"
|
||||||
#include "unicorn_emu/a64_unicorn.h"
|
#include "unicorn_emu/a64_unicorn.h"
|
||||||
|
|
||||||
// Needs to be declaerd before <fmt/ostream.h>
|
// Must be declared last for all necessary operator<< to be declared prior to this.
|
||||||
static std::ostream& operator<<(std::ostream& o, const Dynarmic::A64::Vector& vec) {
|
|
||||||
return o << fmt::format("{:016x}'{:016x}", vec[1], vec[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <fmt/ostream.h>
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
static Vector RandomVector() {
|
|
||||||
return {RandInt<u64>(0, ~u64(0)), RandInt<u64>(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<FP::RoundingMode>(RandInt(0, 3)));
|
|
||||||
fpcr.FZ16(RandInt(0, 1) == 0);
|
|
||||||
return fpcr.Value();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ShouldTestInst(u32 instruction, u64 pc, bool is_last_inst) {
|
static bool ShouldTestInst(u32 instruction, u64 pc, bool is_last_inst) {
|
||||||
const A64::LocationDescriptor location{pc, {}};
|
const A64::LocationDescriptor location{pc, {}};
|
||||||
IR::Block block{location};
|
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 u32 GenRandomInst(u64 pc, bool is_last_inst) {
|
||||||
static const std::vector<InstructionGenerator> instruction_generators = []{
|
static const struct InstructionGeneratorInfo {
|
||||||
|
std::vector<InstructionGenerator> generators;
|
||||||
|
std::vector<InstructionGenerator> invalid;
|
||||||
|
} instructions = []{
|
||||||
const std::vector<std::tuple<std::string, const char*>> list {
|
const std::vector<std::tuple<std::string, const char*>> list {
|
||||||
#define INST(fn, name, bitstring) {#fn, bitstring},
|
#define INST(fn, name, bitstring) {#fn, bitstring},
|
||||||
#include "frontend/A64/decoder/a64.inc"
|
#include "frontend/A64/decoder/a64.inc"
|
||||||
#undef INST
|
#undef INST
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<InstructionGenerator> result;
|
std::vector<InstructionGenerator> generators;
|
||||||
|
std::vector<InstructionGenerator> invalid;
|
||||||
|
|
||||||
// List of instructions not to test
|
// List of instructions not to test
|
||||||
const std::vector<std::string> do_not_test {
|
const std::vector<std::string> do_not_test {
|
||||||
|
@ -98,20 +85,23 @@ static u32 GenRandomInst(u64 pc, bool is_last_inst) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
result.emplace_back(InstructionGenerator{bitstring});
|
generators.emplace_back(InstructionGenerator{bitstring});
|
||||||
}
|
}
|
||||||
return result;
|
return InstructionGeneratorInfo{generators, invalid};
|
||||||
}();
|
}();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const size_t index = RandInt<size_t>(0, instruction_generators.size() - 1);
|
const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1);
|
||||||
const u32 instruction = instruction_generators[index].Generate();
|
const u32 inst = instructions.generators[index].Generate();
|
||||||
|
|
||||||
if (ShouldTestInst(instruction, pc, is_last_inst)) {
|
if (std::any_of(instructions.invalid.begin(), instructions.invalid.end(), [inst](const auto& invalid) { return invalid.Match(inst); })) {
|
||||||
return instruction;
|
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,
|
static Dynarmic::A64::UserConfig GetUserConfig(A64TestEnv& jit_env) {
|
||||||
const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) {
|
Dynarmic::A64::UserConfig jit_user_config{&jit_env};
|
||||||
static A64TestEnv jit_env{};
|
// The below corresponds to the settings for qemu's aarch64_max_initfn
|
||||||
static A64TestEnv uni_env{};
|
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<u32>& instructions, const u32 pstate, const u32 fpcr) {
|
||||||
jit_env.code_mem = instructions;
|
jit_env.code_mem = instructions;
|
||||||
uni_env.code_mem = instructions;
|
uni_env.code_mem = instructions;
|
||||||
jit_env.code_mem.emplace_back(0x14000000); // B .
|
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();
|
jit_env.interrupts.clear();
|
||||||
uni_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<u64>(0x30'0000'0000, 0x40'0000'0000) * 4;
|
const u64 initial_sp = RandInt<u64>(0x30'0000'0000, 0x40'0000'0000) * 4;
|
||||||
|
|
||||||
jit.SetRegisters(regs);
|
jit.SetRegisters(regs);
|
||||||
|
@ -283,6 +271,12 @@ static void RunTestInstance(const A64Unicorn::RegisterArray& regs, const A64Unic
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("A64: Single random instruction", "[a64]") {
|
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::RegisterArray regs;
|
||||||
A64Unicorn::VectorArray vecs;
|
A64Unicorn::VectorArray vecs;
|
||||||
std::vector<u32> instructions(1);
|
std::vector<u32> instructions(1);
|
||||||
|
@ -299,11 +293,17 @@ TEST_CASE("A64: Single random instruction", "[a64]") {
|
||||||
|
|
||||||
INFO("Instruction: 0x" << std::hex << instructions[0]);
|
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]") {
|
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<u64, 80> float_numbers {
|
static constexpr std::array<u64, 80> float_numbers {
|
||||||
0x00000000, // positive zero
|
0x00000000, // positive zero
|
||||||
0x00000001, // smallest positive denormal
|
0x00000001, // smallest positive denormal
|
||||||
|
@ -415,11 +415,17 @@ TEST_CASE("A64: Floating point instructions", "[a64]") {
|
||||||
|
|
||||||
INFO("Instruction: 0x" << std::hex << instructions[0]);
|
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]") {
|
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::RegisterArray regs;
|
||||||
A64Unicorn::VectorArray vecs;
|
A64Unicorn::VectorArray vecs;
|
||||||
std::vector<u32> instructions(5);
|
std::vector<u32> instructions(5);
|
||||||
|
@ -444,6 +450,6 @@ TEST_CASE("A64: Small random block", "[a64]") {
|
||||||
INFO("Instruction 4: 0x" << std::hex << instructions[3]);
|
INFO("Instruction 4: 0x" << std::hex << instructions[3]);
|
||||||
INFO("Instruction 5: 0x" << std::hex << instructions[4]);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,6 @@ add_executable(dynarmic_tests
|
||||||
A32/test_thumb_instructions.cpp
|
A32/test_thumb_instructions.cpp
|
||||||
A32/testenv.h
|
A32/testenv.h
|
||||||
A64/a64.cpp
|
A64/a64.cpp
|
||||||
A64/inst_gen.cpp
|
|
||||||
A64/inst_gen.h
|
|
||||||
A64/testenv.h
|
A64/testenv.h
|
||||||
cpu_info.cpp
|
cpu_info.cpp
|
||||||
fp/FPToFixed.cpp
|
fp/FPToFixed.cpp
|
||||||
|
@ -42,6 +40,8 @@ if (DYNARMIC_TESTS_USE_UNICORN)
|
||||||
target_sources(dynarmic_tests PRIVATE
|
target_sources(dynarmic_tests PRIVATE
|
||||||
A64/fuzz_with_unicorn.cpp
|
A64/fuzz_with_unicorn.cpp
|
||||||
A64/verify_unicorn.cpp
|
A64/verify_unicorn.cpp
|
||||||
|
fuzz_util.cpp
|
||||||
|
fuzz_util.h
|
||||||
unicorn_emu/a32_unicorn.cpp
|
unicorn_emu/a32_unicorn.cpp
|
||||||
unicorn_emu/a32_unicorn.h
|
unicorn_emu/a32_unicorn.h
|
||||||
unicorn_emu/a64_unicorn.cpp
|
unicorn_emu/a64_unicorn.cpp
|
||||||
|
|
|
@ -4,10 +4,37 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <fmt/ostream.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#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"
|
#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<u64>(0, ~u64(0)), RandInt<u64>(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<FP::RoundingMode>(RandInt(0, 3)));
|
||||||
|
fpcr.FZ16(RandInt(0, 1) == 0);
|
||||||
|
return fpcr.Value();
|
||||||
|
}
|
||||||
|
|
||||||
InstructionGenerator::InstructionGenerator(const char* format){
|
InstructionGenerator::InstructionGenerator(const char* format){
|
||||||
ASSERT(std::strlen(format) == 32);
|
ASSERT(std::strlen(format) == 32);
|
||||||
|
|
||||||
|
@ -30,9 +57,7 @@ InstructionGenerator::InstructionGenerator(const char* format){
|
||||||
|
|
||||||
u32 InstructionGenerator::Generate() const {
|
u32 InstructionGenerator::Generate() const {
|
||||||
u32 inst;
|
u32 inst;
|
||||||
do {
|
|
||||||
u32 random = RandInt<u32>(0, 0xFFFFFFFF);
|
u32 random = RandInt<u32>(0, 0xFFFFFFFF);
|
||||||
inst = bits | (random & ~mask);
|
inst = bits | (random & ~mask);
|
||||||
} while (IsInvalidInstruction(inst));
|
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
|
@ -4,12 +4,19 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <algorithm>
|
#pragma once
|
||||||
#include <cstring>
|
|
||||||
#include <vector>
|
#include <array>
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
using Vector = std::array<u64, 2>;
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& o, Vector vec);
|
||||||
|
Vector RandomVector();
|
||||||
|
u32 RandomFpcr();
|
||||||
|
|
||||||
struct InstructionGenerator final {
|
struct InstructionGenerator final {
|
||||||
public:
|
public:
|
||||||
explicit InstructionGenerator(const char* format);
|
explicit InstructionGenerator(const char* format);
|
||||||
|
@ -19,18 +26,7 @@ public:
|
||||||
u32 Mask() const { return mask; }
|
u32 Mask() const { return mask; }
|
||||||
bool Match(u32 inst) const { return (inst & mask) == bits; }
|
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:
|
private:
|
||||||
static inline std::vector<InstructionGenerator> invalid_instructions;
|
|
||||||
|
|
||||||
u32 bits = 0;
|
u32 bits = 0;
|
||||||
u32 mask = 0;
|
u32 mask = 0;
|
||||||
};
|
};
|
Loading…
Reference in a new issue