first commit
This commit is contained in:
commit
b574d39a39
23 changed files with 8604 additions and 0 deletions
115
src/wasm/runtime.zig
Normal file
115
src/wasm/runtime.zig
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
const std = @import("std");
|
||||
const module = @import("module.zig");
|
||||
|
||||
pub const PAGE_SIZE: u32 = 65536;
|
||||
|
||||
pub const Value = union(module.ValType) {
|
||||
i32: i32,
|
||||
i64: i64,
|
||||
f32: f32,
|
||||
f64: f64,
|
||||
};
|
||||
|
||||
pub const Memory = struct {
|
||||
bytes: []u8,
|
||||
max_pages: ?u32,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, min_pages: u32, max_pages: ?u32) !Memory {
|
||||
const size = @as(usize, min_pages) * PAGE_SIZE;
|
||||
const bytes = try allocator.alloc(u8, size);
|
||||
@memset(bytes, 0);
|
||||
return .{ .bytes = bytes, .max_pages = max_pages };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Memory, allocator: std.mem.Allocator) void {
|
||||
allocator.free(self.bytes);
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
/// Grow by delta_pages. Returns old page count, or error.OutOfMemory / error.GrowthExceedsMax.
|
||||
pub fn grow(self: *Memory, allocator: std.mem.Allocator, delta_pages: u32) !u32 {
|
||||
const old_pages: u32 = @intCast(self.bytes.len / PAGE_SIZE);
|
||||
const new_pages = old_pages + delta_pages;
|
||||
if (self.max_pages) |max| {
|
||||
if (new_pages > max) return error.GrowthExceedsMax;
|
||||
}
|
||||
const new_size = @as(usize, new_pages) * PAGE_SIZE;
|
||||
const new_bytes = try allocator.realloc(self.bytes, new_size);
|
||||
@memset(new_bytes[self.bytes.len..], 0);
|
||||
self.bytes = new_bytes;
|
||||
return old_pages;
|
||||
}
|
||||
|
||||
pub fn load(self: *const Memory, comptime T: type, addr: u32) !T {
|
||||
const size = @sizeOf(T);
|
||||
if (@as(usize, addr) + size > self.bytes.len) return error.OutOfBounds;
|
||||
const Bits = std.meta.Int(.unsigned, @bitSizeOf(T));
|
||||
const raw = std.mem.readInt(Bits, self.bytes[addr..][0..size], .little);
|
||||
return @bitCast(raw);
|
||||
}
|
||||
|
||||
pub fn store(self: *Memory, comptime T: type, addr: u32, value: T) !void {
|
||||
const size = @sizeOf(T);
|
||||
if (@as(usize, addr) + size > self.bytes.len) return error.OutOfBounds;
|
||||
const Bits = std.meta.Int(.unsigned, @bitSizeOf(T));
|
||||
const raw: Bits = @bitCast(value);
|
||||
std.mem.writeInt(Bits, self.bytes[addr..][0..size], raw, .little);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Table = struct {
|
||||
elements: []?u32,
|
||||
max: ?u32,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, min: u32, max: ?u32) !Table {
|
||||
const elems = try allocator.alloc(?u32, min);
|
||||
@memset(elems, null);
|
||||
return .{ .elements = elems, .max = max };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Table, allocator: std.mem.Allocator) void {
|
||||
allocator.free(self.elements);
|
||||
self.* = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
test "memory load/store round-trip i32" {
|
||||
const ally = std.testing.allocator;
|
||||
var mem = try Memory.init(ally, 1, null);
|
||||
defer mem.deinit(ally);
|
||||
try mem.store(i32, 0, 42);
|
||||
const v = try mem.load(i32, 0);
|
||||
try std.testing.expectEqual(@as(i32, 42), v);
|
||||
}
|
||||
|
||||
test "memory out-of-bounds returns error" {
|
||||
const ally = std.testing.allocator;
|
||||
var mem = try Memory.init(ally, 1, null);
|
||||
defer mem.deinit(ally);
|
||||
try std.testing.expectError(error.OutOfBounds, mem.load(i32, PAGE_SIZE - 2));
|
||||
}
|
||||
|
||||
test "memory grow" {
|
||||
const ally = std.testing.allocator;
|
||||
var mem = try Memory.init(ally, 1, 4);
|
||||
defer mem.deinit(ally);
|
||||
const old = try mem.grow(ally, 1);
|
||||
try std.testing.expectEqual(@as(u32, 1), old);
|
||||
try std.testing.expectEqual(@as(usize, 2 * PAGE_SIZE), mem.bytes.len);
|
||||
}
|
||||
|
||||
test "memory grow beyond max fails" {
|
||||
const ally = std.testing.allocator;
|
||||
var mem = try Memory.init(ally, 1, 2);
|
||||
defer mem.deinit(ally);
|
||||
try std.testing.expectError(error.GrowthExceedsMax, mem.grow(ally, 2));
|
||||
}
|
||||
|
||||
test "memory store/load f64" {
|
||||
const ally = std.testing.allocator;
|
||||
var mem = try Memory.init(ally, 1, null);
|
||||
defer mem.deinit(ally);
|
||||
try mem.store(f64, 8, 3.14);
|
||||
const v = try mem.load(f64, 8);
|
||||
try std.testing.expectApproxEqAbs(3.14, v, 1e-10);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue