From 2a87337141a9d848276bed4188dbdeab2bd0a770 Mon Sep 17 00:00:00 2001 From: Merry Date: Fri, 20 Jan 2023 11:50:41 +0000 Subject: [PATCH] test_reader: Add A32 modes --- tests/test_reader.cpp | 315 +++++++++++++++++++++++++++++++++++------- 1 file changed, 263 insertions(+), 52 deletions(-) diff --git a/tests/test_reader.cpp b/tests/test_reader.cpp index c0ddac28..7c60a9f9 100644 --- a/tests/test_reader.cpp +++ b/tests/test_reader.cpp @@ -12,12 +12,148 @@ #include #include +#include "./A32/testenv.h" #include "./A64/testenv.h" #include "dynarmic/common/fp/fpsr.h" +#include "dynarmic/interface/A32/a32.h" #include "dynarmic/interface/A64/a64.h" +const bool mask_fpsr_cum_bits = true; + using namespace Dynarmic; +void SkipWhitespace(std::string_view& sv) { + auto nextpos{sv.find_first_not_of(' ')}; + if (nextpos != std::string::npos) { + sv.remove_prefix(nextpos); + } +} + +void SkipHeader(std::string_view& sv) { + sv.remove_prefix(sv.find_first_of(':') + 1); + SkipWhitespace(sv); +} + +std::string_view NextToken(std::string_view& sv) { + auto nextpos{sv.find_first_of(' ')}; + auto tok{sv.substr(0, nextpos)}; + sv.remove_prefix(nextpos == std::string::npos ? sv.size() : nextpos); + SkipWhitespace(sv); + return tok; +} + +u64 ParseHex(std::string_view hex) { + u64 result = 0; + while (!hex.empty()) { + result <<= 4; + if (hex.front() >= '0' && hex.front() <= '9') { + result += hex.front() - '0'; + } else if (hex.front() >= 'a' && hex.front() <= 'f') { + result += hex.front() - 'a' + 0xA; + } else if (hex.front() >= 'A' && hex.front() <= 'F') { + result += hex.front() - 'A' + 0xA; + } else if (hex.front() == ':') { + return result; + } else { + fmt::print("Character {} is not a valid hex character\n", hex.front()); + } + hex.remove_prefix(1); + } + return result; +} + +template +Dynarmic::A32::UserConfig GetA32UserConfig(TestEnv& testenv) { + Dynarmic::A32::UserConfig user_config; + user_config.optimizations &= ~OptimizationFlag::FastDispatch; + user_config.callbacks = &testenv; + user_config.very_verbose_debugging_output = true; + return user_config; +} + +template +void RunTestInstance(Dynarmic::A32::Jit& jit, + TestEnv& jit_env, + const std::array& regs, + const std::array& vecs, + const std::vector& instructions, + const u32 cpsr, + const u32 fpscr, + const size_t ticks_left) { + const u32 initial_pc = regs[15]; + const u32 num_words = initial_pc / sizeof(typename TestEnv::InstructionType); + const u32 code_mem_size = num_words + static_cast(instructions.size()); + + jit.ClearCache(); + + for (size_t jit_rerun_count = 0; jit_rerun_count < num_jit_reruns; ++jit_rerun_count) { + jit_env.code_mem.resize(code_mem_size); + std::fill(jit_env.code_mem.begin(), jit_env.code_mem.end(), TestEnv::infinite_loop); + + std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words); + jit_env.PadCodeMem(); + jit_env.modified_memory.clear(); + jit_env.interrupts.clear(); + + jit.Regs() = regs; + jit.ExtRegs() = vecs; + jit.SetFpscr(fpscr); + jit.SetCpsr(cpsr); + + jit_env.ticks_left = ticks_left; + jit.Run(); + } + + fmt::print("instructions:"); + for (auto instruction : instructions) { + if constexpr (sizeof(decltype(instruction)) == 2) { + fmt::print(" {:04x}", instruction); + } else { + fmt::print(" {:08x}", instruction); + } + } + fmt::print("\n"); + + fmt::print("initial_regs:"); + for (u32 i : regs) { + fmt::print(" {:08x}", i); + } + fmt::print("\n"); + fmt::print("initial_vecs:"); + for (u32 i : vecs) { + fmt::print(" {:08x}", i); + } + fmt::print("\n"); + fmt::print("initial_cpsr: {:08x}\n", cpsr); + fmt::print("initial_fpcr: {:08x}\n", fpscr); + + fmt::print("final_regs:"); + for (u32 i : jit.Regs()) { + fmt::print(" {:08x}", i); + } + fmt::print("\n"); + fmt::print("final_vecs:"); + for (u32 i : jit.ExtRegs()) { + fmt::print(" {:08x}", i); + } + fmt::print("\n"); + fmt::print("final_cpsr: {:08x}\n", jit.Cpsr()); + fmt::print("final_fpsr: {:08x}\n", mask_fpsr_cum_bits ? jit.Fpscr() & 0xffffff00 : jit.Fpscr()); + + 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"); +} + A64::UserConfig GetA64UserConfig(A64TestEnv& jit_env) { A64::UserConfig jit_user_config{&jit_env}; jit_user_config.optimizations &= ~OptimizationFlag::FastDispatch; @@ -111,7 +247,101 @@ void RunTestInstance(A64::Jit& jit, fmt::print("===\n"); } -int main() { +void RunThumb() { + std::array initial_regs{}; + std::array initial_vecs{}; + std::vector instructions{}; + u32 initial_cpsr = 0; + u32 initial_fpcr = 0; + + std::string line; + while (std::getline(std::cin, line)) { + std::string_view sv{line}; + + if (sv.starts_with("instructions:")) { + SkipHeader(sv); + while (!sv.empty()) { + instructions.emplace_back((u16)ParseHex(NextToken(sv))); + } + } else if (sv.starts_with("initial_regs:")) { + SkipHeader(sv); + for (size_t i = 0; i < initial_regs.size(); ++i) { + initial_regs[i] = (u32)ParseHex(NextToken(sv)); + } + } else if (sv.starts_with("initial_vecs:")) { + SkipHeader(sv); + for (size_t i = 0; i < initial_vecs.size(); ++i) { + initial_vecs[i] = (u32)ParseHex(NextToken(sv)); + } + } else if (sv.starts_with("initial_cpsr:")) { + SkipHeader(sv); + initial_cpsr = (u32)ParseHex(NextToken(sv)); + } else if (sv.starts_with("initial_fpcr:")) { + SkipHeader(sv); + initial_fpcr = (u32)ParseHex(NextToken(sv)); + } + } + + ThumbTestEnv jit_env{}; + A32::Jit jit{GetA32UserConfig(jit_env)}; + RunTestInstance(jit, + jit_env, + initial_regs, + initial_vecs, + instructions, + initial_cpsr, + initial_fpcr, + instructions.size()); +} + +void RunArm() { + std::array initial_regs{}; + std::array initial_vecs{}; + std::vector instructions{}; + u32 initial_cpsr = 0; + u32 initial_fpcr = 0; + + std::string line; + while (std::getline(std::cin, line)) { + std::string_view sv{line}; + + if (sv.starts_with("instructions:")) { + SkipHeader(sv); + while (!sv.empty()) { + instructions.emplace_back((u32)ParseHex(NextToken(sv))); + } + } else if (sv.starts_with("initial_regs:")) { + SkipHeader(sv); + for (size_t i = 0; i < initial_regs.size(); ++i) { + initial_regs[i] = (u32)ParseHex(NextToken(sv)); + } + } else if (sv.starts_with("initial_vecs:")) { + SkipHeader(sv); + for (size_t i = 0; i < initial_vecs.size(); ++i) { + initial_vecs[i] = (u32)ParseHex(NextToken(sv)); + } + } else if (sv.starts_with("initial_cpsr:")) { + SkipHeader(sv); + initial_cpsr = (u32)ParseHex(NextToken(sv)); + } else if (sv.starts_with("initial_fpcr:")) { + SkipHeader(sv); + initial_fpcr = (u32)ParseHex(NextToken(sv)); + } + } + + ArmTestEnv jit_env{}; + A32::Jit jit{GetA32UserConfig(jit_env)}; + RunTestInstance(jit, + jit_env, + initial_regs, + initial_vecs, + instructions, + initial_cpsr, + initial_fpcr, + instructions.size()); +} + +void RunA64() { std::array initial_regs{}; std::array, 32> initial_vecs{}; std::vector instructions{}; @@ -124,70 +354,33 @@ int main() { while (std::getline(std::cin, line)) { std::string_view sv{line}; - const auto skip_ws = [&] { - auto nextpos{sv.find_first_not_of(' ')}; - if (nextpos != std::string::npos) { - sv.remove_prefix(nextpos); - } - }; - const auto skip_header = [&] { - sv.remove_prefix(sv.find_first_of(':') + 1); - skip_ws(); - }; - const auto next_token = [&] { - auto nextpos{sv.find_first_of(' ')}; - auto tok{sv.substr(0, nextpos)}; - sv.remove_prefix(nextpos == std::string::npos ? sv.size() : nextpos); - skip_ws(); - return tok; - }; - const auto parse_hex = [&](std::string_view hex) { - u64 result = 0; - while (!hex.empty()) { - result <<= 4; - if (hex.front() >= '0' && hex.front() <= '9') { - result += hex.front() - '0'; - } else if (hex.front() >= 'a' && hex.front() <= 'f') { - result += hex.front() - 'a' + 0xA; - } else if (hex.front() >= 'A' && hex.front() <= 'F') { - result += hex.front() - 'A' + 0xA; - } else if (hex.front() == ':') { - return result; - } else { - fmt::print("Character {} is not a valid hex character\n", hex.front()); - } - hex.remove_prefix(1); - } - return result; - }; - if (sv.starts_with("instructions:")) { - skip_header(); + SkipHeader(sv); while (!sv.empty()) { - instructions.emplace_back((u32)parse_hex(next_token())); + instructions.emplace_back((u32)ParseHex(NextToken(sv))); } } else if (sv.starts_with("initial_regs:")) { - skip_header(); + SkipHeader(sv); for (size_t i = 0; i < initial_regs.size(); ++i) { - initial_regs[i] = parse_hex(next_token()); + initial_regs[i] = ParseHex(NextToken(sv)); } } else if (sv.starts_with("initial_vecs:")) { - skip_header(); + SkipHeader(sv); for (size_t i = 0; i < initial_vecs.size(); ++i) { - auto tok{next_token()}; - initial_vecs[i][0] = parse_hex(tok); + auto tok{NextToken(sv)}; + initial_vecs[i][0] = ParseHex(tok); tok.remove_prefix(tok.find_first_of(':') + 1); - initial_vecs[i][1] = parse_hex(tok); + initial_vecs[i][1] = ParseHex(tok); } } else if (sv.starts_with("initial_sp:")) { - skip_header(); - initial_sp = parse_hex(next_token()); + SkipHeader(sv); + initial_sp = ParseHex(NextToken(sv)); } else if (sv.starts_with("initial_pstate:")) { - skip_header(); - initial_pstate = (u32)parse_hex(next_token()); + SkipHeader(sv); + initial_pstate = (u32)ParseHex(NextToken(sv)); } else if (sv.starts_with("initial_fpcr:")) { - skip_header(); - initial_fpcr = (u32)parse_hex(next_token()); + SkipHeader(sv); + initial_fpcr = (u32)ParseHex(NextToken(sv)); } } @@ -203,6 +396,24 @@ int main() { initial_sp, start_address, instructions.size()); +} + +int main(int argc, char** argv) { + if (argc != 2) { + fmt::print("Usage: {} \n", argv[0]); + return 1; + } + + if (strcmp(argv[1], "thumb") == 0) { + RunThumb(); + } else if (strcmp(argv[1], "arm") == 0) { + RunArm(); + } else if (strcmp(argv[1], "a64") == 0) { + RunA64(); + } else { + fmt::print("unrecognized instruction class\n"); + return 1; + } return 0; }