tests: Add verbose debugging output and test_reader

This commit is contained in:
Merry 2023-01-16 00:25:41 +00:00
parent f865bbbad2
commit ffc3dce9b1
14 changed files with 393 additions and 22 deletions

View file

@ -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

View file

@ -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();

View file

@ -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();

View file

@ -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());

View file

@ -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;

View file

@ -11,10 +11,12 @@
#include <fmt/ostream.h>
#include <mcl/assert.hpp>
#include <mcl/bit_cast.hpp>
#include <xbyak/xbyak.h>
#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<u64>(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<HostLoc>
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<HostLoc>& desired_locations) const {
std::vector<HostLoc> candidates = desired_locations;

View file

@ -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;

View file

@ -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 <fmt/format.h>
#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<size_t>(hostloc) - static_cast<size_t>(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

View file

@ -0,0 +1,36 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2023 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <array>
#include <mcl/stdint.hpp>
#include "dynarmic/backend/x64/stack_layout.h"
namespace Dynarmic::Backend::X64 {
enum class HostLoc;
using Vector = std::array<u64, 2>;
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4324) // Structure was padded due to alignment specifier
#endif
struct alignas(16) RegisterData {
std::array<u64, 16> gprs;
std::array<Vector, 16> 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

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -40,12 +40,6 @@
#include <fmt/format.h>
#include <fmt/ostream.h>
#if defined(__APPLE__) && defined(MCL_ARCHITECTURE_ARM64)
# include <mach/mach_init.h>
# include <mach/mach_port.h>
# include <mach/task.h>
#endif
constexpr bool mask_fpsr_cum_bits = true;
namespace {

208
tests/test_reader.cpp Normal file
View file

@ -0,0 +1,208 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2023 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <array>
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
#include <fmt/format.h>
#include <mcl/stdint.hpp>
#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<size_t num_jit_reruns = 1>
void RunTestInstance(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");
}
int main() {
std::array<u64, 31> initial_regs{};
std::array<std::array<u64, 2>, 32> initial_vecs{};
std::vector<u32> 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;
}