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();
|
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, ¶meters);
|
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, ¶meters);
|
||||||
|
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();
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 = &.{},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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().?;
|
||||||
|
}
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue