diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2510458812..c06ed56255 100644 --- a/src/CMakeLists.txt +++ b/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() diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index 4734bd468d..e05757b6ca 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/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) diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp index 986a6659b1..94f73d65c1 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/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 gpr_order = [this] { boost::container::static_vector 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(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::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() { diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp index 044249a26b..39bbf6880a 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/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 gpr_order = [this] { boost::container::static_vector 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::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() { diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp index 81cdc66780..05394d237d 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp +++ b/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); diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h index 1531d14968..b3614f224e 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h +++ b/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 diff --git a/src/dynarmic/src/dynarmic/backend/x64/hostloc.h b/src/dynarmic/src/dynarmic/backend/x64/hostloc.h index 65eeb11b55..1158e48253 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/hostloc.h +++ b/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(HostLoc::FirstSpill); +constexpr size_t NonSpillHostLocCount = size_t(HostLoc::FirstSpill); constexpr bool HostLocIsGPR(HostLoc reg) { return reg >= HostLoc::RAX && reg <= HostLoc::R15; diff --git a/src/dynarmic/src/dynarmic/backend/x64/reg_alloc.cpp b/src/dynarmic/src/dynarmic/backend/x64/reg_alloc.cpp index 2f13298480..115cb22ee9 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/reg_alloc.cpp +++ b/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::max)()); - accumulated_uses++; - current_references--; - - if (current_references == 0) - ReleaseAll(); + if (current_references > 0) { + ASSERT(size_t(accumulated_uses) + 1 < (std::numeric_limits::max)()); + ++accumulated_uses; + --current_references; + if (current_references == 0) + ReleaseAll(); + } } void HostLocInfo::ReleaseAll() noexcept { + ASSERT(size_t(accumulated_uses) + current_references < (std::numeric_limits::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::max)()); + + ASSERT(size_t(total_uses) + inst->UseCount() < (std::numeric_limits::max)()); total_uses += inst->UseCount(); max_bit_width = std::max(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 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 { diff --git a/src/dynarmic/src/dynarmic/backend/x64/reg_alloc.h b/src/dynarmic/src/dynarmic/backend/x64/reg_alloc.h index e1cccadd6b..a4467e32c7 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/reg_alloc.h +++ b/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::max)()); + ASSERT(size_t(is_being_used_count) + 1 < (std::numeric_limits::max)()); ASSERT(!is_scratch); is_being_used_count++; } inline void WriteLock() noexcept { - ASSERT(size_t(is_being_used_count) + 1 < (std::numeric_limits::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::max)()); - current_references++; - ASSERT(accumulated_uses + current_references <= total_uses); + ASSERT(size_t(current_references) + 1 < (std::numeric_limits::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(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 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) == 40); +static_assert(sizeof(boost::container::static_vector) < 64); } // namespace Dynarmic::Backend::X64 diff --git a/src/dynarmic/src/dynarmic/ir/microinstruction.h b/src/dynarmic/src/dynarmic/ir/microinstruction.h index 7c7031b7c0..843b4cdf18 100644 --- a/src/dynarmic/src/dynarmic/ir/microinstruction.h +++ b/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 args; //16 * 4 = 64 (1 cache line) }; -static_assert(sizeof(Inst) == 128); +//static_assert(sizeof(Inst) == 128); } // namespace Dynarmic::IR