diff --git a/src/main.zig b/src/main.zig index 1777a62..4e92973 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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, ¶meters); - // 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, ¶meters); + 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(); diff --git a/src/mods/Parser.zig b/src/mods/Parser.zig index 624e2b5..b3813e0 100644 --- a/src/mods/Parser.zig +++ b/src/mods/Parser.zig @@ -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 { diff --git a/src/mods/ir.zig b/src/mods/ir.zig index 0eb5162..5ae26b6 100644 --- a/src/mods/ir.zig +++ b/src/mods/ir.zig @@ -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 = &.{}, + }; +} diff --git a/src/mods/vm.zig b/src/mods/vm.zig index 0066e24..36bd205 100644 --- a/src/mods/vm.zig +++ b/src/mods/vm.zig @@ -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().?; +} \ No newline at end of file diff --git a/src/mods/wasm.zig b/src/mods/wasm.zig index a49385e..6be559d 100644 --- a/src/mods/wasm.zig +++ b/src/mods/wasm.zig @@ -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 {