diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 6e39253c..5b084d9a 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -373,6 +373,8 @@ if ("arm64" IN_LIST ARCHITECTURE) backend/arm64/reg_alloc.cpp backend/arm64/reg_alloc.h backend/arm64/stack_layout.h + backend/arm64/verbose_debugging_output.cpp + backend/arm64/verbose_debugging_output.h common/spin_lock_arm64.cpp common/spin_lock_arm64.h ) diff --git a/src/dynarmic/backend/arm64/a32_address_space.cpp b/src/dynarmic/backend/arm64/a32_address_space.cpp index 8a630c93..8723326b 100644 --- a/src/dynarmic/backend/arm64/a32_address_space.cpp +++ b/src/dynarmic/backend/arm64/a32_address_space.cpp @@ -174,6 +174,7 @@ IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const { Optimization::ConstantPropagation(ir_block); Optimization::DeadCodeElimination(ir_block); } + Optimization::IdentityRemovalPass(ir_block); Optimization::VerificationPass(ir_block); return ir_block; @@ -385,6 +386,8 @@ EmitConfig A32AddressSpace::GetEmitConfig() { .state_exclusive_state_offset = offsetof(A32JitState, exclusive_state), .coprocessors = conf.coprocessors, + + .very_verbose_debugging_output = conf.very_verbose_debugging_output, }; } diff --git a/src/dynarmic/backend/arm64/a64_address_space.cpp b/src/dynarmic/backend/arm64/a64_address_space.cpp index 06966695..28016384 100644 --- a/src/dynarmic/backend/arm64/a64_address_space.cpp +++ b/src/dynarmic/backend/arm64/a64_address_space.cpp @@ -562,6 +562,8 @@ EmitConfig A64AddressSpace::GetEmitConfig() { .state_exclusive_state_offset = offsetof(A64JitState, exclusive_state), .coprocessors{}, + + .very_verbose_debugging_output = conf.very_verbose_debugging_output, }; } diff --git a/src/dynarmic/backend/arm64/emit_arm64.cpp b/src/dynarmic/backend/arm64/emit_arm64.cpp index e5ad165c..05358731 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64.cpp @@ -11,6 +11,7 @@ #include "dynarmic/backend/arm64/emit_context.h" #include "dynarmic/backend/arm64/fpsr_manager.h" #include "dynarmic/backend/arm64/reg_alloc.h" +#include "dynarmic/backend/arm64/verbose_debugging_output.h" #include "dynarmic/ir/basic_block.h" #include "dynarmic/ir/microinstruction.h" #include "dynarmic/ir/opcodes.h" @@ -175,6 +176,10 @@ static void EmitAddCycles(oaknut::CodeGenerator& code, EmitContext& ctx, size_t } EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& conf, FastmemManager& fastmem_manager) { + if (conf.very_verbose_debugging_output) { + std::puts(IR::DumpBlock(block).c_str()); + } + EmittedBlockInfo ebi; FpsrManager fpsr_manager{code, conf.state_fpsr_offset}; @@ -223,6 +228,10 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E reg_alloc.UpdateAllUses(); reg_alloc.AssertAllUnlocked(); + + if (conf.very_verbose_debugging_output) { + EmitVerboseDebuggingOutput(code, ctx); + } } fpsr_manager.Spill(); diff --git a/src/dynarmic/backend/arm64/emit_arm64.h b/src/dynarmic/backend/arm64/emit_arm64.h index c15d9266..6d13e7e3 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.h +++ b/src/dynarmic/backend/arm64/emit_arm64.h @@ -155,6 +155,9 @@ struct EmitConfig { // A32 specific std::array, 16> coprocessors{}; + + // Debugging + bool very_verbose_debugging_output; }; EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf, FastmemManager& fastmem_manager); diff --git a/src/dynarmic/backend/arm64/reg_alloc.cpp b/src/dynarmic/backend/arm64/reg_alloc.cpp index 83accac2..a8fd2ec7 100644 --- a/src/dynarmic/backend/arm64/reg_alloc.cpp +++ b/src/dynarmic/backend/arm64/reg_alloc.cpp @@ -7,14 +7,18 @@ #include #include +#include #include #include +#include #include #include #include "dynarmic/backend/arm64/abi.h" +#include "dynarmic/backend/arm64/emit_context.h" #include "dynarmic/backend/arm64/fpsr_manager.h" +#include "dynarmic/backend/arm64/verbose_debugging_output.h" #include "dynarmic/common/always_false.h" namespace Dynarmic::Backend::Arm64 { @@ -234,6 +238,34 @@ void RegAlloc::AssertNoMoreUses() const { ASSERT(std::all_of(spills.begin(), spills.end(), is_empty)); } +void RegAlloc::EmitVerboseDebuggingOutput(EmitContext& ctx) { + code.MOV(X19, mcl::bit_cast(&PrintVerboseDebuggingOutputLine)); // Non-volatile register + + const auto do_location = [&](HostLocInfo& info, HostLocType type, size_t index) { + using namespace oaknut::util; + for (const IR::Inst* value : info.values) { + const auto inst_offset = std::distance(ctx.block.begin(), IR::Block::iterator(const_cast(value))); + code.MOV(X0, SP); + code.MOV(X1, static_cast(type)); + code.MOV(X2, index); + code.MOV(X3, mcl::bit_cast(inst_offset)); + code.MOV(X4, static_cast(value->GetType())); + code.BLR(X19); + } + }; + + for (size_t i = 0; i < gprs.size(); i++) { + do_location(gprs[i], HostLocType::X, i); + } + for (size_t i = 0; i < fprs.size(); i++) { + do_location(fprs[i], HostLocType::Q, i); + } + do_location(flags, HostLocType::Nzcv, 0); + for (size_t i = 0; i < spills.size(); i++) { + do_location(spills[i], HostLocType::Spill, i); + } +} + template int RegAlloc::GenerateImmediate(const IR::Value& value) { ASSERT(value.GetType() != IR::Type::U1); diff --git a/src/dynarmic/backend/arm64/reg_alloc.h b/src/dynarmic/backend/arm64/reg_alloc.h index a101ee6d..6c97d4af 100644 --- a/src/dynarmic/backend/arm64/reg_alloc.h +++ b/src/dynarmic/backend/arm64/reg_alloc.h @@ -23,6 +23,7 @@ namespace Dynarmic::Backend::Arm64 { +struct EmitContext; class FpsrManager; class RegAlloc; @@ -290,6 +291,8 @@ public: void AssertAllUnlocked() const; void AssertNoMoreUses() const; + void EmitVerboseDebuggingOutput(EmitContext& ctx); + private: friend struct Argument; template diff --git a/src/dynarmic/backend/arm64/verbose_debugging_output.cpp b/src/dynarmic/backend/arm64/verbose_debugging_output.cpp new file mode 100644 index 00000000..fbb26bc4 --- /dev/null +++ b/src/dynarmic/backend/arm64/verbose_debugging_output.cpp @@ -0,0 +1,104 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2023 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "dynarmic/backend/arm64/verbose_debugging_output.h" + +#include +#include + +#include "dynarmic/backend/arm64/emit_context.h" +#include "dynarmic/ir/type.h" + +namespace Dynarmic::Backend::Arm64 { + +using namespace oaknut::util; + +void EmitVerboseDebuggingOutput(oaknut::CodeGenerator& code, EmitContext& ctx) { + code.SUB(SP, SP, sizeof(RegisterData)); + for (int i = 0; i < 30; i++) { + if (i == 18) { + continue; // Platform register + } + code.STR(oaknut::XReg{i}, SP, offsetof(RegisterData, x) + i * sizeof(u64)); + } + for (int i = 0; i < 32; i++) { + code.STR(oaknut::QReg{i}, SP, offsetof(RegisterData, q) + i * sizeof(Vector)); + } + code.MRS(X0, oaknut::SystemReg::NZCV); + code.STR(X0, SP, offsetof(RegisterData, nzcv)); + code.ADD(X0, SP, sizeof(RegisterData) + offsetof(StackLayout, spill)); + code.STR(X0, SP, offsetof(RegisterData, spill)); + + ctx.reg_alloc.EmitVerboseDebuggingOutput(ctx); + + code.LDR(X0, SP, offsetof(RegisterData, nzcv)); + code.MSR(oaknut::SystemReg::NZCV, X0); + for (int i = 0; i < 32; i++) { + code.LDR(oaknut::QReg{i}, SP, offsetof(RegisterData, q) + i * sizeof(Vector)); + } + for (int i = 0; i < 30; i++) { + if (i == 18) { + continue; // Platform register + } + code.LDR(oaknut::XReg{i}, SP, offsetof(RegisterData, x) + i * sizeof(u64)); + } + code.ADD(SP, SP, sizeof(RegisterData)); +} + +void PrintVerboseDebuggingOutputLine(RegisterData& reg_data, HostLocType reg_type, size_t reg_index, size_t inst_index, IR::Type inst_type) { + fmt::print("dynarmic debug: %{:05} = ", inst_index); + + Vector value = [&]() -> Vector { + switch (reg_type) { + case HostLocType::X: + return {reg_data.x[reg_index], 0}; + case HostLocType::Q: + return reg_data.q[reg_index]; + case HostLocType::Nzcv: + return {reg_data.nzcv, 0}; + case HostLocType::Spill: + return (*reg_data.spill)[reg_index]; + } + fmt::print("invalid reg_type! "); + return {0, 0}; + }(); + + switch (inst_type) { + case IR::Type::U1: + case IR::Type::U8: + fmt::print("{:02x}", value[0] & 0xff); + break; + case IR::Type::U16: + fmt::print("{:04x}", value[0] & 0xffff); + break; + case IR::Type::U32: + case IR::Type::NZCVFlags: + fmt::print("{:08x}", value[0] & 0xffffffff); + break; + case IR::Type::U64: + fmt::print("{:016x}", value[0]); + break; + case IR::Type::U128: + fmt::print("{:016x}{:016x}", value[1], value[0]); + break; + case IR::Type::A32Reg: + case IR::Type::A32ExtReg: + case IR::Type::A64Reg: + case IR::Type::A64Vec: + case IR::Type::CoprocInfo: + case IR::Type::Cond: + case IR::Type::Void: + case IR::Type::Table: + case IR::Type::AccType: + case IR::Type::Opaque: + default: + fmt::print("invalid inst_type!"); + break; + } + + fmt::print("\n"); +} + +} // namespace Dynarmic::Backend::Arm64 \ No newline at end of file diff --git a/src/dynarmic/backend/arm64/verbose_debugging_output.h b/src/dynarmic/backend/arm64/verbose_debugging_output.h new file mode 100644 index 00000000..1099edc4 --- /dev/null +++ b/src/dynarmic/backend/arm64/verbose_debugging_output.h @@ -0,0 +1,58 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2023 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include + +#include + +#include "dynarmic/backend/arm64/stack_layout.h" + +namespace oaknut { +struct PointerCodeGeneratorPolicy; +template +class BasicCodeGenerator; +using CodeGenerator = BasicCodeGenerator; +struct Label; +} // namespace oaknut + +namespace Dynarmic::IR { +enum class Type; +} // namespace Dynarmic::IR + +namespace Dynarmic::Backend::Arm64 { + +struct EmitContext; + +using Vector = std::array; + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4324) // Structure was padded due to alignment specifier +#endif + +enum class HostLocType { + X, + Q, + Nzcv, + Spill, +}; + +struct alignas(16) RegisterData { + std::array x; + std::array q; + u32 nzcv; + decltype(StackLayout::spill)* spill; +}; + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +void EmitVerboseDebuggingOutput(oaknut::CodeGenerator& code, EmitContext& ctx); +void PrintVerboseDebuggingOutputLine(RegisterData& reg_data, HostLocType reg_type, size_t reg_index, size_t inst_index, IR::Type inst_type); + +} // namespace Dynarmic::Backend::Arm64 \ No newline at end of file