Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memory reduction #142

Merged
merged 5 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions benchmark/src/benchmarks/benchmark.assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,13 @@ namespace zasm::benchmarks
assembler.setCursor(nullptr);
state.ResumeTiming();

for (const auto& instr : zasm::tests::data::Instructions)
for (int i = 0; i < 100; i++)
{
instr.emitter(assembler);
numInstructions++;
for (const auto& instr : zasm::tests::data::Instructions)
{
instr.emitter(assembler);
numInstructions++;
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions tests/src/tests/tests.assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ namespace zasm::tests
Program program(MachineMode::AMD64);

x86::Assembler assembler(program);

ASSERT_EQ(assembler.mov(x86::rax, x86::rax), ErrorCode::None);
auto* nodeA = assembler.getCursor();
ASSERT_EQ(assembler.mov(x86::rdx, x86::rdx), ErrorCode::None);
auto* nodeB = assembler.getCursor();
ASSERT_EQ(assembler.mov(x86::rbx, x86::rbx), ErrorCode::None);
auto* nodeC = assembler.getCursor();

program.destroy(nodeC);
ASSERT_EQ(assembler.getCursor(), nodeB);

Expand Down
22 changes: 22 additions & 0 deletions tests/src/tests/tests.program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,26 @@ namespace zasm::tests
ASSERT_EQ(imm.value<int>(), 3);
}

TEST(ProgramTests, TestNodeType)
{
Program program(MachineMode::AMD64);

auto testIns = Instruction{}.setMnemonic(x86::Mnemonic::Add);

auto* node = program.createNode(testIns);
ASSERT_NE(node, nullptr);

ASSERT_TRUE(node->holds<Instruction>());

ASSERT_FALSE(node->holds<Data>());

auto* inst = node->getIf<Instruction>();
ASSERT_NE(inst, nullptr);

ASSERT_EQ(testIns, *inst);

auto* data = node->getIf<Data>();
ASSERT_EQ(data, nullptr);
}

} // namespace zasm::tests
30 changes: 18 additions & 12 deletions zasm/include/zasm/program/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ namespace zasm
NodeFlags _flags{};
Node* _prev{};
Node* _next{};
std::variant<Sentinel, Instruction, Label, EmbeddedLabel, Data, Section, Align> _data{};
std::variant<Sentinel*, Instruction*, Label*, EmbeddedLabel*, Data*, Section*, Align*> _data{};

union
{
Expand All @@ -51,9 +51,9 @@ namespace zasm
protected:
// Internal use only.
template<typename T>
constexpr Node(Id nodeId, T&& val) noexcept
constexpr Node(Id nodeId, T* val) noexcept
: _id{ nodeId }
, _data{ std::forward<T>(val) }
, _data{ val }
{
}

Expand Down Expand Up @@ -97,7 +97,7 @@ namespace zasm
/// <returns>True if the T is the current type</returns>
template<typename T> constexpr bool holds() const noexcept
{
return std::holds_alternative<T>(_data);
return std::holds_alternative<T*>(_data);
}

/// <summary>
Expand All @@ -108,13 +108,13 @@ namespace zasm
/// <returns>Returns a reference to the data with the type of T</returns>
template<typename T> constexpr const T& get() const
{
return std::get<T>(_data);
return *std::get<T*>(_data);
}

/// <see cref="get"/>
template<typename T> constexpr T& get()
{
return std::get<T>(_data);
return *std::get<T*>(_data);
}

/// <summary>
Expand All @@ -125,13 +125,19 @@ namespace zasm
/// <returns>Pointer of type T</returns>
template<typename T> constexpr const T* getIf() const noexcept
{
return std::get_if<T>(&_data);
auto r = std::get_if<T*>(&_data);
if (r == nullptr)
return nullptr;
return *r;
}

/// <see cref="getIf"/>
template<typename T> constexpr T* getIf() noexcept
{
return std::get_if<T>(&_data);
auto r = std::get_if<T*>(&_data);
if (r == nullptr)
return nullptr;
return *r;
}

/// <summary>
Expand All @@ -141,15 +147,15 @@ namespace zasm
/// <typeparam name="F">Function type</typeparam>
/// <param name="func">Visitor function</param>
/// <returns>The result of the visitor function</returns>
template<typename F> constexpr auto visit(F&& func) const
template<typename TPred> constexpr auto visit(TPred&& func) const
{
return std::visit(std::forward<F>(func), _data);
return std::visit([&](auto&& obj) { return func(*obj); }, _data);
}

/// <see cref="visit"/>
template<typename F> constexpr auto visit(F&& func)
template<typename TPred> constexpr auto visit(TPred&& func)
{
return std::visit(std::forward<F>(func), _data);
return std::visit([&](auto&& obj) { return func(*obj); }, _data);
}

/// <summary>
Expand Down
40 changes: 31 additions & 9 deletions zasm/src/zasm/src/program/program.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ namespace zasm
return entry.node;
}

template<bool TNotify, typename F, typename... TArgs>
static void notifyObservers(const F&& func, const std::vector<Observer*>& observers, TArgs&&... args) noexcept
template<bool TNotify, typename TPred, typename... TArgs>
static void notifyObservers(const TPred&& func, const std::vector<Observer*>& observers, TArgs&&... args) noexcept
{
if constexpr (TNotify)
{
Expand Down Expand Up @@ -374,12 +374,26 @@ namespace zasm

// Release.
auto* nodeToDestroy = detail::toInternal(node);
state.nodePool.destroy(nodeToDestroy);

node->visit([&](auto& ptr) {
using T = std::decay_t<decltype(ptr)>;

auto& objectPool = state.objectPools.get<T>();
objectPool.destroy(&ptr);

if (!quickDestroy)
{
objectPool.deallocate(&ptr, 1);
}
});

auto& nodePool = state.objectPools.get<Node>();
nodePool.destroy(nodeToDestroy);

if (!quickDestroy)
{
// Release memory, when quickDestroy is true the entire pool will be cleared at once.
state.nodePool.deallocate(nodeToDestroy, 1);
nodePool.deallocate(nodeToDestroy, 1);

// Remove mapping.
auto& nodeMap = state.nodeMap;
Expand Down Expand Up @@ -424,7 +438,7 @@ namespace zasm
_state->sections.clear();
_state->labels.clear();
_state->symbolNames.clear();
_state->nodePool.reset();
_state->objectPools.reset();
}

void Program::setEntryPoint(const Label& label)
Expand All @@ -437,19 +451,27 @@ namespace zasm
return _state->entryPoint;
}

template<typename... TArgs> Node* createNode_(detail::ProgramState& state, TArgs&&... args)
template<typename T> Node* createNode_(detail::ProgramState& state, T&& object)
{
const auto nextId = state.nextNodeId;
state.nextNodeId = static_cast<Node::Id>(static_cast<std::underlying_type_t<Node::Id>>(nextId) + 1U);

auto& pool = state.nodePool;
auto* node = detail::toInternal(pool.allocate(1));
auto& nodePool = state.objectPools.get<Node>();
auto* node = detail::toInternal(nodePool.allocate(1));
if (node == nullptr)
{
return nullptr;
}

::new ((void*)node) detail::Node(nextId, std::forward<TArgs&&>(args)...);
// Construct object.
using ObjectType = std::decay_t<T>;
auto& objectPool = state.objectPools.get<ObjectType>();

auto* obj = objectPool.allocate(1);
::new ((void*)obj) ObjectType(std::move(object));

// Construct node.
::new ((void*)node) detail::Node(nextId, obj);

notifyObservers<true>(&Observer::onNodeCreated, state.observer, node);

Expand Down
4 changes: 2 additions & 2 deletions zasm/src/zasm/src/program/program.node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ namespace zasm
{
public:
template<typename T>
constexpr Node(zasm::Node::Id id, T&& val) noexcept
: ::zasm::Node(id, std::forward<T>(val))
constexpr Node(zasm::Node::Id id, T* val) noexcept
: ::zasm::Node(id, val)
{
}
void setPrev(::zasm::Node* node) noexcept
Expand Down
41 changes: 34 additions & 7 deletions zasm/src/zasm/src/program/program.state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <Zydis/Zydis.h>
#include <cstddef>
#include <tuple>
#include <vector>
#include <zasm/base/label.hpp>
#include <zasm/base/mode.hpp>
Expand All @@ -22,8 +23,6 @@ namespace zasm

namespace zasm::detail
{
constexpr std::size_t PoolSize = 1U << 10;

struct LabelData
{
Label::Id id{ Label::Id::Invalid };
Expand All @@ -46,11 +45,36 @@ namespace zasm::detail
zasm::Node* node{};
};

struct NodeStorage
namespace detail
{
ObjectPool<Node, PoolSize> nodePool;
Node::Id nextNodeId{};
};
template<typename T>
struct PoolSize
{
static constexpr std::size_t kSize = 50'000;
};

template<> struct PoolSize<Instruction>
{
static constexpr std::size_t kSize = 30'000;
};

template<typename... TTypes> struct ObjectPools
{
std::tuple<ObjectPool<TTypes, PoolSize<TTypes>::kSize>...> pools;

template<typename T> auto& get()
{
return std::get<ObjectPool<T, PoolSize<T>::kSize>>(pools);
}

void reset()
{
std::apply([](auto&... pool) { (pool.reset(), ...); }, pools);
}
};
} // namespace detail

using ObjectPools = detail::ObjectPools<zasm::Node, Sentinel, Instruction, Label, EmbeddedLabel, Data, Section, Align>;

struct NodeList
{
Expand All @@ -64,7 +88,7 @@ namespace zasm::detail
StringPool symbolNames;
};

struct ProgramState : NodeStorage, NodeList, Symbols
struct ProgramState : NodeList, Symbols
{
MachineMode mode{};

Expand All @@ -79,6 +103,9 @@ namespace zasm::detail

Label entryPoint{ Label::Id::Invalid };

ObjectPools objectPools;
Node::Id nextNodeId{};

ProgramState(MachineMode m)
: mode(m)
{
Expand Down
Loading