Skip to content

Commit

Permalink
add sample test
Browse files Browse the repository at this point in the history
  • Loading branch information
kassane committed Sep 8, 2024
1 parent b77f7ad commit d438eb5
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 8 deletions.
130 changes: 122 additions & 8 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub fn build(b: *std.Build) !void {

const WARM_RootPath = b.dependency("WAMR", .{}).path("");
const WARM_IncludePath = b.pathJoin(&.{ WARM_RootPath.getPath(b), "core/iwasm/include" });
const WARM_corePath = b.pathJoin(&.{ WARM_RootPath.getPath(b), "core" });

// TODO: https://github.com/ziglang/zig/issues/20630
const wasmC_bindgen = b.addTranslateC(.{
Expand Down Expand Up @@ -35,6 +36,46 @@ pub fn build(b: *std.Build) !void {
},
.use_clang = true, // TODO: set 'false' use fno-llvm/fno-clang
});
const bh_reader_bindgen = b.addTranslateC(.{
.link_libc = true,
.optimize = optimize,
.target = target,
.root_source_file = .{
// get absolute path of core/iwasm/include/wasm_export.h
.cwd_relative = b.pathJoin(&.{
WARM_corePath,
"shared",
"utils",
"uncommon",
"bh_read_file.c",
}),
},
.use_clang = true, // TODO: set 'false' use fno-llvm/fno-clang
});
bh_reader_bindgen.addIncludeDir(
b.pathJoin(&.{
WARM_corePath,
"shared",
"utils",
"uncommon",
}),
);
bh_reader_bindgen.addIncludeDir(
b.pathJoin(&.{
WARM_corePath,
"shared",
"utils",
}),
);
bh_reader_bindgen.addIncludeDir(
b.pathJoin(&.{
WARM_corePath,
"shared",
"platform",
@tagName(target.result.os.tag),
}),
);

const vmlib = try buildCMake(b, WARM_RootPath);
wasmExport_bindgen.step.dependOn(&vmlib.step);

Expand All @@ -46,13 +87,18 @@ pub fn build(b: *std.Build) !void {
});
wamr_module.addImport("wasm_export", wasmExport_bindgen.addModule("wasm_export"));
wamr_module.addImport("wasm_c_api", wasmC_bindgen.addModule("wasm_c_api"));
wamr_module.addImport("bh_read_file", bh_reader_bindgen.addModule("bh_read_file"));
wamr_module.addLibraryPath(b.path(".zig-cache"));

if (target.query.isNative()) {
wamr_module.linkSystemLibrary("iwasm", .{});
} else {
wamr_module.linkSystemLibrary("vmlib", .{ .use_pkg_config = .no });
}
wamr_module.linkSystemLibrary("vmlib", .{
.use_pkg_config = .no,
});
// if (target.query.isNative()) {
// wamr_module.linkSystemLibrary("iwasm", .{});
// } else {
// for (llvm_libs) |lib| {
// wamr_module.linkSystemLibrary(lib, .{});
// }
// }

buildTest(b, wamr_module);
}
Expand All @@ -64,12 +110,12 @@ fn buildCMake(b: *std.Build, dependency: std.Build.LazyPath) !*std.Build.Step.Ru
cmake_config.addPrefixedDirectoryArg("-S", dependency);
cmake_config.addPrefixedDirectoryArg("-B", b.path(".zig-cache"));

const cpu_count = b.fmt("{}", .{if (!@import("builtin").single_threaded) try std.Thread.getCpuCount() / 2 else 1});
const cpu_count = b.fmt("{}", .{std.Thread.getCpuCount() catch 1});

const cmake_build = b.addSystemCommand(&.{
cmake_app,
"--build",
b.cache_root.path.?,
".zig-cache",
"--parallel",
cpu_count,
});
Expand All @@ -90,3 +136,71 @@ fn buildTest(b: *std.Build, module: *std.Build.Module) void {
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
}

const llvm_libs = [_][]const u8{
"LLVMAggressiveInstCombine",
"LLVMAnalysis",
"LLVMAsmParser",
"LLVMAsmPrinter",
"LLVMBitReader",
"LLVMBitWriter",
"LLVMCFGuard",
"LLVMCodeGen",
"LLVMCoroutines",
"LLVMCoverage",
"LLVMDWARFLinker",
"LLVMDWP",
"LLVMDebugInfoCodeView",
"LLVMDebugInfoDWARF",
"LLVMDebugInfoGSYM",
"LLVMDebugInfoMSF",
"LLVMDebugInfoPDB",
"LLVMDlltoolDriver",
"LLVMExecutionEngine",
"LLVMExtensions",
"LLVMFileCheck",
"LLVMFrontendOpenACC",
"LLVMFrontendOpenMP",
"LLVMFuzzMutate",
"LLVMGlobalISel",
"LLVMIRReader",
"LLVMInstCombine",
"LLVMInstrumentation",
"LLVMInterfaceStub",
"LLVMInterpreter",
"LLVMJITLink",
"LLVMLTO",
"LLVMLibDriver",
"LLVMLineEditor",
"LLVMLinker",
"LLVMMC",
"LLVMMCA",
"LLVMMCDisassembler",
"LLVMMCJIT",
"LLVMMCParser",
"LLVMMIRParser",
"LLVMObjCARCOpts",
"LLVMObject",
"LLVMObjectYAML",
"LLVMOption",
"LLVMOrcJIT",
"LLVMOrcShared",
"LLVMOrcTargetProcess",
"LLVMPasses",
"LLVMProfileData",
"LLVMRuntimeDyld",
"LLVMScalarOpts",
"LLVMSelectionDAG",
"LLVMSymbolize",
"LLVMTarget",
"LLVMTextAPI",
"LLVMTransformUtils",
"LLVMVectorize",
"LLVMX86AsmParser",
"LLVMX86CodeGen",
"LLVMX86Desc",
"LLVMX86Disassembler",
"LLVMX86Info",
"LLVMXRay",
"LLVMipo",
};
1 change: 1 addition & 0 deletions src/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
pub const c = struct {
pub const wasm_export = @import("wasm_export");
pub const wasm_c = @import("wasm_c_api");
pub const bh_platform = @import("bh_read_file");
};
17 changes: 17 additions & 0 deletions tests/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const wamr_zig = b.dependency("wamr_zig", .{});

const exe = b.addExecutable(.{
.name = "hello",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("wamr", wamr_zig.module("wamr"));
b.installArtifact(exe);
}
10 changes: 10 additions & 0 deletions tests/build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.{
.name = "tests",
.version = "0.0.0",
.dependencies = .{
.wamr_zig = .{
.path = "..",
},
},
.paths = .{""},
}
178 changes: 178 additions & 0 deletions tests/src/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
const std = @import("std");
const wamr = @import("wamr").c;
const c = wamr.wasm_export;
// const c_bh = wamr.bh_platform;

pub const WasmRuntime = struct {
module: c.wasm_module_t,
module_inst: c.wasm_module_inst_t,
exec_env: c.wasm_exec_env_t,
buffer: []u8,
allocator: std.mem.Allocator,

pub fn init(allocator: std.mem.Allocator, wasm_path: []const u8, wasi_dir: []const u8) !*WasmRuntime {
var self = try allocator.create(WasmRuntime);
errdefer allocator.destroy(self);

self.* = WasmRuntime{
.module = null,
.module_inst = null,
.exec_env = null,
.buffer = undefined,
.allocator = allocator,
};

var init_args: c.RuntimeInitArgs = std.mem.zeroes(c.RuntimeInitArgs);
init_args.mem_alloc_type = c.Alloc_With_Pool;
init_args.mem_alloc_option.pool.heap_buf = &global_heap_buf;
init_args.mem_alloc_option.pool.heap_size = global_heap_buf.len;

if (!c.wasm_runtime_full_init(&init_args)) {
return error.RuntimeInitFailed;
}

// var buf_size: u32 = undefined;
// self.buffer = c_bh.bh_read_file_to_buffer(wasm_path.ptr, &buf_size);
// if (self.buffer == null) {
// return error.FileReadFailed;
// }
// buf_size = @intCast(std.mem.len(self.buffer));

std.debug.print("Attempting to open file: {s}\n", .{wasm_path});

// Read file using Zig's std lib
const file = std.fs.cwd().openFile(wasm_path, .{}) catch |err| {
std.log.err("Failed to open file: {s}, error: {}\n", .{ wasm_path, err });
return error.FileOpenFailed;
};
defer file.close();

const file_size = file.getEndPos() catch |err| {
std.log.err("Failed to get file size, error: {}\n", .{err});
return error.FileReadFailed;
};

std.debug.print("File size: {}\n", .{file_size});

if (file_size == 0) {
std.log.err("File is empty\n", .{});
return error.EmptyFile;
}

self.buffer = allocator.alloc(u8, file_size) catch |err| {
std.log.err("Failed to allocate buffer, error: {}\n", .{err});
return error.MemoryAllocationFailed;
};
errdefer allocator.free(self.buffer);

const bytes_read = file.readAll(self.buffer) catch |err| {
std.log.err("Failed to read file, error: {}\n", .{err});
return error.FileReadFailed;
};

std.debug.print("Bytes read: {}\n", .{bytes_read});

if (bytes_read != file_size) {
std.log.err("Incomplete file read: {} of {} bytes\n", .{ bytes_read, file_size });
return error.FileReadIncomplete;
}

std.debug.print("Attempting to load WASM module. Buffer size: {}\n", .{self.buffer.len});

const buf_size: u32 = @min(self.buffer.len, 1024 * 1024); //@intCast(self.buffer.len);
if (!validateWasmFile(self.buffer)) {
std.log.err("Invalid WASM file format\n", .{});
return error.InvalidWasmFormat;
}

var error_buf: [128]u8 = std.mem.zeroes([128]u8);
self.module = c.wasm_runtime_load(self.buffer.ptr, buf_size, &error_buf, error_buf.len);
if (self.module == null) {
std.debug.print("Buffer size: {}\n", .{self.buffer.len});
std.log.err("Failed to load WASM module. Error: {s}\n", .{error_buf});
std.debug.print("First 16 bytes of WASM file: ", .{});
for (self.buffer[0..@min(16, buf_size)]) |byte| {
std.debug.print("{x:0>2} ", .{byte});
}
std.debug.print("\n", .{});
return error.ModuleLoadFailed;
}

const wasi_dir_ptr: [*c][*c]const u8 = @ptrCast(@alignCast(@constCast(wasi_dir.ptr)));
_ = c.wasm_runtime_set_wasi_args_ex(self.module, wasi_dir_ptr, 1, null, 0, null, 0, null, 0, 0, 1, 2);

const stack_size: u32 = 8092;
const heap_size: u32 = 8092;

self.module_inst = c.wasm_runtime_instantiate(self.module, stack_size, heap_size, &error_buf, error_buf.len);
if (self.module_inst == null) {
return error.ModuleInstantiateFailed;
}

self.exec_env = c.wasm_runtime_create_exec_env(self.module_inst, stack_size);
if (self.exec_env == null) {
return error.ExecEnvCreateFailed;
}

return self;
}

pub fn deinit(self: *WasmRuntime) void {
if (self.exec_env) |env| c.wasm_runtime_destroy_exec_env(env);
if (self.module_inst) |inst| c.wasm_runtime_deinstantiate(inst);
if (self.module) |mod| c.wasm_runtime_unload(mod);
// if (self.buffer) |buf| std.c.free(@ptrCast(@constCast(&buf)));
self.allocator.free(self.buffer);
c.wasm_runtime_destroy();
self.allocator.destroy(self);
}

pub fn executeMain(self: *WasmRuntime) !u32 {
if (c.wasm_application_execute_main(self.module_inst, 0, null)) {
return c.wasm_runtime_get_wasi_exit_code(self.module_inst);
} else {
const exception = c.wasm_runtime_get_exception(self.module_inst);
std.log.err("WASM execution failed. Exception: {s}\n", .{exception});
return error.MainExecutionFailed;
}
}
var global_heap_buf: [512 * 1024]u8 = undefined;
};

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{
.verbose_log = (@import("builtin").mode == .Debug),
}){};
defer {
if (gpa.detectLeaks()) {
std.log.err("Memory leak detected!\n", .{});
}
}
const allocator = gpa.allocator();
const args = try std.process.argsAlloc(allocator);
defer std.process.argsFree(allocator, args);

if (args.len != 3) {
std.debug.print("Usage: {s} <wasm_file_path> <wasi_dir_path>\n", .{args[0]});
return;
}

var runtime = WasmRuntime.init(allocator, args[1], args[2]) catch |err| {
std.log.err("Failed to initialize WasmRuntime: {}\n", .{err});
return;
};
defer runtime.deinit();

const result = runtime.executeMain() catch |err| {
std.log.err("Failed to execute WASM main: {}\n", .{err});
return;
};
std.debug.print("WASM application exited with code: {}\n", .{result});
}

fn validateWasmFile(buffer: []const u8) bool {
if (buffer.len < 8) return false;
const magic_number = [_]u8{ 0x00, 0x61, 0x73, 0x6d };
const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 };
return std.mem.eql(u8, buffer[0..4], &magic_number) and std.mem.eql(u8, buffer[4..8], &version);
}
9 changes: 9 additions & 0 deletions tests/wasmfiles/gcd.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const std = @import("std");

export fn gcd(a: u32, b: u32) callconv(.C) u32 {
return if (b != 0) gcd(b, @mod(a, b)) else a;
}

pub fn main() void {
_ = std.c.printf("Hello, world! Please call gcd(10, 5) to see the result.\n");
}
Binary file added tests/wasmfiles/gcd_wasm32_wasi.wasm
Binary file not shown.

0 comments on commit d438eb5

Please sign in to comment.