test_generator: A64
This commit is contained in:
parent
21b4211414
commit
46aef36a4f
1 changed files with 241 additions and 34 deletions
|
@ -14,6 +14,7 @@
|
||||||
#include <mcl/stdint.hpp>
|
#include <mcl/stdint.hpp>
|
||||||
|
|
||||||
#include "./A32/testenv.h"
|
#include "./A32/testenv.h"
|
||||||
|
#include "./A64/testenv.h"
|
||||||
#include "./fuzz_util.h"
|
#include "./fuzz_util.h"
|
||||||
#include "./rand_int.h"
|
#include "./rand_int.h"
|
||||||
#include "dynarmic/common/fp/fpcr.h"
|
#include "dynarmic/common/fp/fpcr.h"
|
||||||
|
@ -22,7 +23,11 @@
|
||||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
#include "dynarmic/frontend/A32/a32_types.h"
|
#include "dynarmic/frontend/A32/a32_types.h"
|
||||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||||
|
#include "dynarmic/frontend/A64/a64_location_descriptor.h"
|
||||||
|
#include "dynarmic/frontend/A64/a64_types.h"
|
||||||
|
#include "dynarmic/frontend/A64/translate/a64_translate.h"
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
|
#include "dynarmic/interface/A64/a64.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/location_descriptor.h"
|
#include "dynarmic/ir/location_descriptor.h"
|
||||||
#include "dynarmic/ir/opcodes.h"
|
#include "dynarmic/ir/opcodes.h"
|
||||||
|
@ -36,21 +41,14 @@ constexpr bool mask_fpsr_cum_bits = true;
|
||||||
namespace {
|
namespace {
|
||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
|
bool ShouldTestInst(IR::Block& block) {
|
||||||
const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb).SetIT(it_state);
|
|
||||||
IR::Block block{location};
|
|
||||||
const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction);
|
|
||||||
|
|
||||||
if (!should_continue && !is_last_inst) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto terminal = block.GetTerminal(); boost::get<IR::Term::Interpret>(&terminal)) {
|
if (auto terminal = block.GetTerminal(); boost::get<IR::Term::Interpret>(&terminal)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& ir_inst : block) {
|
for (const auto& ir_inst : block) {
|
||||||
switch (ir_inst.GetOpcode()) {
|
switch (ir_inst.GetOpcode()) {
|
||||||
|
// A32
|
||||||
case IR::Opcode::A32GetFpscr:
|
case IR::Opcode::A32GetFpscr:
|
||||||
case IR::Opcode::A32ExceptionRaised:
|
case IR::Opcode::A32ExceptionRaised:
|
||||||
case IR::Opcode::A32CallSupervisor:
|
case IR::Opcode::A32CallSupervisor:
|
||||||
|
@ -61,6 +59,11 @@ bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A
|
||||||
case IR::Opcode::A32CoprocGetTwoWords:
|
case IR::Opcode::A32CoprocGetTwoWords:
|
||||||
case IR::Opcode::A32CoprocLoadWords:
|
case IR::Opcode::A32CoprocLoadWords:
|
||||||
case IR::Opcode::A32CoprocStoreWords:
|
case IR::Opcode::A32CoprocStoreWords:
|
||||||
|
// A64
|
||||||
|
case IR::Opcode::A64ExceptionRaised:
|
||||||
|
case IR::Opcode::A64CallSupervisor:
|
||||||
|
case IR::Opcode::A64DataCacheOperationRaised:
|
||||||
|
case IR::Opcode::A64GetCNTPCT:
|
||||||
// Half-precision
|
// Half-precision
|
||||||
case IR::Opcode::FPVectorAbs16:
|
case IR::Opcode::FPVectorAbs16:
|
||||||
case IR::Opcode::FPVectorEqual16:
|
case IR::Opcode::FPVectorEqual16:
|
||||||
|
@ -84,6 +87,30 @@ bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ShouldTestA32Inst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
|
||||||
|
const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb).SetIT(it_state);
|
||||||
|
IR::Block block{location};
|
||||||
|
const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction);
|
||||||
|
|
||||||
|
if (!should_continue && !is_last_inst) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShouldTestInst(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShouldTestA64Inst(u32 instruction, u32 pc, bool is_last_inst) {
|
||||||
|
const A64::LocationDescriptor location = A64::LocationDescriptor{pc, {}};
|
||||||
|
IR::Block block{location};
|
||||||
|
const bool should_continue = A64::TranslateSingleInstruction(block, location, instruction);
|
||||||
|
|
||||||
|
if (!should_continue && !is_last_inst) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShouldTestInst(block);
|
||||||
|
}
|
||||||
|
|
||||||
u32 GenRandomArmInst(u32 pc, bool is_last_inst) {
|
u32 GenRandomArmInst(u32 pc, bool is_last_inst) {
|
||||||
static const struct InstructionGeneratorInfo {
|
static const struct InstructionGeneratorInfo {
|
||||||
std::vector<InstructionGenerator> generators;
|
std::vector<InstructionGenerator> generators;
|
||||||
|
@ -144,7 +171,7 @@ u32 GenRandomArmInst(u32 pc, bool is_last_inst) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShouldTestInst(inst, pc, false, is_last_inst)) {
|
if (ShouldTestA32Inst(inst, pc, false, is_last_inst)) {
|
||||||
return inst;
|
return inst;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +272,7 @@ std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst, A32::ITState it_s
|
||||||
const u32 inst = instructions.generators[index].Generate();
|
const u32 inst = instructions.generators[index].Generate();
|
||||||
const bool is_four_bytes = (inst >> 16) != 0;
|
const bool is_four_bytes = (inst >> 16) != 0;
|
||||||
|
|
||||||
if (ShouldTestInst(is_four_bytes ? mcl::bit::swap_halves_32(inst) : inst, pc, true, is_last_inst, it_state)) {
|
if (ShouldTestA32Inst(is_four_bytes ? mcl::bit::swap_halves_32(inst) : inst, pc, true, is_last_inst, it_state)) {
|
||||||
if (is_four_bytes)
|
if (is_four_bytes)
|
||||||
return {static_cast<u16>(inst >> 16), static_cast<u16>(inst)};
|
return {static_cast<u16>(inst >> 16), static_cast<u16>(inst)};
|
||||||
return {static_cast<u16>(inst)};
|
return {static_cast<u16>(inst)};
|
||||||
|
@ -253,8 +280,65 @@ std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst, A32::ITState it_s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GenRandomA64Inst(u64 pc, bool is_last_inst) {
|
||||||
|
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 "dynarmic/frontend/A64/decoder/a64.inc"
|
||||||
|
#undef INST
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<InstructionGenerator> generators;
|
||||||
|
std::vector<InstructionGenerator> invalid;
|
||||||
|
|
||||||
|
// List of instructions not to test
|
||||||
|
const std::vector<std::string> do_not_test{
|
||||||
|
// Dynarmic and QEMU currently differ on how the exclusive monitor's address range works.
|
||||||
|
"STXR",
|
||||||
|
"STLXR",
|
||||||
|
"STXP",
|
||||||
|
"STLXP",
|
||||||
|
"LDXR",
|
||||||
|
"LDAXR",
|
||||||
|
"LDXP",
|
||||||
|
"LDAXP",
|
||||||
|
// Behaviour differs from QEMU
|
||||||
|
"MSR_reg",
|
||||||
|
"MSR_imm",
|
||||||
|
"MRS",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& [fn, bitstring] : list) {
|
||||||
|
if (fn == "UnallocatedEncoding") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) {
|
||||||
|
invalid.emplace_back(InstructionGenerator{bitstring});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
generators.emplace_back(InstructionGenerator{bitstring});
|
||||||
|
}
|
||||||
|
return InstructionGeneratorInfo{generators, invalid};
|
||||||
|
}();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1);
|
||||||
|
const u32 inst = instructions.generators[index].Generate();
|
||||||
|
|
||||||
|
if (std::any_of(instructions.invalid.begin(), instructions.invalid.end(), [inst](const auto& invalid) { return invalid.Match(inst); })) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ShouldTestA64Inst(inst, pc, is_last_inst)) {
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename TestEnv>
|
template<typename TestEnv>
|
||||||
Dynarmic::A32::UserConfig GetUserConfig(TestEnv& testenv) {
|
Dynarmic::A32::UserConfig GetA32UserConfig(TestEnv& testenv) {
|
||||||
Dynarmic::A32::UserConfig user_config;
|
Dynarmic::A32::UserConfig user_config;
|
||||||
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
|
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
|
||||||
user_config.callbacks = &testenv;
|
user_config.callbacks = &testenv;
|
||||||
|
@ -262,7 +346,7 @@ Dynarmic::A32::UserConfig GetUserConfig(TestEnv& testenv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t num_jit_reruns = 1, typename TestEnv>
|
template<size_t num_jit_reruns = 1, typename TestEnv>
|
||||||
static void RunTestInstance(Dynarmic::A32::Jit& jit,
|
void RunTestInstance(Dynarmic::A32::Jit& jit,
|
||||||
TestEnv& jit_env,
|
TestEnv& jit_env,
|
||||||
const std::array<u32, 16>& regs,
|
const std::array<u32, 16>& regs,
|
||||||
const std::array<u32, 64>& vecs,
|
const std::array<u32, 64>& vecs,
|
||||||
|
@ -343,11 +427,104 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit,
|
||||||
|
|
||||||
fmt::print("===\n");
|
fmt::print("===\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dynarmic::A64::UserConfig GetA64UserConfig(A64TestEnv& jit_env) {
|
||||||
|
Dynarmic::A64::UserConfig jit_user_config{&jit_env};
|
||||||
|
jit_user_config.optimizations &= ~OptimizationFlag::FastDispatch;
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t num_jit_reruns = 1>
|
||||||
|
void RunTestInstance(Dynarmic::A64::Jit& jit,
|
||||||
|
A64TestEnv& jit_env,
|
||||||
|
const std::array<u64, 31>& regs,
|
||||||
|
const std::array<std::array<u64, 2>, 32>& vecs,
|
||||||
|
const std::vector<u32>& instructions,
|
||||||
|
const u32 pstate,
|
||||||
|
const u32 fpcr,
|
||||||
|
const u64 initial_sp,
|
||||||
|
const u64 start_address,
|
||||||
|
const size_t ticks_left) {
|
||||||
|
jit.ClearCache();
|
||||||
|
|
||||||
|
for (size_t jit_rerun_count = 0; jit_rerun_count < num_jit_reruns; ++jit_rerun_count) {
|
||||||
|
jit_env.code_mem = instructions;
|
||||||
|
jit_env.code_mem.emplace_back(0x14000000); // B .
|
||||||
|
jit_env.code_mem_start_address = start_address;
|
||||||
|
jit_env.modified_memory.clear();
|
||||||
|
jit_env.interrupts.clear();
|
||||||
|
|
||||||
|
jit.SetRegisters(regs);
|
||||||
|
jit.SetVectors(vecs);
|
||||||
|
jit.SetPC(start_address);
|
||||||
|
jit.SetSP(initial_sp);
|
||||||
|
jit.SetFpcr(fpcr);
|
||||||
|
jit.SetFpsr(0);
|
||||||
|
jit.SetPstate(pstate);
|
||||||
|
jit.ClearCache();
|
||||||
|
|
||||||
|
jit_env.ticks_left = ticks_left;
|
||||||
|
jit.Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print("instructions:");
|
||||||
|
for (u32 instruction : instructions) {
|
||||||
|
fmt::print(" {:08x}", instruction);
|
||||||
|
}
|
||||||
|
fmt::print("\n");
|
||||||
|
|
||||||
|
fmt::print("initial_regs:");
|
||||||
|
for (u64 i : regs) {
|
||||||
|
fmt::print(" {:016x}", i);
|
||||||
|
}
|
||||||
|
fmt::print("\n");
|
||||||
|
fmt::print("initial_vecs:");
|
||||||
|
for (auto i : vecs) {
|
||||||
|
fmt::print(" {:016x}:{:016x}", i[0], i[1]);
|
||||||
|
}
|
||||||
|
fmt::print("\n");
|
||||||
|
fmt::print("initial_sp: {:016x}\n", initial_sp);
|
||||||
|
fmt::print("initial_pstate: {:08x}\n", pstate);
|
||||||
|
fmt::print("initial_fpcr: {:08x}\n", fpcr);
|
||||||
|
|
||||||
|
fmt::print("final_regs:");
|
||||||
|
for (u64 i : jit.GetRegisters()) {
|
||||||
|
fmt::print(" {:016x}", i);
|
||||||
|
}
|
||||||
|
fmt::print("\n");
|
||||||
|
fmt::print("final_vecs:");
|
||||||
|
for (auto i : jit.GetVectors()) {
|
||||||
|
fmt::print(" {:016x}:{:016x}", i[0], i[1]);
|
||||||
|
}
|
||||||
|
fmt::print("\n");
|
||||||
|
fmt::print("final_sp: {:016x}\n", jit.GetSP());
|
||||||
|
fmt::print("final_pc: {:016x}\n", jit.GetPC());
|
||||||
|
fmt::print("final_pstate: {:08x}\n", jit.GetPstate());
|
||||||
|
fmt::print("final_fpcr: {:08x}\n", jit.GetFpcr());
|
||||||
|
fmt::print("final_qc : {}\n", FP::FPSR{jit.GetFpsr()}.QC());
|
||||||
|
|
||||||
|
fmt::print("mod_mem:");
|
||||||
|
for (auto [addr, value] : jit_env.modified_memory) {
|
||||||
|
fmt::print(" {:08x}:{:02x}", addr, value);
|
||||||
|
}
|
||||||
|
fmt::print("\n");
|
||||||
|
|
||||||
|
fmt::print("interrupts:\n");
|
||||||
|
for (const auto& i : jit_env.interrupts) {
|
||||||
|
std::puts(i.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::print("===\n");
|
||||||
|
}
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
void TestThumb(size_t num_instructions, size_t num_iterations) {
|
void TestThumb(size_t num_instructions, size_t num_iterations) {
|
||||||
ThumbTestEnv jit_env{};
|
ThumbTestEnv jit_env{};
|
||||||
Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
|
Dynarmic::A32::Jit jit{GetA32UserConfig(jit_env)};
|
||||||
|
|
||||||
std::array<u32, 16> regs;
|
std::array<u32, 16> regs;
|
||||||
std::array<u32, 64> ext_reg;
|
std::array<u32, 64> ext_reg;
|
||||||
|
@ -374,7 +551,7 @@ void TestThumb(size_t num_instructions, size_t num_iterations) {
|
||||||
|
|
||||||
void TestArm(size_t num_instructions, size_t num_iterations) {
|
void TestArm(size_t num_instructions, size_t num_iterations) {
|
||||||
ArmTestEnv jit_env{};
|
ArmTestEnv jit_env{};
|
||||||
Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
|
Dynarmic::A32::Jit jit{GetA32UserConfig(jit_env)};
|
||||||
|
|
||||||
std::array<u32, 16> regs;
|
std::array<u32, 16> regs;
|
||||||
std::array<u32, 64> ext_reg;
|
std::array<u32, 64> ext_reg;
|
||||||
|
@ -394,13 +571,43 @@ void TestArm(size_t num_instructions, size_t num_iterations) {
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[15] = start_address;
|
regs[15] = start_address;
|
||||||
RunTestInstance(jit, jit_env, regs, ext_reg, instructions, cpsr, fpcr, 1);
|
RunTestInstance(jit, jit_env, regs, ext_reg, instructions, cpsr, fpcr, num_instructions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestA64(size_t num_instructions, size_t num_iterations) {
|
||||||
|
A64TestEnv jit_env{};
|
||||||
|
Dynarmic::A64::Jit jit{GetA64UserConfig(jit_env)};
|
||||||
|
|
||||||
|
std::array<u64, 31> regs;
|
||||||
|
std::array<std::array<u64, 2>, 32> vecs;
|
||||||
|
std::vector<u32> instructions;
|
||||||
|
|
||||||
|
for (size_t iteration = 0; iteration < num_iterations; ++iteration) {
|
||||||
|
std::generate(regs.begin(), regs.end(), [] { return RandInt<u64>(0, ~u64(0)); });
|
||||||
|
std::generate(vecs.begin(), vecs.end(), RandomVector);
|
||||||
|
|
||||||
|
const u32 start_address = 100;
|
||||||
|
const u32 pstate = (RandInt<u32>(0, 0xF) << 28);
|
||||||
|
const u32 fpcr = RandomFpcr();
|
||||||
|
const u64 initial_sp = RandInt<u64>(0x30'0000'0000, 0x40'0000'0000) * 4;
|
||||||
|
|
||||||
|
instructions.clear();
|
||||||
|
for (size_t i = 0; i < num_instructions; ++i) {
|
||||||
|
instructions.emplace_back(GenRandomA64Inst(static_cast<u32>(start_address + 4 * instructions.size()), i == num_instructions - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
RunTestInstance(jit, jit_env, regs, vecs, instructions, pstate, fpcr, initial_sp, start_address, num_instructions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int, char*[]) {
|
int main(int, char*[]) {
|
||||||
detail::g_rand_int_generator.seed(42069);
|
detail::g_rand_int_generator.seed(42069);
|
||||||
|
|
||||||
|
TestThumb(1, 1);
|
||||||
|
TestArm(1, 1);
|
||||||
|
TestA64(1, 1);
|
||||||
|
|
||||||
TestThumb(1, 100000);
|
TestThumb(1, 100000);
|
||||||
TestArm(1, 100000);
|
TestArm(1, 100000);
|
||||||
TestThumb(5, 100000);
|
TestThumb(5, 100000);
|
||||||
|
|
Loading…
Reference in a new issue