Compare commits
8 Commits
bfdbbb0d5f
...
8afeabfd69
| Author | SHA1 | Date | |
|---|---|---|---|
|
8afeabfd69
|
|||
|
a4fd939cd2
|
|||
|
26d77bdc02
|
|||
|
d86f38d166
|
|||
|
874d1f14cb
|
|||
|
48c6da1134
|
|||
|
464b24eeed
|
|||
|
e7e5f36380
|
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,11 +98,43 @@ 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;
|
||||||
|
|
||||||
|
nref_t m_refCount = 0;
|
||||||
void* m_data = nullptr;
|
void* m_data = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+42
-2
@@ -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
@@ -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
@@ -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)
|
||||||
Reference in New Issue
Block a user