more aggressive inline, motivate 2MB pages (implicit malloc for now)

This commit is contained in:
Esther1024
2025-05-19 20:20:49 +01:00
committed by crueter
parent 7b5355fdd1
commit 5ea8df31a6
7 changed files with 217 additions and 234 deletions

View File

@@ -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<void*>(current_ptr);
current_ptr += object_size;
remaining--;
return ret;
}
void Pool::AllocateNewSlab() {
current_slab = static_cast<char*>(std::malloc(object_size * slab_size));
current_ptr = current_slab;
remaining = slab_size;
}
} // namespace Dynarmic::Common

View File

@@ -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<size_t object_size, size_t slab_size>
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<void*>(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<char*>(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<char*> slabs;
char* current_slab = nullptr;
char* current_ptr = nullptr;
size_t remaining = 0;
};
} // namespace Dynarmic::Common

View File

@@ -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<Common::Pool>(sizeof(Inst), 4096)} {}
: location{location},
end_location{location},
cond{Cond::AL},
instruction_alloc_pool{std::make_unique<std::remove_reference_t<decltype(*instruction_alloc_pool)>>()}
{
Block::~Block() = default;
Block::Block(Block&&) = default;
Block& Block::operator=(Block&&) = default;
void Block::AppendNewInst(Opcode opcode, std::initializer_list<IR::Value> args) {
PrependNewInst(end(), opcode, args);
}
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode opcode, std::initializer_list<Value> 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<Value> 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> {
std::string operator()(const Term::Invalid&) const {
return "<invalid terminal>";
@@ -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());

View File

@@ -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<Inst>;
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<Inst>;
using instruction_list_type = mcl::intrusive_list<Inst>;
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<Value> 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<Value> 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<IR::Value> args) noexcept {
PrependNewInst(end(), opcode, args);
}
iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> 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<LocationDescriptor> 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<LocationDescriptor> 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<Common::Pool> instruction_alloc_pool;
std::unique_ptr<Common::Pool<sizeof(Inst), 2097152UL / sizeof(Inst)>> 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

View File

@@ -0,0 +1,58 @@
#pragma once
#include <cstdint>
#include <cstddef>
#include <deque>
namespace Dynarmic {
template<typename T> 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<value_type>::iterator;
using const_iterator = std::deque<value_type>::const_iterator;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_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<value_type> list;
};
}

View File

@@ -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) {

View File

@@ -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<Inst> {
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);