~maelkum/viuavm

b1430ca74bf433232b5b7fc28d565c291dc34d9e — Marek Marecki 4 months ago 5172f36
Remove buffer- and struct-related instructions

They are (or, rather, will be) possible to implement in terms of memory
instructions - AA, SM, LM, etc. It will be a compiler's job to translate
high-level concepts to VM's lower-level instructions.
32 files changed, 9 insertions(+), 578 deletions(-)

M new/include/viua/arch/ins.h
M new/include/viua/arch/ops.h
M new/include/viua/libs/lexer.h
M new/include/viua/vm/ins.h
M new/src/arch/ops.cpp
M new/src/vm/ins.cpp
D new/tests/asm/buffer.asm
D new/tests/asm/buffer.ebreak
D new/tests/asm/buffer_at.asm
D new/tests/asm/buffer_at.ebreak
D new/tests/asm/buffer_at_signed_index.asm
D new/tests/asm/buffer_at_signed_index.ebreak
D new/tests/asm/buffer_at_void_index.asm
D new/tests/asm/buffer_at_void_index.ebreak
D new/tests/asm/buffer_pop.asm
D new/tests/asm/buffer_pop.ebreak
D new/tests/asm/buffer_pop_signed_index.asm
D new/tests/asm/buffer_pop_signed_index.ebreak
D new/tests/asm/buffer_pop_void_index.asm
D new/tests/asm/buffer_pop_void_index.ebreak
D new/tests/asm/buffer_push.asm
D new/tests/asm/buffer_push.ebreak
D new/tests/asm/buffer_size.asm
D new/tests/asm/buffer_size.ebreak
D new/tests/asm/struct.asm
D new/tests/asm/struct.ebreak
D new/tests/asm/struct_at.asm
D new/tests/asm/struct_at.ebreak
D new/tests/asm/struct_insert.asm
D new/tests/asm/struct_insert.ebreak
D new/tests/asm/struct_remove.asm
D new/tests/asm/struct_remove.ebreak
M new/include/viua/arch/ins.h => new/include/viua/arch/ins.h +0 -56
@@ 254,18 254,6 @@ struct DOUBLE : Instruction {
    DOUBLE(viua::arch::ops::S i) : instruction{i}
    {}
};
struct STRUCT : Instruction {
    viua::arch::ops::S instruction;

    STRUCT(viua::arch::ops::S i) : instruction{i}
    {}
};
struct BUFFER : Instruction {
    viua::arch::ops::S instruction;

    BUFFER(viua::arch::ops::S i) : instruction{i}
    {}
};

/*
 * LUI loads upper bits of a 64-bit value, sign-extending it to register


@@ 365,50 353,6 @@ struct DIVIU : Instruction {
    {}
};

struct BUFFER_PUSH : Instruction {
    viua::arch::ops::D instruction;

    BUFFER_PUSH(viua::arch::ops::D i) : instruction{i}
    {}
};
struct BUFFER_SIZE : Instruction {
    viua::arch::ops::D instruction;

    BUFFER_SIZE(viua::arch::ops::D i) : instruction{i}
    {}
};
struct BUFFER_AT : Instruction {
    viua::arch::ops::T instruction;

    BUFFER_AT(viua::arch::ops::T i) : instruction{i}
    {}
};
struct BUFFER_POP : Instruction {
    viua::arch::ops::T instruction;

    BUFFER_POP(viua::arch::ops::T i) : instruction{i}
    {}
};

struct STRUCT_AT : Instruction {
    viua::arch::ops::T instruction;

    STRUCT_AT(viua::arch::ops::T i) : instruction{i}
    {}
};
struct STRUCT_INSERT : Instruction {
    viua::arch::ops::T instruction;

    STRUCT_INSERT(viua::arch::ops::T i) : instruction{i}
    {}
};
struct STRUCT_REMOVE : Instruction {
    viua::arch::ops::T instruction;

    STRUCT_REMOVE(viua::arch::ops::T i) : instruction{i}
    {}
};

struct REF : Instruction {
    viua::arch::ops::D instruction;


M new/include/viua/arch/ops.h => new/include/viua/arch/ops.h +9 -39
@@ 235,7 235,6 @@ enum class OPCODE : opcode_type {
    MUL = (FORMAT_T | 0x0003),
    DIV = (FORMAT_T | 0x0004),
    MOD = (FORMAT_T | 0x0005),

    BITSHL  = (FORMAT_T | 0x0006),
    BITSHR  = (FORMAT_T | 0x0007),
    BITASHR = (FORMAT_T | 0x0008),


@@ 244,44 243,27 @@ enum class OPCODE : opcode_type {
    BITAND  = (FORMAT_T | 0x000b),
    BITOR   = (FORMAT_T | 0x000c),
    BITXOR  = (FORMAT_T | 0x000d),

    EQ  = (FORMAT_T | 0x000e),
    LT  = (FORMAT_T | 0x000f),
    GT  = (FORMAT_T | 0x0010),
    CMP = (FORMAT_T | 0x0011),
    AND = (FORMAT_T | 0x0012),
    OR  = (FORMAT_T | 0x0013),

    BUFFER_AT  = (FORMAT_T | 0x0014),
    BUFFER_POP = (FORMAT_T | 0x0015),

    STRUCT_AT     = (FORMAT_T | 0x0016),
    STRUCT_INSERT = (FORMAT_T | 0x0017),
    STRUCT_REMOVE = (FORMAT_T | 0x0018),

    IO_SUBMIT   = (FORMAT_T | 0x0019),
    IO_WAIT     = (FORMAT_T | 0x001a),
    IO_SHUTDOWN = (FORMAT_T | 0x001b),
    IO_CTL      = (FORMAT_T | 0x001c),
    IO_SUBMIT   = (FORMAT_T | 0x0014),
    IO_WAIT     = (FORMAT_T | 0x0015),
    IO_SHUTDOWN = (FORMAT_T | 0x0016),
    IO_CTL      = (FORMAT_T | 0x0017),

    CALL   = (FORMAT_D | 0x0001),
    BITNOT = (FORMAT_D | 0x0002),
    NOT    = (FORMAT_D | 0x0003),

    COPY = (FORMAT_D | 0x0004),
    MOVE = (FORMAT_D | 0x0005),
    SWAP = (FORMAT_D | 0x0006),

    BUFFER_PUSH = (FORMAT_D | 0x0007),
    BUFFER_SIZE = (FORMAT_D | 0x0008),

    REF = (FORMAT_D | 0x0009),

    IF = (FORMAT_D | 0x000a),

    IO_PEEK = (FORMAT_D | 0x000b),

    ACTOR = (FORMAT_D | 0x000c),
    REF = (FORMAT_D | 0x0007),
    IF = (FORMAT_D | 0x0008),
    IO_PEEK = (FORMAT_D | 0x0009),
    ACTOR = (FORMAT_D | 0x000a),

    FRAME  = (FORMAT_S | 0x0001),
    RETURN = (FORMAT_S | 0x0002),


@@ 289,10 271,7 @@ enum class OPCODE : opcode_type {
    STRING = (FORMAT_S | 0x0004),
    FLOAT  = (FORMAT_S | 0x0005),
    DOUBLE = (FORMAT_S | 0x0006),
    STRUCT = (FORMAT_S | 0x0007),
    BUFFER = (FORMAT_S | 0x0008),

    SELF = (FORMAT_S | 0x0009),
    SELF = (FORMAT_S | 0x0007),

    LUI  = (FORMAT_E | 0x0001),
    LUIU = (FORMAT_E | 0x0001 | UNSIGNED),


@@ 340,11 319,6 @@ enum class OPCODE_T : opcode_type {
    Make_entry(CMP),
    Make_entry(AND),
    Make_entry(OR),
    Make_entry(BUFFER_AT),
    Make_entry(BUFFER_POP),
    Make_entry(STRUCT_AT),
    Make_entry(STRUCT_INSERT),
    Make_entry(STRUCT_REMOVE),
    Make_entry(IO_SUBMIT),
    Make_entry(IO_WAIT),
    Make_entry(IO_SHUTDOWN),


@@ 357,8 331,6 @@ enum class OPCODE_D : opcode_type {
    Make_entry(COPY),
    Make_entry(MOVE),
    Make_entry(SWAP),
    Make_entry(BUFFER_PUSH),
    Make_entry(BUFFER_SIZE),
    Make_entry(REF),
    Make_entry(IF),
    Make_entry(IO_PEEK),


@@ 371,8 343,6 @@ enum class OPCODE_S : opcode_type {
    Make_entry(STRING),
    Make_entry(FLOAT),
    Make_entry(DOUBLE),
    Make_entry(STRUCT),
    Make_entry(BUFFER),
    Make_entry(SELF),
};
enum class OPCODE_E : opcode_type {

M new/include/viua/libs/lexer.h => new/include/viua/libs/lexer.h +0 -20
@@ 192,10 192,6 @@ inline auto const OPCODE_NAMES = std::set<std::string>{
    "g.atom",
    "string",
    "g.string",
    "struct",
    "g.struct",
    "buffer",
    "g.buffer",

    "lui",
    "g.lui",


@@ 226,25 222,9 @@ inline auto const OPCODE_NAMES = std::set<std::string>{
    "swap",
    "g.swap",

    "buffer_push",
    "g.buffer_push",
    "buffer_size",
    "g.buffer_size",
    "buffer_at",
    "g.buffer_at",
    "buffer_pop",
    "g.buffer_pop",

    "ref",
    "g.ref",

    "struct_at",
    "g.struct_at",
    "struct_insert",
    "g.struct_insert",
    "struct_remove",
    "g.struct_remove",

    "if",
    "g.if",


M new/include/viua/vm/ins.h => new/include/viua/vm/ins.h +0 -11
@@ 67,8 67,6 @@ Work_instruction(SWAP);

Work_instruction(ATOM);
Work_instruction(STRING);
Work_instruction(STRUCT);
Work_instruction(BUFFER);

Work_instruction(FRAME);
Flow_instruction(CALL);


@@ 89,15 87,6 @@ Work_instruction(MULIU);
Work_instruction(DIVI);
Work_instruction(DIVIU);

Work_instruction(BUFFER_PUSH);
Work_instruction(BUFFER_SIZE);
Work_instruction(BUFFER_AT);
Work_instruction(BUFFER_POP);

Work_instruction(STRUCT_AT);
Work_instruction(STRUCT_INSERT);
Work_instruction(STRUCT_REMOVE);

Work_instruction(REF);

Flow_instruction(IF);

M new/src/arch/ops.cpp => new/src/arch/ops.cpp +0 -36
@@ 358,10 358,6 @@ auto to_string(opcode_type const raw) -> std::string
        return greedy + "atom";
    case OPCODE::STRING:
        return greedy + "string";
    case OPCODE::STRUCT:
        return greedy + "struct";
    case OPCODE::BUFFER:
        return greedy + "buffer";
    case OPCODE::FRAME:
        return greedy + "frame";
    case OPCODE::LUI:


@@ 394,20 390,6 @@ auto to_string(opcode_type const raw) -> std::string
        return greedy + "move";
    case OPCODE::SWAP:
        return greedy + "swap";
    case OPCODE::BUFFER_PUSH:
        return greedy + "buffer_push";
    case OPCODE::BUFFER_SIZE:
        return greedy + "buffer_size";
    case OPCODE::BUFFER_AT:
        return greedy + "buffer_at";
    case OPCODE::BUFFER_POP:
        return greedy + "buffer_pop";
    case OPCODE::STRUCT_AT:
        return greedy + "struct_at";
    case OPCODE::STRUCT_INSERT:
        return greedy + "struct_insert";
    case OPCODE::STRUCT_REMOVE:
        return greedy + "struct_remove";
    case OPCODE::REF:
        return greedy + "ref";
    case OPCODE::IF:


@@ 508,10 490,6 @@ auto parse_opcode(std::string_view const raw) -> opcode_type
        return (op | static_cast<opcode_type>(OPCODE::ATOM));
    } else if (sv == "string") {
        return (op | static_cast<opcode_type>(OPCODE::STRING));
    } else if (sv == "struct") {
        return (op | static_cast<opcode_type>(OPCODE::STRUCT));
    } else if (sv == "buffer") {
        return (op | static_cast<opcode_type>(OPCODE::BUFFER));
    } else if (sv == "frame") {
        return (op | static_cast<opcode_type>(OPCODE::FRAME));
    } else if (sv == "lui") {


@@ 544,22 522,8 @@ auto parse_opcode(std::string_view const raw) -> opcode_type
        return (op | static_cast<opcode_type>(OPCODE::MOVE));
    } else if (sv == "swap") {
        return (op | static_cast<opcode_type>(OPCODE::SWAP));
    } else if (sv == "buffer_push") {
        return (op | static_cast<opcode_type>(OPCODE::BUFFER_PUSH));
    } else if (sv == "buffer_size") {
        return (op | static_cast<opcode_type>(OPCODE::BUFFER_SIZE));
    } else if (sv == "buffer_at") {
        return (op | static_cast<opcode_type>(OPCODE::BUFFER_AT));
    } else if (sv == "buffer_pop") {
        return (op | static_cast<opcode_type>(OPCODE::BUFFER_POP));
    } else if (sv == "ref") {
        return (op | static_cast<opcode_type>(OPCODE::REF));
    } else if (sv == "struct_at") {
        return (op | static_cast<opcode_type>(OPCODE::STRUCT_AT));
    } else if (sv == "struct_insert") {
        return (op | static_cast<opcode_type>(OPCODE::STRUCT_INSERT));
    } else if (sv == "struct_remove") {
        return (op | static_cast<opcode_type>(OPCODE::STRUCT_REMOVE));
    } else if (sv == "if") {
        return (op | static_cast<opcode_type>(OPCODE::IF));
    } else if (sv == "io_submit") {

M new/src/vm/ins.cpp => new/src/vm/ins.cpp +0 -240
@@ 91,21 91,6 @@ auto execute(viua::vm::Stack& stack,
            Work(IO_WAIT);
            Work(IO_SHUTDOWN);
            Work(IO_CTL);
        case OPCODE_T::BUFFER_AT:
            execute(BUFFER_AT{instruction}, stack, ip);
            break;
        case OPCODE_T::BUFFER_POP:
            execute(BUFFER_POP{instruction}, stack, ip);
            break;
        case OPCODE_T::STRUCT_AT:
            execute(STRUCT_AT{instruction}, stack, ip);
            break;
        case OPCODE_T::STRUCT_INSERT:
            execute(STRUCT_INSERT{instruction}, stack, ip);
            break;
        case OPCODE_T::STRUCT_REMOVE:
            execute(STRUCT_REMOVE{instruction}, stack, ip);
            break;
#undef Work
        }
        break;


@@ 128,8 113,6 @@ auto execute(viua::vm::Stack& stack,
            Work(STRING);
            Work(FLOAT);
            Work(DOUBLE);
            Work(STRUCT);
            Work(BUFFER);
            Work(SELF);
#undef Work
#undef Flow


@@ 253,12 236,6 @@ auto execute(viua::vm::Stack& stack,
            Flow(IF);
            Work(IO_PEEK);
            Work(ACTOR);
        case OPCODE_D::BUFFER_PUSH:
            execute(BUFFER_PUSH{instruction}, stack, ip);
            break;
        case OPCODE_D::BUFFER_SIZE:
            execute(BUFFER_SIZE{instruction}, stack, ip);
            break;
#undef Work
#undef Flow
        }


@@ 1297,22 1274,6 @@ auto execute(DOUBLE const op, Stack& stack, ip_type const) -> void

    target.value = v;
}
auto execute(STRUCT const op, Stack& stack, ip_type const) -> void
{
    auto& registers = stack.back().registers;
    auto& target    = registers.at(op.instruction.out.index);

    auto s       = std::make_unique<viua::vm::types::Struct>();
    target.value = std::move(s);
}
auto execute(BUFFER const op, Stack& stack, ip_type const) -> void
{
    auto& registers = stack.back().registers;
    auto& target    = registers.at(op.instruction.out.index);

    auto s       = std::make_unique<viua::vm::types::Buffer>();
    target.value = std::move(s);
}

template<typename Op>
auto execute_arithmetic_immediate_op(Op const op,


@@ 1420,207 1381,6 @@ auto execute(DIVIU const op, Stack& stack, ip_type const ip) -> void
    execute_arithmetic_immediate_op(op, stack, ip);
}

auto execute(BUFFER_PUSH const op, Stack& stack, ip_type const ip) -> void
{
    auto dst = get_value(stack, op.instruction.out, ip);
    auto src = get_proxy(stack, op.instruction.in, ip);

    if (src.view().is_void()) {
        throw abort_execution{ip, "cannot buffer_push out of void"};
    }

    if (not dst.holds<viua::vm::types::Buffer>()) {
        throw abort_execution{ip,
                              "invalid destination operand for buffer_push"};
    }
    auto& b = dst.boxed_of<viua::vm::types::Buffer>().value().get();
    b.push(std::move(src.overwrite().value_cell()));
}
auto execute(BUFFER_SIZE const op, Stack& stack, ip_type const ip) -> void
{
    auto dst = get_proxy(stack, op.instruction.out, ip);
    auto src = get_proxy(stack, op.instruction.in, ip);

    if (src.view().is_void()) {
        throw abort_execution{ip, "cannot take buffer_size of void"};
    }
    auto const& b =
        src.view().boxed_of<viua::vm::types::Buffer>().value().get();
    dst.overwrite() = b.size();
}
auto execute(BUFFER_AT const op, Stack& stack, ip_type const ip) -> void
{
    auto dst = get_slot(op.instruction.out, stack, ip);
    auto src = get_value(stack, op.instruction.lhs, ip);
    auto idx = get_value(stack, op.instruction.rhs, ip);

    if (src.is_void()) {
        throw abort_execution{ip, "cannot buffer_at out of void"};
    }

    auto& buf = src.boxed_of<viua::vm::types::Buffer>().value().get();
    auto off  = (buf.size() - 1);
    if (idx.is_void()) {
        // do nothing, and use the default value
    } else if (idx.holds<uint64_t>()) {
        off = idx.get<uint64_t>();
    } else if (idx.holds<int64_t>()) {
        auto const i = idx.get<int64_t>();
        if (i < 0) {
            off = (buf.size() + i);
        } else {
            off = i;
        }
    } else {
        throw abort_execution{ip, "buffer index must be an integer"};
    }

    if (buf.size() <= off) {
        throw abort_execution{ip,
                              ("index " + std::to_string(off) + " out of range "
                               + std::to_string(buf.size()))};
    }

    auto& v = buf.at(off);

    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 (std::holds_alternative<int64_t>(v.content)) {
        v.content = std::make_unique<Signed_integer>(v.get<int64_t>());
    } else if (std::holds_alternative<uint64_t>(v.content)) {
        v.content = std::make_unique<Unsigned_integer>(v.get<uint64_t>());
    } else if (std::holds_alternative<float>(v.content)) {
        v.content = std::make_unique<Float_single>(v.get<float>());
    } else if (std::holds_alternative<double>(v.content)) {
        v.content = std::make_unique<Float_double>(v.get<double>());
    }

    using viua::vm::types::Cell;
    *dst.value() = v.get<Cell::boxed_type>()->reference_to();
}
auto execute(BUFFER_POP const op, Stack& stack, ip_type const ip) -> void
{
    auto dst = get_slot(op.instruction.out, stack, ip);
    auto src = get_value(stack, op.instruction.lhs, ip);
    auto idx = get_value(stack, op.instruction.rhs, ip);

    if (src.is_void()) {
        throw abort_execution{ip, "cannot buffer_pop out of void"};
    }

    auto& buf = src.boxed_of<viua::vm::types::Buffer>().value().get();
    auto off  = (buf.size() - 1);
    if (idx.is_void()) {
        // do nothing, and use the default value
    } else if (idx.holds<uint64_t>()) {
        off = idx.get<uint64_t>();
    } else if (idx.holds<int64_t>()) {
        auto const i = idx.get<int64_t>();
        if (i < 0) {
            off = (buf.size() + i);
        } else {
            off = i;
        }
    } else {
        throw abort_execution{ip, "buffer index must be an integer"};
    }

    using viua::vm::types::Cell;
    auto v = buf.pop(off);

    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>());
    }
}

auto execute(STRUCT_AT const op, Stack& stack, ip_type const ip) -> void
{
    auto dst = get_proxy(stack, op.instruction.out, ip);
    auto src = get_value(stack, op.instruction.lhs, ip);
    auto key = get_value(stack, op.instruction.rhs, ip);

    if (src.is_void()) {
        throw abort_execution{ip, "cannot struct_at out of void"};
    }
    if (key.is_void()) {
        throw abort_execution{ip, "cannot struct_at with a void key"};
    }

    auto& str = src.boxed_of<viua::vm::types::Struct>().value().get();
    auto k    = key.boxed_of<viua::vm::types::Atom>().value().get().content;
    auto& v   = str.at(k);

    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 (std::holds_alternative<int64_t>(v.content)) {
        v.content = std::make_unique<Signed_integer>(v.get<int64_t>());
    } else if (std::holds_alternative<uint64_t>(v.content)) {
        v.content = std::make_unique<Unsigned_integer>(v.get<uint64_t>());
    } else if (std::holds_alternative<float>(v.content)) {
        v.content = std::make_unique<Float_single>(v.get<float>());
    } else if (std::holds_alternative<double>(v.content)) {
        v.content = std::make_unique<Float_double>(v.get<double>());
    }

    using viua::vm::types::Cell;
    dst = v.get<Cell::boxed_type>()->reference_to();
}
auto execute(STRUCT_INSERT const op, Stack& stack, ip_type const ip) -> void
{
    auto dst = get_proxy(stack, op.instruction.out, ip);
    auto key = get_value(stack, op.instruction.lhs, ip);
    auto src = get_proxy(stack, op.instruction.rhs, ip);

    if (key.is_void()) {
        throw abort_execution{ip, "cannot struct_insert with a void key"};
    }
    if (src.view().is_void()) {
        throw abort_execution{ip, "cannot struct_insert with a void value"};
    }

    if (not dst.view().holds<viua::vm::types::Struct>()) {
        throw abort_execution{ip,
                              "invalid destination operand for struct_insert"};
    }

    auto k    = key.boxed_of<viua::vm::types::Atom>().value().get().content;
    auto& str = dst.boxed_of<viua::vm::types::Struct>().value().get();
    str.insert(k, std::move(src.overwrite().value_cell()));
}
auto execute(STRUCT_REMOVE const op, Stack& stack, ip_type const ip) -> void
{
    auto dst = get_slot(op.instruction.out, stack, ip);
    auto src = get_value(stack, op.instruction.lhs, ip);
    auto key = get_proxy(stack, op.instruction.rhs, ip);

    if (key.view().is_void()) {
        throw abort_execution{ip, "cannot struct_remove with a void key"};
    }
    if (src.is_void()) {
        throw abort_execution{ip, "cannot struct_remove with a void value"};
    }

    auto k    = key.boxed_of<viua::vm::types::Atom>().value().get().content;
    auto& str = src.boxed_of<viua::vm::types::Struct>().value().get();

    auto v = str.remove(k);
    if (dst) {
        dst.value()->value = std::move(v);
    }
}

auto execute(REF const op, Stack& stack, ip_type const ip) -> void
{
    auto dst = get_slot(op.instruction.out, stack, ip);

D new/tests/asm/buffer.asm => new/tests/asm/buffer.asm +0 -6
@@ 1,6 0,0 @@
.function: [[entry_point]] main
    buffer $1

    ebreak
    return void
.end

D new/tests/asm/buffer.ebreak => new/tests/asm/buffer.ebreak +0 -1
@@ 1,1 0,0 @@
[1.l] buffer = []

D new/tests/asm/buffer_at.asm => new/tests/asm/buffer_at.asm +0 -15
@@ 1,15 0,0 @@
.function: [[entry_point]] main
    buffer $1

    li $2, 42
    buffer_push $1, $2

    li $2, 64
    buffer_push $1, $2

    li $2, 0u
    buffer_at $3, $1, $2

    ebreak
    return
.end

D new/tests/asm/buffer_at.ebreak => new/tests/asm/buffer_at.ebreak +0 -2
@@ 1,2 0,0 @@
[1.l] buffer = [42, 64]
[3.l] *int = 42

D new/tests/asm/buffer_at_signed_index.asm => new/tests/asm/buffer_at_signed_index.asm +0 -15
@@ 1,15 0,0 @@
.function: [[entry_point]] main
    buffer $1

    li $2, 42
    buffer_push $1, $2

    li $2, 64
    buffer_push $1, $2

    li $2, -1
    buffer_at $3, $1, $2

    ebreak
    return
.end

D new/tests/asm/buffer_at_signed_index.ebreak => new/tests/asm/buffer_at_signed_index.ebreak +0 -2
@@ 1,2 0,0 @@
[1.l] buffer = [42, 64]
[3.l] *int = 64

D new/tests/asm/buffer_at_void_index.asm => new/tests/asm/buffer_at_void_index.asm +0 -14
@@ 1,14 0,0 @@
.function: [[entry_point]] main
    buffer $1

    li $2, 42
    buffer_push $1, $2

    li $2, 64
    buffer_push $1, $2

    buffer_at $3, $1, void

    ebreak
    return
.end

D new/tests/asm/buffer_at_void_index.ebreak => new/tests/asm/buffer_at_void_index.ebreak +0 -2
@@ 1,2 0,0 @@
[1.l] buffer = [42, 64]
[3.l] *int = 64

D new/tests/asm/buffer_pop.asm => new/tests/asm/buffer_pop.asm +0 -15
@@ 1,15 0,0 @@
.function: [[entry_point]] main
    buffer $1

    li $2, 42
    buffer_push $1, $2

    li $2, 64
    buffer_push $1, $2

    li $2, 0u
    buffer_pop $3, $1, $2

    ebreak
    return
.end

D new/tests/asm/buffer_pop.ebreak => new/tests/asm/buffer_pop.ebreak +0 -2
@@ 1,2 0,0 @@
[1.l] buffer = [64]
[3.l] is 000000000000002a 42

D new/tests/asm/buffer_pop_signed_index.asm => new/tests/asm/buffer_pop_signed_index.asm +0 -15
@@ 1,15 0,0 @@
.function: [[entry_point]] main
    buffer $1

    li $2, 42
    buffer_push $1, $2

    li $2, 64
    buffer_push $1, $2

    li $2, -2
    buffer_pop $3, $1, $2

    ebreak
    return
.end

D new/tests/asm/buffer_pop_signed_index.ebreak => new/tests/asm/buffer_pop_signed_index.ebreak +0 -2
@@ 1,2 0,0 @@
[1.l] buffer = [64]
[3.l] is 000000000000002a 42

D new/tests/asm/buffer_pop_void_index.asm => new/tests/asm/buffer_pop_void_index.asm +0 -14
@@ 1,14 0,0 @@
.function: [[entry_point]] main
    buffer $1

    li $2, 42
    buffer_push $1, $2

    li $2, 64
    buffer_push $1, $2

    buffer_pop $3, $1, void

    ebreak
    return
.end

D new/tests/asm/buffer_pop_void_index.ebreak => new/tests/asm/buffer_pop_void_index.ebreak +0 -2
@@ 1,2 0,0 @@
[1.l] buffer = [42]
[3.l] is 0000000000000040 64

D new/tests/asm/buffer_push.asm => new/tests/asm/buffer_push.asm +0 -8
@@ 1,8 0,0 @@
.function: [[entry_point]] main
    buffer $1
    li $2, 42
    buffer_push $1, $2

    ebreak
    return
.end

D new/tests/asm/buffer_push.ebreak => new/tests/asm/buffer_push.ebreak +0 -1
@@ 1,1 0,0 @@
[1.l] buffer = [42]

D new/tests/asm/buffer_size.asm => new/tests/asm/buffer_size.asm +0 -14
@@ 1,14 0,0 @@
.function: [[entry_point]] main
    buffer $1

    li $2, 42
    buffer_push $1, $2

    li $2, 64
    buffer_push $1, $2

    buffer_size $2, $1

    ebreak
    return
.end

D new/tests/asm/buffer_size.ebreak => new/tests/asm/buffer_size.ebreak +0 -1
@@ 1,1 0,0 @@
[2.l] iu 0000000000000002 2

D new/tests/asm/struct.asm => new/tests/asm/struct.asm +0 -6
@@ 1,6 0,0 @@
.function: [[entry_point]] main
    struct $1

    ebreak
    return void
.end

D new/tests/asm/struct.ebreak => new/tests/asm/struct.ebreak +0 -1
@@ 1,1 0,0 @@
[1.l] struct = {}

D new/tests/asm/struct_at.asm => new/tests/asm/struct_at.asm +0 -13
@@ 1,13 0,0 @@
.function: [[entry_point]] main
    struct $1
    atom $2, key
    li $3, 42

    struct_insert $1, $2, $3
    struct_at $3, $1, $2

    addi *3, *3, 1

    ebreak
    return void
.end

D new/tests/asm/struct_at.ebreak => new/tests/asm/struct_at.ebreak +0 -2
@@ 1,2 0,0 @@
[1.l] struct = {key = 43}
[3.l] *int = 43

D new/tests/asm/struct_insert.asm => new/tests/asm/struct_insert.asm +0 -10
@@ 1,10 0,0 @@
.function: [[entry_point]] main
    struct $1
    atom $2, key
    li $3, 42

    struct_insert $1, $2, $3

    ebreak
    return void
.end

D new/tests/asm/struct_insert.ebreak => new/tests/asm/struct_insert.ebreak +0 -1
@@ 1,1 0,0 @@
[1.l] struct = {key = 42}

D new/tests/asm/struct_remove.asm => new/tests/asm/struct_remove.asm +0 -11
@@ 1,11 0,0 @@
.function: [[entry_point]] main
    struct $1
    atom $2, key
    li $3, 42

    struct_insert $1, $2, $3
    struct_remove void, $1, $2

    ebreak
    return void
.end

D new/tests/asm/struct_remove.ebreak => new/tests/asm/struct_remove.ebreak +0 -1
@@ 1,1 0,0 @@
[1.l] struct = {}