[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>
This commit is contained in:
parent
a27d35362d
commit
1cb8bcf531
|
|
@ -7,8 +7,7 @@
|
||||||
include_directories(.)
|
include_directories(.)
|
||||||
|
|
||||||
# Dynarmic
|
# Dynarmic
|
||||||
if ((ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64))
|
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||||
set(DYNARMIC_IGNORE_ASSERTS ON)
|
|
||||||
add_subdirectory(dynarmic)
|
add_subdirectory(dynarmic)
|
||||||
add_library(dynarmic::dynarmic ALIAS dynarmic)
|
add_library(dynarmic::dynarmic ALIAS dynarmic)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ else()
|
||||||
endif()
|
endif()
|
||||||
option(DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT "Enables support for systems that require W^X" ${REQUIRE_WX})
|
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)
|
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)
|
CMAKE_DEPENDENT_OPTION(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF "NOT YUZU_DISABLE_LLVM" OFF)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -102,9 +102,6 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
code.EnableWriting();
|
code.EnableWriting();
|
||||||
SCOPE_EXIT {
|
|
||||||
code.DisableWriting();
|
|
||||||
};
|
|
||||||
|
|
||||||
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
|
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
|
||||||
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
|
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
|
||||||
|
|
@ -126,37 +123,31 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
||||||
|
|
||||||
EmitCondPrelude(ctx);
|
EmitCondPrelude(ctx);
|
||||||
|
|
||||||
auto const loop_all_inst = [this, &block, &ctx](auto const func) {
|
for (auto iter = block.begin(); iter != block.end(); ++iter) [[likely]] {
|
||||||
for (auto iter = block.begin(); iter != block.end(); ++iter) [[likely]] {
|
auto* inst = &*iter;
|
||||||
auto* inst = &*iter;
|
// Call the relevant Emit* member function.
|
||||||
// Call the relevant Emit* member function.
|
switch (inst->GetOpcode()) {
|
||||||
switch (inst->GetOpcode()) {
|
|
||||||
#define OPCODE(name, type, ...) \
|
#define OPCODE(name, type, ...) \
|
||||||
case IR::Opcode::name: \
|
case IR::Opcode::name: \
|
||||||
A32EmitX64::Emit##name(ctx, inst); \
|
A32EmitX64::Emit##name(ctx, inst); \
|
||||||
break;
|
break;
|
||||||
#define A32OPC(name, type, ...) \
|
#define A32OPC(name, type, ...) \
|
||||||
case IR::Opcode::A32##name: \
|
case IR::Opcode::A32##name: \
|
||||||
A32EmitX64::EmitA32##name(ctx, inst);\
|
A32EmitX64::EmitA32##name(ctx, inst);\
|
||||||
break;
|
break;
|
||||||
#define A64OPC(...)
|
#define A64OPC(...)
|
||||||
#include "dynarmic/ir/opcodes.inc"
|
#include "dynarmic/ir/opcodes.inc"
|
||||||
#undef OPCODE
|
#undef OPCODE
|
||||||
#undef A32OPC
|
#undef A32OPC
|
||||||
#undef A64OPC
|
#undef A64OPC
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
|
||||||
reg_alloc.EndOfAllocScope();
|
|
||||||
func(reg_alloc);
|
|
||||||
}
|
}
|
||||||
};
|
reg_alloc.EndOfAllocScope();
|
||||||
if (!conf.very_verbose_debugging_output) [[likely]] {
|
#ifndef NDEBUG
|
||||||
loop_all_inst([](auto&) { /*noop*/ });
|
if (conf.very_verbose_debugging_output)
|
||||||
} else [[unlikely]] {
|
|
||||||
loop_all_inst([this](auto& reg_alloc) {
|
|
||||||
EmitVerboseDebuggingOutput(reg_alloc);
|
EmitVerboseDebuggingOutput(reg_alloc);
|
||||||
});
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_alloc.AssertNoMoreUses();
|
reg_alloc.AssertNoMoreUses();
|
||||||
|
|
@ -172,7 +163,7 @@ A32EmitX64::BlockDescriptor A32EmitX64::Emit(IR::Block& block) {
|
||||||
}
|
}
|
||||||
code.int3();
|
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 descriptor{block.Location()};
|
||||||
const A32::LocationDescriptor end_location{block.EndLocation()};
|
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);
|
const auto range = boost::icl::discrete_interval<u32>::closed(descriptor.PC(), end_location.PC() - 1);
|
||||||
block_ranges.AddRange(range, descriptor);
|
block_ranges.AddRange(range, descriptor);
|
||||||
|
|
||||||
return RegisterBlock(descriptor, entrypoint, size);
|
auto const bdesc = RegisterBlock(descriptor, entrypoint, size);
|
||||||
|
code.DisableWriting();
|
||||||
|
return bdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::ClearCache() {
|
void A32EmitX64::ClearCache() {
|
||||||
|
|
|
||||||
|
|
@ -76,10 +76,6 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) noexcept {
|
||||||
}
|
}
|
||||||
|
|
||||||
code.EnableWriting();
|
code.EnableWriting();
|
||||||
SCOPE_EXIT {
|
|
||||||
code.DisableWriting();
|
|
||||||
};
|
|
||||||
|
|
||||||
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
|
const boost::container::static_vector<HostLoc, 28> gpr_order = [this] {
|
||||||
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
|
boost::container::static_vector<HostLoc, 28> gprs{any_gpr};
|
||||||
if (conf.fastmem_pointer) {
|
if (conf.fastmem_pointer) {
|
||||||
|
|
@ -141,9 +137,10 @@ a64_branch:
|
||||||
(this->*a64_handlers[size_t(opcode) - std::size(opcode_handlers)])(ctx, &inst);
|
(this->*a64_handlers[size_t(opcode) - std::size(opcode_handlers)])(ctx, &inst);
|
||||||
finish_this_inst:
|
finish_this_inst:
|
||||||
ctx.reg_alloc.EndOfAllocScope();
|
ctx.reg_alloc.EndOfAllocScope();
|
||||||
if (conf.very_verbose_debugging_output) [[unlikely]] {
|
#ifndef NDEBUG
|
||||||
|
if (conf.very_verbose_debugging_output)
|
||||||
EmitVerboseDebuggingOutput(reg_alloc);
|
EmitVerboseDebuggingOutput(reg_alloc);
|
||||||
}
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
reg_alloc.AssertNoMoreUses();
|
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);
|
const auto range = boost::icl::discrete_interval<u64>::closed(descriptor.PC(), end_location.PC() - 1);
|
||||||
block_ranges.AddRange(range, descriptor);
|
block_ranges.AddRange(range, descriptor);
|
||||||
|
|
||||||
return RegisterBlock(descriptor, entrypoint, size);
|
auto bdesc = RegisterBlock(descriptor, entrypoint, size);
|
||||||
|
code.DisableWriting();
|
||||||
|
return bdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::ClearCache() {
|
void A64EmitX64::ClearCache() {
|
||||||
|
|
|
||||||
|
|
@ -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());
|
code.mov(dword[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_rsb_ptr], index_reg.cvt32());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
void EmitX64::EmitVerboseDebuggingOutput(RegAlloc& reg_alloc) {
|
void EmitX64::EmitVerboseDebuggingOutput(RegAlloc& reg_alloc) {
|
||||||
code.lea(rsp, ptr[rsp - sizeof(RegisterData)]);
|
code.lea(rsp, ptr[rsp - sizeof(RegisterData)]);
|
||||||
code.stmxcsr(dword[rsp + offsetof(RegisterData, mxcsr)]);
|
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.ldmxcsr(dword[rsp + offsetof(RegisterData, mxcsr)]);
|
||||||
code.add(rsp, sizeof(RegisterData));
|
code.add(rsp, sizeof(RegisterData));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void EmitX64::EmitPushRSB(EmitContext& ctx, IR::Inst* inst) {
|
void EmitX64::EmitPushRSB(EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,9 @@ public:
|
||||||
BlockDescriptor RegisterBlock(const IR::LocationDescriptor& location_descriptor, CodePtr entrypoint, size_t size);
|
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);
|
void PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, IR::LocationDescriptor target);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc);
|
void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc);
|
||||||
|
#endif
|
||||||
virtual void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
virtual void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||||
|
|
||||||
// Patching
|
// Patching
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ enum class HostLoc : std::uint8_t {
|
||||||
FirstSpill,
|
FirstSpill,
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr size_t NonSpillHostLocCount = static_cast<size_t>(HostLoc::FirstSpill);
|
constexpr size_t NonSpillHostLocCount = size_t(HostLoc::FirstSpill);
|
||||||
|
|
||||||
constexpr bool HostLocIsGPR(HostLoc reg) {
|
constexpr bool HostLocIsGPR(HostLoc reg) {
|
||||||
return reg >= HostLoc::RAX && reg <= HostLoc::R15;
|
return reg >= HostLoc::RAX && reg <= HostLoc::R15;
|
||||||
|
|
|
||||||
|
|
@ -56,26 +56,23 @@ static inline bool IsValuelessType(const IR::Type type) noexcept {
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostLocInfo::ReleaseOne() noexcept {
|
void HostLocInfo::ReleaseOne() noexcept {
|
||||||
is_being_used_count--;
|
ASSERT(is_being_used_count > 0);
|
||||||
|
--is_being_used_count;
|
||||||
is_scratch = false;
|
is_scratch = false;
|
||||||
|
if (current_references > 0) {
|
||||||
if (current_references == 0)
|
ASSERT(size_t(accumulated_uses) + 1 < (std::numeric_limits<decltype(accumulated_uses)>::max)());
|
||||||
return;
|
++accumulated_uses;
|
||||||
|
--current_references;
|
||||||
ASSERT(size_t(accumulated_uses) + 1 < (std::numeric_limits<uint16_t>::max)());
|
if (current_references == 0)
|
||||||
accumulated_uses++;
|
ReleaseAll();
|
||||||
current_references--;
|
}
|
||||||
|
|
||||||
if (current_references == 0)
|
|
||||||
ReleaseAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostLocInfo::ReleaseAll() noexcept {
|
void HostLocInfo::ReleaseAll() noexcept {
|
||||||
|
ASSERT(size_t(accumulated_uses) + current_references < (std::numeric_limits<decltype(accumulated_uses)>::max)());
|
||||||
accumulated_uses += current_references;
|
accumulated_uses += current_references;
|
||||||
current_references = 0;
|
current_references = 0;
|
||||||
|
|
||||||
is_set_last_use = false;
|
is_set_last_use = false;
|
||||||
|
|
||||||
if (total_uses == accumulated_uses) {
|
if (total_uses == accumulated_uses) {
|
||||||
values.clear();
|
values.clear();
|
||||||
accumulated_uses = 0;
|
accumulated_uses = 0;
|
||||||
|
|
@ -87,17 +84,19 @@ void HostLocInfo::ReleaseAll() noexcept {
|
||||||
is_scratch = false;
|
is_scratch = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostLocInfo::AddValue(IR::Inst* inst) noexcept {
|
void HostLocInfo::AddValue(HostLoc loc, IR::Inst* inst) noexcept {
|
||||||
if (is_set_last_use) {
|
if (is_set_last_use) {
|
||||||
is_set_last_use = false;
|
is_set_last_use = false;
|
||||||
values.clear();
|
values.clear();
|
||||||
}
|
}
|
||||||
values.push_back(inst);
|
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();
|
total_uses += inst->UseCount();
|
||||||
max_bit_width = std::max<uint8_t>(max_bit_width, std::countr_zero(GetBitWidth(inst->GetType())));
|
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 {
|
void HostLocInfo::EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_index) const noexcept {
|
||||||
using namespace Xbyak::util;
|
using namespace Xbyak::util;
|
||||||
for (auto const value : values) {
|
for (auto const value : values) {
|
||||||
|
|
@ -108,6 +107,7 @@ void HostLocInfo::EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_
|
||||||
code.CallFunction(PrintVerboseDebuggingOutputLine);
|
code.CallFunction(PrintVerboseDebuggingOutputLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool Argument::FitsInImmediateU32() const noexcept {
|
bool Argument::FitsInImmediateU32() const noexcept {
|
||||||
if (!IsImmediate())
|
if (!IsImmediate())
|
||||||
|
|
@ -197,12 +197,13 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(const IR::Inst* inst) noexcept
|
||||||
Argument{},
|
Argument{},
|
||||||
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);
|
const auto arg = inst->GetArg(i);
|
||||||
ret[i].value = arg;
|
ret[i].value = arg;
|
||||||
if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) {
|
if (!arg.IsImmediate() && !IsValuelessType(arg.GetType())) {
|
||||||
ASSERT(ValueLocation(arg.GetInst()) && "argument must already been defined");
|
auto const loc = ValueLocation(arg.GetInst());
|
||||||
LocInfo(*ValueLocation(arg.GetInst())).AddArgReference();
|
ASSERT(loc && "argument must already been defined");
|
||||||
|
LocInfo(*loc).AddArgReference();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -211,9 +212,9 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(const IR::Inst* inst) noexcept
|
||||||
void RegAlloc::RegisterPseudoOperation(const IR::Inst* inst) noexcept {
|
void RegAlloc::RegisterPseudoOperation(const IR::Inst* inst) noexcept {
|
||||||
ASSERT(IsValueLive(inst) || !inst->HasUses());
|
ASSERT(IsValueLive(inst) || !inst->HasUses());
|
||||||
for (size_t i = 0; i < inst->NumArgs(); i++) {
|
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 (!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).
|
// May not necessarily have a value (e.g. CMP variant of Sub32).
|
||||||
LocInfo(*loc).AddArgReference();
|
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));
|
return LoadImmediate(code, use_value, ScratchImpl(code, desired_locations));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* use_inst = use_value.GetInst();
|
auto const* use_inst = use_value.GetInst();
|
||||||
const HostLoc current_location = *ValueLocation(use_inst);
|
HostLoc const current_location = *ValueLocation(use_inst);
|
||||||
const size_t max_bit_width = LocInfo(current_location).GetMaxBitWidth();
|
|
||||||
|
|
||||||
const bool can_use_current_location = std::find(desired_locations.begin(), desired_locations.end(), current_location) != desired_locations.end();
|
const bool can_use_current_location = std::find(desired_locations.begin(), desired_locations.end(), current_location) != desired_locations.end();
|
||||||
if (can_use_current_location) {
|
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);
|
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)) {
|
if (max_bit_width > HostLocBitWidth(destination_location)) {
|
||||||
return UseScratchImpl(code, use_value, desired_locations);
|
return UseScratchImpl(code, use_value, desired_locations);
|
||||||
} else if (CanExchange(destination_location, current_location)) {
|
} 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();
|
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 (can_use_current_location && !LocInfo(current_location).IsLocked()) {
|
||||||
if (!LocInfo(current_location).IsLastUse()) {
|
if (LocInfo(current_location).IsLastUse()) {
|
||||||
MoveOutOfTheWay(code, current_location);
|
LocInfo(current_location).is_set_last_use = true;
|
||||||
} else {
|
} else {
|
||||||
LocInfo(current_location).SetLastUse();
|
MoveOutOfTheWay(code, current_location);
|
||||||
}
|
}
|
||||||
LocInfo(current_location).WriteLock();
|
LocInfo(current_location).WriteLock();
|
||||||
return current_location;
|
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 {
|
std::optional<HostLoc> RegAlloc::ValueLocation(const IR::Inst* value) const noexcept {
|
||||||
for (size_t i = 0; i < hostloc_info.size(); i++)
|
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 HostLoc(i);
|
||||||
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::DefineValueImpl(BlockOfCode& code, IR::Inst* def_inst, HostLoc host_loc) noexcept {
|
void RegAlloc::DefineValueImpl(BlockOfCode& code, IR::Inst* def_inst, HostLoc host_loc) noexcept {
|
||||||
ASSERT(!ValueLocation(def_inst) && "def_inst has already been defined");
|
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 {
|
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");
|
ASSERT(!ValueLocation(def_inst) && "def_inst has already been defined");
|
||||||
|
|
||||||
if (use_inst.IsImmediate()) {
|
if (use_inst.IsImmediate()) {
|
||||||
const HostLoc location = ScratchImpl(code, gpr_order);
|
const HostLoc location = ScratchImpl(code, gpr_order);
|
||||||
DefineValueImpl(code, def_inst, location);
|
DefineValueImpl(code, def_inst, location);
|
||||||
LoadImmediate(code, use_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 {
|
void RegAlloc::Move(BlockOfCode& code, HostLoc to, HostLoc from) noexcept {
|
||||||
|
|
|
||||||
|
|
@ -46,61 +46,55 @@ public:
|
||||||
return is_being_used_count == 0 && values.empty();
|
return is_being_used_count == 0 && values.empty();
|
||||||
}
|
}
|
||||||
inline bool IsLastUse() const {
|
inline bool IsLastUse() const {
|
||||||
return is_being_used_count == 0 && current_references == 1 && accumulated_uses + 1 == total_uses;
|
return is_being_used_count == 0 && current_references == 1 && size_t(accumulated_uses) + 1 == size_t(total_uses);
|
||||||
}
|
|
||||||
inline void SetLastUse() noexcept {
|
|
||||||
ASSERT(IsLastUse());
|
|
||||||
is_set_last_use = true;
|
|
||||||
}
|
}
|
||||||
inline void ReadLock() noexcept {
|
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);
|
ASSERT(!is_scratch);
|
||||||
is_being_used_count++;
|
is_being_used_count++;
|
||||||
}
|
}
|
||||||
inline void WriteLock() noexcept {
|
inline void WriteLock() noexcept {
|
||||||
ASSERT(size_t(is_being_used_count) + 1 < (std::numeric_limits<uint16_t>::max)());
|
|
||||||
ASSERT(is_being_used_count == 0);
|
ASSERT(is_being_used_count == 0);
|
||||||
is_being_used_count++;
|
is_being_used_count++;
|
||||||
is_scratch = true;
|
is_scratch = true;
|
||||||
}
|
}
|
||||||
inline void AddArgReference() noexcept {
|
inline void AddArgReference() noexcept {
|
||||||
ASSERT(size_t(current_references) + 1 < (std::numeric_limits<uint16_t>::max)());
|
ASSERT(size_t(current_references) + 1 < (std::numeric_limits<decltype(current_references)>::max)());
|
||||||
current_references++;
|
++current_references;
|
||||||
ASSERT(accumulated_uses + current_references <= total_uses);
|
ASSERT(size_t(accumulated_uses) + current_references <= size_t(total_uses));
|
||||||
}
|
}
|
||||||
void ReleaseOne() noexcept;
|
void ReleaseOne() noexcept;
|
||||||
void ReleaseAll() 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
|
/// Checks if the given instruction is in our values set
|
||||||
/// SAFETY: Const is casted away, irrelevant since this is only used for checking
|
/// SAFETY: Const is casted away, irrelevant since this is only used for checking
|
||||||
inline bool ContainsValue(const IR::Inst* inst) const noexcept {
|
[[nodiscard]] bool ContainsValue(const IR::Inst* inst) const noexcept {
|
||||||
//return values.contains(const_cast<IR::Inst*>(inst));
|
|
||||||
return std::find(values.begin(), values.end(), inst) != values.end();
|
return std::find(values.begin(), values.end(), inst) != values.end();
|
||||||
}
|
}
|
||||||
inline size_t GetMaxBitWidth() const noexcept {
|
#ifndef NDEBUG
|
||||||
return 1 << max_bit_width;
|
|
||||||
}
|
|
||||||
void AddValue(IR::Inst* inst) noexcept;
|
|
||||||
void EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_index) const noexcept;
|
void EmitVerboseDebuggingOutput(BlockOfCode& code, size_t host_loc_index) const noexcept;
|
||||||
|
#endif
|
||||||
private:
|
private:
|
||||||
//non trivial
|
|
||||||
boost::container::small_vector<IR::Inst*, 3> values; //24
|
boost::container::small_vector<IR::Inst*, 3> values; //24
|
||||||
// Block state
|
//non trivial
|
||||||
uint16_t total_uses = 0; //8
|
// Block state, the total amount of uses for this particular arg
|
||||||
//sometimes zeroed
|
uint16_t total_uses = 0; //2
|
||||||
uint16_t accumulated_uses = 0; //8
|
// Sometimes zeroed, accumulated (non referenced) uses
|
||||||
|
uint16_t accumulated_uses = 0; //2
|
||||||
//always zeroed
|
//always zeroed
|
||||||
// Current instruction state
|
// Current instruction state
|
||||||
uint16_t is_being_used_count = 0; //8
|
uint8_t current_references = 0; //1
|
||||||
uint16_t current_references = 0; //8
|
uint8_t is_being_used_count = 0; //1
|
||||||
// Value state
|
// Value state, count for LRU selection in registers
|
||||||
uint8_t lru_counter : 2 = 0; //1
|
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_scratch : 1 = false; //1
|
||||||
bool is_set_last_use : 1 = false; //1
|
bool is_set_last_use : 1 = false; //1
|
||||||
friend class RegAlloc;
|
friend class RegAlloc;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(HostLocInfo) == 64);
|
//static_assert(sizeof(HostLocInfo) == 64);
|
||||||
|
|
||||||
struct Argument {
|
struct Argument {
|
||||||
public:
|
public:
|
||||||
|
|
@ -213,10 +207,12 @@ public:
|
||||||
inline void AssertNoMoreUses() noexcept {
|
inline void AssertNoMoreUses() noexcept {
|
||||||
ASSERT(std::all_of(hostloc_info.begin(), hostloc_info.end(), [](const auto& i) noexcept { return i.IsEmpty(); }));
|
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 {
|
inline void EmitVerboseDebuggingOutput(BlockOfCode& code) noexcept {
|
||||||
for (size_t i = 0; i < hostloc_info.size(); i++)
|
for (size_t i = 0; i < hostloc_info.size(); i++)
|
||||||
hostloc_info[i].EmitVerboseDebuggingOutput(code, i);
|
hostloc_info[i].EmitVerboseDebuggingOutput(code, i);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
private:
|
private:
|
||||||
friend struct Argument;
|
friend struct Argument;
|
||||||
|
|
||||||
|
|
@ -238,11 +234,11 @@ private:
|
||||||
HostLoc FindFreeSpill(bool is_xmm) const noexcept;
|
HostLoc FindFreeSpill(bool is_xmm) const noexcept;
|
||||||
|
|
||||||
inline HostLocInfo& LocInfo(const HostLoc loc) 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)];
|
return hostloc_info[size_t(loc)];
|
||||||
}
|
}
|
||||||
inline const HostLocInfo& LocInfo(const HostLoc loc) const noexcept {
|
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)];
|
return hostloc_info[size_t(loc)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,6 +252,6 @@ private:
|
||||||
size_t reserved_stack_space = 0;
|
size_t reserved_stack_space = 0;
|
||||||
};
|
};
|
||||||
// Ensure a cache line (or less) is used, this is primordial
|
// 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
|
} // namespace Dynarmic::Backend::X64
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,6 @@ public:
|
||||||
void SetName(unsigned value) { name = value; }
|
void SetName(unsigned value) { name = value; }
|
||||||
unsigned GetName() const { return name; }
|
unsigned GetName() const { return name; }
|
||||||
|
|
||||||
private:
|
|
||||||
void Use(const Value& value);
|
void Use(const Value& value);
|
||||||
void UndoUse(const Value& value);
|
void UndoUse(const Value& value);
|
||||||
|
|
||||||
|
|
@ -87,6 +86,6 @@ private:
|
||||||
unsigned name = 0; //4 (4)
|
unsigned name = 0; //4 (4)
|
||||||
alignas(64) std::array<Value, max_arg_count> args; //16 * 4 = 64 (1 cache line)
|
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
|
} // namespace Dynarmic::IR
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue