PROPOSAL: IR

This is a proposal of a custom IR to run wasm.
At the moment only `ir.zig` has some parts related
to this IR. The idea of the IR is to be a subset
of the one defined in wasm. There are some gaps
(mostly in wasm instructions that have opcode
0xFC) but in wasm they don't use all of the
opcodes for some reason so maybe we could try to
utilize them and be a superset of wasm.
This commit is contained in:
Ernesto Lanchares 2025-03-23 18:20:38 +00:00 committed by Lorenzo Torres
parent b7854d7325
commit 874134f3ff
5 changed files with 317 additions and 5 deletions

View file

@ -55,14 +55,22 @@ pub fn build(b: *std.Build) void {
}, .flags = &[_][]const u8{ "-D_GLFW_X11", "-Wall", "-Wextra" } });
glfw.linkLibC();
const mods = b.addModule("mods", .{
.root_source_file = b.path("src/mods/mods.zig"),
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.name = "sideros",
});
exe.root_module.addImport("mods", mods);
exe.addIncludePath(b.path("ext/glfw/include"));
exe.linkSystemLibrary("vulkan");
compileAllShaders(b, exe);
exe.linkLibrary(glfw);
@ -70,6 +78,27 @@ pub fn build(b: *std.Build) void {
b.installArtifact(exe);
// TODO: This does not generate documentation correctly?
const install_docs = b.addInstallDirectory(.{
.source_dir = exe.getEmittedDocs(),
.install_dir = .prefix,
.install_subdir = "docs",
});
const docs_step = b.step("docs", "Generate documentation");
docs_step.dependOn(&install_docs.step);
// NOTE: This is a hack to generate documentation
const mods_lib = b.addStaticLibrary(.{
.root_module = mods,
.name = "mods",
});
const mods_docs = b.addInstallDirectory(.{
.source_dir = mods_lib.getEmittedDocs(),
.install_dir = .prefix,
.install_subdir = "docs/mods",
});
docs_step.dependOn(&mods_docs.step);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());

View file

@ -5,9 +5,8 @@ const window = @import("rendering/window.zig");
const config = @import("config");
const Renderer = @import("rendering/renderer_vulkan.zig");
const math = @import("math.zig");
const Parser = @import("mods/Parser.zig");
const vm = @import("mods/vm.zig");
const wasm = @import("mods/wasm.zig");
const mods = @import("mods");
const components = @import("ecs/components.zig");
const entities = @import("ecs/entities.zig");
@ -30,7 +29,7 @@ pub fn main() !void {
// const file = try std.fs.cwd().openFile("assets/core.wasm", .{});
// const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB
// var parser = Parser{
// var parser = mods.Parser{
// .bytes = all,
// .byte_idx = 0,
// .allocator = allocator,

View file

@ -144,7 +144,7 @@ fn parseReftype(self: *Parser) !std.wasm.RefType {
// NOTE: Parsing of Valtype can be improved but it makes it less close to spec so...
// TODO: Do we really need Valtype?
const Valtype = union(enum) {
pub const Valtype = union(enum) {
val: std.wasm.Valtype,
ref: std.wasm.RefType,
};

279
src/mods/ir.zig Normal file
View file

@ -0,0 +1,279 @@
const std = @import("std");
const Parser = @import("Parser.zig");
const Allocator = std.mem.Allocator;
const DIndex = packed struct {
first: u32,
second: u32,
};
comptime {
// TODO: is this too big? we could do with 32 bits and a bit more indirection
std.debug.assert(@sizeOf(Index) == 8);
}
/// packed union has no tag
const Index = packed union {
u32: u32,
i32: i32,
u64: u64,
i64: i64,
f32: f32,
f64: f64,
di: DIndex,
};
opcodes: []Opcode,
/// Indices means something different depending on the Opcode.
/// Read the docs of each opcode to know what the index means.
indices: []Index,
select_valtypes: []Parser.Valtype,
/// Opcodes
pub const Opcode = enum(u8) {
// CONTROL INSTRUCTIONS
// The rest of instructions should be implemented in terms of these ones
@"unreachable" = 0x00,
nop = 0x01,
/// Index: `u64`. Meaning: the next instruction pointer
br = 0x0C,
/// Index: `u64`. Meaning: the next instruction pointer
br_if = 0x0D,
/// TODO: this instruction (could be also implemented in terms of br and br_if)
br_table = 0x0E,
@"return" = 0x0F,
/// Index: `u64`. Meaning: The function index to call
call = 0x10,
/// TODO: index (is it enough with using a double index here? if we consider it enough then the other indices should use u32)
call_indirect = 0x11,
// REFERENCE INSTRUCTIONS
// This should be resolved at parse time and therefore not part of IR
// PARAMETRIC INSTRUCTIONS
// Select with no valtypes should be resolved at parse time
drop = 0x1A,
/// Index: `DIndex`. Meaning:
/// `first` is the index into `select_valtypes` array and
/// `second` is the number of valtypes
select = 0x1C,
// VARIABLE INSTRUCTIONS
/// Index: `u32`. Meaing: index into local variables
localget = 0x20,
/// Index: `u32`. Meaing: index into local variables
localset = 0x21,
/// Index: `u32`. Meaing: index into local variables
localtee = 0x22,
/// Index: `u32`. Meaing: index into global variables
globalget = 0x23,
/// Index: `u32`. Meaing: index into global variables
globalset = 0x24,
// TABLE INSTRUCTIONS
/// Index: `u32`. Meaning: index into table index
tableget = 0x25,
/// Index: `u32`. Meaning: index into table index
tableset = 0x26,
/// TODO: table operation. Value in wasm: 0xFC. Note wher is 0x27?
tableop = 0xF0,
// MEMORY INSTRUCTIONS
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i32load = 0x28,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64load = 0x29,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
f32load = 0x2A,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
f64load = 0x2B,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i32load8_s = 0x2C,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i32load8_u = 0x2D,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i32load16_s = 0x2E,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i32load16_u = 0x2F,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64load8_s = 0x30,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64load8_u = 0x31,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64load16_s = 0x32,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64load16_u = 0x33,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64load32_s = 0x34,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64load32_u = 0x35,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i32store = 0x36,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64store = 0x37,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
f32store = 0x38,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
f64store = 0x39,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i32store8 = 0x3A,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i32store16 = 0x3B,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64store8 = 0x3C,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64store16 = 0x3D,
/// Index: `DIndex`. Meaning: `firts` is alignment, `second` is offset
i64store32 = 0x3E,
memorysize = 0x3F,
memorygrow = 0x40,
/// TODO: memory operation. Value in wasm: 0xFC
memoryop = 0xF1,
// NUMERIC INSTRUCTION
/// Index: `i32`. Meaning: constant
i32const = 0x41,
/// Index: `i64`. Meaning: constant
i64const = 0x42,
/// Index: `f32`. Meaning: constant
f32const = 0x43,
/// Index: `f64`. Meaning: constant
f64const = 0x44,
i32eqz = 0x45,
i32eq = 0x46,
i32ne = 0x47,
i32lt_s = 0x48,
i32lt_u = 0x49,
i32gt_s = 0x4A,
i32gt_u = 0x4B,
i32le_s = 0x4C,
i32le_u = 0x4D,
i32ge_s = 0x4E,
i32ge_u = 0x4F,
i64eqz = 0x50,
i64eq = 0x51,
i64ne = 0x52,
i64lt_s = 0x53,
i64lt_u = 0x54,
i64gt_s = 0x55,
i64gt_u = 0x56,
i64le_s = 0x57,
i64le_u = 0x58,
i64ge_s = 0x59,
i64ge_u = 0x5A,
f32eq = 0x5B,
f32ne = 0x5C,
f32lt = 0x5D,
f32gt = 0x5E,
f32le = 0x5F,
f32ge = 0x60,
f64eq = 0x61,
f64ne = 0x62,
f64lt = 0x63,
f64gt = 0x64,
f64le = 0x65,
f64ge = 0x66,
i32clz = 0x67,
i32ctz = 0x68,
i32popcnt = 0x69,
i32add = 0x6A,
i32sub = 0x6B,
i32mul = 0x6C,
i32div_s = 0x6D,
i32div_u = 0x6E,
i32rem_s = 0x6F,
i32rem_u = 0x70,
i32and = 0x71,
i32or = 0x72,
i32xor = 0x73,
i32shl = 0x74,
i32shr_s = 0x75,
i32shr_u = 0x76,
i32rotl = 0x77,
i32rotr = 0x78,
i64clz = 0x79,
i64ctz = 0x7A,
i64popcnt = 0x7B,
i64add = 0x7C,
i64sub = 0x7D,
i64mul = 0x7E,
i64div_s = 0x7F,
i64div_u = 0x80,
i64rem_s = 0x81,
i64rem_u = 0x82,
i64and = 0x83,
i64or = 0x84,
i64xor = 0x85,
i64shl = 0x86,
i64shr_s = 0x87,
i64shr_u = 0x88,
i64rotl = 0x89,
i64rotr = 0x8A,
f32abs = 0x8B,
f32neg = 0x8C,
f32ceil = 0x8D,
f32floor = 0x8E,
f32trunc = 0x8F,
f32nearest = 0x90,
f32sqrt = 0x91,
f32add = 0x92,
f32sub = 0x93,
f32mul = 0x94,
f32div = 0x95,
f32min = 0x96,
f32max = 0x97,
f32copysign = 0x98,
f64abs = 0x99,
f64neg = 0x9A,
f64ceil = 0x9B,
f64floor = 0x9C,
f64trunc = 0x9D,
f64nearest = 0x9E,
f64sqrt = 0x9F,
f64add = 0xA0,
f64sub = 0xA1,
f64mul = 0xA2,
f64div = 0xA3,
f64min = 0xA4,
f64max = 0xA5,
f64copysign = 0xA6,
i32wrap_i64 = 0xA7,
i32trunc_f32_s = 0xA8,
i32trunc_f32_u = 0xA9,
i32trunc_f64_s = 0xAA,
i32trunc_f64_u = 0xAB,
i64extend_i32_s = 0xAC,
i64extend_i32_u = 0xAD,
i64trunc_f32_s = 0xAE,
i64trunc_f32_u = 0xAF,
i64trunc_f64_s = 0xB0,
i64trunc_f64_u = 0xB1,
f32convert_i32_s = 0xB2,
f32convert_i32_u = 0xB3,
f32convert_i64_s = 0xB4,
f32convert_i64_u = 0xB5,
f32demote_f64 = 0xB6,
f64convert_i32_s = 0xB7,
f64convert_i32_u = 0xB8,
f64convert_i64_s = 0xB9,
f64convert_i64_u = 0xBA,
f64promote_f32 = 0xBB,
i32reinterpret_f32 = 0xBC,
i64reinterpret_f64 = 0xBD,
f32reinterpret_i32 = 0xBE,
f64reinterpret_i64 = 0xBF,
i32extend8_s = 0xC0,
i32extend16_s = 0xC1,
i64extend8_s = 0xC2,
i64extend16_s = 0xC3,
i64extend32_s = 0xC4,
/// TODO: saturation truncation instructions. Value in wasm: 0xFC
sattrunc = 0xF2,
// VECTOR INSTRUCTIONS
/// TODO: vector instructions. Value in wasm: 0xFC. Note: there are opcodes available lol
vecinst = 0xF3,
};

5
src/mods/mods.zig Normal file
View file

@ -0,0 +1,5 @@
pub const Parser = @import("Parser.zig");
pub const VM = @import("vm.zig");
// TODO: is this really needed?
pub const Wasm = @import("wasm.zig");
pub const IR = @import("ir.zig");