Compare commits

...

7 Commits

11 changed files with 428 additions and 15 deletions
+28 -1
View File
@@ -4,6 +4,7 @@
#include "furvm/fwd.hpp"
#include <stack>
#include <vector>
namespace furvm {
@@ -44,6 +45,8 @@ public:
mod_p mod; /**< Shared pointer to a module with the bytecode. */
std::size_t position; /**< Cursor to a current instruction in the bytecode. */
std::size_t stackBase; /**< Snapshot of the stack size before this frame. */
std::vector<thing_p> variables; /**< Frame variables. */
};
public:
/**
@@ -139,6 +142,30 @@ public:
* @return The thing.
*/
thing_p thing() const;
public:
/**
* @brief Stores a thing in a variable.
*
* @param variable Variable to store the thing in.
* @param thing Thing to store.
*/
void store_thing(variable_t variable, const thing_p& thing);
/**
* @brief Stores a thing in a variable.
*
* @param variable Variable to store the thing in.
* @param thing Thing to store.
*/
void store_thing(variable_t variable, thing_p&& thing);
/**
* @brief Returns a thing stored in a variable.
*
* @param variable Variable where the thing is stored.
* @return The thing stored in the variable.
*/
thing_p load_thing(variable_t variable) const;
public:
/**
* @brief Executes next instruction.
@@ -155,4 +182,4 @@ private:
} // namespace furvm
#endif // FURVM_EXECUTOR_HPP
#endif // FURVM_EXECUTOR_HPP
+6 -1
View File
@@ -132,6 +132,11 @@ using thing_handle = std::uint32_t;
// executor.hpp
/**
* @brief A variable index type.
*/
using variable_t = std::uint16_t;
/**
* @enum executor_flags
* @brief Flags of an executor.
@@ -181,4 +186,4 @@ class stack_underflow;
} // namespace furvm
#endif // FURVM_FWD_HPP
#endif // FURVM_FWD_HPP
+60 -1
View File
@@ -65,6 +65,50 @@ enum class instruction_t : byte {
*/
Mod,
/**
* @brief Compares two top-most things from the stack for equality.
*/
Equals,
/**
* @brief Compares two top-most things from the stack for inequality.
*/
NotEquals,
/**
* @brief Compares if the first top-most thing is less than the second top-most thing.
*/
LessThan,
/**
* @brief Compares if the first top-most thing is greater than the second top-most thing.
*/
GreaterThan,
/**
* @brief Compares if the first top-most thing is less than or equal to the second top-most thing.
*/
LessEqual,
/**
* @brief Compares if the first top-most thing is greater than or equal to the second top-most thing.
*/
GreaterEqual,
/**
* @brief Pushes a variable onto the stack.
*
* Fetches a variable denoted by next two bytes in little-endian and pushes it onto the stack.
*/
Load,
/**
* @brief Stores an element from the stack in a variable.
*
* Pops a thing from the stack and stores it in a variable denoted by next two bytes in little-endian.
*/
Store,
/**
* @brief Calls a function.
*
@@ -72,6 +116,21 @@ enum class instruction_t : byte {
*/
Call,
/**
* @brief Jumps to an instruction relative to the current instruction.
*
* Jumps to an instruction relative to the current instruction with offset denoted by next byte.
*/
Jump,
/**
* @brief Jumps to an instruction relative to the current instruction if top thing on the stack is not zero.
*
* Jumps to an instruction relative to the current instruction with offset denoted by next byte if the top thing on
* the stack is not zero (is true).
*/
JumpNotZero,
/**
* @brief Pops the current call frame.
*/
@@ -96,4 +155,4 @@ struct instruction {
} // namespace furvm
#endif // FURVM_INSTRUCTION_HPP
#endif // FURVM_INSTRUCTION_HPP
+2 -1
View File
@@ -9,6 +9,7 @@ namespace furvm {
class mod {
friend class function;
friend class serializer;
public:
using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */
public:
@@ -67,4 +68,4 @@ private:
} // namespace furvm
#endif // FURVM_MODULE_HPP
#endif // FURVM_MODULE_HPP
+20
View File
@@ -0,0 +1,20 @@
#ifndef FURVM_SERIALIZER_HPP
#define FURVM_SERIALIZER_HPP
#include "furvm/fwd.hpp"
#include <ostream>
namespace furvm {
class serializer {
public:
static constexpr byte MODULE_MAGIC[4] = { 'F', 'u', 'r', 'M' };
static constexpr std::uint32_t VERSION = 0;
public:
static bool serialize_module(std::ostream& os, const mod_p& mod);
};
} // namespace furvm
#endif // FURVM_SERIALIZER_HPP
+54 -1
View File
@@ -135,6 +135,59 @@ public:
* @return Shared pointer to result thing.
*/
friend thing_p operator%(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Compares two things for equality.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator==(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Compares two things for equality.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator!=(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Compares if the left thing is less than the right thing.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator<(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Compares if the left thing is greater than the right thing.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator>(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Compares if the left thing is less than or equal to the right thing.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator<=(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Compares if the left thing is greater than or equal to the right thing.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator>=(const thing_p& lhs, const thing_p& rhs);
public:
/**
* @brief Returns a new thing.
@@ -208,4 +261,4 @@ private:
} // namespace furvm
#endif // FURVM_THING_HPP
#endif // FURVM_THING_HPP
+77
View File
@@ -3,9 +3,11 @@
#include "furvm/context.hpp" // IWYU pragma: keep
#include "furvm/exceptions.hpp"
#include "furvm/function.hpp" // IWYU pragma: keep
#include "furvm/fwd.hpp"
#include "furvm/instruction.hpp"
#include "furvm/thing.hpp"
#include <cstdint>
#include <stdexcept>
namespace furvm {
@@ -62,6 +64,31 @@ thing_p executor::thing() const {
return m_stack.top();
}
void executor::store_thing(variable_t variable, const thing_p& thing) {
auto& frame = m_frames.top();
frame.variables.resize(variable + 1);
if (frame.variables[variable] != nullptr) {
frame.variables[variable]->remove_reference();
}
frame.variables[variable] = thing;
thing->add_reference();
}
void executor::store_thing(variable_t variable, thing_p&& thing) {
auto& frame = m_frames.top();
frame.variables.resize(variable + 1);
if (frame.variables[variable] != nullptr) {
frame.variables[variable]->remove_reference();
}
thing->add_reference();
frame.variables[variable] = std::move(thing);
}
thing_p executor::load_thing(variable_t variable) const {
const auto& frame = m_frames.top();
return frame.variables[variable];
}
void executor::step() {
if ((m_flags & executor_flags::Suspended) == executor_flags::Suspended) return;
@@ -109,6 +136,48 @@ void executor::step() {
auto lhs = pop_thing();
push_thing(lhs % rhs);
} break;
case instruction_t::Equals: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs == rhs);
} break;
case instruction_t::NotEquals: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs != rhs);
} break;
case instruction_t::LessThan: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs < rhs);
} break;
case instruction_t::GreaterThan: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs > rhs);
} break;
case instruction_t::LessEqual: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs <= rhs);
} break;
case instruction_t::GreaterEqual: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs >= rhs);
} break;
case instruction_t::Load: {
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
frame.position += 2;
push_thing(load_thing(variable));
} break;
case instruction_t::Store: {
variable_t variable = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
frame.position += 2;
store_thing(variable, std::move(pop_thing()));
} break;
case instruction_t::Call: {
function_handle funcId = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
@@ -124,6 +193,14 @@ void executor::step() {
} break;
}
} break;
case instruction_t::Jump: {
frame.position += frame.mod->byte(frame.position++);
} break;
case instruction_t::JumpNotZero: {
byte offset = frame.mod->byte(frame.position++);
auto cond = pop_thing();
if (cond->int32() != 0) frame.position += offset;
} break;
case instruction_t::Return: {
pop_frame();
if (m_frames.empty()) m_flags = m_flags | executor_flags::Done;
+9 -1
View File
@@ -17,6 +17,10 @@ function::function(function&& other) noexcept
case function_t::Native: {
new (&m_value.native) native_function(std::move(other.m_value.native));
} break;
case function_t::Import: {
m_value.imp.mod = other.m_value.imp.mod;
m_value.imp.function = other.m_value.imp.function;
} break;
}
other.m_value.position = 0;
}
@@ -34,10 +38,14 @@ function& function::operator=(function&& other) noexcept {
case function_t::Native: {
new (&m_value.native) native_function(std::move(other.m_value.native));
} break;
case function_t::Import: {
m_value.imp.mod = other.m_value.imp.mod;
m_value.imp.function = other.m_value.imp.function;
} break;
}
other.m_value.position = 0;
return *this;
}
} // namespace furvm
} // namespace furvm
+4 -8
View File
@@ -4,10 +4,12 @@
#include "furvm/executor.hpp"
#include "furvm/function.hpp"
#include "furvm/instruction.hpp"
#include "furvm/serializer.hpp"
#include "furvm/thing.hpp"
#include <array>
#include <cstddef>
#include <fstream>
#include <iostream>
#include <memory>
@@ -16,16 +18,9 @@ static constexpr std::array<furvm::byte, 8> s_bytecode = {
67,
furvm::byte(furvm::instruction_t::Clone),
furvm::byte(furvm::instruction_t::Add),
furvm::byte(furvm::instruction_t::Call),
1,
0,
furvm::byte(furvm::instruction_t::Return),
};
void print(furvm::executor& exec) {
std::cout << exec.pop_thing()->int32() << '\n';
}
int main(void) {
auto context = std::make_shared<furvm::context>();
@@ -33,7 +28,8 @@ int main(void) {
auto mainFunction = furvm::function::create(mainModule, 0);
auto printFunction = furvm::function::create(mainModule, print);
std::ofstream file("./test.furm", std::ios::binary);
furvm::serializer::serialize_module(file, mainModule);
auto executor = furvm::executor::create(context);
executor->push_frame(mainFunction);
+101
View File
@@ -0,0 +1,101 @@
#include "furvm/serializer.hpp"
#include "furvm/function.hpp" // IWYU pragma: keep
#include <array>
#include <cstring>
#include <istream>
#include <optional>
#include <ostream>
#include <stdexcept>
namespace furvm {
template <typename T>
static inline void write_raw(std::ostream& os, const T& value) {
os.write(reinterpret_cast<const char*>(&value), sizeof(T));
}
template <typename T>
static inline void read_raw(std::istream& is, T& value) {
is.read(reinterpret_cast<char*>(&value), sizeof(T));
}
static inline void write_u64(std::ostream& os, std::uint64_t value) {
os.put(static_cast<char>((value >> 0ULL) & 0xFF));
os.put(static_cast<char>((value >> 8ULL) & 0xFF));
os.put(static_cast<char>((value >> 16ULL) & 0xFF));
os.put(static_cast<char>((value >> 24ULL) & 0xFF));
os.put(static_cast<char>((value >> 32ULL) & 0xFF));
os.put(static_cast<char>((value >> 40ULL) & 0xFF));
os.put(static_cast<char>((value >> 48ULL) & 0xFF));
os.put(static_cast<char>((value >> 56ULL) & 0xFF));
}
static inline void read_u64(std::istream& is, std::uint64_t& value) {
value = (static_cast<std::uint64_t>(is.get()) << 0) | (static_cast<std::uint64_t>(is.get()) << 8) |
(static_cast<std::uint64_t>(is.get()) << 16) | (static_cast<std::uint64_t>(is.get()) << 24) |
(static_cast<std::uint64_t>(is.get()) << 32) | (static_cast<std::uint64_t>(is.get()) << 40) |
(static_cast<std::uint64_t>(is.get()) << 48) | (static_cast<std::uint64_t>(is.get()) << 56);
}
static inline void write_u32(std::ostream& os, std::uint32_t value) {
os.put(static_cast<char>((value >> 0ULL) & 0xFF));
os.put(static_cast<char>((value >> 8ULL) & 0xFF));
os.put(static_cast<char>((value >> 16ULL) & 0xFF));
os.put(static_cast<char>((value >> 24ULL) & 0xFF));
}
static inline void read_u32(std::istream& is, std::uint32_t& value) {
value = static_cast<std::uint32_t>(is.get() << 0) | static_cast<std::uint32_t>(is.get() << 8) |
static_cast<std::uint32_t>(is.get() << 16) | static_cast<std::uint32_t>(is.get() << 24);
}
static inline void write_u16(std::ostream& os, std::uint16_t value) {
os.put(static_cast<char>((value >> 0ULL) & 0xFF));
os.put(static_cast<char>((value >> 8ULL) & 0xFF));
}
static inline void read_u16(std::istream& is, std::uint16_t& value) {
value = static_cast<std::uint16_t>(is.get() << 0) | static_cast<std::uint16_t>(is.get() << 8);
}
static inline void write_u8(std::ostream& os, std::uint8_t value) {
os.put(static_cast<char>((value >> 0ULL) & 0xFF));
}
static inline void read_u8(std::istream& is, std::uint8_t& value) {
value = static_cast<std::uint8_t>(is.get() << 0);
}
bool serializer::serialize_module(std::ostream& os, const mod_p& mod) {
os.write(reinterpret_cast<const char*>(MODULE_MAGIC), sizeof(MODULE_MAGIC));
write_u32(os, VERSION);
write_u32(os, mod->m_functions.size());
for (const auto& func : mod->m_functions) {
write_u16(os, func->id());
write_u8(os, static_cast<std::uint8_t>(func->type()));
switch (func->type()) {
case function_t::Normal: {
write_u64(os, func->position());
} break;
case function_t::Native: {
// TODO: Replace with a reference to the function's name from constant pool
throw std::runtime_error("cannot serialize native functions yet");
} break;
case function_t::Import: {
// TODO: Replace with a reference to the module's and function's name from constant pool
throw std::runtime_error("cannot serialize import functions yet");
} break;
}
}
write_u64(os, mod->m_bytecode.size());
os.write(reinterpret_cast<const char*>(mod->m_bytecode.data()),
static_cast<std::streamsize>(mod->m_bytecode.size()));
return false;
}
} // namespace furvm
+67 -1
View File
@@ -141,6 +141,72 @@ thing_p operator%(const thing_p& lhs, const thing_p& rhs) {
}
}
thing_p operator==(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = static_cast<std::int32_t>(lhs->int32() == rhs->int32());
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator!=(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = static_cast<std::int32_t>(lhs->int32() != rhs->int32());
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator<(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = static_cast<std::int32_t>(lhs->int32() < rhs->int32());
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator>(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = static_cast<std::int32_t>(lhs->int32() > rhs->int32());
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator<=(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = static_cast<std::int32_t>(lhs->int32() <= rhs->int32());
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator>=(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = static_cast<std::int32_t>(lhs->int32() >= rhs->int32());
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p thing::clone(const thing_p& thing) {
thing_handle id = thing->m_context->m_things.size();
if (!thing->m_context->m_deadThings.empty()) {
@@ -174,4 +240,4 @@ const std::int32_t& thing::int32() const {
} // namespace furvm
// NOLINTEND(cppcoreguidelines-no-malloc)
// NOLINTEND(cppcoreguidelines-no-malloc)