Browse Source

[dynarmic] disable extra verbose debugging on release builds (#3293)

user doesn't need it and just wastes resources
Signed-off-by: lizzie <lizzie@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3293
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: DraVee <dravee@eden-emu.dev>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
sgsrtry
lizzie 4 days ago
committed by crueter
parent
commit
1cb8bcf531
No known key found for this signature in database GPG Key ID: 425ACD2D4830EBC6
  1. 3
      src/CMakeLists.txt
  2. 2
      src/dynarmic/CMakeLists.txt
  3. 47
      src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp
  4. 13
      src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp
  5. 2
      src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp
  6. 2
      src/dynarmic/src/dynarmic/backend/x64/emit_x64.h
  7. 2
      src/dynarmic/src/dynarmic/backend/x64/hostloc.h
  8. 73
      src/dynarmic/src/dynarmic/backend/x64/reg_alloc.cpp
  9. 56
      src/dynarmic/src/dynarmic/backend/x64/reg_alloc.h
  10. 3
      src/dynarmic/src/dynarmic/ir/microinstruction.h

3
src/CMakeLists.txt

@ -7,8 +7,7 @@
include_directories(.)
# Dynarmic
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64))
set(DYNARMIC_IGNORE_ASSERTS ON)
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
add_subdirectory(dynarmic)
add_library(dynarmic::dynarmic ALIAS dynarmic)
endif()

2
src/dynarmic/CMakeLists.txt

@ -32,7 +32,7 @@ else()
endif()
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${REQUIRE_WX})
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" OFF)
option(DYNARMIC_IGNORE_ASSERTS "Ignore asserts" ON)
option(DYNARMIC_TESTS_USE_UNICORN "Enable fuzzing tests against unicorn" OFF)
CMAKE_DEPENDENT_OPTION(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF "NOT YUZU_DISABLE_LLVM" OFF)

47
src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp

@ -102,9 +102,6 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
}
code.EnableWriting();
SCOPE_EXIT {
code.DisableWriting();
};
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
@ -126,37 +123,31 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
EmitCondPrelude(ctx);
auto const loop_all_inst = [this, &block, &ctx](auto const func) {
for (auto iter = block.begin(); iter != block.end(); ++iter) [[likely]] {
auto* inst = &*iter;
// Call the relevant Emit* member function.
switch (inst->GetOpcode()) {
for (auto iter = block.begin(); iter != block.end(); ++iter) [[likely]] {
auto* inst = &*iter;
// Call the relevant Emit* member function.
switch (inst->GetOpcode()) {
#define OPCODE(name, type, ...) \
case IR::Opcode::name: \
A32EmitX64::Emit##name(ctx, inst); \
break;
case IR::Opcode::name: \
A32EmitX64::Emit##name(ctx, inst); \
break;
#define A32OPC(name, type, ...) \
case IR::Opcode::A32##name: \
A32EmitX64::EmitA32##name(ctx, inst);\
break;
case IR::Opcode::A32##name: \
A32EmitX64::EmitA32##name(ctx, inst);\
break;
#define A64OPC(...)
#include "dynarmic/ir/opcodes.inc"
#undef OPCODE
#undef A32OPC
#undef A64OPC
default:
UNREACHABLE();
}
reg_alloc.EndOfAllocScope();
func(reg_alloc);
default:
UNREACHABLE();
}
};
if (!conf.very_verbose_debugging_output) [[likely]] {
loop_all_inst([](auto&) { /*noop*/ });
} else [[unlikely]] {
loop_all_inst([this](auto& reg_alloc) {
reg_alloc.EndOfAllocScope();
#ifndef NDEBUG
if (conf.very_verbose_debugging_output)
EmitVerboseDebuggingOutput(reg_alloc);
});
#endif
}
reg_alloc.AssertNoMoreUses();
@ -172,7 +163,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
}
code.int3();
const size_t size = static_cast<size_t>(code.getCurr() - entrypoint);
const size_t size = size_t(code.getCurr() - entrypoint);
const A32::LocationDescriptor descriptor{block.Location()};
const A32::LocationDescriptor end_location{block.EndLocation()};
@ -180,7 +171,9 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
const auto range = boost::icl::discrete_interval<u32>::closed(descriptor.PC(), end_location.PC() - 1);
block_ranges.AddRange(range, descriptor);
return RegisterBlock(descriptor, entrypoint, size);
auto const bdesc = RegisterBlock(descriptor, entrypoint, size);
code.DisableWriting();
return bdesc;
}
void A32EmitX64::ClearCache() {

13
src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp

@ -76,10 +76,6 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) noexcept {
}
code.EnableWriting();
SCOPE_EXIT {
code.DisableWriting();
};
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
if (conf.fastmem_pointer) {
@ -141,9 +137,10 @@ a64_branch:
(this->*a64_handlers[size_t(opcode) - std::size(opcode_handlers)])(ctx, &inst);
finish_this_inst:
ctx.reg_alloc.EndOfAllocScope();
if (conf.very_verbose_debugging_output) [[unlikely]] {
#ifndef NDEBUG
if (conf.very_verbose_debugging_output)
EmitVerboseDebuggingOutput(reg_alloc);
}
#endif
}
reg_alloc.AssertNoMoreUses();
@ -167,7 +164,9 @@ finish_this_inst:
const auto range = boost::icl::discrete_interval<u64>::closed(descriptor.PC(), end_location.PC() - 1);
block_ranges.AddRange(range, descriptor);
return RegisterBlock(descriptor, entrypoint, size);
auto bdesc = RegisterBlock(descriptor, entrypoint, size);
code.DisableWriting();
return bdesc;
}
void A64EmitX64::ClearCache() {

2
src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp

@ -105,6 +105,7 @@ void EmitX64::PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, I
code.mov(dword[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_rsb_ptr], index_reg.cvt32());
}
#ifndef NDEBUG
void EmitX64::EmitVerboseDebuggingOutput(RegAlloc& reg_alloc) {
code.lea(rsp, ptr[rsp - sizeof(RegisterData)]);
code.stmxcsr(dword[rsp + offsetof(RegisterData, mxcsr)]);
@ -134,6 +135,7 @@ void EmitX64::EmitVerboseDebuggingOutput(RegAlloc& reg_alloc) {
code.ldmxcsr(dword[rsp + offsetof(RegisterData, mxcsr)]);
code.add(rsp, sizeof(RegisterData));
}
#endif
void EmitX64::EmitPushRSB(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst);

2
src/dynarmic/src/dynarmic/backend/x64/emit_x64.h

@ -113,7 +113,9 @@ public:
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);
#ifndef NDEBUG
void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc);
#endif
virtual void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
// Patching

2
src/dynarmic/src/dynarmic/backend/x64/hostloc.h

@ -58,7 +58,7 @@ enum class HostLoc : std::uint8_t {
FirstSpill,
};
constexpr size_t NonSpillHostLocCount = static_cast<size_t>(HostLoc::FirstSpill);
constexpr size_t NonSpillHostLocCount = size_t(HostLoc::FirstSpill);
constexpr bool HostLocIsGPR(HostLoc reg) {
return reg >= HostLoc::RAX && reg <= HostLoc::R15;

73
src/dynarmic/src/dynarmic/backend/x64/reg_alloc.cpp

@ -56,26 +56,23 @@ static inline bool IsValuelessType(const IR::Type type) noexcept {
}
void HostLocInfo::ReleaseOne() noexcept {
is_being_used_count--;
ASSERT(is_being_used_count > 0);
--is_being_used_count;
is_scratch = false;
if (current_references == 0)
return;
ASSERT(size_t(accumulated_uses) + 1 < (std::numeric_limits<uint16_t>::max)());
accumulated_uses++;
current_references--;
if (current_references == 0)
ReleaseAll();
if (current_references > 0) {
ASSERT(size_t(accumulated_uses) + 1 < (std::numeric_limits<decltype(accumulated_uses)>::max)());
++accumulated_uses;
--current_references;
if (current_references == 0)
ReleaseAll();
}
}
void HostLocInfo::ReleaseAll() noexcept {
ASSERT(size_t(accumulated_uses) + current_references < (std::numeric_limits<decltype(accumulated_uses)>::max)());
accumulated_uses += current_references;
current_references = 0;
is_set_last_use = false;
if (total_uses == accumulated_uses) {
values.clear();
accumulated_uses = 0;
@ -87,17 +84,19 @@ void HostLocInfo::ReleaseAll() noexcept {
is_scratch = false;
}
void HostLocInfo::AddValue(IR::Inst* inst) noexcept {
void HostLocInfo::AddValue(HostLoc loc, IR::Inst* inst) noexcept {
if (is_set_last_use) {
is_set_last_use = false;
values.clear();
}
values.push_back(inst);
ASSERT(size_t(total_uses) + inst->UseCount() < (std::numeric_limits<uint16_t>::max)());
ASSERT(size_t(total_uses) + inst->UseCount() < (std::numeric_limits<decltype(total_uses)>::max)());
total_uses += inst->UseCount();
max_bit_width = std::max<uint8_t>(max_bit_width, std::countr_zero(GetBitWidth(inst->GetType())));
}
#ifndef NDEBUG
void HostLocInfo::EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_index) const noexcept {
using namespace Xbyak::util;
for (auto const value : values) {
@ -108,6 +107,7 @@ void HostLocInfo::EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_
code.CallFunction(PrintVerboseDebuggingOutputLine);
}
}
#endif
bool Argument::FitsInImmediateU32() const noexcept {
if (!IsImmediate())
@ -197,12 +197,13 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(const IR::Inst* inst) noexcept
Argument{},
Argument{}
};
for (size_t i = 0; i < inst->NumArgs(); i++) {
for (size_t i = 0; i < inst->NumArgs() && i < 4; i++) {
const auto arg = inst->GetArg(i);
ret[i].value = arg;
if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) {
ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
LocInfo(*ValueLocation(arg.GetInst())).AddArgReference();
auto const loc = ValueLocation(arg.GetInst());
ASSERT(loc && "argument must already been defined");
LocInfo(*loc).AddArgReference();
}
}
return ret;
@ -211,9 +212,9 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(const IR::Inst* inst) noexcept
void RegAlloc::RegisterPseudoOperation(const IR::Inst* inst) noexcept {
ASSERT(IsValueLive(inst) || !inst->HasUses());
for (size_t i = 0; i < inst->NumArgs(); i++) {
const auto arg = inst->GetArg(i);
auto const arg = inst->GetArg(i);
if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) {
if (const auto loc = ValueLocation(arg.GetInst())) {
if (auto const loc = ValueLocation(arg.GetInst())) {
// May not necessarily have a value (e.g. CMP variant of Sub32).
LocInfo(*loc).AddArgReference();
}
@ -262,9 +263,8 @@ HostLoc RegAlloc::UseImpl(BlockOfCode& code, IR::Value use_value, const boost::c
return LoadImmediate(code, use_value, ScratchImpl(code, desired_locations));
}
const auto* use_inst = use_value.GetInst();
const HostLoc current_location = *ValueLocation(use_inst);
const size_t max_bit_width = LocInfo(current_location).GetMaxBitWidth();
auto const* use_inst = use_value.GetInst();
HostLoc const current_location = *ValueLocation(use_inst);
const bool can_use_current_location = std::find(desired_locations.begin(), desired_locations.end(), current_location) != desired_locations.end();
if (can_use_current_location) {
@ -276,7 +276,8 @@ HostLoc RegAlloc::UseImpl(BlockOfCode& code, IR::Value use_value, const boost::c
return UseScratchImpl(code, use_value, desired_locations);
}
const HostLoc destination_location = SelectARegister(desired_locations);
size_t const max_bit_width = LocInfo(current_location).GetMaxBitWidth();
HostLoc const destination_location = SelectARegister(desired_locations);
if (max_bit_width > HostLocBitWidth(destination_location)) {
return UseScratchImpl(code, use_value, desired_locations);
} else if (CanExchange(destination_location, current_location)) {
@ -300,10 +301,10 @@ HostLoc RegAlloc::UseScratchImpl(BlockOfCode& code, IR::Value use_value, const b
const bool can_use_current_location = std::find(desired_locations.begin(), desired_locations.end(), current_location) != desired_locations.end();
if (can_use_current_location && !LocInfo(current_location).IsLocked()) {
if (!LocInfo(current_location).IsLastUse()) {
MoveOutOfTheWay(code, current_location);
if (LocInfo(current_location).IsLastUse()) {
LocInfo(current_location).is_set_last_use = true;
} else {
LocInfo(current_location).SetLastUse();
MoveOutOfTheWay(code, current_location);
}
LocInfo(current_location).WriteLock();
return current_location;
@ -455,29 +456,31 @@ HostLoc RegAlloc::SelectARegister(const boost::container::static_vector<HostLoc,
std::optional<HostLoc> RegAlloc::ValueLocation(const IR::Inst* value) const noexcept {
for (size_t i = 0; i < hostloc_info.size(); i++)
if (hostloc_info[i].ContainsValue(value))
if (hostloc_info[i].ContainsValue(value)) {
//for (size_t j = 0; j < hostloc_info.size(); ++j)
// ASSERT((i == j || !hostloc_info[j].ContainsValue(value)) && "duplicate defs");
return HostLoc(i);
}
return std::nullopt;
}
void RegAlloc::DefineValueImpl(BlockOfCode& code, IR::Inst* def_inst, HostLoc host_loc) noexcept {
ASSERT(!ValueLocation(def_inst) && "def_inst has already been defined");
LocInfo(host_loc).AddValue(def_inst);
LocInfo(host_loc).AddValue(host_loc, def_inst);
ASSERT(*ValueLocation(def_inst) == host_loc);
}
void RegAlloc::DefineValueImpl(BlockOfCode& code, IR::Inst* def_inst, const IR::Value& use_inst) noexcept {
ASSERT(!ValueLocation(def_inst) && "def_inst has already been defined");
if (use_inst.IsImmediate()) {
const HostLoc location = ScratchImpl(code, gpr_order);
DefineValueImpl(code, def_inst, location);
LoadImmediate(code, use_inst, location);
return;
} else {
ASSERT(ValueLocation(use_inst.GetInst()) && "use_inst must already be defined");
const HostLoc location = *ValueLocation(use_inst.GetInst());
DefineValueImpl(code, def_inst, location);
}
ASSERT(ValueLocation(use_inst.GetInst()) && "use_inst must already be defined");
const HostLoc location = *ValueLocation(use_inst.GetInst());
DefineValueImpl(code, def_inst, location);
}
void RegAlloc::Move(BlockOfCode& code, HostLoc to, HostLoc from) noexcept {

56
src/dynarmic/src/dynarmic/backend/x64/reg_alloc.h

@ -46,61 +46,55 @@ public:
return is_being_used_count == 0 && values.empty();
}
inline bool IsLastUse() const {
return is_being_used_count == 0 && current_references == 1 && accumulated_uses + 1 == total_uses;
}
inline void SetLastUse() noexcept {
ASSERT(IsLastUse());
is_set_last_use = true;
return is_being_used_count == 0 && current_references == 1 && size_t(accumulated_uses) + 1 == size_t(total_uses);
}
inline void ReadLock() noexcept {
ASSERT(size_t(is_being_used_count) + 1 < (std::numeric_limits<uint16_t>::max)());
ASSERT(size_t(is_being_used_count) + 1 < (std::numeric_limits<decltype(is_being_used_count)>::max)());
ASSERT(!is_scratch);
is_being_used_count++;
}
inline void WriteLock() noexcept {
ASSERT(size_t(is_being_used_count) + 1 < (std::numeric_limits<uint16_t>::max)());
ASSERT(is_being_used_count == 0);
is_being_used_count++;
is_scratch = true;
}
inline void AddArgReference() noexcept {
ASSERT(size_t(current_references) + 1 < (std::numeric_limits<uint16_t>::max)());
current_references++;
ASSERT(accumulated_uses + current_references <= total_uses);
ASSERT(size_t(current_references) + 1 < (std::numeric_limits<decltype(current_references)>::max)());
++current_references;
ASSERT(size_t(accumulated_uses) + current_references <= size_t(total_uses));
}
void ReleaseOne() noexcept;
void ReleaseAll() noexcept;
constexpr size_t GetMaxBitWidth() const noexcept { return 1 << max_bit_width; }
void AddValue(HostLoc loc, IR::Inst* inst) noexcept;
/// Checks if the given instruction is in our values set
/// SAFETY: Const is casted away, irrelevant since this is only used for checking
inline bool ContainsValue(const IR::Inst* inst) const noexcept {
//return values.contains(const_cast<IR::Inst*>(inst));
[[nodiscard]] bool ContainsValue(const IR::Inst* inst) const noexcept {
return std::find(values.begin(), values.end(), inst) != values.end();
}
inline size_t GetMaxBitWidth() const noexcept {
return 1 << max_bit_width;
}
void AddValue(IR::Inst* inst) noexcept;
#ifndef NDEBUG
void EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_index) const noexcept;
#endif
private:
//non trivial
boost::container::small_vector<IR::Inst*, 3> values; //24
// Block state
uint16_t total_uses = 0; //8
//sometimes zeroed
uint16_t accumulated_uses = 0; //8
//non trivial
// Block state, the total amount of uses for this particular arg
uint16_t total_uses = 0; //2
// Sometimes zeroed, accumulated (non referenced) uses
uint16_t accumulated_uses = 0; //2
//always zeroed
// Current instruction state
uint16_t is_being_used_count = 0; //8
uint16_t current_references = 0; //8
// Value state
uint8_t current_references = 0; //1
uint8_t is_being_used_count = 0; //1
// Value state, count for LRU selection in registers
uint8_t lru_counter : 2 = 0; //1
uint8_t max_bit_width : 4 = 0; //Valid values: log2(1,2,4,8,16,32,128) = (0, 1, 2, 3, 4, 5, 6)
// Log 2 of bit width, valid values: log2(1,2,4,8,16,32,128) = (0, 1, 2, 3, 4, 5, 6)
uint8_t max_bit_width : 4 = 0;
bool is_scratch : 1 = false; //1
bool is_set_last_use : 1 = false; //1
friend class RegAlloc;
};
static_assert(sizeof(HostLocInfo) == 64);
//static_assert(sizeof(HostLocInfo) == 64);
struct Argument {
public:
@ -213,10 +207,12 @@ public:
inline void AssertNoMoreUses() noexcept {
ASSERT(std::all_of(hostloc_info.begin(), hostloc_info.end(), [](const auto& i) noexcept { return i.IsEmpty(); }));
}
#ifndef NDEBUG
inline void EmitVerboseDebuggingOutput(BlockOfCode& code) noexcept {
for (size_t i = 0; i < hostloc_info.size(); i++)
hostloc_info[i].EmitVerboseDebuggingOutput(code, i);
}
#endif
private:
friend struct Argument;
@ -238,11 +234,11 @@ private:
HostLoc FindFreeSpill(bool is_xmm) const noexcept;
inline HostLocInfo& LocInfo(const HostLoc loc) noexcept {
ASSERT(loc != HostLoc::RSP && loc != ABI_JIT_PTR);
DEBUG_ASSERT(loc != HostLoc::RSP && loc != ABI_JIT_PTR);
return hostloc_info[size_t(loc)];
}
inline const HostLocInfo& LocInfo(const HostLoc loc) const noexcept {
ASSERT(loc != HostLoc::RSP && loc != ABI_JIT_PTR);
DEBUG_ASSERT(loc != HostLoc::RSP && loc != ABI_JIT_PTR);
return hostloc_info[size_t(loc)];
}
@ -256,6 +252,6 @@ private:
size_t reserved_stack_space = 0;
};
// Ensure a cache line (or less) is used, this is primordial
static_assert(sizeof(boost::container::static_vector<HostLoc, 28>) == 40);
static_assert(sizeof(boost::container::static_vector<HostLoc, 28>) < 64);
} // namespace Dynarmic::Backend::X64

3
src/dynarmic/src/dynarmic/ir/microinstruction.h

@ -74,7 +74,6 @@ public:
void SetName(unsigned value) { name = value; }
unsigned GetName() const { return name; }
private:
void Use(const Value& value);
void UndoUse(const Value& value);
@ -87,6 +86,6 @@ private:
unsigned name = 0; //4 (4)
alignas(64) std::array<Value, max_arg_count> args; //16 * 4 = 64 (1 cache line)
};
static_assert(sizeof(Inst) == 128);
//static_assert(sizeof(Inst) == 128);
} // namespace Dynarmic::IR
Loading…
Cancel
Save