~maelkum/viuavm

c511c37c9cc4f9a14415d920e587cc579e2f6ac6 — Marek Marecki 7 days ago 1e1c3ac devel
Rework storing stuff in and fetching stuff from registers

The last attempt was half-assed and broke down pretty quickly. This time
I will do better (at least that's what I am telling myself).

A register set is an array of cells, and each cell may be empty or hold
a value. The value may be of a primitive, unboxed type (int, float), or
of a boxed type (eg, a string or a struct).

Instructions which create new values or move values between registers
use the "slot" view of registers. For them it is not important WHAT
exactly the register contains, as they will just reset the slots
contents or shuffle them around.

Instructions which mutate existing values use the "cell" view of
registers. They are not concerned by WHERE exactly the cell is, as they
will only use its value.

Of course, there are instructions which view some of their operands
through the "slot" lens and some through the "cell view" lens. An
instruction may change its point of view operand by operand.
M new/include/viua/vm/core.h => new/include/viua/vm/core.h +37 -52
@@ 46,8 46,8 @@ struct Env {
};

struct Value {
    using boxed_type = std::unique_ptr<viua::vm::types::Value>;
    using value_type = viua::vm::types::Register_cell;
    using value_type = viua::vm::types::Cell;
    using boxed_type = value_type::boxed_type;
    value_type value;

    Value()             = default;


@@ 67,105 67,90 @@ struct Value {
    }
    inline auto operator=(int64_t const v) -> Value&
    {
        value = v;
        value.content = v;
        return *this;
    }
    inline auto operator=(uint64_t const v) -> Value&
    {
        value = v;
        value.content = v;
        return *this;
    }
    inline auto operator=(float const v) -> Value&
    {
        value = v;
        value.content = v;
        return *this;
    }
    inline auto operator=(double const v) -> Value&
    {
        value = v;
        value.content = v;
        return *this;
    }

    inline auto is_boxed() const -> bool
    {
        return std::holds_alternative<boxed_type>(value);
        return std::holds_alternative<boxed_type>(value.content);
    }
    inline auto is_void() const -> bool
    {
        return std::holds_alternative<std::monostate>(value);
        return std::holds_alternative<std::monostate>(value.content);
    }
    inline auto make_void() -> void
    {
        value = std::monostate{};
        value.content = std::monostate{};
    }

    inline auto boxed_value() const -> boxed_type::element_type const&
    {
        return *std::get<boxed_type>(value);
        return *std::get<boxed_type>(value.content);
    }
    inline auto boxed_value() -> boxed_type::element_type&
    {
        return *std::get<boxed_type>(value);
        return *std::get<boxed_type>(value.content);
    }
    inline auto value_cell() -> viua::vm::types::Value_cell
    inline auto value_cell() -> viua::vm::types::Cell&
    {
        if (holds<int64_t>()) {
            auto t = std::get<int64_t>(value);
            make_void();
            return t;
        } else if (holds<uint64_t>()) {
            auto t = std::get<uint64_t>(value);
            make_void();
            return t;
        } else if (holds<float>()) {
            auto t = std::get<float>(value);
            make_void();
            return t;
        } else if (holds<double>()) {
            auto t = std::get<double>(value);
            make_void();
            return t;
        } else if (holds<boxed_type>()) {
            auto t = std::move(std::get<boxed_type>(value));
            make_void();
            return t;
        } else {
        if (is_void()) {
            throw std::bad_cast{};
        }
        return value;
    }

    template<typename T> auto holds() const -> bool
    {
        if (std::is_same_v<
                T,
                void> and std::holds_alternative<std::monostate>(value)) {
                void> and std::holds_alternative<std::monostate>(value.content)) {
            return true;
        }
        if (std::is_same_v<
                T,
                int64_t> and std::holds_alternative<int64_t>(value)) {
                int64_t> and std::holds_alternative<int64_t>(value.content)) {
            return true;
        }
        if (std::is_same_v<
                T,
                uint64_t> and std::holds_alternative<uint64_t>(value)) {
                uint64_t> and std::holds_alternative<uint64_t>(value.content)) {
            return true;
        }
        if (std::is_same_v<T, float> and std::holds_alternative<float>(value)) {
        if (std::is_same_v<
                T,
                float> and std::holds_alternative<float>(value.content)) {
            return true;
        }
        if (std::is_same_v<T,
                           double> and std::holds_alternative<double>(value)) {
        if (std::is_same_v<
                T,
                double> and std::holds_alternative<double>(value.content)) {
            return true;
        }
        if (std::is_same_v<
                T,
                boxed_type> and std::holds_alternative<boxed_type>(value)) {
                boxed_type> and std::holds_alternative<boxed_type>(value.content)) {
            return true;
        }
        if constexpr (std::is_convertible_v<T*, viua::vm::types::Value*>) {
            if (std::holds_alternative<boxed_type>(value)
                and dynamic_cast<T*>(std::get<boxed_type>(value).get())) {
            if (std::holds_alternative<boxed_type>(value.content)
                and dynamic_cast<T*>(
                    std::get<boxed_type>(value.content).get())) {
                return true;
            }
        }


@@ 173,21 158,21 @@ struct Value {
    }
    template<typename T> auto cast_to() const -> T
    {
        if (std::holds_alternative<T>(value)) {
            return std::get<T>(value);
        if (std::holds_alternative<T>(value.content)) {
            return std::get<T>(value.content);
        }

        if (std::holds_alternative<int64_t>(value)) {
            return static_cast<T>(std::get<int64_t>(value));
        if (std::holds_alternative<int64_t>(value.content)) {
            return static_cast<T>(std::get<int64_t>(value.content));
        }
        if (std::holds_alternative<uint64_t>(value)) {
            return static_cast<T>(std::get<uint64_t>(value));
        if (std::holds_alternative<uint64_t>(value.content)) {
            return static_cast<T>(std::get<uint64_t>(value.content));
        }
        if (std::holds_alternative<float>(value)) {
            return static_cast<T>(std::get<float>(value));
        if (std::holds_alternative<float>(value.content)) {
            return static_cast<T>(std::get<float>(value.content));
        }
        if (std::holds_alternative<double>(value)) {
            return static_cast<T>(std::get<double>(value));
        if (std::holds_alternative<double>(value.content)) {
            return static_cast<T>(std::get<double>(value.content));
        }

        using viua::vm::types::Float_double;

M new/include/viua/vm/types.h => new/include/viua/vm/types.h +11 -9
@@ 116,10 116,10 @@ struct String
    auto type_name() const -> std::string override;
    auto to_string() const -> std::string override;
    operator bool() const override;
    auto operator()(traits::Plus::tag_type const, Register_cell const&) const
        -> Register_cell override;
    auto operator()(traits::Eq::tag_type const, Register_cell const&) const
        -> Register_cell override;
    auto operator()(traits::Plus::tag_type const, Cell const&) const
        -> Cell override;
    auto operator()(traits::Eq::tag_type const, Cell const&) const
        -> Cell override;
};

struct Atom


@@ 130,8 130,8 @@ struct Atom

    auto type_name() const -> std::string override;
    auto to_string() const -> std::string override;
    auto operator()(traits::Eq::tag_type const, Register_cell const&) const
        -> Register_cell override;
    auto operator()(traits::Eq::tag_type const, Cell const&) const
        -> Cell override;
};

struct Struct


@@ 144,11 144,13 @@ struct Struct
struct Buffer
        : Value
        , traits::To_string {
    std::vector<Value_cell> values;
    using value_type = Cell;

    std::vector<value_type> values;
    using size_type = decltype(values)::size_type;

    auto push(Value_cell&&) -> void;
    auto pop(size_type) -> Value_cell;
    auto push(value_type&&) -> void;
    auto pop(size_type) -> value_type;
    auto size() const -> size_type;

    auto type_name() const -> std::string override;

M new/include/viua/vm/types/traits.h => new/include/viua/vm/types/traits.h +4 -5
@@ 34,11 34,10 @@ namespace viua::vm::types::traits {
    static constexpr tag_type tag \
    {}

#define VIUA_TRAIT_BODY(Trait)                                    \
    VIUA_TRAIT_TAG();                                             \
    virtual auto operator()(tag_type const, Register_cell const&) \
        const->Register_cell = 0;                                 \
    virtual ~Trait()         = default
#define VIUA_TRAIT_BODY(Trait)                                            \
    VIUA_TRAIT_TAG();                                                     \
    virtual auto operator()(tag_type const, Cell const&) const->Cell = 0; \
    virtual ~Trait()                                                 = default

#define VIUA_TRAIT(Trait)       \
    struct Trait {              \

M new/include/viua/vm/types/value.h => new/include/viua/vm/types/value.h +71 -10
@@ 30,14 30,76 @@


namespace viua::vm::types {
using Register_cell = std::variant<std::monostate,
                                   int64_t,
                                   uint64_t,
                                   float,
                                   double,
                                   std::unique_ptr<class Value>>;
using Value_cell    = std::
    variant<int64_t, uint64_t, float, double, std::unique_ptr<class Value>>;
struct Cell_view {
    using boxed_type = class Value;
    using void_type  = std::monostate;
    using value_type = std::variant<void_type,
                                    std::reference_wrapper<int64_t>,
                                    std::reference_wrapper<uint64_t>,
                                    std::reference_wrapper<float>,
                                    std::reference_wrapper<double>,
                                    std::reference_wrapper<boxed_type>>;

    value_type content;

    Cell_view() = delete;
    Cell_view(class Cell&);
    explicit Cell_view(boxed_type&);

    template<typename T> auto holds() const -> bool
    {
        if (std::is_same_v<
                T,
                void> and std::holds_alternative<void_type>(content)) {
            return true;
        }

        using Tr = std::reference_wrapper<T>;
        return std::holds_alternative<Tr>(content);
    }

    template<typename T> auto get() -> T&
    {
        return std::get<std::reference_wrapper<T>>(content);
    }
    template<typename T> auto get() const -> T const&
    {
        return std::get<std::reference_wrapper<T>>(content);
    }
};

struct Cell {
    using boxed_type = std::unique_ptr<class Value>;
    using void_type  = std::monostate;
    using value_type =
        std::variant<void_type, int64_t, uint64_t, float, double, boxed_type>;

    value_type content;

    Cell() = default;
    template<typename T> explicit Cell(T&& v) : content{std::move(v)}
    {}

    auto view() -> Cell_view
    {
        return Cell_view{*this};
    }

    template<typename T> auto operator=(T&& v) -> Cell&
    {
        content = std::move(v);
        return *this;
    }

    template<typename T> auto get() -> T&
    {
        return std::get<T>(content);
    }
    template<typename T> auto get() const -> T const&
    {
        return std::get<T>(content);
    }
};

class Value {
  public:


@@ 62,8 124,7 @@ class Value {

        return fn(*dynamic_cast<Trait const*>(this));
    }
    template<typename Trait>
    auto as_trait(Register_cell const& cell) const -> Register_cell
    template<typename Trait> auto as_trait(Cell const& cell) const -> Cell
    {
        if (not has_trait<Trait>()) {
            throw std::runtime_error{type_name()

M new/src/tools/exec/asm.cpp => new/src/tools/exec/asm.cpp +6 -4
@@ 177,7 177,7 @@ auto Operand::make_access() const -> viua::arch::Register_access
    }

    auto const direct = (lx == TOKEN::RA_DIRECT);
    auto const index = std::stoul(ingredients.at(1).text);
    auto const index  = std::stoul(ingredients.at(1).text);
    if (ingredients.size() == 2) {
        return viua::arch::Register_access::make_local(index, direct);
    }


@@ 449,9 449,11 @@ auto parse_function_definition(
            if (lexemes.front() == TOKEN::RA_VOID) {
                operand.ingredients.push_back(
                    consume_token_of(TOKEN::RA_VOID, lexemes));
            } else if (look_ahead({ TOKEN::RA_DIRECT, TOKEN::RA_PTR_DEREF }, lexemes)) {
                auto const access = consume_token_of({ TOKEN::RA_DIRECT, TOKEN::RA_PTR_DEREF }, lexemes);
                auto index        = viua::libs::lexer::Lexeme{};
            } else if (look_ahead({TOKEN::RA_DIRECT, TOKEN::RA_PTR_DEREF},
                                  lexemes)) {
                auto const access = consume_token_of(
                    {TOKEN::RA_DIRECT, TOKEN::RA_PTR_DEREF}, lexemes);
                auto index = viua::libs::lexer::Lexeme{};
                try {
                    index = consume_token_of(TOKEN::LITERAL_INTEGER, lexemes);
                } catch (viua::libs::lexer::Lexeme const& e) {

M new/src/vm/ins.cpp => new/src/vm/ins.cpp +202 -134
@@ 35,13 35,89 @@ using namespace viua::arch::ins;
using viua::vm::Stack;
using ip_type = viua::arch::instruction_type const*;

auto get_value(std::vector<viua::vm::Value>& registers, size_t const i)
    -> viua::vm::types::Cell_view
{
    using viua::vm::types::Cell;
    using viua::vm::types::Cell_view;

    auto& c = registers.at(i);
    if (std::holds_alternative<Cell::boxed_type>(c.value.content)) {
        auto& b = std::get<Cell::boxed_type>(c.value.content);
        if (auto p = dynamic_cast<types::Pointer*>(b.get()); p) {
            return Cell_view{*p->value};
        }
    }

    return c.value.view();
}

template<typename T> auto cast_to(viua::vm::types::Cell_view value) -> T
{
    using viua::vm::types::Cell_view;

    using Tr = std::reference_wrapper<T>;

    if (std::holds_alternative<Tr>(value.content)) {
        return value.template get<T>();
    }

    if (value.template holds<int64_t>()) {
        return static_cast<T>(value.template get<int64_t>());
    }
    if (value.template holds<uint64_t>()) {
        return static_cast<T>(value.template get<uint64_t>());
    }
    if (value.template holds<float>()) {
        return static_cast<T>(value.template get<float>());
    }
    if (value.template holds<double>()) {
        return static_cast<T>(value.template get<double>());
    }

    if (not value.template holds<Cell_view::boxed_type>()) {
        throw std::bad_cast{};
    }

    auto const bv = &value.template get<Cell_view::boxed_type>();

    using viua::vm::types::Float_double;
    using viua::vm::types::Float_single;
    using viua::vm::types::Signed_integer;
    using viua::vm::types::Unsigned_integer;
    if (auto x = dynamic_cast<Signed_integer const*>(bv); x) {
        return static_cast<T>(x->value);
    }

    /*
    if (holds<Signed_integer>()) {
        return static_cast<T>(
            static_cast<Signed_integer const&>(boxed_value()).value);
    }
    if (holds<Unsigned_integer>()) {
        return static_cast<T>(
            static_cast<Unsigned_integer const&>(boxed_value()).value);
    }
    if (holds<Float_single>()) {
        return static_cast<T>(
            static_cast<Float_single const&>(boxed_value()).value);
    }
    if (holds<Float_double>()) {
        return static_cast<T>(
            static_cast<Float_double const&>(boxed_value()).value);
    }
    */

    throw std::bad_cast{};
}

template<typename Op, typename Trait>
auto execute_arithmetic_op(Op const op, Stack& stack, ip_type const ip) -> void
{
    auto& registers = stack.frames.back().registers;
    auto& out       = registers.at(op.instruction.out.index);
    auto& lhs       = registers.at(op.instruction.lhs.index);
    auto& rhs       = registers.at(op.instruction.rhs.index);
    auto rhs        = get_value(registers, op.instruction.rhs.index);

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)
                     + " " + op.instruction.out.to_string() + ", "


@@ 53,32 129,33 @@ auto execute_arithmetic_op(Op const op, Stack& stack, ip_type const ip) -> void
    using viua::vm::types::Signed_integer;
    using viua::vm::types::Unsigned_integer;
    if (lhs.template holds<int64_t>()) {
        out = typename Op::functor_type{}(std::get<int64_t>(lhs.value),
                                          rhs.template cast_to<int64_t>());
        out = typename Op::functor_type{}(lhs.value.template get<int64_t>(),
                                          cast_to<int64_t>(rhs));
    } else if (lhs.template holds<uint64_t>()) {
        out = typename Op::functor_type{}(std::get<uint64_t>(lhs.value),
                                          rhs.template cast_to<uint64_t>());
        out = typename Op::functor_type{}(lhs.value.template get<uint64_t>(),
                                          cast_to<uint64_t>(rhs));
    } else if (lhs.template holds<float>()) {
        out = typename Op::functor_type{}(std::get<float>(lhs.value),
                                          rhs.template cast_to<float>());
        out = typename Op::functor_type{}(lhs.value.template get<float>(),
                                          cast_to<float>(rhs));
    } else if (lhs.template holds<double>()) {
        out = typename Op::functor_type{}(std::get<double>(lhs.value),
                                          rhs.template cast_to<double>());
        out = typename Op::functor_type{}(lhs.value.template get<double>(),
                                          cast_to<double>(rhs));
    } else if (lhs.template holds<Signed_integer>()) {
        out = typename Op::functor_type{}(lhs.template cast_to<int64_t>(),
                                          rhs.template cast_to<int64_t>());
                                          cast_to<int64_t>(rhs));
    } else if (lhs.template holds<Unsigned_integer>()) {
        out = typename Op::functor_type{}(lhs.template cast_to<uint64_t>(),
                                          rhs.template cast_to<uint64_t>());
                                          cast_to<uint64_t>(rhs));
    } else if (lhs.template holds<Float_single>()) {
        out = typename Op::functor_type{}(lhs.template cast_to<float>(),
                                          rhs.template cast_to<float>());
                                          cast_to<float>(rhs));
    } else if (lhs.template holds<Float_double>()) {
        out = typename Op::functor_type{}(lhs.template cast_to<double>(),
                                          rhs.template cast_to<double>());
    } else if (lhs.template has_trait<Trait>()) {
        out.value = lhs.boxed_value().template as_trait<Trait>(rhs.value);
    } else {
                                          cast_to<double>(rhs));
    } /* else if (lhs.template has_trait<Trait>()) {
        out.value = lhs.boxed_value().template as_trait<Trait>(rhs.content);
    } */
    else {
        throw abort_execution{
            ip, "unsupported operand types for arithmetic operation"};
    }


@@ 97,16 174,16 @@ auto execute_arithmetic_op(Op const op, Stack& stack, ip_type const ip) -> void
                     + op.instruction.rhs.to_string() + "\n";

    if (lhs.template holds<int64_t>()) {
        out = typename Op::functor_type{}(std::get<int64_t>(lhs.value),
        out = typename Op::functor_type{}(lhs.value.template get<int64_t>(),
                                          rhs.template cast_to<int64_t>());
    } else if (lhs.template holds<uint64_t>()) {
        out = typename Op::functor_type{}(std::get<uint64_t>(lhs.value),
        out = typename Op::functor_type{}(lhs.value.template get<uint64_t>(),
                                          rhs.template cast_to<uint64_t>());
    } else if (lhs.template holds<float>()) {
        out = typename Op::functor_type{}(std::get<float>(lhs.value),
        out = typename Op::functor_type{}(lhs.value.template get<float>(),
                                          rhs.template cast_to<float>());
    } else if (lhs.template holds<double>()) {
        out = typename Op::functor_type{}(std::get<double>(lhs.value),
        out = typename Op::functor_type{}(lhs.value.template get<double>(),
                                          rhs.template cast_to<double>());
    } else {
        throw abort_execution{


@@ 148,9 225,9 @@ auto execute(MOD const op, Stack& stack, ip_type const ip) -> void
    }

    if (lhs.holds<int64_t>()) {
        out = std::get<int64_t>(lhs.value) % rhs.cast_to<int64_t>();
        out = lhs.value.get<int64_t>() % rhs.cast_to<int64_t>();
    } else if (lhs.holds<uint64_t>()) {
        out = std::get<uint64_t>(lhs.value) % rhs.cast_to<uint64_t>();
        out = lhs.value.get<uint64_t>() % rhs.cast_to<uint64_t>();
    } else {
        throw abort_execution{ip,
                              "unsupported operand types for modulo operation"};


@@ 164,8 241,7 @@ auto execute(BITSHL const op, Stack& stack, ip_type const) -> void
    auto& lhs       = registers.at(op.instruction.lhs.index);
    auto& rhs       = registers.at(op.instruction.rhs.index);

    out.value =
        (std::get<uint64_t>(lhs.value) << std::get<uint64_t>(rhs.value));
    out.value = (lhs.value.get<uint64_t>() << rhs.value.get<uint64_t>());

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)
                     + " " + op.instruction.out.to_string() + ", "


@@ 179,8 255,7 @@ auto execute(BITSHR const op, Stack& stack, ip_type const) -> void
    auto& lhs       = registers.at(op.instruction.lhs.index);
    auto& rhs       = registers.at(op.instruction.rhs.index);

    out.value =
        (std::get<uint64_t>(lhs.value) >> std::get<uint64_t>(rhs.value));
    out.value = (lhs.value.get<uint64_t>() >> rhs.value.get<uint64_t>());

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)
                     + " " + op.instruction.out.to_string() + ", "


@@ 194,8 269,8 @@ auto execute(BITASHR const op, Stack& stack, ip_type const) -> void
    auto& lhs       = registers.at(op.instruction.lhs.index);
    auto& rhs       = registers.at(op.instruction.rhs.index);

    auto const tmp = static_cast<int64_t>(std::get<uint64_t>(lhs.value));
    out.value = static_cast<uint64_t>(tmp >> std::get<uint64_t>(rhs.value));
    auto const tmp = static_cast<int64_t>(lhs.value.get<uint64_t>());
    out.value      = static_cast<uint64_t>(tmp >> rhs.value.get<uint64_t>());

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)
                     + " " + op.instruction.out.to_string() + ", "


@@ 213,7 288,7 @@ auto execute(BITAND const op, Stack& stack, ip_type const) -> void
    auto& lhs       = registers.at(op.instruction.lhs.index);
    auto& rhs       = registers.at(op.instruction.rhs.index);

    out.value = (std::get<uint64_t>(lhs.value) & std::get<uint64_t>(rhs.value));
    out.value = (lhs.value.get<uint64_t>() & rhs.value.get<uint64_t>());

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)
                     + " " + op.instruction.out.to_string() + ", "


@@ 227,7 302,7 @@ auto execute(BITOR const op, Stack& stack, ip_type const) -> void
    auto& lhs       = registers.at(op.instruction.lhs.index);
    auto& rhs       = registers.at(op.instruction.rhs.index);

    out.value = (std::get<uint64_t>(lhs.value) | std::get<uint64_t>(rhs.value));
    out.value = (lhs.value.get<uint64_t>() | rhs.value.get<uint64_t>());

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)
                     + " " + op.instruction.out.to_string() + ", "


@@ 241,7 316,7 @@ auto execute(BITXOR const op, Stack& stack, ip_type const) -> void
    auto& lhs       = registers.at(op.instruction.lhs.index);
    auto& rhs       = registers.at(op.instruction.rhs.index);

    out.value = (std::get<uint64_t>(lhs.value) ^ std::get<uint64_t>(rhs.value));
    out.value = (lhs.value.get<uint64_t>() ^ rhs.value.get<uint64_t>());

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)
                     + " " + op.instruction.out.to_string() + ", "


@@ 254,7 329,7 @@ auto execute(BITNOT const op, Stack& stack, ip_type const) -> void
    auto& out       = registers.at(op.instruction.out.index);
    auto& in        = registers.at(op.instruction.in.index);

    out.value = ~std::get<uint64_t>(in.value);
    out.value = ~in.value.get<uint64_t>();

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)
                     + " " + op.instruction.out.to_string() + ", "


@@ 275,17 350,17 @@ auto execute_cmp_op(Op const op, Stack& stack, ip_type const ip) -> void
                     + op.instruction.rhs.to_string() + "\n";

    if (lhs.template holds<int64_t>()) {
        out = typename Op::functor_type{}(std::get<int64_t>(lhs.value),
                                          std::get<int64_t>(rhs.value));
        out = typename Op::functor_type{}(lhs.value.template get<int64_t>(),
                                          rhs.value.template get<int64_t>());
    } else if (lhs.template holds<uint64_t>()) {
        out = typename Op::functor_type{}(std::get<uint64_t>(lhs.value),
                                          std::get<uint64_t>(rhs.value));
        out = typename Op::functor_type{}(lhs.value.template get<uint64_t>(),
                                          rhs.value.template get<uint64_t>());
    } else if (lhs.template holds<float>()) {
        out = typename Op::functor_type{}(std::get<float>(lhs.value),
                                          std::get<float>(rhs.value));
        out = typename Op::functor_type{}(lhs.value.template get<float>(),
                                          rhs.value.template get<float>());
    } else if (lhs.template holds<double>()) {
        out = typename Op::functor_type{}(std::get<double>(lhs.value),
                                          std::get<double>(rhs.value));
        out = typename Op::functor_type{}(lhs.value.template get<double>(),
                                          rhs.value.template get<double>());
    } else if (lhs.template has_trait<Trait>()) {
        out.value = lhs.boxed_value().template as_trait<Trait>(rhs.value);
    } else {


@@ 332,8 407,8 @@ auto execute(CMP const op, Stack& stack, ip_type const) -> void
            out = Cmp::CMP_LT;
        }
    } else {
        auto const lv = std::get<uint64_t>(lhs.value);
        auto const rv = std::get<uint64_t>(rhs.value);
        auto const lv = lhs.value.get<uint64_t>();
        auto const rv = rhs.value.get<uint64_t>();
        if (lv == rv) {
            out = Cmp::CMP_EQ;
        } else if (lv > rv) {


@@ 368,7 443,7 @@ auto execute(AND const op, Stack& stack, ip_type const) -> void
            [](Bool const& v) -> bool { return static_cast<bool>(v); }, false);
        out = use_lhs ? std::move(lhs) : std::move(rhs);
    } else {
        auto const use_lhs = (std::get<uint64_t>(lhs.value) == 0);
        auto const use_lhs = (lhs.value.get<uint64_t>() == 0);
        out                = use_lhs ? std::move(lhs) : std::move(rhs);
    }



@@ 395,7 470,7 @@ auto execute(OR const op, Stack& stack, ip_type const) -> void
            [](Bool const& v) -> bool { return static_cast<bool>(v); }, false);
        out = use_lhs ? std::move(lhs) : std::move(rhs);
    } else {
        auto const use_lhs = (std::get<uint64_t>(lhs.value) != 0);
        auto const use_lhs = (lhs.value.get<uint64_t>() != 0);
        out                = use_lhs ? std::move(lhs) : std::move(rhs);
    }



@@ 420,7 495,7 @@ auto execute(NOT const op, Stack& stack, ip_type const) -> void
        out = in.boxed_value().as_trait<Bool, bool>(
            [](Bool const& v) -> bool { return static_cast<bool>(v); }, false);
    } else {
        out = static_cast<bool>(std::get<uint64_t>(in.value));
        out = static_cast<bool>(in.value.get<uint64_t>());
    }

    std::cerr << "    " + viua::arch::ops::to_string(op.instruction.opcode)


@@ 429,20 504,20 @@ auto execute(NOT const op, Stack& stack, ip_type const) -> void
}

namespace {
auto type_name(viua::vm::types::Register_cell const& c) -> std::string
auto type_name(viua::vm::types::Cell const& c) -> std::string
{
    if (std::holds_alternative<std::monostate>(c)) {
    if (std::holds_alternative<std::monostate>(c.content)) {
        return "void";
    } else if (std::holds_alternative<int64_t>(c)) {
    } else if (std::holds_alternative<int64_t>(c.content)) {
        return "int";
    } else if (std::holds_alternative<uint64_t>(c)) {
    } else if (std::holds_alternative<uint64_t>(c.content)) {
        return "uint";
    } else if (std::holds_alternative<float>(c)) {
    } else if (std::holds_alternative<float>(c.content)) {
        return "float";
    } else if (std::holds_alternative<double>(c)) {
    } else if (std::holds_alternative<double>(c.content)) {
        return "double";
    } else {
        return std::get<std::unique_ptr<viua::vm::types::Value>>(c)
        return std::get<std::unique_ptr<viua::vm::types::Value>>(c.content)
            ->type_name();
    }
}


@@ 483,13 558,13 @@ auto execute(COPY const op, Stack& stack, ip_type const) -> void
    }

    if (in.holds<int64_t>()) {
        out = std::get<int64_t>(in.value);
        out = in.value.get<int64_t>();
    } else if (in.holds<uint64_t>()) {
        out = std::get<uint64_t>(in.value);
        out = in.value.get<uint64_t>();
    } else if (in.holds<float>()) {
        out = std::get<float>(in.value);
        out = in.value.get<float>();
    } else if (in.holds<double>()) {
        out = std::get<double>(in.value);
        out = in.value.get<double>();
    } else {
        out.value = in.boxed_value().as_trait<Copy>().copy();
    }


@@ 530,7 605,7 @@ auto execute(ATOM const op, Stack& stack, ip_type const) -> void
    auto& target    = registers.at(op.instruction.out.index);

    auto const& env        = stack.environment;
    auto const data_offset = std::get<uint64_t>(target.value);
    auto const data_offset = target.value.get<uint64_t>();
    auto const data_size   = [env, data_offset]() -> uint64_t {
        auto const size_offset = (data_offset - sizeof(uint64_t));
        auto tmp               = uint64_t{};


@@ 554,7 629,7 @@ auto execute(STRING const op, Stack& stack, ip_type const) -> void
    auto& target    = registers.at(op.instruction.out.index);

    auto const& env        = stack.environment;
    auto const data_offset = std::get<uint64_t>(target.value);
    auto const data_offset = target.value.get<uint64_t>();
    auto const data_size   = [env, data_offset]() -> uint64_t {
        auto const size_offset = (data_offset - sizeof(uint64_t));
        auto tmp               = uint64_t{};


@@ 581,7 656,7 @@ auto execute(FRAME const op, Stack& stack, ip_type const ip) -> void
    auto capacity = viua::arch::register_index_type{};
    if (rs == viua::arch::RS::LOCAL) {
        capacity = static_cast<viua::arch::register_index_type>(
            std::get<uint64_t>(stack.back().registers.at(index).value));
            stack.back().registers.at(index).value.get<uint64_t>());
    } else if (rs == viua::arch::RS::ARGUMENT) {
        capacity = index;
    } else {


@@ 613,7 688,7 @@ auto execute(CALL const op, Stack& stack, ip_type const ip) -> ip_type
        }

        std::tie(fn_name, fn_addr) =
            stack.environment.function_at(std::get<uint64_t>(fn_offset.value));
            stack.environment.function_at(fn_offset.value.get<uint64_t>());
    }

    std::cerr << "    " << viua::arch::ops::to_string(op.instruction.opcode)


@@ 685,7 760,7 @@ auto execute(FLOAT const op, Stack& stack, ip_type const) -> void

    auto& target = registers.at(op.instruction.out.index);

    auto const data_offset = std::get<uint64_t>(target.value);
    auto const data_offset = target.value.get<uint64_t>();
    auto const data_size   = [&stack, data_offset]() -> uint64_t {
        auto const size_offset = (data_offset - sizeof(uint64_t));
        auto tmp               = uint64_t{};


@@ 714,7 789,7 @@ auto execute(DOUBLE const op, Stack& stack, ip_type const) -> void

    auto& target = registers.at(op.instruction.out.index);

    auto const data_offset = std::get<uint64_t>(target.value);
    auto const data_offset = target.value.get<uint64_t>();
    auto const data_size   = [&stack, data_offset]() -> uint64_t {
        auto const size_offset = (data_offset - sizeof(uint64_t));
        auto tmp               = uint64_t{};


@@ 788,16 863,17 @@ auto execute_arithmetic_immediate_op(Op const op,
    if (in.template holds<void>()) {
        out = typename Op::functor_type{}(0, immediate);
    } else if (in.template holds<uint64_t>()) {
        out = typename Op::functor_type{}(std::get<uint64_t>(in.value),
        out = typename Op::functor_type{}(in.value.template get<uint64_t>(),
                                          immediate);
    } else if (in.template holds<int64_t>()) {
        out =
            typename Op::functor_type{}(std::get<int64_t>(in.value), immediate);
        out = typename Op::functor_type{}(in.value.template get<int64_t>(),
                                          immediate);
    } else if (in.template holds<float>()) {
        out = typename Op::functor_type{}(std::get<float>(in.value), immediate);
        out = typename Op::functor_type{}(in.value.template get<float>(),
                                          immediate);
    } else if (in.template holds<double>()) {
        out =
            typename Op::functor_type{}(std::get<double>(in.value), immediate);
        out = typename Op::functor_type{}(in.value.template get<double>(),
                                          immediate);
    } else {
        throw abort_execution{
            ip,


@@ 860,7 936,7 @@ auto execute(BUFFER_PUSH const op, Stack& stack, ip_type const ip) -> void
                              "invalid destination operand for buffer_push"};
    }
    static_cast<viua::vm::types::Buffer&>(dst.value()->boxed_value())
        .push(src.value()->value_cell());
        .push(std::move(src.value()->value_cell()));
}
auto execute(BUFFER_SIZE const op, Stack& stack, ip_type const ip) -> void
{


@@ 897,22 973,22 @@ auto execute(BUFFER_POP const op, Stack& stack, ip_type const ip) -> void
        static_cast<viua::vm::types::Buffer&>(src.value()->boxed_value());
    auto off = (buf.size() - 1);
    if (idx.has_value()) {
        off = std::get<uint64_t>(idx.value()->value);
        off = idx.value()->value.get<uint64_t>();
    }

    using viua::vm::types::Cell;
    auto v = buf.pop(off);
    if (std::holds_alternative<int64_t>(v)) {
        *dst.value() = std::get<int64_t>(v);
    } else if (std::holds_alternative<uint64_t>(v)) {
        *dst.value() = std::get<uint64_t>(v);
    } else if (std::holds_alternative<float>(v)) {
        *dst.value() = std::get<float>(v);
    } else if (std::holds_alternative<double>(v)) {
        *dst.value() = std::get<double>(v);
    } else if (std::holds_alternative<std::unique_ptr<viua::vm::types::Value>>(
                   v)) {
        dst.value()->value =
            std::move(std::get<std::unique_ptr<viua::vm::types::Value>>(v));

    if (std::holds_alternative<int64_t>(v.content)) {
        *dst.value() = v.get<int64_t>();
    } else if (std::holds_alternative<uint64_t>(v.content)) {
        *dst.value() = v.get<uint64_t>();
    } else if (std::holds_alternative<float>(v.content)) {
        *dst.value() = v.get<float>();
    } else if (std::holds_alternative<double>(v.content)) {
        *dst.value() = v.get<double>();
    } else if (std::holds_alternative<Cell::boxed_type>(v.content)) {
        dst.value()->value = std::move(v.get<Cell::boxed_type>());
    }
}



@@ 934,17 1010,17 @@ auto execute(PTR const op, Stack& stack, ip_type const ip) -> void
    using viua::vm::types::Signed_integer;
    using viua::vm::types::Unsigned_integer;
    if (src.value()->holds<int64_t>()) {
        src.value()->value = std::make_unique<Signed_integer>(
            std::get<int64_t>(src.value()->value));
        src.value()->value =
            std::make_unique<Signed_integer>(src.value()->value.get<int64_t>());
    } else if (src.value()->holds<uint64_t>()) {
        src.value()->value = std::make_unique<Unsigned_integer>(
            std::get<uint64_t>(src.value()->value));
            src.value()->value.get<uint64_t>());
    } else if (src.value()->holds<float>()) {
        src.value()->value =
            std::make_unique<Float_single>(std::get<float>(src.value()->value));
            std::make_unique<Float_single>(src.value()->value.get<float>());
    } else if (src.value()->holds<double>()) {
        src.value()->value = std::make_unique<Float_double>(
            std::get<double>(src.value()->value));
        src.value()->value =
            std::make_unique<Float_double>(src.value()->value.get<double>());
    }

    dst.value()->value = src.value()->boxed_value().pointer_to();


@@ 982,28 1058,25 @@ auto execute(EBREAK const, Stack& stack, ip_type const) -> void
            } else if (each.holds<int64_t>()) {
                std::cerr
                    << "is " << std::hex << std::setw(16) << std::setfill('0')
                    << std::get<uint64_t>(each.value) << " " << std::dec
                    << static_cast<int64_t>(std::get<uint64_t>(each.value))
                    << "\n";
                    << each.value.get<uint64_t>() << " " << std::dec
                    << static_cast<int64_t>(each.value.get<uint64_t>()) << "\n";
            } else if (each.holds<uint64_t>()) {
                std::cerr << "iu " << std::hex << std::setw(16)
                          << std::setfill('0') << std::get<uint64_t>(each.value)
                          << " " << std::dec << std::get<uint64_t>(each.value)
                          << std::setfill('0') << each.value.get<uint64_t>()
                          << " " << std::dec << each.value.get<uint64_t>()
                          << "\n";
            } else if (each.holds<float>()) {
                std::cerr << "fl " << std::hex << std::setw(8)
                          << std::setfill('0')
                          << static_cast<float>(std::get<uint64_t>(each.value))
                          << " " << std::dec
                          << static_cast<float>(std::get<uint64_t>(each.value))
                          << "\n";
                std::cerr
                    << "fl " << std::hex << std::setw(8) << std::setfill('0')
                    << static_cast<float>(each.value.get<uint64_t>()) << " "
                    << std::dec
                    << static_cast<float>(each.value.get<uint64_t>()) << "\n";
            } else if (each.holds<double>()) {
                std::cerr << "db " << std::hex << std::setw(16)
                          << std::setfill('0')
                          << static_cast<double>(std::get<uint64_t>(each.value))
                          << " " << std::dec
                          << static_cast<double>(std::get<uint64_t>(each.value))
                          << "\n";
                std::cerr
                    << "db " << std::hex << std::setw(16) << std::setfill('0')
                    << static_cast<double>(each.value.get<uint64_t>()) << " "
                    << std::dec
                    << static_cast<double>(each.value.get<uint64_t>()) << "\n";
            }
        }
    }


@@ 1034,28 1107,25 @@ auto execute(EBREAK const, Stack& stack, ip_type const) -> void
            } else if (each.holds<int64_t>()) {
                std::cerr
                    << "is " << std::hex << std::setw(16) << std::setfill('0')
                    << std::get<uint64_t>(each.value) << " " << std::dec
                    << static_cast<int64_t>(std::get<uint64_t>(each.value))
                    << "\n";
                    << each.value.get<uint64_t>() << " " << std::dec
                    << static_cast<int64_t>(each.value.get<uint64_t>()) << "\n";
            } else if (each.holds<uint64_t>()) {
                std::cerr << "iu " << std::hex << std::setw(16)
                          << std::setfill('0') << std::get<uint64_t>(each.value)
                          << " " << std::dec << std::get<uint64_t>(each.value)
                          << std::setfill('0') << each.value.get<uint64_t>()
                          << " " << std::dec << each.value.get<uint64_t>()
                          << "\n";
            } else if (each.holds<float>()) {
                std::cerr << "fl " << std::hex << std::setw(8)
                          << std::setfill('0')
                          << static_cast<float>(std::get<uint64_t>(each.value))
                          << " " << std::dec
                          << static_cast<float>(std::get<uint64_t>(each.value))
                          << "\n";
                std::cerr
                    << "fl " << std::hex << std::setw(8) << std::setfill('0')
                    << static_cast<float>(each.value.get<uint64_t>()) << " "
                    << std::dec
                    << static_cast<float>(each.value.get<uint64_t>()) << "\n";
            } else if (each.holds<double>()) {
                std::cerr << "db " << std::hex << std::setw(16)
                          << std::setfill('0')
                          << static_cast<double>(std::get<uint64_t>(each.value))
                          << " " << std::dec
                          << static_cast<double>(std::get<uint64_t>(each.value))
                          << "\n";
                std::cerr
                    << "db " << std::hex << std::setw(16) << std::setfill('0')
                    << static_cast<double>(each.value.get<uint64_t>()) << " "
                    << std::dec
                    << static_cast<double>(each.value.get<uint64_t>()) << "\n";
            }
        }
    }


@@ 1085,31 1155,29 @@ auto execute(EBREAK const, Stack& stack, ip_type const) -> void
                /* do nothing */
            } else if (each.holds<int64_t>()) {
                std::cerr << "is " << std::hex << std::setw(16)
                          << std::setfill('0') << std::get<int64_t>(each.value)
                          << " " << std::dec << std::get<int64_t>(each.value)
                          << std::setfill('0') << each.value.get<int64_t>()
                          << " " << std::dec << each.value.get<int64_t>()
                          << "\n";
            } else if (each.holds<uint64_t>()) {
                std::cerr << "iu " << std::hex << std::setw(16)
                          << std::setfill('0') << std::get<uint64_t>(each.value)
                          << " " << std::dec << std::get<uint64_t>(each.value)
                          << std::setfill('0') << each.value.get<uint64_t>()
                          << " " << std::dec << each.value.get<uint64_t>()
                          << "\n";
            } else if (each.holds<float>()) {
                auto const precision = std::cerr.precision();
                std::cerr << "fl " << std::hexfloat
                          << std::get<float>(each.value) << " "
                          << std::defaultfloat
                std::cerr << "fl " << std::hexfloat << each.value.get<float>()
                          << " " << std::defaultfloat
                          << std::setprecision(
                                 std::numeric_limits<float>::digits10 + 1)
                          << std::get<float>(each.value) << "\n";
                          << each.value.get<float>() << "\n";
                std::cerr << std::setprecision(precision);
            } else if (each.holds<double>()) {
                auto const precision = std::cerr.precision();
                std::cerr << "db " << std::hexfloat
                          << std::get<double>(each.value) << " "
                          << std::defaultfloat
                std::cerr << "db " << std::hexfloat << each.value.get<double>()
                          << " " << std::defaultfloat
                          << std::setprecision(
                                 std::numeric_limits<double>::digits10 + 1)
                          << std::get<double>(each.value) << "\n";
                          << each.value.get<double>() << "\n";
                std::cerr << std::setprecision(precision);
            }
        }

M new/src/vm/types.cpp => new/src/vm/types.cpp +49 -40
@@ 5,6 5,21 @@


namespace viua::vm::types {
Cell_view::Cell_view(Cell& cell) : content{std::monostate{}}
{
    if (std::holds_alternative<int64_t>(cell.content)) {
        content =
            std::reference_wrapper<int64_t>{std::get<int64_t>(cell.content)};
    } else if (std::holds_alternative<Cell::boxed_type>(cell.content)) {
        content = std::reference_wrapper<boxed_type>{
            *std::get<Cell::boxed_type>(cell.content)};
    } else {
        content = std::monostate{};
    }
}
Cell_view::Cell_view(boxed_type& v) : content{v}
{}

Value::~Value()
{}



@@ 78,41 93,38 @@ String::operator bool() const
{
    return (not content.empty());
}
auto String::operator()(traits::Plus::tag_type const,
                        Register_cell const& c) const -> Register_cell
auto String::operator()(traits::Plus::tag_type const, Cell const& c) const
    -> Cell
{
    if (not std::holds_alternative<std::unique_ptr<Value>>(c)) {
    if (not std::holds_alternative<Cell::boxed_type>(c.content)) {
        throw std::runtime_error{"cannot add unboxed value to String"};
    }

    auto const v =
        dynamic_cast<String*>(std::get<std::unique_ptr<Value>>(c).get());
    auto const v = dynamic_cast<String*>(c.get<Cell::boxed_type>().get());
    if (not v) {
        throw std::runtime_error{
            "cannot add " + std::get<std::unique_ptr<Value>>(c)->type_name()
            + " value to String"};
        throw std::runtime_error{"cannot add "
                                 + c.get<Cell::boxed_type>()->type_name()
                                 + " value to String"};
    }

    auto s     = std::make_unique<String>();
    s->content = (content + v->content);
    return s;
    return Cell{std::move(s)};
}
auto String::operator()(traits::Eq::tag_type const,
                        Register_cell const& c) const -> Register_cell
auto String::operator()(traits::Eq::tag_type const, Cell const& c) const -> Cell
{
    if (not std::holds_alternative<std::unique_ptr<Value>>(c)) {
    if (not std::holds_alternative<Cell::boxed_type>(c.content)) {
        throw std::runtime_error{"cannot compare unboxed value to String"};
    }

    auto const v =
        dynamic_cast<String*>(std::get<std::unique_ptr<Value>>(c).get());
    auto const v = dynamic_cast<String*>(c.get<Cell::boxed_type>().get());
    if (not v) {
        throw std::runtime_error{
            "cannot compare " + std::get<std::unique_ptr<Value>>(c)->type_name()
            + " value to String"};
        throw std::runtime_error{"cannot compare "
                                 + c.get<Cell::boxed_type>()->type_name()
                                 + " value to String"};
    }

    return static_cast<uint64_t>(v->content == content);
    return Cell{static_cast<uint64_t>(v->content == content)};
}

auto Atom::type_name() const -> std::string


@@ 123,22 135,20 @@ auto Atom::to_string() const -> std::string
{
    return viua::vm::types::traits::To_string::quote_and_escape(content);
}
auto Atom::operator()(traits::Eq::tag_type const, Register_cell const& c) const
    -> Register_cell
auto Atom::operator()(traits::Eq::tag_type const, Cell const& c) const -> Cell
{
    if (not std::holds_alternative<std::unique_ptr<Value>>(c)) {
    if (not std::holds_alternative<Cell::boxed_type>(c.content)) {
        throw std::runtime_error{"cannot compare unboxed value to Atom"};
    }

    auto const v =
        dynamic_cast<Atom*>(std::get<std::unique_ptr<Value>>(c).get());
    auto const v = dynamic_cast<Atom*>(c.get<Cell::boxed_type>().get());
    if (not v) {
        throw std::runtime_error{
            "cannot compare " + std::get<std::unique_ptr<Value>>(c)->type_name()
            + " value to Atom"};
        throw std::runtime_error{"cannot compare "
                                 + c.get<Cell::boxed_type>()->type_name()
                                 + " value to Atom"};
    }

    return static_cast<uint64_t>(v->content == content);
    return Cell{static_cast<uint64_t>(v->content == content)};
}

auto Struct::type_name() const -> std::string


@@ 150,19 160,18 @@ auto Struct::to_string() const -> std::string
    return "{}";
}

auto stringify_cell(Value_cell const& vc) -> std::string
auto stringify_cell(Cell const& vc) -> std::string
{
    if (std::holds_alternative<int64_t>(vc)) {
        return std::to_string(std::get<int64_t>(vc));
    } else if (std::holds_alternative<uint64_t>(vc)) {
        return std::to_string(std::get<uint64_t>(vc));
    } else if (std::holds_alternative<float>(vc)) {
        return std::to_string(std::get<float>(vc));
    } else if (std::holds_alternative<double>(vc)) {
        return std::to_string(std::get<double>(vc));
    if (std::holds_alternative<int64_t>(vc.content)) {
        return std::to_string(std::get<int64_t>(vc.content));
    } else if (std::holds_alternative<uint64_t>(vc.content)) {
        return std::to_string(std::get<uint64_t>(vc.content));
    } else if (std::holds_alternative<float>(vc.content)) {
        return std::to_string(std::get<float>(vc.content));
    } else if (std::holds_alternative<double>(vc.content)) {
        return std::to_string(std::get<double>(vc.content));
    } else {
        auto const* v =
            std::get<std::unique_ptr<viua::vm::types::Value>>(vc).get();
        auto const* v = vc.get<Cell::boxed_type>().get();
        auto const* s =
            dynamic_cast<viua::vm::types::traits::To_string const*>(v);
        if (s) {


@@ 192,11 201,11 @@ auto Buffer::to_string() const -> std::string
    out << "]";
    return out.str();
}
auto Buffer::push(Value_cell&& v) -> void
auto Buffer::push(value_type&& v) -> void
{
    values.push_back(std::move(v));
}
auto Buffer::pop(size_t const n) -> Value_cell
auto Buffer::pop(size_t const n) -> value_type
{
    auto v = std::move(values.at(n));
    values.erase(values.begin() + n);