diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index b50e1b2a..6e39253c 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -24,10 +24,6 @@ add_library(dynarmic common/fp/op/FPMulAdd.cpp common/fp/op/FPMulAdd.h common/fp/op/FPNeg.h - common/fp/op/FPRSqrtEstimate.cpp - common/fp/op/FPRSqrtEstimate.h - common/fp/op/FPRSqrtStepFused.cpp - common/fp/op/FPRSqrtStepFused.h common/fp/op/FPRecipEstimate.cpp common/fp/op/FPRecipEstimate.h common/fp/op/FPRecipExponent.cpp @@ -36,6 +32,10 @@ add_library(dynarmic common/fp/op/FPRecipStepFused.h common/fp/op/FPRoundInt.cpp common/fp/op/FPRoundInt.h + common/fp/op/FPRSqrtEstimate.cpp + common/fp/op/FPRSqrtEstimate.h + common/fp/op/FPRSqrtStepFused.cpp + common/fp/op/FPRSqrtStepFused.h common/fp/op/FPToFixed.cpp common/fp/op/FPToFixed.h common/fp/process_exception.cpp @@ -98,6 +98,10 @@ add_library(dynarmic if ("A32" IN_LIST DYNARMIC_FRONTENDS) target_sources(dynarmic PRIVATE + frontend/A32/a32_ir_emitter.cpp + frontend/A32/a32_ir_emitter.h + frontend/A32/a32_location_descriptor.cpp + frontend/A32/a32_location_descriptor.h frontend/A32/decoder/arm.h frontend/A32/decoder/arm.inc frontend/A32/decoder/asimd.h @@ -112,21 +116,17 @@ if ("A32" IN_LIST DYNARMIC_FRONTENDS) frontend/A32/disassembler/disassembler_arm.cpp frontend/A32/disassembler/disassembler_thumb.cpp frontend/A32/FPSCR.h - frontend/A32/a32_ir_emitter.cpp - frontend/A32/a32_ir_emitter.h - frontend/A32/a32_location_descriptor.cpp - frontend/A32/a32_location_descriptor.h frontend/A32/ITState.h frontend/A32/PSR.h frontend/A32/translate/a32_translate.cpp frontend/A32/translate/a32_translate.h + frontend/A32/translate/conditional_state.cpp + frontend/A32/translate/conditional_state.h frontend/A32/translate/impl/a32_branch.cpp frontend/A32/translate/impl/a32_crc32.cpp frontend/A32/translate/impl/a32_exception_generating.cpp frontend/A32/translate/impl/a32_translate_impl.cpp frontend/A32/translate/impl/a32_translate_impl.h - frontend/A32/translate/conditional_state.cpp - frontend/A32/translate/conditional_state.h frontend/A32/translate/impl/asimd_load_store_structures.cpp frontend/A32/translate/impl/asimd_misc.cpp frontend/A32/translate/impl/asimd_one_reg_modified_immediate.cpp @@ -284,12 +284,12 @@ if ("x86_64" IN_LIST ARCHITECTURE) backend/x64/emit_x64_crc32.cpp backend/x64/emit_x64_data_processing.cpp backend/x64/emit_x64_floating_point.cpp - backend/x64/emit_x64_memory.h backend/x64/emit_x64_memory.cpp.inc + backend/x64/emit_x64_memory.h backend/x64/emit_x64_packed.cpp backend/x64/emit_x64_saturation.cpp - backend/x64/emit_x64_sm4.cpp backend/x64/emit_x64_sha.cpp + backend/x64/emit_x64_sm4.cpp backend/x64/emit_x64_vector.cpp backend/x64/emit_x64_vector_floating_point.cpp backend/x64/emit_x64_vector_saturation.cpp @@ -305,6 +305,8 @@ if ("x86_64" IN_LIST ARCHITECTURE) backend/x64/reg_alloc.cpp backend/x64/reg_alloc.h backend/x64/stack_layout.h + backend/x64/verbose_debugging_output.cpp + backend/x64/verbose_debugging_output.h common/spin_lock_x64.cpp common/spin_lock_x64.h common/x64_disassemble.cpp @@ -334,7 +336,7 @@ if ("x86_64" IN_LIST ARCHITECTURE) endif() endif() -if("arm64" IN_LIST ARCHITECTURE) +if ("arm64" IN_LIST ARCHITECTURE) target_link_libraries(dynarmic PRIVATE merry::oaknut) target_architecture_specific_sources(dynarmic "arm64" @@ -433,7 +435,7 @@ elseif (APPLE) ) endif() elseif (UNIX) - if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if (CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(dynarmic PRIVATE rt) endif() target_sources(dynarmic PRIVATE backend/exception_handler_posix.cpp) @@ -487,7 +489,7 @@ if (DYNARMIC_USE_PRECOMPILED_HEADERS) if ("x86_64" IN_LIST ARCHITECTURE) list(PREPEND PRECOMPILED_HEADERS "$<$:>") endif() - if("arm64" IN_LIST ARCHITECTURE) + if ("arm64" IN_LIST ARCHITECTURE) list(PREPEND PRECOMPILED_HEADERS "$<$:>") endif() target_precompile_headers(dynarmic PRIVATE ${PRECOMPILED_HEADERS}) diff --git a/src/dynarmic/backend/x64/a32_emit_x64.cpp b/src/dynarmic/backend/x64/a32_emit_x64.cpp index 2930f3cc..52c4a15c 100644 --- a/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -94,6 +94,10 @@ A32EmitX64::A32EmitX64(BlockOfCode& code, A32::UserConfig conf, A32::Jit* jit_in A32EmitX64::~A32EmitX64() = default; A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { + if (conf.very_verbose_debugging_output) { + std::puts(IR::DumpBlock(block).c_str()); + } + code.EnableWriting(); SCOPE_EXIT { code.DisableWriting(); }; @@ -142,6 +146,10 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) { } reg_alloc.EndOfAllocScope(); + + if (conf.very_verbose_debugging_output) { + EmitVerboseDebuggingOutput(reg_alloc); + } } reg_alloc.AssertNoMoreUses(); diff --git a/src/dynarmic/backend/x64/a64_emit_x64.cpp b/src/dynarmic/backend/x64/a64_emit_x64.cpp index aa336106..dd45049a 100644 --- a/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -65,6 +65,10 @@ A64EmitX64::A64EmitX64(BlockOfCode& code, A64::UserConfig conf, A64::Jit* jit_in A64EmitX64::~A64EmitX64() = default; A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { + if (conf.very_verbose_debugging_output) { + std::puts(IR::DumpBlock(block).c_str()); + } + code.EnableWriting(); SCOPE_EXIT { code.DisableWriting(); }; @@ -113,6 +117,10 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) { } ctx.reg_alloc.EndOfAllocScope(); + + if (conf.very_verbose_debugging_output) { + EmitVerboseDebuggingOutput(reg_alloc); + } } reg_alloc.AssertNoMoreUses(); diff --git a/src/dynarmic/backend/x64/emit_x64.cpp b/src/dynarmic/backend/x64/emit_x64.cpp index 62af1ad4..22ed7e3d 100644 --- a/src/dynarmic/backend/x64/emit_x64.cpp +++ b/src/dynarmic/backend/x64/emit_x64.cpp @@ -17,6 +17,7 @@ #include "dynarmic/backend/x64/nzcv_util.h" #include "dynarmic/backend/x64/perf_map.h" #include "dynarmic/backend/x64/stack_layout.h" +#include "dynarmic/backend/x64/verbose_debugging_output.h" #include "dynarmic/common/variant_util.h" #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/microinstruction.h" @@ -102,6 +103,34 @@ void EmitX64::PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, I code.mov(dword[r15 + code.GetJitStateInfo().offsetof_rsb_ptr], index_reg.cvt32()); } +void EmitX64::EmitVerboseDebuggingOutput(RegAlloc& reg_alloc) { + code.sub(rsp, sizeof(RegisterData)); + for (int i = 0; i < 16; i++) { + if (rsp.getIdx() == i) { + continue; + } + code.mov(qword[rsp + offsetof(RegisterData, gprs) + sizeof(u64) * i], Xbyak::Reg64{i}); + } + for (int i = 0; i < 16; i++) { + code.movaps(xword[rsp + offsetof(RegisterData, xmms) + 2 * sizeof(u64) * i], Xbyak::Xmm{i}); + } + code.lea(rax, ptr[rsp + sizeof(RegisterData) + offsetof(StackLayout, spill)]); + code.mov(xword[rsp + offsetof(RegisterData, spill)], rax); + + reg_alloc.EmitVerboseDebuggingOutput(); + + for (int i = 0; i < 16; i++) { + if (rsp.getIdx() == i) { + continue; + } + code.mov(Xbyak::Reg64{i}, qword[rsp + offsetof(RegisterData, gprs) + sizeof(u64) * i]); + } + for (int i = 0; i < 16; i++) { + code.movaps(Xbyak::Xmm{i}, xword[rsp + offsetof(RegisterData, xmms) + 2 * sizeof(u64) * i]); + } + code.add(rsp, sizeof(RegisterData)); +} + void EmitX64::EmitPushRSB(EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); ASSERT(args[0].IsImmediate()); diff --git a/src/dynarmic/backend/x64/emit_x64.h b/src/dynarmic/backend/x64/emit_x64.h index 4efec77f..fcf51a6c 100644 --- a/src/dynarmic/backend/x64/emit_x64.h +++ b/src/dynarmic/backend/x64/emit_x64.h @@ -108,6 +108,8 @@ protected: BlockDescriptor RegisterBlock(const IR::LocationDescriptor& location_descriptor, CodePtr entrypoint, size_t size); void PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, IR::LocationDescriptor target); + void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc); + // Terminal instruction emitters void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); virtual void EmitTerminalImpl(IR::Term::Interpret terminal, IR::LocationDescriptor initial_location, bool is_single_step) = 0; diff --git a/src/dynarmic/backend/x64/reg_alloc.cpp b/src/dynarmic/backend/x64/reg_alloc.cpp index 0de490d4..d37269a3 100644 --- a/src/dynarmic/backend/x64/reg_alloc.cpp +++ b/src/dynarmic/backend/x64/reg_alloc.cpp @@ -11,10 +11,12 @@ #include #include +#include #include #include "dynarmic/backend/x64/abi.h" #include "dynarmic/backend/x64/stack_layout.h" +#include "dynarmic/backend/x64/verbose_debugging_output.h" namespace Dynarmic::Backend::X64 { @@ -85,6 +87,11 @@ bool HostLocInfo::IsLastUse() const { return is_being_used_count == 0 && current_references == 1 && accumulated_uses + 1 == total_uses; } +void HostLocInfo::SetLastUse() { + ASSERT(IsLastUse()); + is_set_last_use = true; +} + void HostLocInfo::ReadLock() { ASSERT(!is_scratch); is_being_used_count++; @@ -119,7 +126,7 @@ void HostLocInfo::ReleaseAll() { accumulated_uses += current_references; current_references = 0; - ASSERT(total_uses == std::accumulate(values.begin(), values.end(), size_t(0), [](size_t sum, IR::Inst* inst) { return sum + inst->UseCount(); })); + is_set_last_use = false; if (total_uses == accumulated_uses) { values.clear(); @@ -141,11 +148,25 @@ size_t HostLocInfo::GetMaxBitWidth() const { } void HostLocInfo::AddValue(IR::Inst* inst) { + if (is_set_last_use) { + is_set_last_use = false; + values.clear(); + } values.push_back(inst); total_uses += inst->UseCount(); max_bit_width = std::max(max_bit_width, GetBitWidth(inst->GetType())); } +void HostLocInfo::EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_index) const { + using namespace Xbyak::util; + for (IR::Inst* value : values) { + code.mov(code.ABI_PARAM1, rsp); + code.mov(code.ABI_PARAM2, host_loc_index); + code.mov(code.ABI_PARAM3, mcl::bit_cast(value)); + code.CallFunction(PrintVerboseDebuggingOutputLine); + } +} + IR::Type Argument::GetType() const { return value.GetType(); } @@ -369,6 +390,8 @@ HostLoc RegAlloc::UseScratchImpl(IR::Value use_value, const std::vector if (can_use_current_location && !LocInfo(current_location).IsLocked()) { if (!LocInfo(current_location).IsLastUse()) { MoveOutOfTheWay(current_location); + } else { + LocInfo(current_location).SetLastUse(); } LocInfo(current_location).WriteLock(); return current_location; @@ -471,6 +494,12 @@ void RegAlloc::AssertNoMoreUses() { ASSERT(std::all_of(hostloc_info.begin(), hostloc_info.end(), [](const auto& i) { return i.IsEmpty(); })); } +void RegAlloc::EmitVerboseDebuggingOutput() { + for (size_t i = 0; i < hostloc_info.size(); i++) { + hostloc_info[i].EmitVerboseDebuggingOutput(code, i); + } +} + HostLoc RegAlloc::SelectARegister(const std::vector& desired_locations) const { std::vector candidates = desired_locations; diff --git a/src/dynarmic/backend/x64/reg_alloc.h b/src/dynarmic/backend/x64/reg_alloc.h index 49eba4a3..b6cdf796 100644 --- a/src/dynarmic/backend/x64/reg_alloc.h +++ b/src/dynarmic/backend/x64/reg_alloc.h @@ -35,6 +35,8 @@ public: bool IsEmpty() const; bool IsLastUse() const; + void SetLastUse(); + void ReadLock(); void WriteLock(); void AddArgReference(); @@ -46,10 +48,13 @@ public: void AddValue(IR::Inst* inst); + void EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_index) const; + private: // Current instruction state size_t is_being_used_count = 0; bool is_scratch = false; + bool is_set_last_use = false; // Block state size_t current_references = 0; @@ -140,6 +145,8 @@ public: void AssertNoMoreUses(); + void EmitVerboseDebuggingOutput(); + private: friend struct Argument; diff --git a/src/dynarmic/backend/x64/verbose_debugging_output.cpp b/src/dynarmic/backend/x64/verbose_debugging_output.cpp new file mode 100644 index 00000000..7c862783 --- /dev/null +++ b/src/dynarmic/backend/x64/verbose_debugging_output.cpp @@ -0,0 +1,29 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2023 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "dynarmic/backend/x64/verbose_debugging_output.h" + +#include + +#include "dynarmic/backend/x64/hostloc.h" + +namespace Dynarmic::Backend::X64 { + +void PrintVerboseDebuggingOutputLine(RegisterData& reg_data, HostLoc hostloc, u64 inst_addr) { + if (HostLocIsGPR(hostloc)) { + const u64 value = reg_data.gprs[HostLocToReg64(hostloc).getIdx()]; + fmt::print("dynarmic debug: {:016x} = {:016x}{:016x}\n", inst_addr, 0, value); + } else if (HostLocIsXMM(hostloc)) { + const Vector value = reg_data.xmms[HostLocToXmm(hostloc).getIdx()]; + fmt::print("dynarmic debug: {:016x} = {:016x}{:016x}\n", inst_addr, value[1], value[0]); + } else if (HostLocIsSpill(hostloc)) { + const Vector value = (*reg_data.spill)[static_cast(hostloc) - static_cast(HostLoc::FirstSpill)]; + fmt::print("dynarmic debug: {:016x} = {:016x}{:016x}\n", inst_addr, value[1], value[0]); + } else { + fmt::print("dynarmic debug: Invalid hostloc\n"); + } +} + +} // namespace Dynarmic::Backend::X64 diff --git a/src/dynarmic/backend/x64/verbose_debugging_output.h b/src/dynarmic/backend/x64/verbose_debugging_output.h new file mode 100644 index 00000000..95be7edf --- /dev/null +++ b/src/dynarmic/backend/x64/verbose_debugging_output.h @@ -0,0 +1,36 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2023 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include + +#include + +#include "dynarmic/backend/x64/stack_layout.h" + +namespace Dynarmic::Backend::X64 { + +enum class HostLoc; +using Vector = std::array; + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4324) // Structure was padded due to alignment specifier +#endif + +struct alignas(16) RegisterData { + std::array gprs; + std::array xmms; + decltype(StackLayout::spill)* spill; +}; + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +void PrintVerboseDebuggingOutputLine(RegisterData& reg_data, HostLoc hostloc, u64 inst_addr); + +} // namespace Dynarmic::Backend::X64 diff --git a/src/dynarmic/interface/A32/config.h b/src/dynarmic/interface/A32/config.h index a71dd03d..a7b194ab 100644 --- a/src/dynarmic/interface/A32/config.h +++ b/src/dynarmic/interface/A32/config.h @@ -231,6 +231,9 @@ struct UserConfig { // Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). // Maximum size is limited by the maximum length of a x86_64 / arm64 jump. size_t code_cache_size = 128 * 1024 * 1024; // bytes + + /// Internal use only + bool very_verbose_debugging_output = false; }; } // namespace A32 diff --git a/src/dynarmic/interface/A64/config.h b/src/dynarmic/interface/A64/config.h index 390f3159..b10c65bc 100644 --- a/src/dynarmic/interface/A64/config.h +++ b/src/dynarmic/interface/A64/config.h @@ -288,6 +288,9 @@ struct UserConfig { // Minimum size is about 8MiB. Maximum size is about 128MiB (arm64 host) or 2GiB (x64 host). // Maximum size is limited by the maximum length of a x86_64 / arm64 jump. size_t code_cache_size = 128 * 1024 * 1024; // bytes + + /// Internal use only + bool very_verbose_debugging_output = false; }; } // namespace A64 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e52f4da4..7277bc94 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -102,6 +102,19 @@ if (("A32" IN_LIST DYNARMIC_FRONTENDS) AND ("A64" IN_LIST DYNARMIC_FRONTENDS)) target_compile_definitions(dynarmic_test_generator PRIVATE FMT_USE_USER_DEFINED_LITERALS=1) endif() +if (("A32" IN_LIST DYNARMIC_FRONTENDS) AND ("A64" IN_LIST DYNARMIC_FRONTENDS)) + add_executable(dynarmic_test_reader + test_reader.cpp + ) + + create_target_directory_groups(dynarmic_test_reader) + + target_link_libraries(dynarmic_test_reader PRIVATE dynarmic Boost::boost fmt::fmt merry::mcl) + target_include_directories(dynarmic_test_reader PRIVATE . ../src) + target_compile_options(dynarmic_test_reader PRIVATE ${DYNARMIC_CXX_FLAGS}) + target_compile_definitions(dynarmic_test_reader PRIVATE FMT_USE_USER_DEFINED_LITERALS=1) +endif() + create_target_directory_groups(dynarmic_tests) target_link_libraries(dynarmic_tests PRIVATE dynarmic Boost::boost Catch2::Catch2WithMain fmt::fmt merry::mcl) diff --git a/tests/test_generator.cpp b/tests/test_generator.cpp index 938aff4d..00bfd73c 100644 --- a/tests/test_generator.cpp +++ b/tests/test_generator.cpp @@ -40,12 +40,6 @@ #include #include -#if defined(__APPLE__) && defined(MCL_ARCHITECTURE_ARM64) -# include -# include -# include -#endif - constexpr bool mask_fpsr_cum_bits = true; namespace { diff --git a/tests/test_reader.cpp b/tests/test_reader.cpp new file mode 100644 index 00000000..c0ddac28 --- /dev/null +++ b/tests/test_reader.cpp @@ -0,0 +1,208 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2023 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "./A64/testenv.h" +#include "dynarmic/common/fp/fpsr.h" +#include "dynarmic/interface/A64/a64.h" + +using namespace Dynarmic; + +A64::UserConfig GetA64UserConfig(A64TestEnv& jit_env) { + 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; + jit_user_config.very_verbose_debugging_output = true; + return jit_user_config; +} + +template +void RunTestInstance(A64::Jit& jit, + A64TestEnv& jit_env, + const std::array& regs, + const std::array, 32>& vecs, + const std::vector& 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"); +} + +int main() { + std::array initial_regs{}; + std::array, 32> initial_vecs{}; + std::vector instructions{}; + u32 initial_pstate = 0; + u32 initial_fpcr = 0; + u64 initial_sp = 0; + u64 start_address = 100; + + std::string line; + 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(); + while (!sv.empty()) { + instructions.emplace_back((u32)parse_hex(next_token())); + } + } else if (sv.starts_with("initial_regs:")) { + skip_header(); + for (size_t i = 0; i < initial_regs.size(); ++i) { + initial_regs[i] = parse_hex(next_token()); + } + } else if (sv.starts_with("initial_vecs:")) { + skip_header(); + for (size_t i = 0; i < initial_vecs.size(); ++i) { + auto tok{next_token()}; + initial_vecs[i][0] = parse_hex(tok); + tok.remove_prefix(tok.find_first_of(':') + 1); + initial_vecs[i][1] = parse_hex(tok); + } + } else if (sv.starts_with("initial_sp:")) { + skip_header(); + initial_sp = parse_hex(next_token()); + } else if (sv.starts_with("initial_pstate:")) { + skip_header(); + initial_pstate = (u32)parse_hex(next_token()); + } else if (sv.starts_with("initial_fpcr:")) { + skip_header(); + initial_fpcr = (u32)parse_hex(next_token()); + } + } + + A64TestEnv jit_env{}; + A64::Jit jit{GetA64UserConfig(jit_env)}; + RunTestInstance(jit, + jit_env, + initial_regs, + initial_vecs, + instructions, + initial_pstate, + initial_fpcr, + initial_sp, + start_address, + instructions.size()); + + return 0; +}