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