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 "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 <fmt/ostream.h>
|
||||
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 <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
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) {
|
||||
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<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 {
|
||||
#define INST(fn, name, bitstring) {#fn, bitstring},
|
||||
#include "frontend/A64/decoder/a64.inc"
|
||||
#undef INST
|
||||
};
|
||||
|
||||
std::vector<InstructionGenerator> result;
|
||||
std::vector<InstructionGenerator> generators;
|
||||
std::vector<InstructionGenerator> invalid;
|
||||
|
||||
// List of instructions not to test
|
||||
const std::vector<std::string> 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<size_t>(0, instruction_generators.size() - 1);
|
||||
const u32 instruction = instruction_generators[index].Generate();
|
||||
const size_t index = RandInt<size_t>(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<u32>& 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<u32>& 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<u64>(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<u32> 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<u64, 80> 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<u32> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,10 +4,37 @@
|
|||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.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"
|
||||
|
||||
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){
|
||||
ASSERT(std::strlen(format) == 32);
|
||||
|
||||
|
@ -30,9 +57,7 @@ InstructionGenerator::InstructionGenerator(const char* format){
|
|||
|
||||
u32 InstructionGenerator::Generate() const {
|
||||
u32 inst;
|
||||
do {
|
||||
u32 random = RandInt<u32>(0, 0xFFFFFFFF);
|
||||
inst = bits | (random & ~mask);
|
||||
} while (IsInvalidInstruction(inst));
|
||||
u32 random = RandInt<u32>(0, 0xFFFFFFFF);
|
||||
inst = bits | (random & ~mask);
|
||||
return inst;
|
||||
}
|
|
@ -4,12 +4,19 @@
|
|||
* General Public License version 2 or any later version.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <iosfwd>
|
||||
|
||||
#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 {
|
||||
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<InstructionGenerator> invalid_instructions;
|
||||
|
||||
u32 bits = 0;
|
||||
u32 mask = 0;
|
||||
};
|
Loading…
Reference in a new issue