beginning of haku2: a reimplementation of haku in Zig
the goal is to rewrite haku completely, starting with the VM---because it was the most obvious point of improvement the reason is because Rust is kinda too verbose for low level stuff like this. compare the line numbers between haku1 and haku2's VM and how verbose those lines are, and it's kind of an insane difference it also feels like Zig's compilation model can work better for small wasm binary sizes and of course, I also just wanted an excuse to try out Zig :3
This commit is contained in:
parent
598c0348f6
commit
01d4514a65
19 changed files with 1946 additions and 11 deletions
79
crates/haku2/src/bytecode.zig
Normal file
79
crates/haku2/src/bytecode.zig
Normal file
|
@ -0,0 +1,79 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
/// NOTE: This must be mirrored in bytecode.rs.
|
||||
pub const Opcode = enum(u8) {
|
||||
// Push literal values onto the stack.
|
||||
nil,
|
||||
false,
|
||||
true,
|
||||
tag,
|
||||
number, // (float: f32)
|
||||
rgba, // (r: u8, g: u8, b: u8, a: u8)
|
||||
|
||||
// Duplicate existing values.
|
||||
local, // (index: u8) push a value relative to the bottom of the current stack window
|
||||
set_local, // (index: u8) set the value of a value relative to the bottom of the current stack window
|
||||
capture, // (index: u8) push a value captured by the current closure
|
||||
def, // (index: u16) get the value of a definition
|
||||
set_def, // (index: u16) set the value of a definition
|
||||
|
||||
// Create lists.
|
||||
list, // (len: u16)
|
||||
|
||||
// Create literal functions.
|
||||
function, // (params: u8, then: u16), at `then`: (local_count: u8, capture_count: u8, captures: [capture_count](source: u8, index: u8))
|
||||
|
||||
// Control flow.
|
||||
jump, // (offset: u16)
|
||||
jump_if_not, // (offset: u16)
|
||||
field, // (count: u8, tags: [count]u16) look up a closure capture named by a tag. this is used to implement records with fields
|
||||
|
||||
// Function calls.
|
||||
call, // (argc: u8)
|
||||
system, // (index: u8, argc: u8) fast path for system calls
|
||||
|
||||
ret, // must be the last opcode; opcodes after .ret are treated as invalid
|
||||
|
||||
_, // invalid opcodes
|
||||
};
|
||||
|
||||
// Constants used by the .function opcode to indicate capture sources.
|
||||
pub const capture_local: u8 = 0;
|
||||
pub const capture_capture: u8 = 1;
|
||||
|
||||
pub const Chunk = struct {
|
||||
bytecode: []const u8,
|
||||
};
|
||||
|
||||
pub const Loc = u16;
|
||||
|
||||
pub const Defs = struct {
|
||||
num_defs: usize,
|
||||
num_tags: usize,
|
||||
// Unlike the Rust version, we currently do not store strings here, because we still don't
|
||||
// support stack traces.
|
||||
// The VM therefore never needs the names of Defs for any practical purposes.
|
||||
|
||||
pub fn parse(
|
||||
a: mem.Allocator,
|
||||
// These strings are expected to contain a list of names, where each name is terminated
|
||||
// by a newline (LF).
|
||||
defs_string: []const u8,
|
||||
tags_string: []const u8,
|
||||
) !*Defs {
|
||||
const d = try a.create(Defs);
|
||||
errdefer a.destroy(d);
|
||||
|
||||
d.* = .{
|
||||
.num_defs = mem.count(u8, defs_string, "\n"),
|
||||
.num_tags = mem.count(u8, tags_string, "\n"),
|
||||
};
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
pub fn destroy(defs: *Defs, a: mem.Allocator) void {
|
||||
a.destroy(defs);
|
||||
}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue