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();
defer if (gpa.deinit() != .ok) @panic("Leaked memory");
// var global_runtime = mods.GlobalRuntime.init(allocator);
// defer global_runtime.deinit();
// try global_runtime.addFunction("debug", mods.Wasm.debug);
var global_runtime = mods.GlobalRuntime.init(allocator);
defer global_runtime.deinit();
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("./test.wasm", .{});
// const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB
// defer allocator.free(all);
// var parser = try mods.Parser.init(allocator, all);
// defer parser.deinit();
// parser.parseModule() catch |err| {
// std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] });
// return err;
// };
// const module = parser.module();
// // defer module.deinit(allocator);
//const file = try std.fs.cwd().openFile("assets/core.wasm", .{});
const file = try std.fs.cwd().openFile("./test.wasm", .{});
const all = try file.readToEndAlloc(allocator, 1_000_000); // 1 MB
defer allocator.free(all);
var parser = try mods.Parser.init(allocator, all);
defer parser.deinit();
parser.parseModule() catch |err| {
std.debug.print("[ERROR]: error at byte {x}(0x{x})\n", .{ parser.byte_idx, parser.bytes[parser.byte_idx] });
return err;
};
const module = parser.module();
defer module.deinit(allocator);
// var runtime = try mods.Runtime.init(allocator, module, &global_runtime);
// defer runtime.deinit(allocator);
for (0..parser.globalTypes.len) |i| {
try global_runtime.addGlobal(@intCast(i), parser.globalTypes[i], parser.globalValues[i]);
}
// 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 runtime = try mods.Runtime.init(allocator, module, &global_runtime);
defer runtime.deinit(allocator);
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");
//defer w.destroy();

View file

@ -13,6 +13,9 @@ memory: Memtype,
exports: vm.Exports,
importCount: u32,
globalValues: []vm.Value,
globalTypes: []Globaltype,
const Parser = @This();
pub const Error = error{
@ -35,6 +38,7 @@ pub const Error = error{
double_else,
duplicated_funcsec,
duplicated_typesec,
duplicated_globalsec,
unresolved_branch,
unterminated_wasm,
};
@ -53,6 +57,8 @@ pub fn init(allocator: Allocator, bytes: []const u8) !Parser {
.max = 0,
},
},
.globalValues = &.{},
.globalTypes = &.{},
.exports = .{},
};
}
@ -249,7 +255,7 @@ fn parseTabletype(self: *Parser) !Tabletype {
};
}
const Globaltype = struct {
pub const Globaltype = struct {
t: vm.Valtype,
m: enum {
@"const",
@ -435,10 +441,36 @@ fn parseMemsec(self: *Parser) !void {
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 {
self.warn("globalsec");
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 {

View file

@ -723,6 +723,18 @@ const IRParserState = struct {
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 {
// TODO: Should we do something with this?
_ = try self.parseBlockType();
@ -831,3 +843,19 @@ pub fn parse(parser: *Parser) !IR {
.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);
}
};
pub const Function = struct {
func_type: Functype,
typ: union(enum) {
internal: struct {
locals: []Valtype,
ir: IR,
},
external: void,
}
};
pub const Function = struct { func_type: Functype, typ: union(enum) {
internal: struct {
locals: []Valtype,
ir: IR,
},
external: void,
} };
pub const ExportFunction = enum {
preinit,
preinit,
logErr,
logWarn,
logInfo,
logDebug,
};
pub const Exports = struct {
preinit: ?u32 = null,
preinit: ?u32 = null,
logErr: ?u32 = null,
logWarn: ?u32 = null,
logInfo: ?u32 = null,
logDebug: ?u32 = null,
};
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 {
memory: Memory,
functions: []Function,
@ -62,7 +58,7 @@ pub const Module = struct {
pub fn deinit(self: Module, allocator: Allocator) void {
// self.exports.deinit(allocator);
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.returns);
switch (f.typ) {
@ -173,7 +169,7 @@ pub const Runtime = struct {
.localget => try self.stack.append(frame.locals[index.u32]),
.localset => frame.locals[index.u32] = self.stack.pop().?,
.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"),
.tableget => @panic("UNIMPLEMENTED"),
@ -433,18 +429,18 @@ pub const Runtime = struct {
// TODO: Do name resolution at parseTime
pub fn callExternal(self: *Runtime, allocator: Allocator, name: ExportFunction, parameters: []Value) !void {
switch (name) {
.preinit => {
if (self.module.exports.preinit) |func| {
try self.call(allocator, func, parameters);
} else {
std.debug.panic("Function preinit unavailable\n", .{});
}
},
switch (name) {
.preinit => {
if (self.module.exports.preinit) |func| {
try self.call(allocator, func, parameters);
} else {
std.debug.panic("Function preinit unavailable\n", .{});
}
},
else => {
std.debug.panic("Function {any} not handled\n", .{name});
}
}
},
}
}
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 Parser = @import("Parser.zig");
const std = @import("std");
const Allocator = std.mem.Allocator;
@ -12,22 +13,46 @@ pub const Type = enum(u8) {
pub const GlobalRuntime = struct {
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 {
return GlobalRuntime{
.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 {
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 {
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 {