From 0c84194f7fd36d26e309d3fbad0aaaa5a1e717a2 Mon Sep 17 00:00:00 2001 From: lizzie Date: Fri, 2 Jan 2026 20:43:43 +0000 Subject: [PATCH] reduce CPU usage on Spooky Mansion by making blocks be page-sized and page-aligned & remove unused set<> overhead in arm64 --- .../src/dynarmic/backend/arm64/emit_arm64.cpp | 2 +- .../dynarmic/backend/arm64/emit_arm64_a32.cpp | 6 +- .../src/dynarmic/backend/arm64/reg_alloc.cpp | 12 ++-- .../src/dynarmic/backend/arm64/reg_alloc.h | 17 ++--- .../backend/block_range_information.cpp | 20 +++--- .../backend/block_range_information.h | 10 ++- .../dynarmic/backend/riscv64/emit_riscv64.cpp | 2 +- .../src/dynarmic/backend/x64/a32_emit_x64.cpp | 8 +-- .../src/dynarmic/backend/x64/a64_emit_x64.cpp | 2 +- .../src/dynarmic/backend/x64/emit_x64.cpp | 5 -- .../src/dynarmic/backend/x64/emit_x64.h | 4 -- .../dynarmic/backend/x64/emit_x64_vector.cpp | 9 +-- .../A32/translate/conditional_state.cpp | 4 +- .../frontend/A64/translate/impl/system.cpp | 2 +- src/dynarmic/src/dynarmic/ir/basic_block.cpp | 64 ++++++------------- src/dynarmic/src/dynarmic/ir/basic_block.h | 38 ++--------- src/dynarmic/src/dynarmic/ir/dense_list.h | 61 ------------------ src/dynarmic/src/dynarmic/ir/ir_emitter.h | 11 ++-- src/dynarmic/src/dynarmic/ir/opt_passes.cpp | 53 +++++++-------- src/dynarmic/tests/A32/fuzz_arm.cpp | 2 +- src/dynarmic/tests/A64/fuzz_with_unicorn.cpp | 2 +- src/dynarmic/tests/decoder_tests.cpp | 4 +- src/dynarmic/tests/test_generator.cpp | 2 +- .../frontend/ir/basic_block.cpp | 2 +- 24 files changed, 96 insertions(+), 246 deletions(-) delete mode 100644 src/dynarmic/src/dynarmic/ir/dense_list.h diff --git a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.cpp b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.cpp index 0a65cd5aa8..1ad6614682 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64.cpp @@ -218,7 +218,7 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E code.l(pass); } - for (auto iter = block.begin(); iter != block.end(); ++iter) { + for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) { IR::Inst* inst = &*iter; switch (inst->GetOpcode()) { diff --git a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp index d3ba664ce4..0fbfddf503 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp @@ -582,11 +582,9 @@ void EmitIR(oaknut::CodeGenerator& code, EmitContext& template<> void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst*) { - for (auto& inst : ctx.block) { - if (inst.GetOpcode() == IR::Opcode::A32BXWritePC) { + for (auto& inst : ctx.block.instructions) + if (inst.GetOpcode() == IR::Opcode::A32BXWritePC) return; - } - } EmitSetUpperLocationDescriptor(code, ctx, ctx.block.EndLocation(), ctx.block.Location()); } diff --git a/src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.cpp b/src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.cpp index 5e0cb79de2..3b55fa2eff 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.cpp @@ -144,7 +144,7 @@ RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) { } bool RegAlloc::WasValueDefined(IR::Inst* inst) const { - return defined_insts.count(inst) > 0; + return defined_insts_count > 0; } void RegAlloc::PrepareForCall(std::optional arg0, std::optional arg1, std::optional arg2, std::optional arg3) { @@ -192,7 +192,7 @@ void RegAlloc::PrepareForCall(std::optional arg0, } void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) { - defined_insts.insert(inst); + ++defined_insts_count; ASSERT(!ValueLocation(inst)); @@ -207,7 +207,7 @@ void RegAlloc::DefineAsExisting(IR::Inst* inst, Argument& arg) { } void RegAlloc::DefineAsRegister(IR::Inst* inst, oaknut::Reg reg) { - defined_insts.insert(inst); + ++defined_insts_count; ASSERT(!ValueLocation(inst)); auto& info = reg.is_vector() ? fprs[reg.index()] : gprs[reg.index()]; @@ -374,7 +374,7 @@ int RegAlloc::RealizeReadImpl(const IR::Value& value) { template int RegAlloc::RealizeWriteImpl(const IR::Inst* value) { - defined_insts.insert(value); + ++defined_insts_count; ASSERT(!ValueLocation(value)); @@ -399,7 +399,7 @@ int RegAlloc::RealizeWriteImpl(const IR::Inst* value) { template int RegAlloc::RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst* write_value) { - defined_insts.insert(write_value); + ++defined_insts_count; // TODO: Move elimination @@ -463,7 +463,7 @@ void RegAlloc::SpillFpr(int index) { } void RegAlloc::ReadWriteFlags(Argument& read, IR::Inst* write) { - defined_insts.insert(write); + ++defined_insts_count; const auto current_location = ValueLocation(read.value.GetInst()); ASSERT(current_location); diff --git a/src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.h b/src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.h index 099be4ec10..8327406a99 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.h +++ b/src/dynarmic/src/dynarmic/backend/arm64/reg_alloc.h @@ -302,17 +302,12 @@ public: private: friend struct Argument; - template - friend struct RAReg; + template friend struct RAReg; - template - int GenerateImmediate(const IR::Value& value); - template - int RealizeReadImpl(const IR::Value& value); - template - int RealizeWriteImpl(const IR::Inst* value); - template - int RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst* write_value); + template int GenerateImmediate(const IR::Value& value); + template int RealizeReadImpl(const IR::Value& value); + template int RealizeWriteImpl(const IR::Inst* value); + template int RealizeReadWriteImpl(const IR::Value& read_value, const IR::Inst* write_value); int AllocateRegister(const std::array& regs, const std::vector& order) const; void SpillGpr(int index); @@ -338,7 +333,7 @@ private: mutable std::mt19937 rand_gen; - ankerl::unordered_dense::set defined_insts; + size_t defined_insts_count; }; template diff --git a/src/dynarmic/src/dynarmic/backend/block_range_information.cpp b/src/dynarmic/src/dynarmic/backend/block_range_information.cpp index 0e5904ae1c..436263b9ca 100644 --- a/src/dynarmic/src/dynarmic/backend/block_range_information.cpp +++ b/src/dynarmic/src/dynarmic/backend/block_range_information.cpp @@ -15,26 +15,24 @@ namespace Dynarmic::Backend { -template -void BlockRangeInformation::AddRange(boost::icl::discrete_interval range, IR::LocationDescriptor location) { - block_ranges.add(std::make_pair(range, std::set{location})); +template +void BlockRangeInformation

::AddRange(boost::icl::discrete_interval

range, IR::LocationDescriptor location) { + block_ranges.add(std::make_pair(range, ankerl::unordered_dense::set{location})); } -template -void BlockRangeInformation::ClearCache() { +template +void BlockRangeInformation

::ClearCache() { block_ranges.clear(); } -template -ankerl::unordered_dense::set BlockRangeInformation::InvalidateRanges(const boost::icl::interval_set& ranges) { +template +ankerl::unordered_dense::set BlockRangeInformation

::InvalidateRanges(const boost::icl::interval_set

& ranges) { ankerl::unordered_dense::set erase_locations; for (auto invalidate_interval : ranges) { auto pair = block_ranges.equal_range(invalidate_interval); - for (auto it = pair.first; it != pair.second; ++it) { - for (const auto& descriptor : it->second) { + for (auto it = pair.first; it != pair.second; ++it) + for (const auto& descriptor : it->second) erase_locations.insert(descriptor); - } - } } // TODO: EFFICIENCY: Remove ranges that are to be erased. return erase_locations; diff --git a/src/dynarmic/src/dynarmic/backend/block_range_information.h b/src/dynarmic/src/dynarmic/backend/block_range_information.h index e39bf992e4..273f76da6f 100644 --- a/src/dynarmic/src/dynarmic/backend/block_range_information.h +++ b/src/dynarmic/src/dynarmic/backend/block_range_information.h @@ -15,15 +15,13 @@ namespace Dynarmic::Backend { -template +template class BlockRangeInformation { public: - void AddRange(boost::icl::discrete_interval range, IR::LocationDescriptor location); + void AddRange(boost::icl::discrete_interval

range, IR::LocationDescriptor location); void ClearCache(); - ankerl::unordered_dense::set InvalidateRanges(const boost::icl::interval_set& ranges); - -private: - boost::icl::interval_map> block_ranges; + ankerl::unordered_dense::set InvalidateRanges(const boost::icl::interval_set

& ranges); + boost::icl::interval_map> block_ranges; }; } // namespace Dynarmic::Backend diff --git a/src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64.cpp b/src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64.cpp index bc94fda91a..cd824a21f8 100644 --- a/src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64.cpp +++ b/src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64.cpp @@ -122,7 +122,7 @@ EmittedBlockInfo EmitRV64(biscuit::Assembler& as, IR::Block block, const EmitCon ebi.entry_point = reinterpret_cast(as.GetCursorPointer()); - for (auto iter = block.begin(); iter != block.end(); ++iter) { + for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) { IR::Inst* inst = &*iter; switch (inst->GetOpcode()) { 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..70cdefb086 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -127,7 +127,7 @@ 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]] { + for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) [[likely]] { auto* inst = &*iter; // Call the relevant Emit* member function. switch (inst->GetOpcode()) { @@ -734,11 +734,9 @@ void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) { } void A32EmitX64::EmitA32UpdateUpperLocationDescriptor(A32EmitContext& ctx, IR::Inst*) { - for (auto& inst : ctx.block) { - if (inst.GetOpcode() == IR::Opcode::A32BXWritePC) { + for (auto& inst : ctx.block.instructions) + if (inst.GetOpcode() == IR::Opcode::A32BXWritePC) return; - } - } EmitSetUpperLocationDescriptor(ctx.EndLocation(), ctx.Location()); } 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..2f7f564435 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -120,7 +120,7 @@ A64EmitX64::BlockDescriptor A64EmitX64::Emit(IR::Block& block) noexcept { #undef A64OPC }; - for (auto& inst : block) { + for (auto& inst : block.instructions) { auto const opcode = inst.GetOpcode(); // Call the relevant Emit* member function. switch (opcode) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp index 81cdc66780..f0af28b4df 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp @@ -38,11 +38,6 @@ EmitContext::EmitContext(RegAlloc& reg_alloc, IR::Block& block) EmitContext::~EmitContext() = default; -void EmitContext::EraseInstruction(IR::Inst* inst) { - block.Instructions().erase(inst); - inst->ClearArgs(); -} - EmitX64::EmitX64(BlockOfCode& code) : code(code) { exception_handler.Register(code); diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h index 1531d14968..48dcf78441 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h @@ -56,11 +56,7 @@ using HalfVectorArray = std::array static void EmitVectorOperation(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst, Function fn) { diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/conditional_state.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/conditional_state.cpp index 66ff795ef0..dbf5a633ce 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/conditional_state.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/conditional_state.cpp @@ -26,7 +26,7 @@ bool CondCanContinue(const ConditionalState cond_state, const A32::IREmitter& ir return true; // TODO: This is more conservative than necessary. - return std::all_of(ir.block.begin(), ir.block.end(), [](const IR::Inst& inst) { + return std::all_of(ir.block.instructions.begin(), ir.block.instructions.end(), [](const IR::Inst& inst) { return !WritesToCPSR(inst.GetOpcode()); }); } @@ -66,7 +66,7 @@ bool IsConditionPassed(TranslatorVisitor& v, IR::Cond cond) { // non-AL cond - if (!v.ir.block.empty()) { + if (!v.ir.block.instructions.empty()) { // We've already emitted instructions. Quit for now, we'll make a new block here later. v.cond_state = ConditionalState::Break; v.ir.SetTerm(IR::Term::LinkBlockFast{v.ir.current_location}); diff --git a/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/system.cpp b/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/system.cpp index 9c35a8f364..89b7d34cd6 100644 --- a/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/system.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/system.cpp @@ -126,7 +126,7 @@ bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3 return true; case SystemRegisterEncoding::CNTPCT_EL0: // HACK: Ensure that this is the first instruction in the block it's emitted in, so the cycle count is most up-to-date. - if (!ir.block.empty() && !options.wall_clock_cntpct) { + if (!ir.block.instructions.empty() && !options.wall_clock_cntpct) { ir.block.CycleCount()--; ir.SetTerm(IR::Term::LinkBlock{*ir.current_location}); return false; diff --git a/src/dynarmic/src/dynarmic/ir/basic_block.cpp b/src/dynarmic/src/dynarmic/ir/basic_block.cpp index ae9e0b103b..48cb9c02fe 100644 --- a/src/dynarmic/src/dynarmic/ir/basic_block.cpp +++ b/src/dynarmic/src/dynarmic/ir/basic_block.cpp @@ -100,57 +100,39 @@ std::string DumpBlock(const IR::Block& block) noexcept { std::string ret = fmt::format("Block: location={}-{}\n", block.Location(), block.EndLocation()) + fmt::format("cycles={}", block.CycleCount()) + fmt::format(", entry_cond={}", A64::CondToString(block.GetCondition())); - if (block.GetCondition() != Cond::AL) { + if (block.GetCondition() != Cond::AL) ret += fmt::format(", cond_fail={}", block.ConditionFailedLocation()); - } ret += '\n'; const auto arg_to_string = [](const IR::Value& arg) -> std::string { if (arg.IsEmpty()) { return ""; } else if (!arg.IsImmediate()) { - if (const unsigned name = arg.GetInst()->GetName()) { + if (auto const name = arg.GetInst()->GetName()) return fmt::format("%{}", name); - } - return fmt::format("%", reinterpret_cast(arg.GetInst())); + return fmt::format("%", u64(arg.GetInst())); } switch (arg.GetType()) { - case Type::U1: - return fmt::format("#{}", arg.GetU1() ? '1' : '0'); - case Type::U8: - return fmt::format("#{}", arg.GetU8()); - case Type::U16: - return fmt::format("#{:#x}", arg.GetU16()); - case Type::U32: - return fmt::format("#{:#x}", arg.GetU32()); - case Type::U64: - return fmt::format("#{:#x}", arg.GetU64()); - case Type::U128: - return fmt::format("#"); - case Type::A32Reg: - return A32::RegToString(arg.GetA32RegRef()); - case Type::A32ExtReg: - return A32::ExtRegToString(arg.GetA32ExtRegRef()); - case Type::A64Reg: - return A64::RegToString(arg.GetA64RegRef()); - case Type::A64Vec: - return A64::VecToString(arg.GetA64VecRef()); - case Type::CoprocInfo: - return fmt::format("#"); - case Type::NZCVFlags: - return fmt::format("#"); - case Type::Cond: - return fmt::format("#", A32::CondToString(arg.GetCond())); - case Type::Table: - return fmt::format("#"); - case Type::AccType: - return fmt::format("#", u32(arg.GetAccType())); - default: - return fmt::format("", arg.GetType()); + case Type::U1: return fmt::format("#{}", arg.GetU1() ? '1' : '0'); + case Type::U8: return fmt::format("#{}", arg.GetU8()); + case Type::U16: return fmt::format("#{:#x}", arg.GetU16()); + case Type::U32: return fmt::format("#{:#x}", arg.GetU32()); + case Type::U64: return fmt::format("#{:#x}", arg.GetU64()); + case Type::U128: return fmt::format("#"); + case Type::A32Reg: return A32::RegToString(arg.GetA32RegRef()); + case Type::A32ExtReg: return A32::ExtRegToString(arg.GetA32ExtRegRef()); + case Type::A64Reg: return A64::RegToString(arg.GetA64RegRef()); + case Type::A64Vec: return A64::VecToString(arg.GetA64VecRef()); + case Type::CoprocInfo: return fmt::format("#"); + case Type::NZCVFlags: return fmt::format("#"); + case Type::Cond: return fmt::format("#", A32::CondToString(arg.GetCond())); + case Type::Table: return fmt::format("#
"); + case Type::AccType: return fmt::format("#", u32(arg.GetAccType())); + default: return fmt::format("", arg.GetType()); } }; - for (const auto& inst : block) { + for (const auto& inst : block.instructions) { const Opcode op = inst.GetOpcode(); ret += fmt::format("[{:016x}] ", reinterpret_cast(&inst)); @@ -180,13 +162,9 @@ std::string DumpBlock(const IR::Block& block) noexcept { } } - ret += fmt::format(" (uses: {})", inst.UseCount()); - - ret += '\n'; + ret += fmt::format(" (uses: {})", inst.UseCount()) + '\n'; } - ret += "terminal = " + TerminalToString(block.GetTerminal()) + '\n'; - return ret; } diff --git a/src/dynarmic/src/dynarmic/ir/basic_block.h b/src/dynarmic/src/dynarmic/ir/basic_block.h index dd978da68d..87e83ea43c 100644 --- a/src/dynarmic/src/dynarmic/ir/basic_block.h +++ b/src/dynarmic/src/dynarmic/ir/basic_block.h @@ -23,7 +23,6 @@ #include "dynarmic/ir/microinstruction.h" #include "dynarmic/ir/terminal.h" #include "dynarmic/ir/value.h" -#include "dynarmic/ir/dense_list.h" namespace Dynarmic::IR { @@ -34,7 +33,7 @@ enum class Opcode; /// Note that this is a linear IR and not a pure tree-based IR: i.e.: there is an ordering to /// the microinstructions. This only matters before chaining is done in order to correctly /// order memory accesses. -class Block final { +alignas(4096) class Block final { public: //using instruction_list_type = dense_list; using instruction_list_type = mcl::intrusive_list; @@ -51,37 +50,12 @@ public: Block(Block&&) = default; Block& operator=(Block&&) = default; - bool empty() const { return instructions.empty(); } - size_type size() const { return instructions.size(); } - - Inst& front() { return instructions.front(); } - const Inst& front() const { return instructions.front(); } - - Inst& back() { return instructions.back(); } - const Inst& back() const { return instructions.back(); } - - iterator begin() { return instructions.begin(); } - const_iterator begin() const { return instructions.begin(); } - iterator end() { return instructions.end(); } - const_iterator end() const { return instructions.end(); } - - reverse_iterator rbegin() { return instructions.rbegin(); } - const_reverse_iterator rbegin() const { return instructions.rbegin(); } - reverse_iterator rend() { return instructions.rend(); } - const_reverse_iterator rend() const { return instructions.rend(); } - - const_iterator cbegin() const { return instructions.cbegin(); } - const_iterator cend() const { return instructions.cend(); } - - const_reverse_iterator crbegin() const { return instructions.crbegin(); } - const_reverse_iterator crend() const { return instructions.crend(); } - /// Appends a new instruction to the end of this basic block, /// handling any allocations necessary to do so. /// @param op Opcode representing the instruction to add. /// @param args A sequence of Value instances used as arguments for the instruction. - inline void AppendNewInst(const Opcode opcode, const std::initializer_list args) noexcept { - PrependNewInst(end(), opcode, args); + inline iterator AppendNewInst(const Opcode opcode, const std::initializer_list args) noexcept { + return PrependNewInst(instructions.end(), opcode, args); } iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list args) noexcept; @@ -165,9 +139,9 @@ public: inline const size_t& CycleCount() const noexcept { return cycle_count; } -private: + /// "Hot cache" for small blocks so we don't call global allocator - boost::container::static_vector inlined_inst; + boost::container::static_vector inlined_inst; /// List of instructions in this block. instruction_list_type instructions; /// "Long/far" memory pool @@ -187,7 +161,7 @@ private: /// Number of cycles this block takes to execute. size_t cycle_count = 0; }; -static_assert(sizeof(Block) == 2048); +static_assert(sizeof(Block) == 4096); /// Returns a string representation of the contents of block. Intended for debugging. std::string DumpBlock(const IR::Block& block) noexcept; diff --git a/src/dynarmic/src/dynarmic/ir/dense_list.h b/src/dynarmic/src/dynarmic/ir/dense_list.h deleted file mode 100644 index 895992170d..0000000000 --- a/src/dynarmic/src/dynarmic/ir/dense_list.h +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include -#include - -namespace Dynarmic { - template struct dense_list { - using difference_type = std::ptrdiff_t; - using size_type = std::size_t; - using value_type = T; - using pointer = value_type*; - using const_pointer = const value_type*; - using reference = value_type&; - using const_reference = const value_type&; - using iterator = typename std::deque::iterator; - using const_iterator = typename std::deque::const_iterator; - using reverse_iterator = typename std::reverse_iterator; - using const_reverse_iterator = typename std::reverse_iterator; - - inline bool empty() const noexcept { return list.empty(); } - inline size_type size() const noexcept { return list.size(); } - - inline value_type& front() noexcept { return list.front(); } - inline const value_type& front() const noexcept { return list.front(); } - - inline value_type& back() noexcept { return list.back(); } - inline const value_type& back() const noexcept { return list.back(); } - - inline iterator begin() noexcept { return list.begin(); } - inline const_iterator begin() const noexcept { return list.begin(); } - inline iterator end() noexcept { return list.end(); } - inline const_iterator end() const noexcept { return list.end(); } - - inline reverse_iterator rbegin() noexcept { return list.rbegin(); } - inline const_reverse_iterator rbegin() const noexcept { return list.rbegin(); } - inline reverse_iterator rend() noexcept { return list.rend(); } - inline const_reverse_iterator rend() const noexcept { return list.rend(); } - - inline const_iterator cbegin() const noexcept { return list.cbegin(); } - inline const_iterator cend() const noexcept { return list.cend(); } - - inline const_reverse_iterator crbegin() const noexcept { return list.crbegin(); } - inline const_reverse_iterator crend() const noexcept { return list.crend(); } - - inline iterator insert_before(iterator it, value_type& value) noexcept { - if (it == list.begin()) { - list.push_front(value); - return list.begin(); - } - auto const index = std::distance(list.begin(), it - 1); - list.insert(it - 1, value); - return list.begin() + index; - } - - std::deque list; - }; -} diff --git a/src/dynarmic/src/dynarmic/ir/ir_emitter.h b/src/dynarmic/src/dynarmic/ir/ir_emitter.h index 3d65a7b797..f6b3f7e889 100644 --- a/src/dynarmic/src/dynarmic/ir/ir_emitter.h +++ b/src/dynarmic/src/dynarmic/ir/ir_emitter.h @@ -65,15 +65,12 @@ enum class MemOp { PREFETCH, }; -/** - * Convenience class to construct a basic block of the intermediate representation. - * `block` is the resulting block. - * The user of this class updates `current_location` as appropriate. - */ +/// @brief Convenience class to construct a basic block of the intermediate representation. +/// `block` is the resulting block. +/// The user of this class updates `current_location` as appropriate. class IREmitter { public: - explicit IREmitter(Block& block) - : block(block), insertion_point(block.end()) {} + explicit IREmitter(Block& block) : block(block), insertion_point(block.instructions.end()) {} Block& block; diff --git a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp index 6cb0abc4ef..70e2ba2dc2 100644 --- a/src/dynarmic/src/dynarmic/ir/opt_passes.cpp +++ b/src/dynarmic/src/dynarmic/ir/opt_passes.cpp @@ -33,7 +33,7 @@ namespace Dynarmic::Optimization { static void ConstantMemoryReads(IR::Block& block, A32::UserCallbacks* cb) { - for (auto& inst : block) { + for (auto& inst : block.instructions) { switch (inst.GetOpcode()) { case IR::Opcode::A32ReadMemory8: case IR::Opcode::A64ReadMemory8: { @@ -131,7 +131,7 @@ static void FlagsPass(IR::Block& block) { A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}}; - for (auto inst = block.rbegin(); inst != block.rend(); ++inst) { + for (auto inst = block.instructions.rbegin(); inst != block.instructions.rend(); ++inst) { auto const opcode = inst->GetOpcode(); switch (opcode) { case IR::Opcode::A32GetCFlag: { @@ -318,7 +318,7 @@ static void RegisterPass(IR::Block& block) { // Location and version don't matter here. A32::IREmitter ir{block, A32::LocationDescriptor{block.Location()}, {}}; - for (auto inst = block.begin(); inst != block.end(); ++inst) { + for (auto inst = block.instructions.begin(); inst != block.instructions.end(); ++inst) { auto const opcode = inst->GetOpcode(); switch (opcode) { case IR::Opcode::A32GetRegister: { @@ -448,7 +448,7 @@ static void A64CallbackConfigPass(IR::Block& block, const A64::UserConfig& conf) return; } - for (auto& inst : block) { + for (auto& inst : block.instructions) { if (inst.GetOpcode() != IR::Opcode::A64DataCacheOperationRaised) { continue; } @@ -541,7 +541,7 @@ static void A64GetSetElimination(IR::Block& block) { do_nothing(); }; - for (auto inst = block.begin(); inst != block.end(); ++inst) { + for (auto inst = block.instructions.begin(); inst != block.instructions.end(); ++inst) { auto const opcode = inst->GetOpcode(); switch (opcode) { case IR::Opcode::A64GetW: { @@ -1041,7 +1041,7 @@ static void FoldZeroExtendXToLong(IR::Inst& inst) { } static void ConstantPropagation(IR::Block& block) { - for (auto& inst : block) { + for (auto& inst : block.instructions) { auto const opcode = inst.GetOpcode(); switch (opcode) { case Op::LeastSignificantWord: @@ -1221,43 +1221,34 @@ static void ConstantPropagation(IR::Block& block) { static void DeadCodeElimination(IR::Block& block) { // We iterate over the instructions in reverse order. // This is because removing an instruction reduces the number of uses for earlier instructions. - for (auto it = block.rbegin(); it != block.rend(); ++it) + for (auto it = block.instructions.rbegin(); it != block.instructions.rend(); ++it) if (!it->HasUses() && !MayHaveSideEffects(it->GetOpcode())) it->Invalidate(); } static void IdentityRemovalPass(IR::Block& block) { boost::container::small_vector to_invalidate; - - auto iter = block.begin(); - while (iter != block.end()) { - IR::Inst& inst = *iter; - - const size_t num_args = inst.NumArgs(); - for (size_t i = 0; i < num_args; i++) { - while (true) { - IR::Value arg = inst.GetArg(i); - if (!arg.IsIdentity()) - break; - inst.SetArg(i, arg.GetInst()->GetArg(0)); + for (auto it = block.instructions.begin(); it != block.instructions.end();) { + auto const num_args = it->NumArgs(); + for (size_t i = 0; i < num_args; ++i) + if (IR::Value arg = it->GetArg(i); arg.IsIdentity()) { + do arg = arg.GetInst()->GetArg(0); while (arg.IsIdentity()); + it->SetArg(i, arg); } - } - - if (inst.GetOpcode() == IR::Opcode::Identity || inst.GetOpcode() == IR::Opcode::Void) { - iter = block.Instructions().erase(inst); - to_invalidate.push_back(&inst); + if (it->GetOpcode() == IR::Opcode::Identity || it->GetOpcode() == IR::Opcode::Void) { + to_invalidate.push_back(&*it); + it = block.Instructions().erase(it); } else { - ++iter; + ++it; } } - for (IR::Inst* inst : to_invalidate) { + for (IR::Inst* const inst : to_invalidate) inst->Invalidate(); - } } static void NamingPass(IR::Block& block) { u32 name = 1; - for (auto& inst : block) + for (auto& inst : block.instructions) inst.SetName(name++); } @@ -1406,7 +1397,7 @@ static void PolyfillPass(IR::Block& block, const PolyfillOptions& polyfill) { IR::IREmitter ir{block}; - for (auto& inst : block) { + for (auto& inst : block.instructions) { ir.SetInsertionPointBefore(&inst); switch (inst.GetOpcode()) { @@ -1462,7 +1453,7 @@ static void PolyfillPass(IR::Block& block, const PolyfillOptions& polyfill) { } static void VerificationPass(const IR::Block& block) { - for (auto const& inst : block) { + for (auto const& inst : block.instructions) { for (size_t i = 0; i < inst.NumArgs(); i++) { const IR::Type t1 = inst.GetArg(i).GetType(); const IR::Type t2 = IR::GetArgTypeOf(inst.GetOpcode(), i); @@ -1470,7 +1461,7 @@ static void VerificationPass(const IR::Block& block) { } } ankerl::unordered_dense::map actual_uses; - for (auto const& inst : block) { + for (auto const& inst : block.instructions) { for (size_t i = 0; i < inst.NumArgs(); i++) if (IR::Value const arg = inst.GetArg(i); !arg.IsImmediate()) actual_uses[arg.GetInst()]++; diff --git a/src/dynarmic/tests/A32/fuzz_arm.cpp b/src/dynarmic/tests/A32/fuzz_arm.cpp index 4edc9d5bcc..c80bc5d1cd 100644 --- a/src/dynarmic/tests/A32/fuzz_arm.cpp +++ b/src/dynarmic/tests/A32/fuzz_arm.cpp @@ -93,7 +93,7 @@ bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A return false; } - for (const auto& ir_inst : block) { + for (const auto& ir_inst : block.instructions) { switch (ir_inst.GetOpcode()) { case IR::Opcode::A32ExceptionRaised: case IR::Opcode::A32CallSupervisor: diff --git a/src/dynarmic/tests/A64/fuzz_with_unicorn.cpp b/src/dynarmic/tests/A64/fuzz_with_unicorn.cpp index 2e1e56a291..99f0614b2e 100644 --- a/src/dynarmic/tests/A64/fuzz_with_unicorn.cpp +++ b/src/dynarmic/tests/A64/fuzz_with_unicorn.cpp @@ -45,7 +45,7 @@ static bool ShouldTestInst(u32 instruction, u64 pc, bool is_last_inst) { return false; if (auto terminal = block.GetTerminal(); boost::get(&terminal)) return false; - for (const auto& ir_inst : block) { + for (const auto& ir_inst : block.instructions) { switch (ir_inst.GetOpcode()) { case IR::Opcode::A64ExceptionRaised: case IR::Opcode::A64CallSupervisor: diff --git a/src/dynarmic/tests/decoder_tests.cpp b/src/dynarmic/tests/decoder_tests.cpp index 4ad9d90833..c9858e4ab5 100644 --- a/src/dynarmic/tests/decoder_tests.cpp +++ b/src/dynarmic/tests/decoder_tests.cpp @@ -36,9 +36,9 @@ TEST_CASE("ASIMD Decoder: Ensure table order correctness", "[decode][a32][.]") { const auto is_decode_error = [&get_ir](const A32::ASIMDMatcher& matcher, u32 instruction) { const auto block = get_ir(matcher, instruction); - return std::find_if(block.cbegin(), block.cend(), [](auto const& e) { + return std::find_if(block.instructions.cbegin(), block.instructions.cend(), [](auto const& e) { return e.GetOpcode() == IR::Opcode::A32ExceptionRaised && A32::Exception(e.GetArg(1).GetU64()) == A32::Exception::DecodeError; - }) != block.cend(); + }) != block.instructions.cend(); }; for (auto iter = table.cbegin(); iter != table.cend(); ++iter) { diff --git a/src/dynarmic/tests/test_generator.cpp b/src/dynarmic/tests/test_generator.cpp index 7330e143b6..a44d76a78c 100644 --- a/src/dynarmic/tests/test_generator.cpp +++ b/src/dynarmic/tests/test_generator.cpp @@ -54,7 +54,7 @@ bool ShouldTestInst(IR::Block& block) { return false; } - for (const auto& ir_inst : block) { + for (const auto& ir_inst : block.instructions) { switch (ir_inst.GetOpcode()) { // A32 case IR::Opcode::A32GetFpscr: diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp index 14293770ed..d21ffe5f27 100644 --- a/src/shader_recompiler/frontend/ir/basic_block.cpp +++ b/src/shader_recompiler/frontend/ir/basic_block.cpp @@ -16,7 +16,7 @@ Block::Block(ObjectPool& inst_pool_) : inst_pool{&inst_pool_} {} Block::~Block() = default; void Block::AppendNewInst(Opcode op, std::initializer_list args) { - PrependNewInst(end(), op, args); + PrependNewInst(instructions.end(), op, args); } Block::iterator Block::PrependNewInst(iterator insertion_point, const Inst& base_inst) {