Added support for global variables

This commit is contained in:
luccie-cmd 2025-08-04 19:21:12 +02:00
parent 1da655d164
commit 94195fc774
5 changed files with 158 additions and 52 deletions

View file

@ -18,30 +18,34 @@ pub fn main() !void {
const allocator = gpa.allocator(); const allocator = gpa.allocator();
defer if (gpa.deinit() != .ok) @panic("Leaked memory"); defer if (gpa.deinit() != .ok) @panic("Leaked memory");
// var global_runtime = mods.GlobalRuntime.init(allocator); var global_runtime = mods.GlobalRuntime.init(allocator);
// defer global_runtime.deinit(); defer global_runtime.deinit();
// try global_runtime.addFunction("debug", mods.Wasm.debug); try global_runtime.addFunction("debug", mods.Wasm.debug);
// //const file = try std.fs.cwd().openFile("assets/core.wasm", .{}); //const file = try std.fs.cwd().openFile("assets/core.wasm", .{});
// const file = try std.fs.cwd().openFile("./test.wasm", .{}); const file = try std.fs.cwd().openFile("./test.wasm", .{});
// const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB
// defer allocator.free(all); defer allocator.free(all);
// var parser = try mods.Parser.init(allocator, all); var parser = try mods.Parser.init(allocator, all);
// defer parser.deinit(); defer parser.deinit();
// parser.parseModule() catch |err| { parser.parseModule() catch |err| {
// std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] }); std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] });
// return err; return err;
// }; };
// const module = parser.module(); const module = parser.module();
// // defer module.deinit(allocator); defer module.deinit(allocator);
// var runtime = try mods.Runtime.init(allocator, module, &global_runtime); for (0..parser.globalTypes.len) |i| {
// defer runtime.deinit(allocator); try global_runtime.addGlobal(@intCast(i), parser.globalTypes[i], parser.globalValues[i]);
}
// var parameters = [_]mods.VM.Value{.{ .i32 = 17 }}; var runtime = try mods.Runtime.init(allocator, module, &global_runtime);
// try runtime.callExternal(allocator, .preinit, &parameters); defer runtime.deinit(allocator);
// const result = runtime.stack.pop().?;
// std.debug.print("Result of preinit: {any}\n", .{result}); var parameters = [_]mods.VM.Value{.{ .i32 = 17 }};
try runtime.callExternal(allocator, .preinit, &parameters);
const result = runtime.stack.pop().?;
std.debug.print("Result of preinit: {any}\n", .{result});
//var w = try Renderer.Window.create(800, 600, "sideros"); //var w = try Renderer.Window.create(800, 600, "sideros");
//defer w.destroy(); //defer w.destroy();

View file

@ -13,6 +13,9 @@ memory: Memtype,
exports: vm.Exports, exports: vm.Exports,
importCount: u32, importCount: u32,
globalValues: []vm.Value,
globalTypes: []Globaltype,
const Parser = @This(); const Parser = @This();
pub const Error = error{ pub const Error = error{
@ -35,6 +38,7 @@ pub const Error = error{
double_else, double_else,
duplicated_funcsec, duplicated_funcsec,
duplicated_typesec, duplicated_typesec,
duplicated_globalsec,
unresolved_branch, unresolved_branch,
unterminated_wasm, unterminated_wasm,
}; };
@ -53,6 +57,8 @@ pub fn init(allocator: Allocator, bytes: []const u8) !Parser {
.max = 0, .max = 0,
}, },
}, },
.globalValues = &.{},
.globalTypes = &.{},
.exports = .{}, .exports = .{},
}; };
} }
@ -249,7 +255,7 @@ fn parseTabletype(self: *Parser) !Tabletype {
}; };
} }
const Globaltype = struct { pub const Globaltype = struct {
t: vm.Valtype, t: vm.Valtype,
m: enum { m: enum {
@"const", @"const",
@ -435,10 +441,36 @@ fn parseMemsec(self: *Parser) !void {
std.debug.assert(self.byte_idx == end_idx); std.debug.assert(self.byte_idx == end_idx);
} }
pub const Global = struct {
t: Globaltype,
ir: IR,
};
fn parseGlobal(self: *Parser) !Global {
return .{
.t = try parseGlobaltype(self),
.ir = try IR.parseGlobalExpr(self),
};
}
fn parseGlobalsec(self: *Parser) !void { fn parseGlobalsec(self: *Parser) !void {
self.warn("globalsec");
const size = try self.readU32(); const size = try self.readU32();
_ = try self.read(size); const end_idx = self.byte_idx + size;
const globals = try self.parseVector(Parser.parseGlobal);
defer self.allocator.free(globals);
if (self.globalValues.len != 0) return Error.duplicated_globalsec;
if (self.globalTypes.len != 0) return Error.duplicated_globalsec;
self.globalValues = try self.allocator.alloc(vm.Value, globals.len);
self.globalTypes = try self.allocator.alloc(Globaltype, globals.len);
for(globals, 0..) |global, i| {
self.globalValues[i] = try vm.handleGlobalInit(self.allocator, global.ir);
self.globalTypes[i] = global.t;
}
std.debug.assert(self.byte_idx == end_idx);
} }
pub const Export = struct { pub const Export = struct {

View file

@ -723,6 +723,18 @@ const IRParserState = struct {
try self.fix_branches_for_block(start, end, jump_addr); try self.fix_branches_for_block(start, end, jump_addr);
} }
fn parseGlobal(self: *IRParserState) !void {
while (true) {
const op = self.parser.peek() orelse return Parser.Error.unterminated_wasm;
if (op == 0x0B) {
_ = try self.parser.readByte();
break;
} else {
try self.parseExpression();
}
}
}
fn parseIf(self: *IRParserState) !void { fn parseIf(self: *IRParserState) !void {
// TODO: Should we do something with this? // TODO: Should we do something with this?
_ = try self.parseBlockType(); _ = try self.parseBlockType();
@ -831,3 +843,19 @@ pub fn parse(parser: *Parser) !IR {
.select_valtypes = &.{}, .select_valtypes = &.{},
}; };
} }
pub fn parseGlobalExpr(parser: *Parser) !IR {
var state = IRParserState{
.opcodes = .{},
.indices = .{},
.branches = .{},
.parser = parser,
.allocator = parser.allocator,
};
try state.parseGlobal();
return .{
.opcodes = try state.opcodes.toOwnedSlice(state.allocator),
.indices = try state.indices.toOwnedSlice(state.allocator),
.select_valtypes = &.{},
};
}

View file

@ -24,36 +24,32 @@ pub const Functype = struct {
allocator.free(self.returns); allocator.free(self.returns);
} }
}; };
pub const Function = struct { pub const Function = struct { func_type: Functype, typ: union(enum) {
func_type: Functype, internal: struct {
typ: union(enum) { locals: []Valtype,
internal: struct { ir: IR,
locals: []Valtype, },
ir: IR, external: void,
}, } };
external: void,
}
};
pub const ExportFunction = enum { pub const ExportFunction = enum {
preinit, preinit,
logErr, logErr,
logWarn, logWarn,
logInfo, logInfo,
logDebug, logDebug,
}; };
pub const Exports = struct { pub const Exports = struct {
preinit: ?u32 = null, preinit: ?u32 = null,
logErr: ?u32 = null, logErr: ?u32 = null,
logWarn: ?u32 = null, logWarn: ?u32 = null,
logInfo: ?u32 = null, logInfo: ?u32 = null,
logDebug: ?u32 = null, logDebug: ?u32 = null,
}; };
comptime { comptime {
std.debug.assert(@typeInfo(ExportFunction).@"enum".fields.len == @typeInfo(Exports).@"struct".fields.len ); std.debug.assert(@typeInfo(ExportFunction).@"enum".fields.len == @typeInfo(Exports).@"struct".fields.len);
} }
pub const Module = struct { pub const Module = struct {
memory: Memory, memory: Memory,
functions: []Function, functions: []Function,
@ -62,7 +58,7 @@ pub const Module = struct {
pub fn deinit(self: Module, allocator: Allocator) void { pub fn deinit(self: Module, allocator: Allocator) void {
// self.exports.deinit(allocator); // self.exports.deinit(allocator);
for (self.functions) |f| { for (self.functions) |f| {
std.debug.print("Freeing function parameters at {*}\n", .{f.func_type.parameters.ptr}); std.debug.print("Freeing function parameters at {*}\n", .{f.func_type.parameters.ptr});
allocator.free(f.func_type.parameters); allocator.free(f.func_type.parameters);
allocator.free(f.func_type.returns); allocator.free(f.func_type.returns);
switch (f.typ) { switch (f.typ) {
@ -173,7 +169,7 @@ pub const Runtime = struct {
.localget => try self.stack.append(frame.locals[index.u32]), .localget => try self.stack.append(frame.locals[index.u32]),
.localset => frame.locals[index.u32] = self.stack.pop().?, .localset => frame.locals[index.u32] = self.stack.pop().?,
.localtee => frame.locals[index.u32] = self.stack.items[self.stack.items.len - 1], .localtee => frame.locals[index.u32] = self.stack.items[self.stack.items.len - 1],
.globalget => @panic("UNIMPLEMENTED"), .globalget => try self.stack.append(self.global_runtime.getGlobal(index.u32)),
.globalset => @panic("UNIMPLEMENTED"), .globalset => @panic("UNIMPLEMENTED"),
.tableget => @panic("UNIMPLEMENTED"), .tableget => @panic("UNIMPLEMENTED"),
@ -433,18 +429,18 @@ pub const Runtime = struct {
// TODO: Do name resolution at parseTime // TODO: Do name resolution at parseTime
pub fn callExternal(self: *Runtime, allocator: Allocator, name: ExportFunction, parameters: []Value) !void { pub fn callExternal(self: *Runtime, allocator: Allocator, name: ExportFunction, parameters: []Value) !void {
switch (name) { switch (name) {
.preinit => { .preinit => {
if (self.module.exports.preinit) |func| { if (self.module.exports.preinit) |func| {
try self.call(allocator, func, parameters); try self.call(allocator, func, parameters);
} else { } else {
std.debug.panic("Function preinit unavailable\n", .{}); std.debug.panic("Function preinit unavailable\n", .{});
} }
}, },
else => { else => {
std.debug.panic("Function {any} not handled\n", .{name}); std.debug.panic("Function {any} not handled\n", .{name});
} },
} }
} }
pub fn call(self: *Runtime, allocator: Allocator, function: usize, parameters: []Value) AllocationError!void { pub fn call(self: *Runtime, allocator: Allocator, function: usize, parameters: []Value) AllocationError!void {
@ -491,3 +487,24 @@ pub const Runtime = struct {
} }
} }
}; };
pub fn handleGlobalInit(allocator: Allocator, ir: IR) !Value {
var instruction_pointer: usize = 0;
var stack = try std.ArrayList(Value).initCapacity(allocator, 10);
defer stack.deinit();
while (instruction_pointer < ir.opcodes.len){
const opcode: IR.Opcode = ir.opcodes[instruction_pointer];
const index = ir.indices[instruction_pointer];
switch (opcode) {
.i32_const => try stack.append(Value{ .i32 = index.i32 }),
else => {
std.debug.panic("TODO: Handle opcode {any}\n", .{opcode});
}
}
instruction_pointer += 1;
}
if (stack.items.len != 1){
std.debug.panic("Improper amount of variables at end\n", .{});
}
return stack.pop().?;
}

View file

@ -1,4 +1,5 @@
const vm = @import("vm.zig"); const vm = @import("vm.zig");
const Parser = @import("Parser.zig");
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
@ -12,22 +13,46 @@ pub const Type = enum(u8) {
pub const GlobalRuntime = struct { pub const GlobalRuntime = struct {
functions: std.StringHashMap(*const fn (stack: *std.ArrayList(vm.Value)) void), functions: std.StringHashMap(*const fn (stack: *std.ArrayList(vm.Value)) void),
// globals: [_]vm.Value, globals: std.AutoHashMap(u32, Parser.Globaltype),
globalExprs: std.AutoHashMap(u32, vm.Value),
pub fn init(allocator: Allocator) GlobalRuntime { pub fn init(allocator: Allocator) GlobalRuntime {
return GlobalRuntime{ return GlobalRuntime{
.functions = std.StringHashMap(*const fn (stack: *std.ArrayList(vm.Value)) void).init(allocator), .functions = std.StringHashMap(*const fn (stack: *std.ArrayList(vm.Value)) void).init(allocator),
// .globals = .{} .globals = std.AutoHashMap(u32, Parser.Globaltype).init(allocator),
.globalExprs = std.AutoHashMap(u32, vm.Value).init(allocator)
}; };
} }
pub fn deinit(self: *GlobalRuntime) void { pub fn deinit(self: *GlobalRuntime) void {
self.functions.deinit(); self.functions.deinit();
self.globals.deinit();
self.globalExprs.deinit();
} }
pub fn addFunction(self: *GlobalRuntime, name: []const u8, function: *const fn (stack: *std.ArrayList(vm.Value)) void) !void { pub fn addFunction(self: *GlobalRuntime, name: []const u8, function: *const fn (stack: *std.ArrayList(vm.Value)) void) !void {
try self.functions.put(name, function); try self.functions.put(name, function);
} }
pub fn addGlobal(self: *GlobalRuntime, index: u32, @"type": Parser.Globaltype, initValue: vm.Value) !void {
try self.globals.put(index, @"type");
try self.globalExprs.put(index, initValue);
}
pub fn updateGlobal(self: *GlobalRuntime, index: u32, value: vm.Value) !void {
const globType = self.globals.get(index) orelse std.debug.panic("Tried updating global {any} but couldn't find it.\n", .{index});
if(globType.m == globType.m.@"const"){
std.debug.panic("Attempted write to immutable global\n", .{});
}
if (globType.@"type" != value) {
std.debug.panic("Type mismatches for global {any}\n", .{index});
}
try self.globalExprs.put(index, value);
}
pub fn getGlobal(self: *GlobalRuntime, index: u32) vm.Value {
return self.globalExprs.get(index) orelse std.debug.panic("Tried getting global {any} but couldn't find it.\n", .{index});
}
}; };
pub fn debug(stack: *std.ArrayList(vm.Value)) void { pub fn debug(stack: *std.ArrayList(vm.Value)) void {