From 5ea8df31a6f8d634fcd8af61e7b93eee75eb00f4 Mon Sep 17 00:00:00 2001 From: Esther1024 Date: Mon, 19 May 2025 20:20:49 +0100 Subject: [PATCH] more aggressive inline, motivate 2MB pages (implicit malloc for now) --- src/dynarmic/common/memory_pool.cpp | 31 ----- src/dynarmic/common/memory_pool.h | 52 +++++--- src/dynarmic/ir/basic_block.cpp | 105 +++------------ src/dynarmic/ir/basic_block.h | 187 +++++++++++++++------------ src/dynarmic/ir/dense_list.h | 58 +++++++++ src/dynarmic/ir/microinstruction.cpp | 4 - src/dynarmic/ir/microinstruction.h | 14 +- 7 files changed, 217 insertions(+), 234 deletions(-) create mode 100644 src/dynarmic/ir/dense_list.h diff --git a/src/dynarmic/common/memory_pool.cpp b/src/dynarmic/common/memory_pool.cpp index a62cd112..f41dd92a 100644 --- a/src/dynarmic/common/memory_pool.cpp +++ b/src/dynarmic/common/memory_pool.cpp @@ -9,36 +9,5 @@ namespace Dynarmic::Common { -Pool::Pool(size_t object_size, size_t initial_pool_size) - : object_size(object_size), slab_size(initial_pool_size) { - AllocateNewSlab(); -} - -Pool::~Pool() { - std::free(current_slab); - - for (char* slab : slabs) { - std::free(slab); - } -} - -void* Pool::Alloc() { - if (remaining == 0) { - slabs.push_back(current_slab); - AllocateNewSlab(); - } - - void* ret = static_cast(current_ptr); - current_ptr += object_size; - remaining--; - - return ret; -} - -void Pool::AllocateNewSlab() { - current_slab = static_cast(std::malloc(object_size * slab_size)); - current_ptr = current_slab; - remaining = slab_size; -} } // namespace Dynarmic::Common diff --git a/src/dynarmic/common/memory_pool.h b/src/dynarmic/common/memory_pool.h index 1cbdd909..c99316e1 100644 --- a/src/dynarmic/common/memory_pool.h +++ b/src/dynarmic/common/memory_pool.h @@ -10,14 +10,20 @@ namespace Dynarmic::Common { +/// @tparam object_size Byte-size of objects to construct +/// @tparam slab_size Number of objects to have per slab +template class Pool { public: - /** - * @param object_size Byte-size of objects to construct - * @param initial_pool_size Number of objects to have per slab - */ - Pool(size_t object_size, size_t initial_pool_size); - ~Pool(); + inline Pool() noexcept { + AllocateNewSlab(); + } + inline ~Pool() noexcept { + std::free(current_slab); + for (char* slab : slabs) { + std::free(slab); + } + } Pool(const Pool&) = delete; Pool(Pool&&) = delete; @@ -25,21 +31,31 @@ public: Pool& operator=(const Pool&) = delete; Pool& operator=(Pool&&) = delete; - /// Returns a pointer to an `object_size`-bytes block of memory. - void* Alloc(); - + /// @brief Returns a pointer to an `object_size`-bytes block of memory. + [[nodiscard]] void* Alloc() noexcept { + if (remaining == 0) { + slabs.push_back(current_slab); + AllocateNewSlab(); + } + void* ret = static_cast(current_ptr); + current_ptr += object_size; + remaining--; + return ret; + } private: - // Allocates a completely new memory slab. - // Used when an entirely new slab is needed - // due the current one running out of usable space. - void AllocateNewSlab(); + /// @brief Allocates a completely new memory slab. + /// Used when an entirely new slab is needed + /// due the current one running out of usable space. + void AllocateNewSlab() noexcept { + current_slab = static_cast(std::malloc(object_size * slab_size)); + current_ptr = current_slab; + remaining = slab_size; + } - size_t object_size; - size_t slab_size; - char* current_slab; - char* current_ptr; - size_t remaining; std::vector slabs; + char* current_slab = nullptr; + char* current_ptr = nullptr; + size_t remaining = 0; }; } // namespace Dynarmic::Common diff --git a/src/dynarmic/ir/basic_block.cpp b/src/dynarmic/ir/basic_block.cpp index cc7eb48e..c818fe0b 100644 --- a/src/dynarmic/ir/basic_block.cpp +++ b/src/dynarmic/ir/basic_block.cpp @@ -22,105 +22,31 @@ namespace Dynarmic::IR { Block::Block(const LocationDescriptor& location) - : location{location}, end_location{location}, cond{Cond::AL}, instruction_alloc_pool{std::make_unique(sizeof(Inst), 4096)} {} + : location{location}, + end_location{location}, + cond{Cond::AL}, + instruction_alloc_pool{std::make_unique>()} +{ -Block::~Block() = default; - -Block::Block(Block&&) = default; - -Block& Block::operator=(Block&&) = default; - -void Block::AppendNewInst(Opcode opcode, std::initializer_list args) { - PrependNewInst(end(), opcode, args); } -Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, std::initializer_list args) { +/// Prepends a new instruction to this basic block before the insertion point, +/// handling any allocations necessary to do so. +/// @param insertion_point Where to insert the new instruction. +/// @param op Opcode representing the instruction to add. +/// @param args A sequence of Value instances used as arguments for the instruction. +/// @returns Iterator to the newly created instruction. +Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, std::initializer_list args) noexcept { IR::Inst* inst = new (instruction_alloc_pool->Alloc()) IR::Inst(opcode); - ASSERT(args.size() == inst->NumArgs()); - + DEBUG_ASSERT(args.size() == inst->NumArgs()); std::for_each(args.begin(), args.end(), [&inst, index = size_t(0)](const auto& arg) mutable { inst->SetArg(index, arg); index++; }); - return instructions.insert_before(insertion_point, inst); } -LocationDescriptor Block::Location() const { - return location; -} - -LocationDescriptor Block::EndLocation() const { - return end_location; -} - -void Block::SetEndLocation(const LocationDescriptor& descriptor) { - end_location = descriptor; -} - -Cond Block::GetCondition() const { - return cond; -} - -void Block::SetCondition(Cond condition) { - cond = condition; -} - -LocationDescriptor Block::ConditionFailedLocation() const { - return *cond_failed; -} - -void Block::SetConditionFailedLocation(LocationDescriptor fail_location) { - cond_failed = fail_location; -} - -size_t& Block::ConditionFailedCycleCount() { - return cond_failed_cycle_count; -} - -const size_t& Block::ConditionFailedCycleCount() const { - return cond_failed_cycle_count; -} - -bool Block::HasConditionFailedLocation() const { - return cond_failed.has_value(); -} - -Block::InstructionList& Block::Instructions() { - return instructions; -} - -const Block::InstructionList& Block::Instructions() const { - return instructions; -} - -Terminal Block::GetTerminal() const { - return terminal; -} - -void Block::SetTerminal(Terminal term) { - ASSERT_MSG(!HasTerminal(), "Terminal has already been set."); - terminal = std::move(term); -} - -void Block::ReplaceTerminal(Terminal term) { - ASSERT_MSG(HasTerminal(), "Terminal has not been set."); - terminal = std::move(term); -} - -bool Block::HasTerminal() const { - return terminal.which() != 0; -} - -size_t& Block::CycleCount() { - return cycle_count; -} - -const size_t& Block::CycleCount() const { - return cycle_count; -} - -static std::string TerminalToString(const Terminal& terminal_variant) { +static std::string TerminalToString(const Terminal& terminal_variant) noexcept { struct : boost::static_visitor { std::string operator()(const Term::Invalid&) const { return ""; @@ -153,11 +79,10 @@ static std::string TerminalToString(const Terminal& terminal_variant) { return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_)); } } visitor; - return boost::apply_visitor(visitor, terminal_variant); } -std::string DumpBlock(const IR::Block& block) { +std::string DumpBlock(const IR::Block& block) noexcept { std::string ret; ret += fmt::format("Block: location={}\n", block.Location()); diff --git a/src/dynarmic/ir/basic_block.h b/src/dynarmic/ir/basic_block.h index 18d7fada..6608f0e3 100644 --- a/src/dynarmic/ir/basic_block.h +++ b/src/dynarmic/ir/basic_block.h @@ -17,39 +17,34 @@ #include "dynarmic/ir/microinstruction.h" #include "dynarmic/ir/terminal.h" #include "dynarmic/ir/value.h" - -namespace Dynarmic::Common { -class Pool; -} +#include "dynarmic/ir/dense_list.h" +#include "dynarmic/common/memory_pool.h" namespace Dynarmic::IR { enum class Cond; enum class Opcode; -/** - * A basic block. It consists of zero or more instructions followed by exactly one terminal. - * 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. - */ +/// A basic block. It consists of zero or more instructions followed by exactly one terminal. +/// 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 { public: - using InstructionList = mcl::intrusive_list; - using size_type = InstructionList::size_type; - using iterator = InstructionList::iterator; - using const_iterator = InstructionList::const_iterator; - using reverse_iterator = InstructionList::reverse_iterator; - using const_reverse_iterator = InstructionList::const_reverse_iterator; + //using instruction_list_type = dense_list; + using instruction_list_type = mcl::intrusive_list; + using size_type = instruction_list_type::size_type; + using iterator = instruction_list_type::iterator; + using const_iterator = instruction_list_type::const_iterator; + using reverse_iterator = instruction_list_type::reverse_iterator; + using const_reverse_iterator = instruction_list_type::const_reverse_iterator; explicit Block(const LocationDescriptor& location); - ~Block(); - + ~Block() = default; Block(const Block&) = delete; Block& operator=(const Block&) = delete; - - Block(Block&&); - Block& operator=(Block&&); + Block(Block&&) = default; + Block& operator=(Block&&) = default; bool empty() const { return instructions.empty(); } size_type size() const { return instructions.size(); } @@ -76,93 +71,117 @@ public: 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. - */ - void AppendNewInst(Opcode op, std::initializer_list args); - - /** - * Prepends a new instruction to this basic block before the insertion point, - * handling any allocations necessary to do so. - * - * @param insertion_point Where to insert the new instruction. - * @param op Opcode representing the instruction to add. - * @param args A sequence of Value instances used as arguments for the instruction. - * @returns Iterator to the newly created instruction. - */ - iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list args); - - /// Gets the starting location for this basic block. - LocationDescriptor Location() const; - /// Gets the end location for this basic block. - LocationDescriptor EndLocation() const; - /// Sets the end location for this basic block. - void SetEndLocation(const LocationDescriptor& descriptor); - - /// Gets the condition required to pass in order to execute this block. - Cond GetCondition() const; - /// Sets the condition required to pass in order to execute this block. - void SetCondition(Cond condition); - - /// Gets the location of the block to execute if the predicated condition fails. - LocationDescriptor ConditionFailedLocation() const; - /// Sets the location of the block to execute if the predicated condition fails. - void SetConditionFailedLocation(LocationDescriptor fail_location); - /// Determines whether or not a predicated condition failure block is present. - bool HasConditionFailedLocation() const; - - /// Gets a mutable reference to the condition failed cycle count. - size_t& ConditionFailedCycleCount(); - /// Gets an immutable reference to the condition failed cycle count. - const size_t& ConditionFailedCycleCount() const; + /// 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); + } + iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list args) noexcept; /// Gets a mutable reference to the instruction list for this basic block. - InstructionList& Instructions(); + inline instruction_list_type& Instructions() noexcept { + return instructions; + } /// Gets an immutable reference to the instruction list for this basic block. - const InstructionList& Instructions() const; + inline const instruction_list_type& Instructions() const noexcept { + return instructions; + } + /// Gets the starting location for this basic block. + inline LocationDescriptor Location() const noexcept { + return location; + } + /// Gets the end location for this basic block. + inline LocationDescriptor EndLocation() const noexcept { + return end_location; + } + /// Sets the end location for this basic block. + inline void SetEndLocation(const LocationDescriptor& descriptor) noexcept { + end_location = descriptor; + } + + /// Gets the condition required to pass in order to execute this block. + inline Cond GetCondition() const noexcept { + return cond; + } + /// Sets the condition required to pass in order to execute this block. + inline void SetCondition(Cond condition) noexcept { + cond = condition; + } + + /// Gets the location of the block to execute if the predicated condition fails. + inline LocationDescriptor ConditionFailedLocation() const noexcept { + return *cond_failed; + } + /// Sets the location of the block to execute if the predicated condition fails. + inline void SetConditionFailedLocation(LocationDescriptor fail_location) noexcept { + cond_failed = fail_location; + } + /// Determines whether or not a predicated condition failure block is present. + inline bool HasConditionFailedLocation() const noexcept { + return cond_failed.has_value(); + } + + /// Gets a mutable reference to the condition failed cycle count. + inline size_t& ConditionFailedCycleCount() noexcept { + return cond_failed_cycle_count; + } + /// Gets an immutable reference to the condition failed cycle count. + inline const size_t& ConditionFailedCycleCount() const noexcept { + return cond_failed_cycle_count; + } + /// Gets the terminal instruction for this basic block. - Terminal GetTerminal() const; + inline Terminal GetTerminal() const noexcept { + return terminal; + } /// Sets the terminal instruction for this basic block. - void SetTerminal(Terminal term); + inline void SetTerminal(Terminal term) noexcept { + ASSERT_MSG(!HasTerminal(), "Terminal has already been set."); + terminal = std::move(term); + } /// Replaces the terminal instruction for this basic block. - void ReplaceTerminal(Terminal term); + inline void ReplaceTerminal(Terminal term) noexcept { + ASSERT_MSG(HasTerminal(), "Terminal has not been set."); + terminal = std::move(term); + } /// Determines whether or not this basic block has a terminal instruction. - bool HasTerminal() const; - + inline bool HasTerminal() const noexcept { + return terminal.which() != 0; + } + /// Gets a mutable reference to the cycle count for this basic block. - size_t& CycleCount(); + inline size_t& CycleCount() noexcept { + return cycle_count; + } /// Gets an immutable reference to the cycle count for this basic block. - const size_t& CycleCount() const; - + inline const size_t& CycleCount() const noexcept { + return cycle_count; + } private: + /// List of instructions in this block. + instruction_list_type instructions; + /// Block to execute next if `cond` did not pass. + std::optional cond_failed = {}; /// Description of the starting location of this block LocationDescriptor location; /// Description of the end location of this block LocationDescriptor end_location; /// Conditional to pass in order to execute this block Cond cond; - /// Block to execute next if `cond` did not pass. - std::optional cond_failed = {}; - /// Number of cycles this block takes to execute if the conditional fails. - size_t cond_failed_cycle_count = 0; - - /// List of instructions in this block. - InstructionList instructions; /// Memory pool for instruction list - std::unique_ptr instruction_alloc_pool; + std::unique_ptr> instruction_alloc_pool; /// Terminal instruction of this block. Terminal terminal = Term::Invalid{}; - + /// Number of cycles this block takes to execute if the conditional fails. + size_t cond_failed_cycle_count = 0; /// Number of cycles this block takes to execute. size_t cycle_count = 0; }; /// Returns a string representation of the contents of block. Intended for debugging. -std::string DumpBlock(const IR::Block& block); +std::string DumpBlock(const IR::Block& block) noexcept; } // namespace Dynarmic::IR diff --git a/src/dynarmic/ir/dense_list.h b/src/dynarmic/ir/dense_list.h new file mode 100644 index 00000000..a399c12d --- /dev/null +++ b/src/dynarmic/ir/dense_list.h @@ -0,0 +1,58 @@ +#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 = std::deque::iterator; + using const_iterator = std::deque::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = 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/ir/microinstruction.cpp b/src/dynarmic/ir/microinstruction.cpp index 50af036d..c52b7bef 100644 --- a/src/dynarmic/ir/microinstruction.cpp +++ b/src/dynarmic/ir/microinstruction.cpp @@ -605,10 +605,6 @@ bool Inst::AreAllArgsImmediates() const { return std::all_of(args.begin(), args.begin() + NumArgs(), [](const auto& value) { return value.IsImmediate(); }); } -bool Inst::HasAssociatedPseudoOperation() const { - return next_pseudoop && !IsAPseudoOperation(); -} - Inst* Inst::GetAssociatedPseudoOperation(Opcode opcode) { Inst* pseudoop = next_pseudoop; while (pseudoop) { diff --git a/src/dynarmic/ir/microinstruction.h b/src/dynarmic/ir/microinstruction.h index 8bd13437..95eef2fb 100644 --- a/src/dynarmic/ir/microinstruction.h +++ b/src/dynarmic/ir/microinstruction.h @@ -19,14 +19,12 @@ enum class Type; constexpr size_t max_arg_count = 4; -/** - * A representation of a microinstruction. A single ARM/Thumb instruction may be - * converted into zero or more microinstructions. - */ +/// A representation of a microinstruction. A single ARM/Thumb instruction may be +/// converted into zero or more microinstructions. +//class Inst final { class Inst final : public mcl::intrusive_list_node { public: - explicit Inst(Opcode op) - : op(op) {} + explicit Inst(Opcode op) : op(op) {} /// Determines whether or not this instruction performs an arithmetic shift. bool IsArithmeticShift() const; @@ -122,7 +120,9 @@ public: bool HasUses() const { return use_count > 0; } /// Determines if there is a pseudo-operation associated with this instruction. - bool HasAssociatedPseudoOperation() const; + inline bool HasAssociatedPseudoOperation() const noexcept { + return next_pseudoop && !IsAPseudoOperation(); + } /// Gets a pseudo-operation associated with this instruction. Inst* GetAssociatedPseudoOperation(Opcode opcode);