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

Implement simple linear allocator #57

Merged
merged 15 commits into from
Nov 3, 2023
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ cmake_minimum_required(VERSION 3.26)
project(ChaiVM)
set(CMAKE_CXX_STANDARD 20)

set(COMPILER_WARNINGS "-Wall -Wextra -Wpedantic -Werror -Wno-missing-field-initializers")

set(CMAKE_BUILD_TYPE DEBUG)
set(CMAKE_C_FLAGS ${COMPILER_WARNINGS})
set(CMAKE_C_FLAGS_DEBUG "-O0 -g ${COMPILER_WARNINGS}")
set(CMAKE_C_FLAGS_RELEASE "-O2 ${COMPILER_WARNINGS}")
set(CMAKE_CXX_FLAGS ${COMPILER_WARNINGS})
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g ${COMPILER_WARNINGS}")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 ${COMPILER_WARNINGS}")

add_subdirectory(third_party)
add_subdirectory(test)
add_subdirectory(src)
Expand Down
4 changes: 2 additions & 2 deletions include/ChaiVM/interpreter/reg-file.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace chai::interpreter {
class RegisterFile {
public:
RegisterFile(chsize_t pc);
chsize_t &operator[](int n) &;
const chsize_t &operator[](int n) const &;
chsize_t &operator[](size_t n) &;
const chsize_t &operator[](size_t n) const &;
chsize_t &pc();
chsize_t pc() const;
chsize_t &acc();
Expand Down
16 changes: 16 additions & 0 deletions include/ChaiVM/memory/allocator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <cstdlib>

namespace chai::memory {

template <class T> class IAllocator {
public:
using value_type = T;
IAllocator() noexcept = default;
virtual T *allocate(size_t n) = 0;
virtual void deallocate(T *p, size_t n) = 0;
virtual ~IAllocator() {}
};

} // namespace chai::memory
36 changes: 36 additions & 0 deletions include/ChaiVM/memory/linear-allocator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <cstdlib>
#include <new>
#include <numeric>

#include "ChaiVM/memory/allocator.hpp"
#include "ChaiVM/memory/linear-buffer.hpp"
#include "ChaiVM/utils/non-copyable.hpp"

namespace chai::memory {

template <class T> class LinearAllocator : IAllocator<T> {
public:
using value_type = T;

explicit LinearAllocator(LinearBuffer &buffer) : buffer_(buffer) {}
levBagryansky marked this conversation as resolved.
Show resolved Hide resolved

T *allocate(std::size_t n) override {
if (n > (buffer_.size() - buffer_.offset()) / sizeof(T)) {
throw std::bad_array_new_length();
}
void *current = buffer_.currentPosition();
buffer_.shiftOffset(n * sizeof(T));
return reinterpret_cast<T *>(current);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
void deallocate(T *p, std::size_t n) override {}
#pragma GCC diagnostic pop

private:
LinearBuffer &buffer_;
};

} // namespace chai::memory
28 changes: 28 additions & 0 deletions include/ChaiVM/memory/linear-buffer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <cstdlib>
#include <iostream>

#include "ChaiVM/utils/non-copyable.hpp"

namespace chai::memory {

class LinearBuffer : public INonCopyable {
c71n93 marked this conversation as resolved.
Show resolved Hide resolved
public:
explicit LinearBuffer(size_t sz);
LinearBuffer(LinearBuffer &&other) noexcept;
LinearBuffer &operator=(LinearBuffer &&other) noexcept;
~LinearBuffer();

size_t size() const;
size_t offset() const;
void *currentPosition() const;
void shiftOffset(size_t n);

private:
size_t size_ = 0;
size_t offset_ = 0;
char *buf_ = nullptr;
};

} // namespace chai::memory
1 change: 0 additions & 1 deletion include/ChaiVM/utils/constant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ struct Constant {
virtual void write(std::ofstream &ofs) = 0;
virtual int8_t getType() = 0;
virtual ~Constant() = default;
;
};

struct ConstI64 : public Constant {
Expand Down
4 changes: 3 additions & 1 deletion include/ChaiVM/utils/non-copyable.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#pragma once

class INonCopyable {
protected:
INonCopyable() {}

public:
INonCopyable(const INonCopyable &rhs) = delete;
INonCopyable &operator=(const INonCopyable &rhs) = delete;
virtual ~INonCopyable() = 0;
};
2 changes: 1 addition & 1 deletion src/ChaiVM/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(DIRS interpreter utils)
set(DIRS interpreter utils memory)
foreach(DIR ${DIRS})
add_subdirectory(${DIR})
endforeach()
3 changes: 3 additions & 0 deletions src/ChaiVM/interpreter/executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ Executor::Executor(CodeManager *manager)
void Executor::run() { DO_NEXT_INS() }
void Executor::restart() { regFile_.pc() = codeManager_->startPC(); }
const RegisterFile &Executor::getState() const & { return regFile_; }

#pragma GCC diagnostic ignored "-Wunused-parameter"

void Executor::inv(Instruction ins) {
throw InvalidInstruction("Invalid operation at pc: " +
std::to_string(regFile_.pc()));
Expand Down
8 changes: 4 additions & 4 deletions src/ChaiVM/interpreter/reg-file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,24 @@

namespace chai::interpreter {

chai::chsize_t &RegisterFile::operator[](int n) & {
chai::chsize_t &RegisterFile::operator[](size_t n) & {
assert(n <= Size);
return registers_[n];
}
const chsize_t &RegisterFile::operator[](int n) const & {
const chsize_t &RegisterFile::operator[](size_t n) const & {
assert(n <= Size);
return registers_[n];
}
chai::chsize_t &RegisterFile::pc() { return pc_; }
chai::chsize_t RegisterFile::pc() const { return pc_; }
chai::chsize_t &RegisterFile::acc() { return acc_; }
chai::chsize_t RegisterFile::acc() const { return acc_; }
RegisterFile::RegisterFile(chsize_t pc) : pc_(pc), acc_(0), registers_{} {}
RegisterFile::RegisterFile(chsize_t pc) : acc_(0), pc_(pc), registers_{} {}

void RegisterFile::dump() {
std::cout << "pc = " << pc_ << ", acc = " << std::bit_cast<int64_t>(acc_)
<< " = " << std::bit_cast<double>(acc_) << std::endl;
for (int i = 0; i < Size; ++i) {
for (size_t i = 0; i < Size; ++i) {
if (registers_[i] != 0) {
std::cout << "rf[" << i
<< "] = " << std::bit_cast<int64_t>(registers_[i])
Expand Down
8 changes: 8 additions & 0 deletions src/ChaiVM/memory/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
add_library(chai_memory STATIC)
target_sources(chai_memory PRIVATE
./linear-buffer.cpp
)
target_link_libraries(chai_memory
PRIVATE
chai_include
)
22 changes: 22 additions & 0 deletions src/ChaiVM/memory/linear-buffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "ChaiVM/memory/linear-buffer.hpp"

namespace chai::memory {

LinearBuffer::LinearBuffer(size_t sz) : size_(sz), buf_(new char[size_]) {}
LinearBuffer::LinearBuffer(LinearBuffer &&other) noexcept {
std::swap(size_, other.size_);
std::swap(buf_, other.buf_);
}
LinearBuffer &LinearBuffer::operator=(LinearBuffer &&other) noexcept {
std::swap(size_, other.size_);
c71n93 marked this conversation as resolved.
Show resolved Hide resolved
std::swap(buf_, other.buf_);
return *this;
}
LinearBuffer::~LinearBuffer() { delete[] buf_; }

size_t LinearBuffer::size() const { return size_; }
size_t LinearBuffer::offset() const { return offset_; }
void *LinearBuffer::currentPosition() const { return buf_ + offset_; }
void LinearBuffer::shiftOffset(size_t n) { offset_ += n; }

} // namespace chai::memory
1 change: 0 additions & 1 deletion src/ChaiVM/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

add_library(chai_utils STATIC)
target_sources(chai_utils PRIVATE
./instr2Raw.cpp
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ target_link_libraries(chai_testif INTERFACE
chai_include
chai_interpreter
chai_utils
chai_memory
gmock_main
gmock
gtest
Expand Down
2 changes: 1 addition & 1 deletion test/ChaiVM/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
set(DIRS interpreter utils)
set(DIRS interpreter utils memory)
foreach(DIR ${DIRS})
add_subdirectory(${DIR})
endforeach()
2 changes: 1 addition & 1 deletion test/ChaiVM/interpreter/code-manager-test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ TEST(CodeManager, returnsBytecodes) {
}

const chai::chsize_t initial_pc = manager.startPC();
for (int i = 0; i < seq.size(); ++i) {
for (size_t i = 0; i < seq.size(); ++i) {
chai::chsize_t pc = initial_pc + i * sizeof(chai::bytecode_t);
EXPECT_EQ(manager.getBytecode(pc), seq[i]);
}
Expand Down
1 change: 1 addition & 0 deletions test/ChaiVM/memory/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
chai_test(./linear-allocator-test.cpp)
75 changes: 75 additions & 0 deletions test/ChaiVM/memory/linear-allocator-test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "ChaiVM/memory/linear-allocator.hpp"
#include <gtest/gtest.h>

using namespace chai::memory;

class LinearAllocatorTest : public ::testing::Test {
protected:
static constexpr size_t BUFFER_SIZE = 1024;
LinearBuffer buffer_ = LinearBuffer(BUFFER_SIZE);
};

class Stub {
public:
explicit Stub(int num) : stub(num) {}
Stub() {}
int stub = 0;
};

TEST_F(LinearAllocatorTest, Primitives) {
size_t n = 5;
LinearAllocator<int> allocator{buffer_};
int *buf = allocator.allocate(n);
int *arr = new (buf) int[n];
for (size_t i = 0; i < n; ++i) {
arr[i] = i;
}
for (size_t i = 0; i < n; ++i) {
EXPECT_EQ(arr[i], i);
}
}
TEST_F(LinearAllocatorTest, Classes) {
size_t n = 1;
LinearAllocator<Stub> allocator{buffer_};
Stub *buf = allocator.allocate(n);
Stub *inst = new (buf) Stub;
inst->stub = 10;
EXPECT_EQ(inst->stub, 10);
inst->~Stub();
}
TEST_F(LinearAllocatorTest, StdContainers) {
size_t n = 10;
LinearAllocator<Stub> allocator{buffer_};
std::vector<Stub, decltype(allocator)> vec(n, allocator);
for (auto &e : vec) {
c71n93 marked this conversation as resolved.
Show resolved Hide resolved
e = Stub(42);
}
for (auto &e : vec) {
EXPECT_EQ(e.stub, 42);
}
}

TEST_F(LinearAllocatorTest, PrimitiveAllocation) {
size_t n = 42;
LinearAllocator<int> allocator{buffer_};
allocator.allocate(n);
EXPECT_EQ(buffer_.offset(), n * sizeof(int));
}
TEST_F(LinearAllocatorTest, ClassAllocation) {
size_t n = 80;
LinearAllocator<Stub> allocator{buffer_};
allocator.allocate(n);
EXPECT_EQ(buffer_.offset(), n * sizeof(Stub));
}
TEST_F(LinearAllocatorTest, StdContainersAllocation) {
size_t n = 23;
LinearAllocator<Stub> allocator{buffer_};
std::vector<Stub, decltype(allocator)> vec(n, allocator);
EXPECT_EQ(buffer_.offset(), n * sizeof(Stub));
}

TEST_F(LinearAllocatorTest, BasArrayNewLength) {
size_t n = BUFFER_SIZE / sizeof(Stub) + 1;
LinearAllocator<Stub> allocator{buffer_};
EXPECT_THROW(allocator.allocate(n), std::bad_array_new_length);
}