Skip to content

Commit

Permalink
Merge pull request #142 from zyantific/memory-reduction
Browse files Browse the repository at this point in the history
Memory reduction
  • Loading branch information
ZehMatt authored Sep 20, 2024
2 parents b327c4c + 74d5cbb commit 87f86ee
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 35 deletions.
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

0 comments on commit 87f86ee

Please sign in to comment.