diff --git a/crates/artifact/src/engine.rs b/crates/artifact/src/engine.rs index c42d6bcb..ed7a90d9 100644 --- a/crates/artifact/src/engine.rs +++ b/crates/artifact/src/engine.rs @@ -135,11 +135,13 @@ impl FromStr for EngineRef { #[derive(Clone, Debug)] pub enum WellKnownEngine { Wasmtime, + V8, } impl fmt::Display for WellKnownEngine { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { Self::Wasmtime => "wasmtime", + Self::V8 => "v8", }; write!(f, "{}", s) } @@ -149,6 +151,7 @@ impl FromStr for WellKnownEngine { fn from_str(s: &str) -> Result { match s { "wasmtime" => Ok(Self::Wasmtime), + "v8" => Ok(Self::V8), _ => Err(anyhow!("unable to parse an unknown engine: {}", s)), } } diff --git a/crates/cli/src/benchmark.rs b/crates/cli/src/benchmark.rs index da4069c9..1c6e6102 100644 --- a/crates/cli/src/benchmark.rs +++ b/crates/cli/src/benchmark.rs @@ -106,6 +106,12 @@ pub struct BenchmarkCommand { /// supplied. #[structopt(short, long, default_value = "0.01")] significance_level: f64, + + /// Sightglass checks that the benchmark has emitted log files for `stdout` + /// and `stderr` and will attempt to check these against `stdout.expected` + /// and `stderr.expected` files; enabling this flag skips these checks. + #[structopt(long)] + skip_output_check: bool, } impl BenchmarkCommand { @@ -186,7 +192,9 @@ impl BenchmarkCommand { &mut measurements, )?; - self.check_output(Path::new(wasm_file), stdout, stderr)?; + if !self.skip_output_check { + self.check_output(Path::new(wasm_file), stdout, stderr)?; + } measurements.next_iteration(); } diff --git a/engines/v8/.dockerignore b/engines/v8/.dockerignore new file mode 100644 index 00000000..e6170441 --- /dev/null +++ b/engines/v8/.dockerignore @@ -0,0 +1,3 @@ +archive +build +v8 diff --git a/engines/v8/.gitignore b/engines/v8/.gitignore new file mode 100644 index 00000000..3d0f7c20 --- /dev/null +++ b/engines/v8/.gitignore @@ -0,0 +1,3 @@ +build +v8 +archive diff --git a/engines/v8/Dockerfile b/engines/v8/Dockerfile new file mode 100644 index 00000000..83c1352a --- /dev/null +++ b/engines/v8/Dockerfile @@ -0,0 +1,25 @@ +FROM ubuntu:latest +ARG REPOSITORY=https://github.com/WebAssembly/wasm-c-api +ARG REVISION=branch-heads/9.9 + +# Install necessary dependencies +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yqq \ + apt-utils \ + curl \ + git \ + g++ \ + make \ + ninja-build \ + pkg-config \ + python3 + +# Setup V8 and build the `libwee8.a` library. TODO map REVISION to V8_VERSION. +WORKDIR /usr/src +COPY args.gn Makefile ./ +RUN make v8-checkout +RUN make wee8 + +# Build the Sightglass engine. +COPY src ./src +RUN make build/libengine.so +RUN cp build/libengine.so /libengine.so diff --git a/engines/v8/Makefile b/engines/v8/Makefile new file mode 100644 index 00000000..aa23cee7 --- /dev/null +++ b/engines/v8/Makefile @@ -0,0 +1,109 @@ +# This Makefile builds both the V8 components and the bench API wrapper for +# Sightglass. +all: v8-checkout test + +######### V8 ########## + +# In order to build the `libwee8` library, we must perform the following steps: +# - `make v8-checkout` will retrieve the V8 repository (borrowing much the same +# logic as https://github.com/WebAssembly/wasm-c-api) +# - `make wee8` will build the `libwee8.a` library inside the v8 tree (see See +# https://docs.google.com/document/d/1oFPHyNb_eXg6NzrE6xJDNPdJrHMZvx0LqsD6wpbd9vY/edit#) + +# TODO rename `v8` directory to `upstream`; potentially move this section to a +# separate Makefile there. +V8_VERSION = branch-heads/9.9 +V8_ARCH = x64 +V8_MODE = release +V8_BUILD = ${V8_ARCH}.${V8_MODE} +V8_OUT = ${V8_V8}/out.gn/${V8_BUILD} +V8_DIR = v8 +V8_DEPOT_TOOLS = ${V8_DIR}/depot_tools +V8_V8 = ${V8_DIR}/v8 +V8_PATH = $(abspath ${V8_DEPOT_TOOLS}):${PATH} +V8_LIBWEE8 = ${V8_V8}/out/wee8/obj/libwee8.a + +.PHONY: v8-checkout +v8-checkout: ${V8_DEPOT_TOOLS} ${V8_V8} + (cd ${V8_V8}; git checkout -f main) + (cd ${V8_V8}; git pull) + (cd ${V8_V8}; git checkout ${V8_VERSION}) + (cd ${V8_V8}; PATH=${V8_PATH} gclient sync) + mkdir -p ${V8_OUT} + echo >${V8_OUT}/version ${V8_VERSION} +${V8_DEPOT_TOOLS}: + mkdir -p ${V8_DIR} + (cd ${V8_DIR}; git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git --depth 1) +${V8_V8}: + mkdir -p ${V8_DIR} + (cd ${V8_DIR}; PATH=${V8_PATH} fetch --no-history v8) + (cd ${V8_V8}; git checkout ${V8_VERSION}) + +.PHONY: wee8 +wee8: ${V8_LIBWEE8} +${V8_LIBWEE8}: args.gn + mkdir -p ${V8_V8}/out/wee8 + cp args.gn ${V8_V8}/out/wee8/ + (cd ${V8_V8}; PATH=${V8_PATH} gn gen out/wee8) + (cd ${V8_V8}; PATH=${V8_PATH} autoninja -C out/wee8 wee8) + +.PHONY: v8-clean +v8-clean: + rm -rf v8 + + + +######### BENCH-API ########## + +# The shared library that Sightglass expects must conform to a specific +# interface (see `src/bench-api.h`). We interact with `libwee8.a` in +# `src/bench-state.cc`. To build and test this functionality, use `make test`. + +# This Makefile setup roughly follows https://stackoverflow.com/a/2481326. +CXXFLAGS=-g -O0 -fPIC -I ${V8_V8}/third_party/wasm-api +WASM_HH=${V8_V8}/third_party/wasm-api/wasm.hh +SRC_DIR=src +TEST_DIR=test +BUILD_DIR=build +SRCS=src/bench-api.cc src/bench-state.cc src/link.cc src/wasi.cc +OBJS=$(patsubst $(SRC_DIR)/%.cc,$(BUILD_DIR)/%.o,$(SRCS)) +LDLIBS=$(V8_LIBWEE8) -ldl -pthread +# See https://stackoverflow.com/questions/36692315. +LDFLAGS=-rdynamic + +# Create the build directory, if necessary. +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# Built-in rules to compile the various source files. +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cc + $(CXX) $(CXXFLAGS) -c $< -o $@ +$(BUILD_DIR)/%.o: $(TEST_DIR)/%.c + $(CC) $(CFLAGS) -I $(SRC_DIR) -c $< -o $@ + +# Define the dependencies for each object. +$(BUILD_DIR)/bench-api.o: $(SRC_DIR)/bench-api.cc $(SRC_DIR)/bench-api.h $(WASM_HH) +$(BUILD_DIR)/bench-state.o: $(SRC_DIR)/bench-state.cc $(SRC_DIR)/bench-state.hh $(SRC_DIR)/bench-api.h $(WASM_HH) +$(BUILD_DIR)/link.o: $(SRC_DIR)/link.cc $(SRC_DIR)/link.hh $(SRC_DIR)/bench-state.hh $(SRC_DIR)/wasi.hh $(WASM_HH) +$(BUILD_DIR)/wasi.o: $(SRC_DIR)/wasi.cc $(SRC_DIR)/wasi.hh $(WASM_HH) +$(BUILD_DIR)/main.o: $(TEST_DIR)/main.c $(SRC_DIR)/bench-api.h + +# Create a test target for building and running the test file with `make test`. +.PHONY: test +test: $(BUILD_DIR)/test + $< ../../benchmarks-next/noop/benchmark.wasm +$(BUILD_DIR)/test: $(BUILD_DIR) $(OBJS) $(BUILD_DIR)/main.o + $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(BUILD_DIR)/main.o $(LDLIBS) + +# Create the shared library target; this is what can be used by sightglass to +# measure benchmarks. +$(BUILD_DIR)/libengine.so: $(BUILD_DIR) $(OBJS) + $(CXX) -shared $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS) +test-lib: $(BUILD_DIR)/libengine.so + RUST_LOG=trace cargo run -- benchmark ../../benchmarks-next/noop/benchmark.wasm \ + --engine $(BUILD_DIR)/libengine.so --processes 1 --iterations-per-process 1 \ + --skip-output-check + +.PHONY: test +clean: + rm -rf build diff --git a/engines/v8/args.gn b/engines/v8/args.gn new file mode 100644 index 00000000..7cd5c132 --- /dev/null +++ b/engines/v8/args.gn @@ -0,0 +1,9 @@ +is_component_build = false +use_custom_libcxx = false +v8_enable_fast_mksnapshot = true +v8_enable_i18n_support = false +v8_use_external_startup_data = false +is_debug = true +symbol_level = 2 +v8_optimized_debug = false +v8_expose_symbols = true diff --git a/engines/v8/src/bench-api.cc b/engines/v8/src/bench-api.cc new file mode 100644 index 00000000..a829d57c --- /dev/null +++ b/engines/v8/src/bench-api.cc @@ -0,0 +1,103 @@ +#include "bench-api.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "bench-state.hh" +#include "link.hh" +#include "wasm.hh" + +extern "C" { + +int wasm_bench_create(wasm_bench_config_t config, void** out_ptr) { + BenchState* st = new BenchState(Config::make(config)); + st->engine = wasm::Engine::make(); // TODO cannot instantiate an EngineImpl + // multiple times. + assert(st->engine != nullptr); + st->store = wasm::Store::make(st->engine.get()); + assert(st->store != nullptr); + + assert(out_ptr != NULL); + *out_ptr = (void*)st; + return 0; +} + +void wasm_bench_free(void* state_) { + BenchState* state = (BenchState*)state_; + delete state; +} + +int wasm_bench_compile(void* state_, + char* wasm_bytes, + size_t wasm_bytes_length) { + BenchState* st = (BenchState*)state_; + assert(st != nullptr); + assert(st->store != nullptr); + auto wasm = wasm::vec::make(wasm_bytes_length, wasm_bytes); + + st->config.compilation.start(); + st->module = wasm::Module::make(st->store.get(), wasm); + st->config.compilation.end(); + + if (!st->module) { + std::cerr << "> Error compiling module!" << std::endl; + return 1; + } else { + return 0; + } +} + +int wasm_bench_instantiate(void* state_) { + BenchState* st = (BenchState*)state_; + assert(st != nullptr); + assert(st->module != nullptr); + assert(st->store != nullptr); + + // Retrieve linked functions and extract their pointers here to avoid dropping + // them too early. + auto imports = link(st->store.get(), st->module.get(), &st->config.execution); + const wasm::Extern* imports_as_extern[imports.size()]; + for (size_t i = 0; i < imports.size(); ++i) { + imports_as_extern[i] = imports[i].get(); + } + + st->config.instantiation.start(); + st->instance = wasm::Instance::make(st->store.get(), st->module.get(), + imports_as_extern); + st->config.instantiation.end(); + + if (!st->instance) { + std::cerr << "> Error instantiating module!" << std::endl; + return 1; + } else { + return 0; + } +} + +int wasm_bench_execute(void* state_) { + BenchState* st = (BenchState*)state_; + assert(st != nullptr); + assert(st->module != nullptr); + assert(st->instance != nullptr); + + // Find the _start function. + auto start_fn = find_start_fn(st->module.get(), st->instance.get()); + if (!start_fn) { + std::cerr << "> Unable to find the '_start' function!" << std::endl; + return 1; + } + + // Run the start function. + if (!start_fn->func()->call()) { + std::cerr << "> Error calling start function!" << std::endl; + return 1; + } + + return 0; +} + +} // extern "C" diff --git a/engines/v8/src/bench-api.h b/engines/v8/src/bench-api.h new file mode 100644 index 00000000..ad6618c0 --- /dev/null +++ b/engines/v8/src/bench-api.h @@ -0,0 +1,50 @@ +// Describe the C API that this engine must conform to for use within +// Sightglass. +#ifndef __BENCH_API_H +#define __BENCH_API_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef char* wasm_bench_timer_t; +typedef void (*wasm_bench_callback_t)(wasm_bench_timer_t); + +/** + * @brief This struct must match what is passed by sightglass (TODO cite). + */ +typedef struct wasm_bench_config_t { + char* working_dir_ptr; + size_t working_dir_len; + char* stdout_path_ptr; + size_t stdout_path_len; + char* stderr_path_ptr; + size_t stderr_path_len; + char* stdin_path_ptr; + size_t stdin_path_len; + wasm_bench_timer_t compilation_timer; + wasm_bench_callback_t compilation_start; + wasm_bench_callback_t compilation_end; + wasm_bench_timer_t instantiation_timer; + wasm_bench_callback_t instantiation_start; + wasm_bench_callback_t instantiation_end; + wasm_bench_timer_t execution_timer; + wasm_bench_callback_t execution_start; + wasm_bench_callback_t execution_end; +} wasm_bench_config_t; + +/// API functions (TODO cite). + +int wasm_bench_create(wasm_bench_config_t config, void** state_out_ptr); +void wasm_bench_free(void* state); +int wasm_bench_compile(void* state, char* wasm_bytes, size_t wasm_bytes_length); +int wasm_bench_instantiate(void* state); +int wasm_bench_execute(void* state); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // #ifdef __BENCH_API_H diff --git a/engines/v8/src/bench-state.cc b/engines/v8/src/bench-state.cc new file mode 100644 index 00000000..12f35099 --- /dev/null +++ b/engines/v8/src/bench-state.cc @@ -0,0 +1,33 @@ +#include "bench-state.hh" +#include + +Config Config::make(wasm_bench_config_t config) { + auto working_dir = + std::string(config.working_dir_ptr, config.working_dir_len); + auto stdout_path = + std::string(config.stdout_path_ptr, config.stdout_path_len); + auto stderr_path = + std::string(config.stderr_path_ptr, config.stderr_path_len); + auto stdin_path = std::string(config.stdin_path_ptr, config.stdin_path_len); + auto compilation = Timer(config.compilation_timer, config.compilation_start, + config.compilation_end); + auto instantiation = + Timer(config.instantiation_timer, config.instantiation_start, + config.instantiation_end); + auto execution = Timer(config.execution_timer, config.execution_start, + config.execution_end); + return Config(working_dir, stdout_path, stderr_path, stdin_path, compilation, + instantiation, execution); +} + +void Timer::start() const { + if (this->start_timer != NULL) { + (this->start_timer)(this->timer); + } +} + +void Timer::end() const { + if (this->end_timer != NULL) { + (this->end_timer)(this->timer); + } +} diff --git a/engines/v8/src/bench-state.hh b/engines/v8/src/bench-state.hh new file mode 100644 index 00000000..16f12ecf --- /dev/null +++ b/engines/v8/src/bench-state.hh @@ -0,0 +1,72 @@ +// Data structures maintained during a Sightglass benchmarking session. +#ifndef __BENCH_STATE_HH +#define __BENCH_STATE_HH + +#include +#include "bench-api.h" +#include "wasm.hh" + +/** + * @brief A C++-wrapper around the timer pointers passed in `config_t`. + */ +class Timer { + public: + Timer(wasm_bench_timer_t timer, + wasm_bench_callback_t start_timer, + wasm_bench_callback_t end_timer) + : timer(timer), start_timer(start_timer), end_timer(end_timer) {} + void start() const; + void end() const; + + private: + wasm_bench_timer_t timer; + wasm_bench_callback_t start_timer; + wasm_bench_callback_t end_timer; +}; + +/** + * @brief A C++-wrapper around the `config_t` passed in `wasm_bench_create`. + */ +class Config { + public: + static Config make(wasm_bench_config_t config); + std::string working_dir; + std::string stdout_path; + std::string stderr_path; + std::string stdin_path; + Timer compilation; + Timer instantiation; + Timer execution; + + private: + Config(std::string working_dir, + std::string stdout_path, + std::string stderr_path, + std::string stdin_path, + Timer compilation, + Timer instantiation, + Timer execution) + : working_dir(working_dir), + stdout_path(stdout_path), + stderr_path(stderr_path), + stdin_path(stdin_path), + compilation(compilation), + instantiation(instantiation), + execution(execution) {} +}; + +/** + * @brief The internal state maintained through a Sightglass benchmarking + * session. + */ +class BenchState { + public: + BenchState(Config config) : config(config) {} + Config config; + std::unique_ptr engine; + std::unique_ptr store; + std::unique_ptr module; + std::unique_ptr instance; +}; + +#endif // #ifdef __BENCH_STATE_HH diff --git a/engines/v8/src/link.cc b/engines/v8/src/link.cc new file mode 100644 index 00000000..2d7b1f78 --- /dev/null +++ b/engines/v8/src/link.cc @@ -0,0 +1,116 @@ + +#include "link.hh" +#include +#include +#include "wasi.hh" + +std::vector list_imports(const wasm::Module* module) { + auto import_types = module->imports(); + auto import_names = std::vector(); + for (size_t i = 0; i < import_types.size(); ++i) { + wasm::ImportType* type = import_types[i].get(); + auto module_str = std::string(type->module().get(), type->module().size()); + auto name_str = std::string(type->name().get(), type->name().size()); + // std::cerr << "Found import: " << module_str << " " << name_str << + // std::endl; + import_names.push_back(ImportName(module_str, name_str)); + } + // std::cerr << "Number of imports: " << import_names.size() << std::endl; + return import_names; +} + +auto bench_start(void* env, const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + auto timer = *reinterpret_cast(env); + std::cout << "bench_start" << std::endl; + timer.start(); + return nullptr; +} + +auto bench_end(void* env, const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + auto timer = *reinterpret_cast(env); + timer.end(); + std::cout << "bench_end" << std::endl; + return nullptr; +} + +std::vector> link(wasm::Store* store, + const wasm::Module* module, + Timer* timer) { + // Set up import types. + auto fn_type_none_none = wasm::FuncType::make( + wasm::ownvec::make(), wasm::ownvec::make()); + auto fn_type_i32_none = wasm::FuncType::make( + wasm::ownvec::make(wasm::ValType::make(wasm::I32)), + wasm::ownvec::make()); + auto fn_type_i32_i32 = wasm::FuncType::make( + wasm::ownvec::make(wasm::ValType::make(wasm::I32)), + wasm::ownvec::make(wasm::ValType::make(wasm::I32))); + auto fn_type_2xi32_i32 = wasm::FuncType::make( + wasm::ownvec::make(wasm::ValType::make(wasm::I32), + wasm::ValType::make(wasm::I32)), + wasm::ownvec::make(wasm::ValType::make(wasm::I32))); + auto fn_type_seek_i32 = wasm::FuncType::make( + wasm::ownvec::make( + wasm::ValType::make(wasm::I32), wasm::ValType::make(wasm::I64), + wasm::ValType::make(wasm::I32), wasm::ValType::make(wasm::I32)), + wasm::ownvec::make(wasm::ValType::make(wasm::I32))); + auto fn_type_4xi32_i32 = wasm::FuncType::make( + wasm::ownvec::make( + wasm::ValType::make(wasm::I32), wasm::ValType::make(wasm::I32), + wasm::ValType::make(wasm::I32), wasm::ValType::make(wasm::I32)), + wasm::ownvec::make(wasm::ValType::make(wasm::I32))); + + // Set up available functions to link to. + std::map> available_functions; + available_functions[ImportName("bench", "start")] = std::move( + wasm::Func::make(store, fn_type_none_none.get(), bench_start, timer)); + available_functions[ImportName("bench", "end")] = std::move( + wasm::Func::make(store, fn_type_none_none.get(), bench_end, timer)); + available_functions[ImportName("wasi_snapshot_preview1", "proc_exit")] = + std::move(wasm::Func::make(store, fn_type_i32_none.get(), proc_exit)); + available_functions[ImportName("wasi_snapshot_preview1", "fd_write")] = + std::move(wasm::Func::make(store, fn_type_4xi32_i32.get(), fd_write)); + available_functions[ImportName("wasi_snapshot_preview1", "fd_seek")] = + std::move(wasm::Func::make(store, fn_type_seek_i32.get(), fd_seek)); + available_functions[ImportName("wasi_snapshot_preview1", "fd_close")] = + std::move(wasm::Func::make(store, fn_type_i32_i32.get(), fd_close)); + available_functions[ImportName("wasi_snapshot_preview1", "fd_fdstat_get")] = + std::move( + wasm::Func::make(store, fn_type_2xi32_i32.get(), fd_fdstat_get)); + + // Construct list of import functions. + auto import_names = list_imports(module); + std::vector> imports = {}; + for (ImportName i : import_names) { + auto found = available_functions.find(i) != available_functions.end(); + if (found) { + imports.push_back(std::move(available_functions[i])); + } else { + std::cerr << "Unable to find a function for import: " << i.first << " " + << i.second << std::endl; + exit(1); + } + } + + return imports; +} + +wasm::own find_start_fn(const wasm::Module* module, + const wasm::Instance* instance) { + std::string start_fn_name = "_start"; + auto exports = instance->exports(); + auto export_types = module->exports(); + for (size_t i = 0; i < exports.size(); ++i) { + if (exports[i]->type()->kind() == wasm::EXTERN_FUNC) { + assert(exports[i]->type()->kind() == export_types[i]->type()->kind()); + const wasm::Name& name = export_types[i]->name(); + // std::cerr << "Found function: " << name.get() << std::endl; + if (start_fn_name == name.get()) { + return std::move(exports[i]); + } + } + } + return nullptr; +} diff --git a/engines/v8/src/link.hh b/engines/v8/src/link.hh new file mode 100644 index 00000000..e2760063 --- /dev/null +++ b/engines/v8/src/link.hh @@ -0,0 +1,13 @@ +#include +#include +#include +#include "bench-state.hh" +#include "wasm.hh" + +using ImportName = std::pair; +std::vector list_imports(const wasm::Module* module); +std::vector> link(wasm::Store* store, + const wasm::Module* module, + Timer* timer); +wasm::own find_start_fn(const wasm::Module* module, + const wasm::Instance* instance); diff --git a/engines/v8/src/wasi.cc b/engines/v8/src/wasi.cc new file mode 100644 index 00000000..0204f951 --- /dev/null +++ b/engines/v8/src/wasi.cc @@ -0,0 +1,48 @@ +#include "wasi.hh" +#include +#include + +// WebAssembly type: (func (param i32)) +auto proc_exit(const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + std::cerr << "proc_exit" << std::endl; + // exit(args[0].i32()); // TODO this cannot actually exit here; should trap? + return nullptr; +} + +// WebAssembly type: (func (param i32 i32 i32 i32) (result i32)) +auto fd_write(const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + std::cerr << "fd_write" << std::endl; + // TODO actually write contents; needs access to linear memory. + // results[0] = args[0].copy(); + results[0] = wasm::Val::i32(65553); + return nullptr; +} + +// WebAssembly type: (func (param i32 i64 i32 i32) (result i32)) +auto fd_seek(const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + std::cerr << "fd_seek" << std::endl; + // TODO actually seek contents; needs access to linear memory. + results[0] = wasm::Val::i32(0); + return nullptr; +} + +// WebAssembly type: (func (param i32 i32) (result i32)) +auto fd_fdstat_get(const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + std::cerr << "fd_fdstat_get" << std::endl; + // TODO + results[0] = wasm::Val::i32(0); + return nullptr; +} + +// WebAssembly type: (func (param i32) (result i32)) +auto fd_close(const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + std::cerr << "fd_close" << std::endl; + // TODO + results[0] = wasm::Val::i32(0); + return nullptr; +} diff --git a/engines/v8/src/wasi.hh b/engines/v8/src/wasi.hh new file mode 100644 index 00000000..fce315ad --- /dev/null +++ b/engines/v8/src/wasi.hh @@ -0,0 +1,12 @@ +#include "wasm.hh" + +auto proc_exit(const wasm::Val args[], wasm::Val results[]) + -> wasm::own; +auto fd_write(const wasm::Val args[], wasm::Val results[]) + -> wasm::own; +auto fd_seek(const wasm::Val args[], wasm::Val results[]) + -> wasm::own; +auto fd_fdstat_get(const wasm::Val args[], wasm::Val results[]) + -> wasm::own; +auto fd_close(const wasm::Val args[], wasm::Val results[]) + -> wasm::own; diff --git a/engines/v8/test/main.c b/engines/v8/test/main.c new file mode 100644 index 00000000..0de990d4 --- /dev/null +++ b/engines/v8/test/main.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include "bench-api.h" + +int read_file(char* path, char** out_str); +wasm_bench_config_t empty_config(); +#define RETURN_ON_INVALID_POINTER(pointer, name) \ + if (!pointer) { \ + printf("Invalid pointer: " #name "\n"); \ + return 1; \ + } +#define EXIT_ON_INVALID_POINTER(pointer, name) \ + if (!pointer) { \ + printf("Invalid pointer: " #name "\n"); \ + exit(1); \ + } + +int main(int argc, char* argv[]) { + void* state = NULL; + int create_result = wasm_bench_create(empty_config(), (void**)&state); + assert(create_result == 0); + EXIT_ON_INVALID_POINTER(state, state); + printf("> created engine state: %p\n", state); + + // Get first argument; expects a path to a .wasm file. + assert(argc > 1); + char* wasm_path = argv[1]; + + // Compile. + char* wasm_bytes = NULL; + int wasm_bytes_len = read_file(wasm_path, &wasm_bytes); + EXIT_ON_INVALID_POINTER(wasm_bytes, wasm_bytes); + int compile_result = wasm_bench_compile(state, wasm_bytes, wasm_bytes_len); + assert(compile_result == 0); + printf("> compiled %d bytes\n", wasm_bytes_len); + + // Instantiate. + int instantiate_result = wasm_bench_instantiate(state); + assert(instantiate_result == 0); + printf("> instantiated module\n"); + + // Execute + int execution_result = wasm_bench_execute(state); + assert(execution_result == 0); + printf("> executed module\n"); + + return 0; +} + +wasm_bench_config_t empty_config() { + wasm_bench_config_t config = {NULL, 0, NULL, 0, NULL, 0, + NULL, 0, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL}; + return config; +} + +int read_file(char* filename, char** out_str) { + char* buffer = NULL; + long length; + FILE* file = fopen(filename, "rb"); + if (file) { + fseek(file, 0, SEEK_END); + length = ftell(file); + fseek(file, 0, SEEK_SET); + buffer = malloc(length); + if (buffer) { + fread(buffer, 1, length, file); + } + fclose(file); + *out_str = buffer; + return length; + } else { + return 0; + } +}