Added support for global variables
This commit is contained in:
parent
1da655d164
commit
94195fc774
5 changed files with 158 additions and 52 deletions
46
src/main.zig
46
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();
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 = &.{},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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().?;
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue