backend/arm64: Add verbose debugging output
This commit is contained in:
parent
2a87337141
commit
191125a208
9 changed files with 216 additions and 0 deletions
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -155,6 +155,9 @@ struct EmitConfig {
|
|||
|
||||
// A32 specific
|
||||
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);
|
||||
|
|
|
@ -7,14 +7,18 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
|
||||
#include <mcl/assert.hpp>
|
||||
#include <mcl/bit/bit_field.hpp>
|
||||
#include <mcl/bit_cast.hpp>
|
||||
#include <mcl/mp/metavalue/lift_value.hpp>
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
#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<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>
|
||||
int RegAlloc::GenerateImmediate(const IR::Value& value) {
|
||||
ASSERT(value.GetType() != IR::Type::U1);
|
||||
|
|
|
@ -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<typename>
|
||||
|
|
104
src/dynarmic/backend/arm64/verbose_debugging_output.cpp
Normal file
104
src/dynarmic/backend/arm64/verbose_debugging_output.cpp
Normal 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
|
58
src/dynarmic/backend/arm64/verbose_debugging_output.h
Normal file
58
src/dynarmic/backend/arm64/verbose_debugging_output.h
Normal 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
|
Loading…
Reference in a new issue