backend/arm64: Add verbose debugging output

This commit is contained in:
Merry 2023-01-20 11:52:23 +00:00
parent 2a87337141
commit 191125a208
9 changed files with 216 additions and 0 deletions

View file

@ -373,6 +373,8 @@ if ("arm64" IN_LIST ARCHITECTURE)
backend/arm64/reg_alloc.cpp backend/arm64/reg_alloc.cpp
backend/arm64/reg_alloc.h backend/arm64/reg_alloc.h
backend/arm64/stack_layout.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.cpp
common/spin_lock_arm64.h common/spin_lock_arm64.h
) )

View file

@ -174,6 +174,7 @@ IR::Block A32AddressSpace::GenerateIR(IR::LocationDescriptor descriptor) const {
Optimization::ConstantPropagation(ir_block); Optimization::ConstantPropagation(ir_block);
Optimization::DeadCodeElimination(ir_block); Optimization::DeadCodeElimination(ir_block);
} }
Optimization::IdentityRemovalPass(ir_block);
Optimization::VerificationPass(ir_block); Optimization::VerificationPass(ir_block);
return ir_block; return ir_block;
@ -385,6 +386,8 @@ EmitConfig A32AddressSpace::GetEmitConfig() {
.state_exclusive_state_offset = offsetof(A32JitState, exclusive_state), .state_exclusive_state_offset = offsetof(A32JitState, exclusive_state),
.coprocessors = conf.coprocessors, .coprocessors = conf.coprocessors,
.very_verbose_debugging_output = conf.very_verbose_debugging_output,
}; };
} }

View file

@ -562,6 +562,8 @@ EmitConfig A64AddressSpace::GetEmitConfig() {
.state_exclusive_state_offset = offsetof(A64JitState, exclusive_state), .state_exclusive_state_offset = offsetof(A64JitState, exclusive_state),
.coprocessors{}, .coprocessors{},
.very_verbose_debugging_output = conf.very_verbose_debugging_output,
}; };
} }

View file

@ -11,6 +11,7 @@
#include "dynarmic/backend/arm64/emit_context.h" #include "dynarmic/backend/arm64/emit_context.h"
#include "dynarmic/backend/arm64/fpsr_manager.h" #include "dynarmic/backend/arm64/fpsr_manager.h"
#include "dynarmic/backend/arm64/reg_alloc.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/basic_block.h"
#include "dynarmic/ir/microinstruction.h" #include "dynarmic/ir/microinstruction.h"
#include "dynarmic/ir/opcodes.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) { 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; EmittedBlockInfo ebi;
FpsrManager fpsr_manager{code, conf.state_fpsr_offset}; 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.UpdateAllUses();
reg_alloc.AssertAllUnlocked(); reg_alloc.AssertAllUnlocked();
if (conf.very_verbose_debugging_output) {
EmitVerboseDebuggingOutput(code, ctx);
}
} }
fpsr_manager.Spill(); fpsr_manager.Spill();

View file

@ -155,6 +155,9 @@ struct EmitConfig {
// A32 specific // A32 specific
std::array<std::shared_ptr<A32::Coprocessor>, 16> coprocessors{}; std::array<std::shared_ptr<A32::Coprocessor>, 16> coprocessors{};
// Debugging
bool very_verbose_debugging_output;
}; };
EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf, FastmemManager& fastmem_manager); EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf, FastmemManager& fastmem_manager);

View file

@ -7,14 +7,18 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <iterator>
#include <mcl/assert.hpp> #include <mcl/assert.hpp>
#include <mcl/bit/bit_field.hpp> #include <mcl/bit/bit_field.hpp>
#include <mcl/bit_cast.hpp>
#include <mcl/mp/metavalue/lift_value.hpp> #include <mcl/mp/metavalue/lift_value.hpp>
#include <mcl/stdint.hpp> #include <mcl/stdint.hpp>
#include "dynarmic/backend/arm64/abi.h" #include "dynarmic/backend/arm64/abi.h"
#include "dynarmic/backend/arm64/emit_context.h"
#include "dynarmic/backend/arm64/fpsr_manager.h" #include "dynarmic/backend/arm64/fpsr_manager.h"
#include "dynarmic/backend/arm64/verbose_debugging_output.h"
#include "dynarmic/common/always_false.h" #include "dynarmic/common/always_false.h"
namespace Dynarmic::Backend::Arm64 { namespace Dynarmic::Backend::Arm64 {
@ -234,6 +238,34 @@ void RegAlloc::AssertNoMoreUses() const {
ASSERT(std::all_of(spills.begin(), spills.end(), is_empty)); ASSERT(std::all_of(spills.begin(), spills.end(), is_empty));
} }
void RegAlloc::EmitVerboseDebuggingOutput(EmitContext& ctx) {
code.MOV(X19, mcl::bit_cast<u64>(&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<IR::Inst*>(value)));
code.MOV(X0, SP);
code.MOV(X1, static_cast<u64>(type));
code.MOV(X2, index);
code.MOV(X3, mcl::bit_cast<u64>(inst_offset));
code.MOV(X4, static_cast<u64>(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<HostLoc::Kind kind> template<HostLoc::Kind kind>
int RegAlloc::GenerateImmediate(const IR::Value& value) { int RegAlloc::GenerateImmediate(const IR::Value& value) {
ASSERT(value.GetType() != IR::Type::U1); ASSERT(value.GetType() != IR::Type::U1);

View file

@ -23,6 +23,7 @@
namespace Dynarmic::Backend::Arm64 { namespace Dynarmic::Backend::Arm64 {
struct EmitContext;
class FpsrManager; class FpsrManager;
class RegAlloc; class RegAlloc;
@ -290,6 +291,8 @@ public:
void AssertAllUnlocked() const; void AssertAllUnlocked() const;
void AssertNoMoreUses() const; void AssertNoMoreUses() const;
void EmitVerboseDebuggingOutput(EmitContext& ctx);
private: private:
friend struct Argument; friend struct Argument;
template<typename> template<typename>

View file

@ -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 <fmt/format.h>
#include <oaknut/oaknut.hpp>
#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

View file

@ -0,0 +1,58 @@
/* 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/arm64/stack_layout.h"
namespace oaknut {
struct PointerCodeGeneratorPolicy;
template<typename>
class BasicCodeGenerator;
using CodeGenerator = BasicCodeGenerator<PointerCodeGeneratorPolicy>;
struct Label;
} // namespace oaknut
namespace Dynarmic::IR {
enum class Type;
} // namespace Dynarmic::IR
namespace Dynarmic::Backend::Arm64 {
struct EmitContext;
using Vector = std::array<u64, 2>;
#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<u64, 30> x;
std::array<Vector, 32> 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