Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
- magic numbers for tables start in bytecode files have been changed from 0x01, 0x02, 0x03 to 0xA1, 0xA2, 0xA3 (symbols, values, code) to make them stand out in hex editors
- magic numbers for value types in bytecode files have been changed from 0x01, 0x02, 0x03 to 0xF1, 0xF2, 0xF3 (number, string, function)
- numbers in the values table in bytecode files are no longer stringified but their IEEE754 representation is now encoded on 12 bytes (4 for the exponent, 8 for the mantissa)
- changed how scopes are stored inside the VM to enhance performances. All scope data are now contiguous!

### Removed
- removed unused `NodeType::Closure`
Expand Down
1 change: 1 addition & 0 deletions include/Ark/Constants.hpp.in
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ namespace Ark
constexpr std::size_t MaxMacroProcessingDepth = 256; ///< Controls the number of recursive calls to MacroProcessor::processNode
constexpr std::size_t MaxMacroUnificationDepth = 256; ///< Controls the number of recursive calls to MacroProcessor::unify
constexpr std::size_t VMStackSize = 8192;
constexpr std::size_t ScopeStackSize = 8192;
}

#endif
14 changes: 9 additions & 5 deletions include/Ark/VM/ExecutionContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @version 0.1
* @date 2021-11-15
*
* @copyright Copyright (c) 2021-2024
* @copyright Copyright (c) 2021-2025
*
*/

Expand All @@ -19,7 +19,8 @@

#include <Ark/Constants.hpp>
#include <Ark/VM/Value.hpp>
#include <Ark/VM/Scope.hpp>
#include <Ark/VM/ScopeView.hpp>
#include <Ark/VM/Value/ClosureScope.hpp>

#ifdef max
# undef max
Expand All @@ -38,9 +39,12 @@ namespace Ark::internal
uint16_t last_symbol;
const bool primary; ///< Tells if the current ExecutionContext is the primary one or not

std::optional<Scope> saved_scope {}; ///< Scope created by CAPTURE <x> instructions, used by the MAKE_CLOSURE instruction
std::vector<Scope> locals {};
std::vector<std::shared_ptr<Scope>> stacked_closure_scopes {}; ///< Stack the closure scopes to keep the closure alive as long as we are calling them
std::optional<ClosureScope> saved_scope {}; ///< Scope created by CAPTURE <x> instructions, used by the MAKE_CLOSURE instruction
std::vector<std::shared_ptr<ClosureScope>> stacked_closure_scopes {}; ///< Stack the closure scopes to keep the closure alive as long as we are calling them

std::vector<ScopeView> locals {};
std::array<ScopeView::pair_t, ScopeStackSize> scopes_storage {}; ///< All the ScopeView use this array to store id->value

std::array<Value, VMStackSize> stack {};

ExecutionContext() noexcept :
Expand Down
54 changes: 40 additions & 14 deletions include/Ark/VM/Scope.hpp → include/Ark/VM/ScopeView.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
* @version 0.2
* @date 2020-10-27
*
* @copyright Copyright (c) 2020-2024
* @copyright Copyright (c) 2020-2025
*
*/

#ifndef ARK_VM_SCOPE_HPP
#define ARK_VM_SCOPE_HPP

#include <vector>
#include <array>
#include <cinttypes>

#include <Ark/Platform.hpp>
Expand All @@ -24,21 +24,23 @@ namespace Ark::internal
* @brief A class to handle the VM scope more efficiently
*
*/
class ARK_API Scope
class ARK_API ScopeView
{
public:
using pair_t = std::pair<uint16_t, Value>;

/**
* @brief Construct a new Scope object
*
* @brief Deleted constructor to avoid creating ScopeViews pointing to nothing. Helps catch bugs at compile time
*/
Scope() noexcept;
ScopeView() = delete;

/**
* @brief Merge values from this scope as refs in the other scope
* @details This scope must be kept alive for the ref to be used
* @param other
* @brief Create a new ScopeView
*
* @param storage pointer to the shared scope storage
* @param start first free starting position
*/
void mergeRefInto(Scope& other);
ScopeView(pair_t* storage, std::size_t start) noexcept;

/**
* @brief Put a value in the scope
Expand Down Expand Up @@ -89,20 +91,44 @@ namespace Ark::internal
*/
[[nodiscard]] uint16_t idFromValue(const Value& val) const noexcept;

/**
* @brief Return the start index of the current
*
* @return const std::size_t
*/
[[nodiscard]] inline const pair_t& atPos(const std::size_t i) const noexcept
{
return m_storage[m_start + i];
}

/**
* @brief Return the size of the scope
*
* @return const std::size_t
*/
[[nodiscard]] std::size_t size() const noexcept;
[[nodiscard]] inline std::size_t size() const noexcept
{
return m_size;
}

/**
* @brief Compute the position of the first free slot in the shared storage, after this scope
*
* @return std::size_t
*/
[[nodiscard]] inline std::size_t storageEnd() const noexcept
{
return m_start + m_size;
}

friend ARK_API bool operator==(const Scope& A, const Scope& B) noexcept;
friend ARK_API bool operator==(const ScopeView& A, const ScopeView& B) noexcept;

friend class Ark::VM;
friend class Ark::internal::Closure;

private:
std::vector<std::pair<uint16_t, Value>> m_data;
pair_t* m_storage;
std::size_t m_start;
std::size_t m_size;
uint16_t m_min_id; ///< Minimum stored ID, used for a basic bloom filter
uint16_t m_max_id; ///< Maximum stored ID, used for a basic bloom filter
};
Expand Down
3 changes: 2 additions & 1 deletion include/Ark/VM/VM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* @version 2.0
* @date 2020-10-27
*
* @copyright Copyright (c) 2020-2024
* @copyright Copyright (c) 2020-2025
*
*/

Expand All @@ -25,6 +25,7 @@
#include <Ark/Compiler/Instructions.hpp>
#include <Ark/VM/Value.hpp>
#include <Ark/VM/State.hpp>
#include <Ark/VM/ScopeView.hpp>
#include <Ark/VM/ErrorKind.hpp>
#include <Ark/VM/ExecutionContext.hpp>
#include <Ark/Builtins/Builtins.hpp>
Expand Down
9 changes: 4 additions & 5 deletions include/Ark/VM/VM.inl
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,6 @@ inline void VM::returnFromFuncCall(internal::ExecutionContext& context)
{
--context.fc;
context.stacked_closure_scopes.pop_back();
// NOTE: high cpu cost because destroying variants cost
context.locals.pop_back();
}

Expand Down Expand Up @@ -332,8 +331,8 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc)
{
const PageAddr_t new_page_pointer = function.pageAddr();

// create dedicated frame
context.locals.emplace_back();
// create dedicated scope
context.locals.emplace_back(context.scopes_storage.data(), context.locals.back().storageEnd());
swapStackForFunCall(argc, context);

// store "reference" to the function to speed the recursive functions
Expand All @@ -351,8 +350,8 @@ inline void VM::call(internal::ExecutionContext& context, const uint16_t argc)
Closure& c = function.refClosure();
const PageAddr_t new_page_pointer = c.pageAddr();

// create dedicated frame
context.locals.emplace_back();
// create dedicated scope
context.locals.emplace_back(context.scopes_storage.data(), context.locals.back().storageEnd());
// load saved scope
c.refScope().mergeRefInto(context.locals.back());
context.stacked_closure_scopes.back() = c.scopePtr();
Expand Down
2 changes: 2 additions & 0 deletions include/Ark/VM/Value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ namespace Ark
return A.string().empty();

case ValueType::User:
[[fallthrough]];
case ValueType::Nil:
[[fallthrough]];
case ValueType::False:
return true;

Expand Down
18 changes: 9 additions & 9 deletions include/Ark/VM/Value/Closure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ namespace Ark

namespace Ark::internal
{
class Scope;

using PageAddr_t = uint16_t;

class ClosureScope;

/**
* @brief Closure management
*
Expand All @@ -42,18 +42,18 @@ namespace Ark::internal
* @param scope the scope of the function turned into a closure
* @param pa the current page address of the function turned into a closure
*/
Closure(const Scope& scope, PageAddr_t pa) noexcept;
Closure(const ClosureScope& scope, PageAddr_t pa) noexcept;

/**
* @brief Construct a new Closure object
* @param scope_ptr a shared pointer to the scope of the function turned into a closure
* @param pa the current page address of the function turned into a closure
*/
Closure(const std::shared_ptr<Scope>& scope_ptr, PageAddr_t pa) noexcept;
Closure(const std::shared_ptr<ClosureScope>& scope_ptr, PageAddr_t pa) noexcept;

[[nodiscard]] const Scope& scope() const noexcept { return *m_scope; }
[[nodiscard]] Scope& refScope() const noexcept { return *m_scope; }
[[nodiscard]] const std::shared_ptr<Scope>& scopePtr() const { return m_scope; }
[[nodiscard]] const ClosureScope& scope() const noexcept { return *m_scope; }
[[nodiscard]] ClosureScope& refScope() const noexcept { return *m_scope; }
[[nodiscard]] const std::shared_ptr<ClosureScope>& scopePtr() const { return m_scope; }

/**
*
Expand All @@ -68,7 +68,7 @@ namespace Ark::internal
* @param vm
* @return true if the closure has a field which is the end of 'end'
*/
[[nodiscard]] bool hasFieldEndingWith(const std::string& end, VM& vm) const;
[[nodiscard]] bool hasFieldEndingWith(const std::string& end, const VM& vm) const;

/**
* @brief Print the closure to a string
Expand All @@ -81,7 +81,7 @@ namespace Ark::internal
friend ARK_API_INLINE bool operator<(const Closure& A, const Closure& B) noexcept;

private:
std::shared_ptr<Scope> m_scope;
std::shared_ptr<ClosureScope> m_scope;
// keep track of the code page number, in case we need it later
PageAddr_t m_page_addr;
};
Expand Down
71 changes: 71 additions & 0 deletions include/Ark/VM/Value/ClosureScope.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* @file ClosureScope.hpp
* @author Alexandre Plateau (lexplt.dev@gmail.com)
* @brief Subtype of the value type, handling closures
* @version 0.1
* @date 2025-03-17
*
* @copyright Copyright (c) 2025
*
*/

#ifndef ARK_VM_VALUE_CLOSURESCOPE_HPP
#define ARK_VM_VALUE_CLOSURESCOPE_HPP

#include <vector>
#include <utility>
#include <cinttypes>

#include <Ark/Platform.hpp>
#include <Ark/VM/Value.hpp>

namespace Ark::internal
{
class ScopeView;

/**
* @brief A class to store fields captured by a closure
*/
class ARK_API ClosureScope
{
public:
/**
* @brief Create a new ClosureScope
*/
ClosureScope() noexcept = default;

/**
* @brief Put a value in the scope
*
* @param id The symbol id of the variable
* @param val The value linked to the symbol
*/
void push_back(uint16_t id, Value&& val);

/**
* @brief Put a value in the scope
*
* @param id The symbol id of the variable
* @param val The value linked to the symbol
*/
void push_back(uint16_t id, const Value& val);

Value* operator[](uint16_t id_to_look_for);

/**
* @brief Merge values from this scope as refs in the other scope
* @details This scope must be kept alive for the ref to be used
* @param other
*/
void mergeRefInto(ScopeView& other);

friend class Closure;

friend ARK_API bool operator==(const ClosureScope& A, const ClosureScope& B) noexcept;

private:
std::vector<std::pair<uint16_t, Value>> m_data;
};
}

#endif // ARK_VM_VALUE_CLOSURESCOPE_HPP
Loading
Loading