Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 120 additions & 38 deletions src/Stack.zig
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const builtin = @import("builtin");
const std = @import("std");
const assert = std.debug.assert;

Expand All @@ -14,13 +15,34 @@ const f32x4 = def.f32x4;
const f64x2 = def.f64x2;
const v128 = def.v128;
const Instruction = def.Instruction;
const Val = def.Val;
const ValType = def.ValType;
const FuncRef = def.FuncRef;
const ExternRef = def.ExternRef;

const inst = @import("instance.zig");
const ModuleInstance = inst.ModuleInstance;
const TrapError = inst.TrapError;

// V128 values are packed into 2 separate StackVals. This helps reduce wasted memory due to
// alignment requirements since most values are 4 or 8 bytes.
// This is an extern union to avoid the debug error checking Zig adds to unions by default -
// bytebox is aware of the bitwise contents of the stack and often uses types interchangably
const StackVal = extern union {
I32: i32,
I64: i64,
F32: f32,
F64: f64,
FuncRef: FuncRef,
ExternRef: ExternRef,

comptime {
if (builtin.mode == .ReleaseFast) {
std.debug.assert(@sizeOf(StackVal) == 8);
}
}
};

// The number of locals/params/returns in this struct counts V128 as 2 values
pub const FunctionInstance = struct {
type_def_index: usize,
def_index: usize,
Expand Down Expand Up @@ -57,9 +79,10 @@ pub const FuncCallData = struct {

const Stack = @This();

values: []Val,
values: []StackVal,
labels: []Label,
frames: []CallFrame,
locals: []StackVal, // references values
num_values: u32,
num_labels: u16,
num_frames: u16,
Expand All @@ -74,9 +97,10 @@ const AllocOpts = struct {

pub fn init(allocator: std.mem.Allocator) Stack {
const stack = Stack{
.values = &[_]Val{},
.values = &[_]StackVal{},
.labels = &[_]Label{},
.frames = &[_]CallFrame{},
.locals = &.{},
.num_values = 0,
.num_labels = 0,
.num_frames = 0,
Expand All @@ -94,8 +118,8 @@ pub fn deinit(stack: *Stack) void {
}

pub fn allocMemory(stack: *Stack, opts: AllocOpts) !void {
const alignment = @max(@alignOf(Val), @alignOf(Label), @alignOf(CallFrame));
const values_alloc_size = std.mem.alignForward(usize, @as(usize, @intCast(opts.max_values)) * @sizeOf(Val), alignment);
const alignment = @max(@alignOf(StackVal), @alignOf(Label), @alignOf(CallFrame));
const values_alloc_size = std.mem.alignForward(usize, @as(usize, @intCast(opts.max_values)) * @sizeOf(StackVal), alignment);
const labels_alloc_size = std.mem.alignForward(usize, @as(usize, @intCast(opts.max_labels)) * @sizeOf(Label), alignment);
const frames_alloc_size = std.mem.alignForward(usize, @as(usize, @intCast(opts.max_frames)) * @sizeOf(CallFrame), alignment);
const total_alloc_size: usize = values_alloc_size + labels_alloc_size + frames_alloc_size;
Expand All @@ -104,52 +128,49 @@ pub fn allocMemory(stack: *Stack, opts: AllocOpts) !void {
const begin_frames = values_alloc_size + labels_alloc_size;

stack.mem = try stack.allocator.alloc(u8, total_alloc_size);
stack.values.ptr = @as([*]Val, @alignCast(@ptrCast(stack.mem.ptr)));
stack.values.ptr = @as([*]StackVal, @alignCast(@ptrCast(stack.mem.ptr)));
stack.values.len = opts.max_values;
stack.labels.ptr = @as([*]Label, @alignCast(@ptrCast(stack.mem[begin_labels..].ptr)));
stack.labels.len = opts.max_labels;
stack.frames.ptr = @as([*]CallFrame, @alignCast(@ptrCast(stack.mem[begin_frames..].ptr)));
stack.frames.len = opts.max_frames;
}

pub fn pushValue(stack: *Stack, value: Val) void {
stack.values[stack.num_values] = value;
stack.num_values += 1;
}

pub fn pushI32(stack: *Stack, v: i32) void {
stack.values[stack.num_values] = Val{ .I32 = v };
stack.values[stack.num_values] = .{ .I32 = v };
stack.num_values += 1;
}

pub fn pushI64(stack: *Stack, v: i64) void {
stack.values[stack.num_values] = Val{ .I64 = v };
stack.values[stack.num_values] = .{ .I64 = v };
stack.num_values += 1;
}

pub fn pushF32(stack: *Stack, v: f32) void {
stack.values[stack.num_values] = Val{ .F32 = v };
stack.values[stack.num_values] = .{ .F32 = v };
stack.num_values += 1;
}

pub fn pushF64(stack: *Stack, v: f64) void {
stack.values[stack.num_values] = Val{ .F64 = v };
stack.values[stack.num_values] = .{ .F64 = v };
stack.num_values += 1;
}

pub fn pushV128(stack: *Stack, v: v128) void {
stack.values[stack.num_values] = Val{ .V128 = v };
pub fn pushFuncRef(stack: *Stack, v: FuncRef) void {
stack.values[stack.num_values] = .{ .FuncRef = v };
stack.num_values += 1;
}

pub fn popValue(stack: *Stack) Val {
stack.num_values -= 1;
const value: Val = stack.values[stack.num_values];
return value;
pub fn pushExternRef(stack: *Stack, v: ExternRef) void {
stack.values[stack.num_values] = .{ .ExternRef = v };
stack.num_values += 1;
}

pub fn topValue(stack: *const Stack) Val {
return stack.values[stack.num_values - 1];
pub fn pushV128(stack: *Stack, v: v128) void {
const vec2 = @as(f64x2, @bitCast(v));
stack.values[stack.num_values + 0].F64 = vec2[0];
stack.values[stack.num_values + 1].F64 = vec2[1];
stack.num_values += 2;
}

pub fn popI32(stack: *Stack) i32 {
Expand All @@ -172,9 +193,16 @@ pub fn popF64(stack: *Stack) f64 {
return stack.values[stack.num_values].F64;
}

pub fn popV128(stack: *Stack) v128 {
pub fn popFuncRef(stack: *Stack) FuncRef {
stack.num_values -= 1;
return stack.values[stack.num_values].V128;
return stack.values[stack.num_values].FuncRef;
}

pub fn popV128(stack: *Stack) v128 {
stack.num_values -= 2;
const f0 = stack.values[stack.num_values + 0].F64;
const f1 = stack.values[stack.num_values + 1].F64;
return @bitCast(@as(f64x2, .{ f0, f1 }));
}

pub fn popIndexType(stack: *Stack, index_type: ValType) i64 {
Expand Down Expand Up @@ -225,12 +253,12 @@ pub fn popAllUntilLabelId(stack: *Stack, label_id: u64, pop_final_label: bool, n
const dest_begin: usize = label.start_offset_values;
const dest_end: usize = label.start_offset_values + num_returns;

const returns_source: []const Val = stack.values[source_begin..source_end];
const returns_dest: []Val = stack.values[dest_begin..dest_end];
const returns_source: []const StackVal = stack.values[source_begin..source_end];
const returns_dest: []StackVal = stack.values[dest_begin..dest_end];
if (dest_begin <= source_begin) {
std.mem.copyForwards(Val, returns_dest, returns_source);
std.mem.copyForwards(StackVal, returns_dest, returns_source);
} else {
std.mem.copyBackwards(Val, returns_dest, returns_source);
std.mem.copyBackwards(StackVal, returns_dest, returns_source);
}

stack.num_values = @as(u32, @intCast(dest_end));
Expand Down Expand Up @@ -271,6 +299,7 @@ pub fn pushFrame(stack: *Stack, func: *const FunctionInstance, module_instance:
@memset(std.mem.sliceAsBytes(func_locals), 0);

stack.num_values = values_index_end;
stack.locals = stack.values[values_index_begin..values_index_end];

stack.frames[stack.num_frames] = CallFrame{
.func = func,
Expand All @@ -283,7 +312,7 @@ pub fn pushFrame(stack: *Stack, func: *const FunctionInstance, module_instance:
}

pub fn popFrame(stack: *Stack) ?FuncCallData {
const frame: *CallFrame = stack.topFrame();
var frame: *CallFrame = stack.topFrame();

const continuation: u32 = stack.labels[frame.start_offset_labels].continuation;
const num_returns: usize = frame.num_returns;
Expand All @@ -296,17 +325,20 @@ pub fn popFrame(stack: *Stack) ?FuncCallData {
// Because a function's locals take up stack space, the return values are located
// after the locals, so we need to copy them back down to the start of the function's
// stack space, where the caller expects them to be.
const returns_source: []const Val = stack.values[source_begin..source_end];
const returns_dest: []Val = stack.values[dest_begin..dest_end];
std.mem.copyForwards(Val, returns_dest, returns_source);
const returns_source: []const StackVal = stack.values[source_begin..source_end];
const returns_dest: []StackVal = stack.values[dest_begin..dest_end];
std.mem.copyForwards(StackVal, returns_dest, returns_source);

stack.num_values = @as(u32, @intCast(dest_end));
stack.num_labels = frame.start_offset_labels;
stack.num_frames -= 1;

if (stack.num_frames > 0) {
frame = stack.topFrame();
stack.locals = stack.values[frame.start_offset_values .. stack.num_values + frame.func.num_locals];

return FuncCallData{
.code = stack.topFrame().func.code,
.code = frame.func.code,
.continuation = continuation,
};
}
Expand All @@ -318,21 +350,71 @@ pub fn topFrame(stack: *const Stack) *CallFrame {
return &stack.frames[stack.num_frames - 1];
}

pub fn locals(stack: *const Stack) []Val {
const frame = stack.topFrame();
return stack.values[frame.start_offset_values..];
pub fn localGet(stack: *Stack, local_index: usize) void {
stack.values[stack.num_values] = stack.locals[local_index];
stack.num_values += 1;
}

pub fn localGetV128(stack: *Stack, local_index: usize) void {
stack.values[stack.num_values + 0] = stack.locals[local_index + 0];
stack.values[stack.num_values + 1] = stack.locals[local_index + 1];
stack.num_values += 2;
}

pub fn localSet(stack: *Stack, local_index: usize) void {
stack.num_values -= 1;
stack.locals[local_index] = stack.values[stack.num_values];
}

pub fn localSetV128(stack: *Stack, local_index: usize) void {
stack.num_values -= 2;
stack.locals[local_index + 0] = stack.values[stack.num_values + 0];
stack.locals[local_index + 1] = stack.values[stack.num_values + 1];
}

pub fn localTee(stack: *Stack, local_index: usize) void {
stack.locals[local_index] = stack.values[stack.num_values - 1];
}

pub fn localTeeV128(stack: *Stack, local_index: usize) void {
stack.locals[local_index + 0] = stack.values[stack.num_values - 2];
stack.locals[local_index + 1] = stack.values[stack.num_values - 1];
}

pub fn select(stack: *Stack) void {
const boolean: i32 = stack.values[stack.num_values - 1].I32;
if (boolean == 0) {
stack.values[stack.num_values - 3] = stack.values[stack.num_values - 2];
}
stack.num_values -= 2;
}

pub fn selectV128(stack: *Stack) void {
const boolean: i32 = stack.values[stack.num_values - 1].I32;
if (boolean == 0) {
stack.values[stack.num_values - 5] = stack.values[stack.num_values - 3];
stack.values[stack.num_values - 4] = stack.values[stack.num_values - 2];
}
stack.num_values -= 3;
}

pub fn popAll(stack: *Stack) void {
stack.num_values = 0;
stack.num_labels = 0;
stack.num_frames = 0;
stack.locals = &.{};
}

pub fn debugDump(stack: Stack) void {
std.debug.print("===== stack dump =====\n", .{});
for (stack.values[0..stack.num_values]) |val| {
std.debug.print("I32: {}, I64: {}, F32: {}, F64: {}\n", .{ val.I32, val.I64, val.F32, val.F64 });
std.debug.print("I32: {}, I64: {}, F32: {}, F64: {}, FuncRef: {}\n", .{
val.I32,
val.I64,
val.F32,
val.F64,
val.FuncRef.func,
});
}
std.debug.print("======================\n", .{});
}
1 change: 1 addition & 0 deletions src/core.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub const ModuleDefinitionOpts = def.ModuleDefinitionOpts;
pub const TaggedVal = def.TaggedVal;
pub const Val = def.Val;
pub const ValType = def.ValType;
pub const ExternRef = def.ExternRef;

pub const UnlinkableError = inst.UnlinkableError;
pub const UninstantiableError = inst.UninstantiableError;
Expand Down
Loading
Loading