Skip to content

Commit

Permalink
First pass at adding metering
Browse files Browse the repository at this point in the history
  • Loading branch information
Southporter committed Jun 7, 2024
1 parent 711dc99 commit 8ef7271
Show file tree
Hide file tree
Showing 5 changed files with 549 additions and 459 deletions.
14 changes: 14 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ const ExeOpts = struct {
description: []const u8,
step_dependencies: ?[]*Build.Step = null,
should_emit_asm: bool = false,
options: *Build.Step.Options,
};

pub fn build(b: *Build) void {
const should_emit_asm = b.option(bool, "asm", "Emit asm for the bytebox binaries") orelse false;
const no_clang = b.option(bool, "noclang", "Pass this if clang isn't in the PATH") orelse false;
const enable_metering = b.option(bool, "meter", "Enable metering") orelse false;

const options = b.addOptions();
options.addOption(bool, "enable_metering", enable_metering);

const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
Expand All @@ -38,6 +43,8 @@ pub fn build(b: *Build) void {
.imports = &[_]ModuleImport{stable_array_import},
});

bytebox_module.addOptions("config", options);

// exe.root_module.addImport(import.name, import.module);

const imports = [_]ModuleImport{
Expand All @@ -51,6 +58,7 @@ pub fn build(b: *Build) void {
.step_name = "run",
.description = "Run a wasm program",
.should_emit_asm = should_emit_asm,
.options = options,
});

var bench_steps = [_]*Build.Step{
Expand All @@ -64,6 +72,7 @@ pub fn build(b: *Build) void {
.step_name = "bench",
.description = "Run the benchmark suite",
.step_dependencies = &bench_steps,
.options = options,
});

const lib_bytebox: *Build.Step.Compile = b.addStaticLibrary(.{
Expand All @@ -73,6 +82,7 @@ pub fn build(b: *Build) void {
.optimize = optimize,
});
lib_bytebox.root_module.addImport(stable_array_import.name, stable_array_import.module);
lib_bytebox.root_module.addOptions("config", options);
lib_bytebox.installHeader(b.path("src/bytebox.h"), "bytebox.h");
b.installArtifact(lib_bytebox);

Expand All @@ -83,6 +93,7 @@ pub fn build(b: *Build) void {
.optimize = optimize,
});
unit_tests.root_module.addImport(stable_array_import.name, stable_array_import.module);
unit_tests.root_module.addOptions("config", options);
const run_unit_tests = b.addRunArtifact(unit_tests);
const unit_test_step = b.step("test-unit", "Run unit tests");
unit_test_step.dependOn(&run_unit_tests.step);
Expand All @@ -93,6 +104,7 @@ pub fn build(b: *Build) void {
.root_src = "test/wasm/main.zig",
.step_name = "test-wasm",
.description = "Run the wasm testsuite",
.options = options,
});

// wasi tests
Expand Down Expand Up @@ -127,6 +139,7 @@ pub fn build(b: *Build) void {
.root_src = "test/mem64/main.zig",
.step_name = "test-mem64",
.description = "Run the mem64 test",
.options = options,
});
}

Expand All @@ -151,6 +164,7 @@ fn buildExeWithRunStep(b: *Build, target: Build.ResolvedTarget, optimize: std.bu
for (imports) |import| {
exe.root_module.addImport(import.name, import.module);
}
exe.root_module.addOptions("config", opts.options);

// exe.emit_asm = if (opts.should_emit_asm) .emit else .default;
b.installArtifact(exe);
Expand Down
18 changes: 12 additions & 6 deletions src/instance.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const AllocError = std.mem.Allocator.Error;

const builtin = @import("builtin");

const metering = @import("metering.zig");

const common = @import("common.zig");
const StableArray = common.StableArray;
const Logger = common.Logger;
Expand Down Expand Up @@ -57,7 +59,7 @@ pub const TrapError = error{
TrapOutOfBoundsTableAccess,
TrapStackExhausted,
TrapUnknown,
};
} || metering.MeteringTrapError;

pub const DebugTrace = struct {
pub const Mode = enum {
Expand Down Expand Up @@ -616,6 +618,10 @@ pub const ModuleInstantiateOpts = struct {

pub const InvokeOpts = struct {
trap_on_start: bool = false,
meter: metering.Meter = metering.initial_meter,
};
pub const ResumeInvokeOpts = struct {
meter: metering.Meter = metering.initial_meter,
};

pub const DebugTrapInstructionMode = enum {
Expand All @@ -629,7 +635,7 @@ pub const VM = struct {
const InstantiateFn = *const fn (vm: *VM, module: *ModuleInstance, opts: ModuleInstantiateOpts) anyerror!void;
const InvokeFn = *const fn (vm: *VM, module: *ModuleInstance, handle: FunctionHandle, params: [*]const Val, returns: [*]Val, opts: InvokeOpts) anyerror!void;
const InvokeWithIndexFn = *const fn (vm: *VM, module: *ModuleInstance, func_index: usize, params: [*]const Val, returns: [*]Val) anyerror!void;
const ResumeInvokeFn = *const fn (vm: *VM, module: *ModuleInstance, returns: []Val) anyerror!void;
const ResumeInvokeFn = *const fn (vm: *VM, module: *ModuleInstance, returns: []Val, opts: ResumeInvokeOpts) anyerror!void;
const StepFn = *const fn (vm: *VM, module: *ModuleInstance, returns: []Val) anyerror!void;
const SetDebugTrapFn = *const fn (vm: *VM, module: *ModuleInstance, wasm_address: u32, mode: DebugTrapInstructionMode) anyerror!bool;
const FormatBacktraceFn = *const fn (vm: *VM, indent: u8, allocator: std.mem.Allocator) anyerror!std.ArrayList(u8);
Expand Down Expand Up @@ -699,8 +705,8 @@ pub const VM = struct {
try vm.invoke_with_index_fn(vm, module, func_index, params, returns);
}

pub fn resumeInvoke(vm: *VM, module: *ModuleInstance, returns: []Val) anyerror!void {
try vm.resume_invoke_fn(vm, module, returns);
pub fn resumeInvoke(vm: *VM, module: *ModuleInstance, returns: []Val, opts: ResumeInvokeOpts) anyerror!void {
try vm.resume_invoke_fn(vm, module, returns, opts);
}

pub fn step(vm: *VM, module: *ModuleInstance, returns: []Val) anyerror!void {
Expand Down Expand Up @@ -1186,8 +1192,8 @@ pub const ModuleInstance = struct {
}

/// Use to resume an invoked function after it returned error.DebugTrap
pub fn resumeInvoke(self: *ModuleInstance, returns: []Val) anyerror!void {
try self.vm.resumeInvoke(self, returns);
pub fn resumeInvoke(self: *ModuleInstance, returns: []Val, opts: ResumeInvokeOpts) anyerror!void {
try self.vm.resumeInvoke(self, returns, opts);
}

pub fn step(self: *ModuleInstance, returns: []Val) anyerror!void {
Expand Down
20 changes: 20 additions & 0 deletions src/metering.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const config = @import("config");
const Instruction = @import("definition.zig").Instruction;

pub const enabled = config.enable_metering;

pub const Meter = if (enabled) usize else void;

pub const initial_meter = if (enabled) 0 else {};

pub const MeteringTrapError = if (enabled) error{TrapMeterExceeded} else error{};

pub fn reduce(fuel: Meter, instruction: Instruction) Meter {
if (fuel == 0) {
return fuel;
}
return switch (instruction.opcode) {
.Invalid, .Unreachable, .DebugTrap, .Noop, .Block, .Loop, .If, .IfNoElse, .Else, .End, .Branch, .Branch_If, .Branch_Table, .Drop => fuel,
else => fuel - 1,
};
}
4 changes: 3 additions & 1 deletion src/vm_register.zig
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const inst = @import("instance.zig");
const VM = inst.VM;
const ModuleInstance = inst.ModuleInstance;
const InvokeOpts = inst.InvokeOpts;
const ResumeInvokeOpts = inst.ResumeInvokeOpts;
const DebugTrapInstructionMode = inst.DebugTrapInstructionMode;
const ModuleInstantiateOpts = inst.ModuleInstantiateOpts;

Expand Down Expand Up @@ -1131,10 +1132,11 @@ pub const RegisterVM = struct {
return error.Unimplemented;
}

pub fn resumeInvoke(vm: *VM, module: *ModuleInstance, returns: []Val) anyerror!void {
pub fn resumeInvoke(vm: *VM, module: *ModuleInstance, returns: []Val, opts: ResumeInvokeOpts) anyerror!void {
_ = vm;
_ = module;
_ = returns;
_ = opts;
return error.Unimplemented;
}

Expand Down
Loading

0 comments on commit 8ef7271

Please sign in to comment.