Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove clang dependency #51

Merged
merged 1 commit into from
Jun 10, 2024
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
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ Bytebox currently builds with [Zig 0.13.x](https://ziglang.org/download) to avoi

To run the tests:
* `wasm-tools` is required to run the wasm testsuite. You can install it via the rust toolchain `cargo install wasm-tools` or directly from the [release page](https://github.com/bytecodealliance/wasm-tools/releases).
* `clang` v15.x+ is required to build the mem64 tests. However, if you don't have a compatible version of `clang` installed, you can pass `--noclang` to `zig build` to avoid the requirement.
* `python3` is required to run the wasi testsuite. You may need to run `python3 -m pip install -r test/wasi/wasi-testsuite/test-runner/requirements.txt` to ensure the wasi test runner has all the necessary dependencies installed.

## Run Tests
Expand Down
75 changes: 35 additions & 40 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const ExeOpts = struct {

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 target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
Expand All @@ -27,9 +26,9 @@ pub fn build(b: *Build) void {
.optimize = optimize,
});

var bench_add_one_step: *CompileStep = buildWasmExe(b, "bench/samples/add-one.zig");
var bench_fibonacci_step: *CompileStep = buildWasmExe(b, "bench/samples/fibonacci.zig");
var bench_mandelbrot_step: *CompileStep = buildWasmExe(b, "bench/samples/mandelbrot.zig");
var bench_add_one_step: *CompileStep = buildWasmExe(b, "bench/samples/add-one.zig", .Wasm32);
var bench_fibonacci_step: *CompileStep = buildWasmExe(b, "bench/samples/fibonacci.zig", .Wasm32);
var bench_mandelbrot_step: *CompileStep = buildWasmExe(b, "bench/samples/mandelbrot.zig", .Wasm32);

const stable_array_import = ModuleImport{ .name = "stable-array", .module = stable_array.module("zig-stable-array") };

Expand Down Expand Up @@ -101,43 +100,23 @@ pub fn build(b: *Build) void {
const wasi_testsuite_step = b.step("test-wasi", "Run wasi testsuite");
wasi_testsuite_step.dependOn(&wasi_testsuite.step);

// mem64 step
var mem64_test_step: ?*Build.Step = null;
if (!no_clang) {
// need to use clang to compile the C test due to https://github.com/ziglang/zig/issues/19942
// eventually we will ziggify this test
// ideally this test would go away, but the existing spec tests don't provide very good coverage
// of the instructions
const compile_memtest = b.addSystemCommand(&.{"clang"});
compile_memtest.addArg("--target=wasm64-freestanding");
compile_memtest.addArg("-mbulk-memory");
compile_memtest.addArg("-nostdlib");
compile_memtest.addArg("-O2");
compile_memtest.addArg("-Wl,--no-entry");
compile_memtest.addArg("-Wl,--export-dynamic");
compile_memtest.addArg("-o");
compile_memtest.addArg("test/mem64/memtest.wasm");
compile_memtest.addFileArg(b.path("test/mem64/memtest.c"));
compile_memtest.has_side_effects = true;

b.getInstallStep().dependOn(&compile_memtest.step);

mem64_test_step = buildExeWithRunStep(b, target, optimize, &imports, .{
.exe_name = "test-mem64",
.root_src = "test/mem64/main.zig",
.step_name = "test-mem64",
.description = "Run the mem64 test",
});
}
// mem64 test
const compile_mem64_test = buildWasmExe(b, "test/mem64/memtest.zig", .Wasm64);
b.getInstallStep().dependOn(&compile_mem64_test.step);

const mem64_test_step: *Build.Step = buildExeWithRunStep(b, target, optimize, &imports, .{
.exe_name = "test-mem64",
.root_src = "test/mem64/main.zig",
.step_name = "test-mem64",
.description = "Run the mem64 test",
});

// All tests
const all_tests_step = b.step("test", "Run unit, wasm, and wasi tests");
all_tests_step.dependOn(unit_test_step);
all_tests_step.dependOn(wasm_testsuite_step);
all_tests_step.dependOn(wasi_testsuite_step);
if (mem64_test_step) |step| {
all_tests_step.dependOn(step);
}
all_tests_step.dependOn(mem64_test_step);
}

fn buildExeWithRunStep(b: *Build, target: Build.ResolvedTarget, optimize: std.builtin.Mode, imports: []const ModuleImport, opts: ExeOpts) *Build.Step {
Expand Down Expand Up @@ -173,17 +152,33 @@ fn buildExeWithRunStep(b: *Build, target: Build.ResolvedTarget, optimize: std.bu
return step;
}

fn buildWasmExe(b: *Build, filepath: []const u8) *CompileStep {
const WasmArch = enum {
Wasm32,
Wasm64,
};

fn buildWasmExe(b: *Build, filepath: []const u8, arch: WasmArch) *CompileStep {
var filename: []const u8 = std.fs.path.basename(filepath);
const filename_no_extension: []const u8 = filename[0 .. filename.len - 4];

const cpu_arch: std.Target.Cpu.Arch = if (arch == .Wasm32) .wasm32 else .wasm64;

var target_query: std.Target.Query = .{
.cpu_arch = cpu_arch,
.os_tag = .freestanding,
};
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory));
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.nontrapping_fptoint));
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.multivalue));
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.mutable_globals));
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.reference_types));
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.sign_ext));
target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.simd128));

var exe = b.addExecutable(.{
.name = filename_no_extension,
.root_source_file = b.path(filepath),
.target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
}),
.target = b.resolveTargetQuery(target_query),
.optimize = .ReleaseSmall,
});
exe.rdynamic = true;
Expand Down
38 changes: 19 additions & 19 deletions src/vm_stack.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3466,28 +3466,28 @@ const InstructionFuncs = struct {
try debugPreamble("Memory_Copy", pc, code, stack);
const memory: *MemoryInstance = &stack.topFrame().module_instance.store.memories.items[0];

const length = stack.popI32();
const source_offset = stack.popI32();
const dest_offset = stack.popI32();
const length_s = stack.popIndexType();
const source_offset_s = stack.popIndexType();
const dest_offset_s = stack.popIndexType();

if (length < 0) {
if (length_s < 0) {
return error.TrapOutOfBoundsMemoryAccess;
}

const buffer = memory.buffer();
if (buffer.len < source_offset + length or source_offset < 0) {
if (buffer.len < source_offset_s + length_s or source_offset_s < 0) {
return error.TrapOutOfBoundsMemoryAccess;
}
if (buffer.len < dest_offset + length or dest_offset < 0) {
if (buffer.len < dest_offset_s + length_s or dest_offset_s < 0) {
return error.TrapOutOfBoundsMemoryAccess;
}

const source_offset_u32 = @as(u32, @intCast(source_offset));
const dest_offset_u32 = @as(u32, @intCast(dest_offset));
const length_u32 = @as(u32, @intCast(length));
const source_offset = @as(u64, @intCast(source_offset_s));
const dest_offset = @as(u64, @intCast(dest_offset_s));
const length = @as(u64, @intCast(length_s));

const source = buffer[source_offset_u32 .. source_offset_u32 + length_u32];
const destination = buffer[dest_offset_u32 .. dest_offset_u32 + length_u32];
const source = buffer[source_offset .. source_offset + length];
const destination = buffer[dest_offset .. dest_offset + length];

if (@intFromPtr(destination.ptr) < @intFromPtr(source.ptr)) {
std.mem.copyForwards(u8, destination, source);
Expand All @@ -3501,25 +3501,25 @@ const InstructionFuncs = struct {
try debugPreamble("Memory_Fill", pc, code, stack);
const memory: *MemoryInstance = &stack.topFrame().module_instance.store.memories.items[0];

const length = stack.popI32();
const length_s: i64 = stack.popIndexType();
const value: u8 = @as(u8, @truncate(@as(u32, @bitCast(stack.popI32()))));
const offset = stack.popI32();
const offset_s: i64 = stack.popIndexType();

if (length < 0) {
if (length_s < 0) {
return error.TrapOutOfBoundsMemoryAccess;
}

const buffer = memory.buffer();
if (buffer.len < offset + length or offset < 0) {
if (buffer.len < offset_s + length_s or offset_s < 0) {
return error.TrapOutOfBoundsMemoryAccess;
}

const offset_u32 = @as(u32, @intCast(offset));
const length_u32 = @as(u32, @intCast(length));

const destination = buffer[offset_u32 .. offset_u32 + length_u32];
const offset = @as(u64, @intCast(offset_s));
const length = @as(u64, @intCast(length_s));

const destination = buffer[offset .. offset + length];
@memset(destination, value);

try @call(.always_tail, InstructionFuncs.lookup(code[pc + 1].opcode), .{ pc + 1, code, stack });
}

Expand Down
2 changes: 1 addition & 1 deletion test/mem64/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var allocator: std.mem.Allocator = gpa.allocator();

const wasm_data: []u8 = try std.fs.cwd().readFileAlloc(allocator, "test/mem64/memtest.wasm", 1024 * 128);
const wasm_data: []u8 = try std.fs.cwd().readFileAlloc(allocator, "zig-out/bin/memtest.wasm", 1024 * 128);
defer allocator.free(wasm_data);

const module_def = try bytebox.createModuleDefinition(allocator, .{});
Expand Down
66 changes: 0 additions & 66 deletions test/mem64/memtest.c

This file was deleted.

111 changes: 97 additions & 14 deletions test/mem64/memtest.zig
Original file line number Diff line number Diff line change
@@ -1,25 +1,108 @@
// 0.12.0: zig build-exe memtest.zig -target wasm64-freestanding -fno-entry --export=memtest -O ReleaseSmall
// 0.11.0: zig build-lib memtest.zig -target wasm64-freestanding -dynamic -rdynamic -O ReleaseSmall

const KB = 1024;
const MB = 1024 * KB;
const GB = 1024 * MB;

const PAGE_SIZE = 64 * KB;
const PAGES_PER_GB = GB / PAGE_SIZE;

export fn memtest() i32 {
_ = @wasmMemoryGrow(0, PAGES_PER_GB * 4);

var mem: [*]u8 = @ptrFromInt(4 * GB);

for (0..MB) |i| {
mem[i] = 0xFF;
mem[(4 * GB) - MB + i] = 0xFF;
fn assert(cond: bool) !void {
if (!cond) {
return error.Failed;
}
}

export fn memtest(val_i32: i32, val_i64: i64, val_f32: f32, val_f64: f64) i32 {
testInternal(val_i32, val_i64, val_f32, val_f64) catch {
return 1;
};
return 0;
}

// export fn memtest() void {
// _ = @wasmMemoryGrow(0, PAGES_PER_GB * 8);
// }
fn testInternal(val_i32: i32, val_i64: i64, val_f32: f32, val_f64: f64) !void {
_ = @wasmMemoryGrow(0, PAGES_PER_GB * 4);

const grow_value: isize = @wasmMemoryGrow(0, PAGES_PER_GB * 6); // memory.grow
try assert(grow_value != -1);
const start_page: [*]volatile u8 = @ptrFromInt(@as(usize, @intCast(grow_value)));

const mem = start_page + (GB * 4);
const mem_stores = mem + MB * 1; // volatile?
const mem_loads = mem + MB * 2; // volatile?

const num_pages: usize = @wasmMemorySize(0);
try assert(num_pages >= PAGES_PER_GB * 6);

const ptr_load_i32 = @as(*volatile i32, @ptrCast(@alignCast(mem_loads)));
const ptr_load_i64 = @as(*volatile i64, @ptrCast(@alignCast(mem_loads + 8)));
const ptr_load_f32 = @as(*volatile f32, @ptrCast(@alignCast(mem_loads + 16)));
const ptr_load_f64 = @as(*volatile f64, @ptrCast(@alignCast(mem_loads + 24)));

ptr_load_i32.* = val_i32; // i32.store
ptr_load_i64.* = val_i64; // i64.store
ptr_load_f32.* = val_f32; // f32.store
ptr_load_f64.* = val_f64; // f64.store

try assert(ptr_load_i32.* == val_i32);
try assert(ptr_load_i64.* == val_i64);
try assert(ptr_load_f32.* == val_f32);
try assert(ptr_load_f64.* == val_f64);

const ptr_store_i32 = @as(*volatile i32, @ptrCast(@alignCast(mem_stores)));
const ptr_store_i64 = @as(*volatile i64, @ptrCast(@alignCast(mem_stores + 8)));
const ptr_store_f32 = @as(*volatile f32, @ptrCast(@alignCast(mem_stores + 16)));
const ptr_store_f64 = @as(*volatile f64, @ptrCast(@alignCast(mem_stores + 24)));

ptr_store_i32.* = ptr_load_i32.*; // i32.load && i32.store
ptr_store_i64.* = ptr_load_i64.*; // i64.load && i64.store
ptr_store_f32.* = ptr_load_f32.*; // f32.load && f32.store
ptr_store_f64.* = ptr_load_f64.*; // f64.load && f64.store

try assert(ptr_store_i32.* == ptr_load_i32.*);
try assert(ptr_store_i64.* == ptr_load_i64.*);
try assert(ptr_store_f32.* == ptr_load_f32.*);
try assert(ptr_store_f64.* == ptr_load_f64.*);

var load32: i32 = 0;
ptr_load_i32.* = 0x7F;
load32 = @as(*volatile i8, @ptrCast(@alignCast(ptr_load_i32))).*; // i32.load8_s
try assert(load32 == 0x7F);
ptr_load_i32.* = 0xFF;
load32 = @as(*volatile u8, @ptrCast(@alignCast(ptr_load_i32))).*; // i32.load8_u
try assert(load32 == 0xFF);
ptr_load_i32.* = 0x7FFF;
load32 = @as(*volatile i16, @ptrCast(@alignCast(ptr_load_i32))).*; // i32.load16_s
try assert(load32 == 0x7FFF);
ptr_load_i32.* = 0xFFFF;
load32 = @as(*volatile u16, @ptrCast(@alignCast(ptr_load_i32))).*; // i32.load16_s
try assert(load32 == 0xFFFF);

var load64: i64 = 0;
ptr_load_i64.* = 0x7F;
load64 = @as(*volatile i8, @ptrCast(@alignCast(ptr_load_i64))).*; // i64.load8_s
try assert(load64 == 0x7F);
ptr_load_i64.* = 0xFF;
load64 = @as(*volatile u8, @ptrCast(@alignCast(ptr_load_i64))).*; // i64.load8_u
try assert(load64 == 0xFF);
ptr_load_i64.* = 0x7FFF;
load64 = @as(*volatile i16, @ptrCast(@alignCast(ptr_load_i64))).*; // i64.load16_s
try assert(load64 == 0x7FFF);
ptr_load_i64.* = 0xFFFF;
load64 = @as(*volatile u16, @ptrCast(@alignCast(ptr_load_i64))).*; // i64.load16_s
try assert(load64 == 0xFFFF);
ptr_load_i64.* = 0x7FFFFFFF;
load64 = @as(*volatile i32, @ptrCast(@alignCast(ptr_load_i64))).*; // i64.load32_s
try assert(load64 == 0x7FFFFFFF);
ptr_load_i64.* = 0xFFFFFFFF;
load64 = @as(*volatile u32, @ptrCast(@alignCast(ptr_load_i64))).*; // i64.load32_s
try assert(load64 == 0xFFFFFFFF);

const memset_dest = (mem + KB)[0..KB];
const memcpy_dest = (mem + KB * 2)[0..KB];
@memset(memset_dest, 0xFF); // memory.fill
@memcpy(memcpy_dest, memset_dest); // memory.copy

try assert(memset_dest[0] == 0xFF);
try assert(memset_dest[KB - 1] == 0xFF);
try assert(memcpy_dest[0] == 0xFF);
try assert(memcpy_dest[KB - 1] == 0xFF);
}
Loading