Compare commits

...

8 Commits

9 changed files with 255 additions and 13 deletions
+8 -2
View File
@@ -1,9 +1,11 @@
#ifndef FURVM_CONTEXT_HPP #ifndef FURVM_CONTEXT_HPP
#define FURVM_CONTEXT_HPP #define FURVM_CONTEXT_HPP
#include "furlang/arena.hpp"
#include "furvm/fwd.hpp" #include "furvm/fwd.hpp"
#include "furvm/module.hpp" #include "furvm/module.hpp"
#include <queue>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
@@ -20,12 +22,12 @@ public:
/** /**
* @brief Move constructor. * @brief Move constructor.
*/ */
context(context&&) = default; context(context&&) noexcept = default;
/** /**
* @brief Move constructor. * @brief Move constructor.
*/ */
context& operator=(context&&) = default; context& operator=(context&&) noexcept = default;
context(const context&) = delete; context(const context&) = delete;
context& operator=(const context&) = delete; context& operator=(const context&) = delete;
@@ -95,6 +97,10 @@ private:
std::vector<mod_p> m_modules; std::vector<mod_p> m_modules;
std::vector<thing_p> m_things; std::vector<thing_p> m_things;
std::vector<executor_p> m_executors; std::vector<executor_p> m_executors;
std::queue<thing_handle> m_deadThings;
std::vector<void*> m_deadThingData;
furlang::arena m_thingArena;
}; };
} // namespace furvm } // namespace furvm
+43
View File
@@ -0,0 +1,43 @@
#ifndef FURVM_EXCEPTIONS_HPP
#define FURVM_EXCEPTIONS_HPP
#include <exception>
namespace furvm {
class stack_underflow : public std::exception {
public:
stack_underflow() = default;
~stack_underflow() override = default;
/**
* @brief Move constructor.
*/
stack_underflow(stack_underflow&&) noexcept = default;
/**
* @brief Move constructor.
*/
stack_underflow& operator=(stack_underflow&&) noexcept = default;
/**
* @brief Copy constructor.
*/
stack_underflow(const stack_underflow&) = default;
/**
* @brief Copy constructor.
*/
stack_underflow& operator=(const stack_underflow&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "stack underflow"; }
};
} // namespace furvm
#endif // FURVM_EXCEPTIONS_HPP
+6
View File
@@ -9,6 +9,7 @@ namespace furvm {
enum class executor_flags : std::uint32_t { enum class executor_flags : std::uint32_t {
Suspended = (1 << 0), /**< Execution suspended. */ Suspended = (1 << 0), /**< Execution suspended. */
Done = (1 << 1), /**< Execution is finished. */
}; };
static inline executor_flags operator|(executor_flags lhs, executor_flags rhs) { static inline executor_flags operator|(executor_flags lhs, executor_flags rhs) {
@@ -132,6 +133,11 @@ public:
* @return The thing. * @return The thing.
*/ */
thing_p thing() const; thing_p thing() const;
public:
/**
* @brief Executes next instruction.
*/
void step();
private: private:
executor_handle m_id; executor_handle m_id;
executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization) executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization)
+14
View File
@@ -75,6 +75,12 @@ using module_handle = std::uint32_t;
*/ */
enum class thing_t : std::uint8_t; enum class thing_t : std::uint8_t;
/**
* @class bad_thing_access
* @brief Bad thing access exception.
*/
class bad_thing_access;
/** /**
* @class thing * @class thing
* @brief Furvm thing. * @brief Furvm thing.
@@ -134,6 +140,14 @@ class context;
*/ */
using context_p = std::shared_ptr<context>; using context_p = std::shared_ptr<context>;
// exceptions.hpp:
/**
* @class stack_underflow
* @brief Stack underflow exception.
*/
class stack_underflow;
} // namespace furvm } // namespace furvm
#endif // FURVM_FWD_HPP #endif // FURVM_FWD_HPP
+8
View File
@@ -42,6 +42,14 @@ public:
* @return The id. * @return The id.
*/ */
constexpr module_handle id() const { return m_id; } constexpr module_handle id() const { return m_id; }
/**
* @brief Returns a byte from bytecode of this module.
*
* @param offset An offset of the byte.
* @return The byte.
*/
constexpr byte byte(std::size_t offset) const { return m_bytecode.at(offset); }
private: private:
module_handle m_id; module_handle m_id;
bytecode_t m_bytecode; bytecode_t m_bytecode;
+72 -3
View File
@@ -6,7 +6,7 @@
namespace furvm { namespace furvm {
enum class thing_t : std::uint8_t { enum class thing_t : std::uint8_t {
Int, Int32,
}; };
/** /**
@@ -17,7 +17,40 @@ enum class thing_t : std::uint8_t {
*/ */
std::size_t thing_type_size(thing_t type); std::size_t thing_type_size(thing_t type);
class thing { class bad_thing_access : public std::exception {
public:
bad_thing_access() = default;
~bad_thing_access() override = default;
/**
* @brief Move constructor.
*/
bad_thing_access(bad_thing_access&&) noexcept = default;
/**
* @brief Move constructor.
*/
bad_thing_access& operator=(bad_thing_access&&) noexcept = default;
/**
* @brief Copy constructor.
*/
bad_thing_access(const bad_thing_access&) = default;
/**
* @brief Copy constructor.
*/
bad_thing_access& operator=(const bad_thing_access&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "bad thing access"; }
};
class thing final {
friend class executor; friend class executor;
private: private:
/** /**
@@ -28,6 +61,10 @@ private:
struct rzecz { struct rzecz {
explicit rzecz() = default; explicit rzecz() = default;
}; };
public:
using nref_t = std::size_t; /**< Type of reference count. */
static constexpr thing_handle GENERATION_SIZE = 12; /**< Bit size of generation part in thing_handle. */
public: public:
/** /**
* @brief Private constructor. * @brief Private constructor.
@@ -61,12 +98,44 @@ public:
* @return Shared pointer to the new thing. * @return Shared pointer to the new thing.
*/ */
static thing_p create(const context_p& context, thing_t type); static thing_p create(const context_p& context, thing_t type);
public:
/**
* @brief Returns an int32 value from this thing.
*
* @return The value.
*/
std::int32_t& int32();
/**
* @brief Returns an int32 value from this thing.
*
* @return The value.
*/
const std::int32_t& int32() const;
public:
/**
* @brief Increments reference count of this thing by one.
*/
void add_reference() { ++m_refCount; }
/**
* @brief Decrements reference count of this thing by one.
*/
void remove_reference() { --m_refCount; }
/**
* @brief Returns reference count of this thing.
*
* @return The reference count.
*/
constexpr nref_t reference_count() const { return m_refCount; }
private: private:
thing_handle m_id; thing_handle m_id;
thing_t m_type; thing_t m_type;
context_p m_context; context_p m_context;
void* m_data = nullptr; nref_t m_refCount = 0;
void* m_data = nullptr;
}; };
} // namespace furvm } // namespace furvm
+42 -2
View File
@@ -1,6 +1,11 @@
#include "furvm/executor.hpp" #include "furvm/executor.hpp"
#include "furvm/context.hpp" // IWYU pragma: keep #include "furvm/context.hpp" // IWYU pragma: keep
#include "furvm/exceptions.hpp"
#include "furvm/instruction.hpp"
#include "furvm/thing.hpp"
#include <stdexcept>
namespace furvm { namespace furvm {
@@ -22,6 +27,7 @@ void executor::push_frame(const mod_p& mod, std::size_t position) {
} }
struct executor::frame executor::pop_frame() { struct executor::frame executor::pop_frame() {
if (m_frames.empty()) throw stack_underflow();
struct executor::frame frame = m_frames.top(); struct executor::frame frame = m_frames.top();
m_frames.pop(); m_frames.pop();
return frame; return frame;
@@ -32,19 +38,53 @@ struct executor::frame executor::frame() const {
} }
void executor::push_thing(const thing_p& thing) { void executor::push_thing(const thing_p& thing) {
thing->add_reference();
m_stack.push(thing); m_stack.push(thing);
} }
thing_p executor::pop_thing() { thing_p executor::pop_thing() {
if (m_frames.top().stackBase >= m_stack.size()) return {}; if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
thing_p top = std::move(m_stack.top()); thing_p top = std::move(m_stack.top());
m_stack.pop(); m_stack.pop();
top->remove_reference();
return top; return top;
} }
thing_p executor::thing() const { thing_p executor::thing() const {
if (m_frames.top().stackBase >= m_stack.size()) return {}; if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
return m_stack.top(); return m_stack.top();
} }
void executor::step() {
if ((m_flags & executor_flags::Suspended) == executor_flags::Suspended) return;
struct frame& frame = m_frames.top();
instruction_t instr = static_cast<instruction_t>(frame.mod->byte(frame.position++));
switch (instr) {
case instruction_t::NoOperation: break;
case instruction_t::PushB2I: {
auto thing = thing::create(m_context, thing_t::Int32);
thing->int32() = frame.mod->byte(frame.position++);
m_stack.push(std::move(thing));
} break;
case instruction_t::Drop: {
pop_thing();
} break;
case instruction_t::Return: {
pop_frame();
if (m_frames.empty()) m_flags = m_flags | executor_flags::Done;
} break;
case instruction_t::ReturnValue: {
auto value = pop_thing();
pop_frame();
m_stack.push(std::move(value));
} break;
case instruction_t::PushConstant:
case instruction_t::Duplicate: {
throw std::runtime_error("unimplemented");
}
}
}
} // namespace furvm } // namespace furvm
+6 -1
View File
@@ -8,9 +8,10 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
static constexpr std::array<furvm::byte, 3> s_bytecode = { static constexpr std::array<furvm::byte, 4> s_bytecode = {
furvm::byte(furvm::instruction_t::PushB2I), furvm::byte(furvm::instruction_t::PushB2I),
67, 67,
furvm::byte(furvm::instruction_t::Drop),
furvm::byte(furvm::instruction_t::Return), furvm::byte(furvm::instruction_t::Return),
}; };
@@ -22,6 +23,10 @@ int main(void) {
auto executor = furvm::executor::create(context); auto executor = furvm::executor::create(context);
executor->push_frame(mainModule, 0); executor->push_frame(mainModule, 0);
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
executor->step();
}
return 0; return 0;
} }
+56 -5
View File
@@ -5,29 +5,80 @@
#include "furvm/context.hpp" // IWYU pragma: keep #include "furvm/context.hpp" // IWYU pragma: keep
#include <cstdlib> #include <cstdlib>
#include <cstring>
#include <stdexcept>
namespace furvm { namespace furvm {
std::size_t thing_type_size(thing_t type) { std::size_t thing_type_size(thing_t type) {
switch (type) { switch (type) {
case thing_t::Int: return 4; case thing_t::Int32: return 4;
} }
return 0; return 0;
} }
thing::thing(rzecz, thing_handle id, thing_t type, const context_p& context) thing::thing(rzecz, thing_handle id, thing_t type, const context_p& context)
: m_id(id), m_type(type), m_context(context), m_data(std::calloc(1, thing_type_size(type))) {} : m_id(id), m_type(type), m_context(context) {
std::size_t size = thing_type_size(type);
std::byte* data = nullptr;
if (!m_context->m_deadThingData.empty()) {
thing_t itType{};
for (auto it = m_context->m_deadThingData.rbegin(); it != m_context->m_deadThingData.rend(); ++it) {
std::memcpy(&itType, static_cast<std::byte*>(*it) - sizeof(itType), sizeof(itType));
if (size == thing_type_size(itType)) {
data = static_cast<std::byte*>(*it);
m_context->m_deadThingData.erase(std::next(it).base());
break;
}
}
}
if (data == nullptr) data = m_context->m_thingArena.allocate<std::byte>(sizeof(type) + size);
if (data == nullptr) throw std::runtime_error("failed to allocate data for new thing");
std::memcpy(data, &type, sizeof(type));
m_data = data + sizeof(type);
switch (m_type) {
// Primitives are zero-initialized.
default:
case thing_t::Int32: std::memset(m_data, 0, size);
}
}
thing::~thing() { thing::~thing() {
std::free(m_data); switch (m_type) {
// Primitives are not destructed.
default:
case thing_t::Int32: break;
}
m_context->m_deadThingData.push_back(m_data);
} }
thing_p thing::create(const context_p& context, thing_t type) { thing_p thing::create(const context_p& context, thing_t type) {
auto th = std::make_shared<thing>(rzecz{}, context->m_things.size(), type, context); thing_handle id = context->m_things.size();
context->m_things.push_back(th); if (!context->m_deadThings.empty()) {
id = context->m_deadThings.front();
context->m_deadThings.pop();
id += 1 << GENERATION_SIZE;
}
thing_handle idx = id & ((1ULL << ((sizeof(id) * 8) - GENERATION_SIZE)) - 1);
auto th = std::make_shared<thing>(rzecz{}, id, type, context);
context->m_things.emplace(context->m_things.begin() + idx, th);
return std::move(th); return std::move(th);
} }
std::int32_t& thing::int32() {
if (m_type != thing_t::Int32) throw bad_thing_access();
return *static_cast<std::int32_t*>(m_data);
}
const std::int32_t& thing::int32() const {
if (m_type != thing_t::Int32) throw bad_thing_access();
return *static_cast<std::int32_t*>(m_data);
}
} // namespace furvm } // namespace furvm
// NOLINTEND(cppcoreguidelines-no-malloc) // NOLINTEND(cppcoreguidelines-no-malloc)