From f6f716e8c98b786f12076b41a6fe6ee053ace899 Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Sun, 14 Jun 2026 14:22:45 +0200 Subject: [PATCH 1/2] feat(furvm): add variables --- furvm/include/furvm/executor.hpp | 29 +++++++++++++++++++++- furvm/include/furvm/fwd.hpp | 7 +++++- furvm/include/furvm/instruction.hpp | 16 ++++++++++++- furvm/src/executor.cpp | 37 +++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/furvm/include/furvm/executor.hpp b/furvm/include/furvm/executor.hpp index 15f6af1..a2c264c 100644 --- a/furvm/include/furvm/executor.hpp +++ b/furvm/include/furvm/executor.hpp @@ -4,6 +4,7 @@ #include "furvm/fwd.hpp" #include +#include 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 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 \ No newline at end of file +#endif // FURVM_EXECUTOR_HPP diff --git a/furvm/include/furvm/fwd.hpp b/furvm/include/furvm/fwd.hpp index c7bb8ff..aaa832a 100644 --- a/furvm/include/furvm/fwd.hpp +++ b/furvm/include/furvm/fwd.hpp @@ -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 \ No newline at end of file +#endif // FURVM_FWD_HPP diff --git a/furvm/include/furvm/instruction.hpp b/furvm/include/furvm/instruction.hpp index 5ec3714..2a73ea7 100644 --- a/furvm/include/furvm/instruction.hpp +++ b/furvm/include/furvm/instruction.hpp @@ -65,6 +65,20 @@ enum class instruction_t : byte { */ Mod, + /** + * @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. * @@ -96,4 +110,4 @@ struct instruction { } // namespace furvm -#endif // FURVM_INSTRUCTION_HPP \ No newline at end of file +#endif // FURVM_INSTRUCTION_HPP diff --git a/furvm/src/executor.cpp b/furvm/src/executor.cpp index 4a6cb78..e4b5f28 100644 --- a/furvm/src/executor.cpp +++ b/furvm/src/executor.cpp @@ -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 #include 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,16 @@ void executor::step() { auto lhs = pop_thing(); push_thing(lhs % rhs); } break; + case instruction_t::Load: { + variable_t variable = static_cast(frame.mod->byte(frame.position)) | + (static_cast(frame.mod->byte(frame.position + 1)) << 8); + push_thing(load_thing(variable)); + } break; + case instruction_t::Store: { + variable_t variable = static_cast(frame.mod->byte(frame.position)) | + (static_cast(frame.mod->byte(frame.position + 1)) << 8); + store_thing(variable, std::move(pop_thing())); + } break; case instruction_t::Call: { function_handle funcId = static_cast(frame.mod->byte(frame.position)) | (static_cast(frame.mod->byte(frame.position + 1)) << 8); From 759ccad7a6193fafffba5176321640a67e0344bd Mon Sep 17 00:00:00 2001 From: CHatingPython Date: Sun, 14 Jun 2026 15:19:12 +0200 Subject: [PATCH 2/2] feat(furvm): add serializer --- furvm/include/furvm/module.hpp | 3 +- furvm/include/furvm/serializer.hpp | 20 ++++++ furvm/src/main.cpp | 12 ++-- furvm/src/serializer.cpp | 101 +++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 furvm/include/furvm/serializer.hpp create mode 100644 furvm/src/serializer.cpp diff --git a/furvm/include/furvm/module.hpp b/furvm/include/furvm/module.hpp index adc4957..709a763 100644 --- a/furvm/include/furvm/module.hpp +++ b/furvm/include/furvm/module.hpp @@ -9,6 +9,7 @@ namespace furvm { class mod { friend class function; + friend class serializer; public: using bytecode_t = std::vector; /**< An alias to a vector of bytes. */ public: @@ -67,4 +68,4 @@ private: } // namespace furvm -#endif // FURVM_MODULE_HPP \ No newline at end of file +#endif // FURVM_MODULE_HPP diff --git a/furvm/include/furvm/serializer.hpp b/furvm/include/furvm/serializer.hpp new file mode 100644 index 0000000..276dd43 --- /dev/null +++ b/furvm/include/furvm/serializer.hpp @@ -0,0 +1,20 @@ +#ifndef FURVM_SERIALIZER_HPP +#define FURVM_SERIALIZER_HPP + +#include "furvm/fwd.hpp" + +#include + +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 diff --git a/furvm/src/main.cpp b/furvm/src/main.cpp index 87ebe72..16a07bd 100644 --- a/furvm/src/main.cpp +++ b/furvm/src/main.cpp @@ -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 #include +#include #include #include @@ -16,16 +18,9 @@ static constexpr std::array 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(); @@ -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); diff --git a/furvm/src/serializer.cpp b/furvm/src/serializer.cpp new file mode 100644 index 0000000..94a8a71 --- /dev/null +++ b/furvm/src/serializer.cpp @@ -0,0 +1,101 @@ +#include "furvm/serializer.hpp" + +#include "furvm/function.hpp" // IWYU pragma: keep + +#include +#include +#include +#include +#include +#include + +namespace furvm { + +template +static inline void write_raw(std::ostream& os, const T& value) { + os.write(reinterpret_cast(&value), sizeof(T)); +} + +template +static inline void read_raw(std::istream& is, T& value) { + is.read(reinterpret_cast(&value), sizeof(T)); +} + +static inline void write_u64(std::ostream& os, std::uint64_t value) { + os.put(static_cast((value >> 0ULL) & 0xFF)); + os.put(static_cast((value >> 8ULL) & 0xFF)); + os.put(static_cast((value >> 16ULL) & 0xFF)); + os.put(static_cast((value >> 24ULL) & 0xFF)); + os.put(static_cast((value >> 32ULL) & 0xFF)); + os.put(static_cast((value >> 40ULL) & 0xFF)); + os.put(static_cast((value >> 48ULL) & 0xFF)); + os.put(static_cast((value >> 56ULL) & 0xFF)); +} + +static inline void read_u64(std::istream& is, std::uint64_t& value) { + value = (static_cast(is.get()) << 0) | (static_cast(is.get()) << 8) | + (static_cast(is.get()) << 16) | (static_cast(is.get()) << 24) | + (static_cast(is.get()) << 32) | (static_cast(is.get()) << 40) | + (static_cast(is.get()) << 48) | (static_cast(is.get()) << 56); +} + +static inline void write_u32(std::ostream& os, std::uint32_t value) { + os.put(static_cast((value >> 0ULL) & 0xFF)); + os.put(static_cast((value >> 8ULL) & 0xFF)); + os.put(static_cast((value >> 16ULL) & 0xFF)); + os.put(static_cast((value >> 24ULL) & 0xFF)); +} + +static inline void read_u32(std::istream& is, std::uint32_t& value) { + value = static_cast(is.get() << 0) | static_cast(is.get() << 8) | + static_cast(is.get() << 16) | static_cast(is.get() << 24); +} + +static inline void write_u16(std::ostream& os, std::uint16_t value) { + os.put(static_cast((value >> 0ULL) & 0xFF)); + os.put(static_cast((value >> 8ULL) & 0xFF)); +} + +static inline void read_u16(std::istream& is, std::uint16_t& value) { + value = static_cast(is.get() << 0) | static_cast(is.get() << 8); +} + +static inline void write_u8(std::ostream& os, std::uint8_t value) { + os.put(static_cast((value >> 0ULL) & 0xFF)); +} + +static inline void read_u8(std::istream& is, std::uint8_t& value) { + value = static_cast(is.get() << 0); +} + +bool serializer::serialize_module(std::ostream& os, const mod_p& mod) { + os.write(reinterpret_cast(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(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(mod->m_bytecode.data()), + static_cast(mod->m_bytecode.size())); + + return false; +} + +} // namespace furvm