From 62cac757233cc768fa3f8eecd2880f47d410143e Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 8 Jan 2021 15:48:17 -0800 Subject: [PATCH 1/6] Add V8 engine --- crates/artifact/src/engine.rs | 3 + engines/v8/Dockerfile | 39 ++++++++++++ engines/v8/patch.diff | 40 ++++++++++++ engines/v8/wasm-bench.cc | 111 ++++++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) create mode 100644 engines/v8/Dockerfile create mode 100644 engines/v8/patch.diff create mode 100644 engines/v8/wasm-bench.cc 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/engines/v8/Dockerfile b/engines/v8/Dockerfile new file mode 100644 index 00000000..b3a20856 --- /dev/null +++ b/engines/v8/Dockerfile @@ -0,0 +1,39 @@ +FROM ubuntu:bionic +ARG REPOSITORY=https://github.com/WebAssembly/wasm-c-api +ARG REVISION=branch-heads/7.6 + +# The V8 build process untar's an archive; this causes issues inside some container frameworks, like +# `Cannot change ownership to uid 65002, gid 100005: Invalid argument` (see +# https://github.com/containers/buildah/issues/1702). Adding this flag to tar avoids the isue. +ENV TAR_OPTIONS=--no-same-owner + +# Install dependencies for building v8; see +# https://github.com/WebAssembly/wasm-c-api/blob/master/Dockerfile. +RUN apt-get update && apt-get install -y \ + apt-utils \ + clang \ + cmake \ + curl \ + git \ + libc++-dev \ + libc++abi-dev \ + libglib2.0-dev \ + libgmp-dev \ + ninja-build \ + python + +# Setup the wasm-c-api; TODO checkout a specific version of the wasm-c-api here. +WORKDIR /usr/src +RUN git clone ${REPOSITORY} wasm-c-api +WORKDIR /usr/src/wasm-c-api + +# Build v8 as a static library; override the checked out V8 version using the passed $REVISION. +RUN make v8-checkout V8_VERSION=${REVISION} +RUN make v8 + +# Create the engine shared library and place it in the location `sightglass` expects. +COPY patch.diff . +RUN git apply patch.diff +COPY wasm-bench.cc ./src +RUN make out/libengine.so +RUN cp out/libengine.so /libengine.so diff --git a/engines/v8/patch.diff b/engines/v8/patch.diff new file mode 100644 index 00000000..9a8b728c --- /dev/null +++ b/engines/v8/patch.diff @@ -0,0 +1,40 @@ +diff --git a/Makefile b/Makefile +index 3d9707f..0d755a8 100644 +--- a/Makefile ++++ b/Makefile +@@ -8,9 +8,9 @@ V8_ARCH = x64 + V8_MODE = release + + WASM_FLAGS = -DWASM_API_DEBUG # -DWASM_API_DEBUG_LOG +-C_FLAGS = ${WASM_FLAGS} -Wall -Werror -ggdb -O -fsanitize=address ++C_FLAGS = ${WASM_FLAGS} -Wall -Werror -ggdb -O -fPIC + CC_FLAGS = -std=c++11 ${C_FLAGS} +-LD_FLAGS = -fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor ++LD_FLAGS = + + C_COMP = clang + +@@ -45,7 +45,7 @@ EXAMPLES = \ + WASM_INCLUDE = ${WASM_DIR}/include + WASM_SRC = ${WASM_DIR}/src + WASM_OUT = ${OUT_DIR}/${WASM_DIR} +-WASM_C_LIBS = wasm-bin wasm-c ++WASM_C_LIBS = wasm-bin wasm-c wasm-bench + WASM_CC_LIBS = wasm-bin wasm-v8 + WASM_C_O = ${WASM_C_LIBS:%=${WASM_OUT}/%.o} + WASM_CC_O = ${WASM_CC_LIBS:%=${WASM_OUT}/%.o} +@@ -306,3 +306,14 @@ v8-gn-args: + .PHONY: docker + docker: + docker build -t wasm:Dockerfile . ++ ++ ++############################################################################### ++# Benchmarking Engine ++${OUT_DIR}/libengine.so: ${WASM_OUT}/wasm-bench.o ${WASM_C_O} ++ ${CC_COMP} ${CC_FLAGS} ${LD_FLAGS} -shared $< -o $@ \ ++ ${WASM_OUT}/wasm-bin.o ${WASM_OUT}/wasm-c.o \ ++ ${LD_GROUP_START} \ ++ ${V8_LIBS:%=${V8_OUT}/obj/libv8_%.a} \ ++ ${LD_GROUP_END} \ ++ -ldl -pthread diff --git a/engines/v8/wasm-bench.cc b/engines/v8/wasm-bench.cc new file mode 100644 index 00000000..e570355b --- /dev/null +++ b/engines/v8/wasm-bench.cc @@ -0,0 +1,111 @@ +#include +#include +#include +#include "include/wasm.h" + +extern "C" { + + typedef struct state_t + { + wasm_engine_t* engine; + wasm_store_t* store; + wasm_module_t* module; + wasm_instance_t* instance; + } state_t; + +#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); \ + } + + void* wasm_bench_create() + { + state_t* state = (state_t*)calloc(1, sizeof(state_t)); // TODO initialize to all 0s + state->engine = wasm_engine_new(); + EXIT_ON_INVALID_POINTER(state->engine, engine); + state->store = wasm_store_new(state->engine); + EXIT_ON_INVALID_POINTER(state->store, store); + return state; + } + + void wasm_bench_free(void* state_) + { + state_t* state = (state_t*)state_; + wasm_module_delete(state->module); + wasm_instance_delete(state->instance); + wasm_store_delete(state->store); + wasm_engine_delete(state->engine); + } + + int wasm_bench_compile(void* state_, char* wasm_bytes, size_t wasm_bytes_length) + { + state_t* state = (state_t*)state_; + RETURN_ON_INVALID_POINTER(state, state); + wasm_byte_vec_t binary = { .data = wasm_bytes, .size = wasm_bytes_length }; + RETURN_ON_INVALID_POINTER(state->store, store); + state->module = wasm_module_new(state->store, &binary); + RETURN_ON_INVALID_POINTER(state->module, module); + return 0; + } + + int wasm_bench_instantiate(void* state_, void (*bench_start)(), void (*bench_end)()) + { + state_t* state = (state_t*)state_; + RETURN_ON_INVALID_POINTER(state, state); + RETURN_ON_INVALID_POINTER(state->store, store); + wasm_functype_t* bench_fn_type = wasm_functype_new_0_0(); + wasm_func_t* bench_start_fn = wasm_func_new(state->store, bench_fn_type, + (wasm_func_callback_t)bench_start); + wasm_func_t* bench_end_fn = wasm_func_new(state->store, bench_fn_type, (wasm_func_callback_t)bench_end); + wasm_functype_delete(bench_fn_type); + + wasm_extern_t* externs[] = { wasm_func_as_extern(bench_start_fn), wasm_func_as_extern(bench_end_fn) }; + wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); + RETURN_ON_INVALID_POINTER(state->module, module); + state->instance = wasm_instance_new(state->store, state->module, &imports, NULL); + RETURN_ON_INVALID_POINTER(state->instance, instance); + wasm_func_delete(bench_start_fn); + wasm_func_delete(bench_end_fn); + + return 0; + } + + int wasm_bench_execute(void* state_) + { + state_t* state = (state_t*)state_; + RETURN_ON_INVALID_POINTER(state, state); + RETURN_ON_INVALID_POINTER(state->instance, instance); + + wasm_extern_vec_t exports; + wasm_instance_exports(state->instance, &exports); + if (exports.size == 0) + { + printf("> Error accessing exports!\n"); + return 1; + } + + // TODO check that this is the _start function. + const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); + RETURN_ON_INVALID_POINTER(run_func, run_func); + + wasm_val_vec_t args = WASM_EMPTY_VEC; + wasm_val_vec_t results = WASM_EMPTY_VEC; + if (wasm_func_call(run_func, &args, &results)) + { + printf("> Error calling function!\n"); + return 1; + } + + wasm_extern_vec_delete(&exports); + return 0; + } +} // extern "C" From a40ef68cf3b6b775c4b0e08f36d9693e06002acb Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Fri, 15 Apr 2022 16:19:40 -0700 Subject: [PATCH 2/6] Update to use libwee8 --- engines/v8/.gitignore | 3 + engines/v8/Dockerfile | 2 +- engines/v8/Makefile | 95 ++++++++++++++++++++++++++++ engines/v8/args.gn | 9 +++ engines/v8/src/bench-api.cc | 113 ++++++++++++++++++++++++++++++++++ engines/v8/src/bench-api.h | 58 +++++++++++++++++ engines/v8/src/bench-state.cc | 32 ++++++++++ engines/v8/src/bench-state.hh | 75 ++++++++++++++++++++++ engines/v8/src/link.cc | 82 ++++++++++++++++++++++++ engines/v8/src/link.hh | 11 ++++ engines/v8/src/wasi.cc | 17 +++++ engines/v8/src/wasi.hh | 6 ++ engines/v8/test/main.c | 79 ++++++++++++++++++++++++ 13 files changed, 581 insertions(+), 1 deletion(-) create mode 100644 engines/v8/.gitignore create mode 100644 engines/v8/Makefile create mode 100644 engines/v8/args.gn create mode 100644 engines/v8/src/bench-api.cc create mode 100644 engines/v8/src/bench-api.h create mode 100644 engines/v8/src/bench-state.cc create mode 100644 engines/v8/src/bench-state.hh create mode 100644 engines/v8/src/link.cc create mode 100644 engines/v8/src/link.hh create mode 100644 engines/v8/src/wasi.cc create mode 100644 engines/v8/src/wasi.hh create mode 100644 engines/v8/test/main.c diff --git a/engines/v8/.gitignore b/engines/v8/.gitignore new file mode 100644 index 00000000..902fb41d --- /dev/null +++ b/engines/v8/.gitignore @@ -0,0 +1,3 @@ +build +v8 +wasm-c-api diff --git a/engines/v8/Dockerfile b/engines/v8/Dockerfile index b3a20856..3fbc8ce0 100644 --- a/engines/v8/Dockerfile +++ b/engines/v8/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:bionic ARG REPOSITORY=https://github.com/WebAssembly/wasm-c-api -ARG REVISION=branch-heads/7.6 +ARG REVISION=branch-heads/9.9 # The V8 build process untar's an archive; this causes issues inside some container frameworks, like # `Cannot change ownership to uid 65002, gid 100005: Invalid argument` (see diff --git a/engines/v8/Makefile b/engines/v8/Makefile new file mode 100644 index 00000000..6b4e947f --- /dev/null +++ b/engines/v8/Makefile @@ -0,0 +1,95 @@ +# 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#) + +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}: + 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 + +# 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) -g $(LDFLAGS) -o $@ $(OBJS) $(BUILD_DIR)/main.o $(LDLIBS) + +.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..13a81150 --- /dev/null +++ b/engines/v8/src/bench-api.cc @@ -0,0 +1,113 @@ +#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(); + 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); + + auto imports = link(st->store.get(), st->module.get(), &st->config.execution); + auto instance = + wasm::Instance::make(st->store.get(), st->module.get(), &imports[0]); + if (!instance) { + std::cerr << "> Error instantiating module!" << std::endl; + return 1; + } else { + return 0; + } + return 0; +} + +int wasm_bench_execute(void* state_) { + BenchState* st = (BenchState*)state_; + assert(st != nullptr); + assert(st->module != nullptr); + assert(st->instance != nullptr); + // RETURN_ON_INVALID_POINTER(state, state); + // RETURN_ON_INVALID_POINTER(state->module, module); + // RETURN_ON_INVALID_POINTER(state->instance, instance); + + // const wasm_func_t* run_func = find_start_fn(state->module, + // state->instance); RETURN_ON_INVALID_POINTER(run_func, run_func); if + // (wasm_func_call(run_func, NULL, NULL)) { + // printf("> Error calling function!\n"); + // return 1; + // } + + return 0; +} + +// wasm_func_t* find_start_fn(const wasm_module_t* module, +// const wasm_instance_t* instance) { +// wasm_exporttype_vec_t export_types; +// wasm_extern_vec_t exports; +// wasm_module_exports(module, &export_types); +// wasm_instance_exports(instance, &exports); +// assert(exports.size == export_types.size); + +// for (size_t i = 0; i < exports.size; ++i) { +// if (wasm_extern_kind(exports.data[i]) == WASM_EXTERN_FUNC) { +// assert(wasm_extern_kind(exports.data[i]) == +// wasm_externtype_kind(wasm_exporttype_type(export_types.data[i]))); +// const wasm_name_t* fn_name = +// wasm_exporttype_name(export_types.data[i]); int len = (fn_name->size > +// 6) ? fn_name->size : 6; if (strncmp("_start", fn_name->data, len) == 0) +// { +// return wasm_extern_as_func(exports.data[i]); +// } +// } +// } +// return NULL; +// } + +} // extern "C" diff --git a/engines/v8/src/bench-api.h b/engines/v8/src/bench-api.h new file mode 100644 index 00000000..3f09819f --- /dev/null +++ b/engines/v8/src/bench-api.h @@ -0,0 +1,58 @@ +// 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; + // void (*compilation_start)(char*); + // void (*compilation_end)(char*); + // wasm_bench_timer_t instantiation_timer; + // void (*instantiation_start)(char*); + // void (*instantiation_end)(char*); + // wasm_bench_timer_t execution_timer; + // void (*execution_start)(char*); + // void (*execution_end)(char*); +} 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..1c3dc042 --- /dev/null +++ b/engines/v8/src/bench-state.cc @@ -0,0 +1,32 @@ +#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.compilation_timer, config.compilation_start, + config.compilation_end); + auto execution = Timer(config.compilation_timer, config.compilation_start, + config.compilation_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..d7d63a25 --- /dev/null +++ b/engines/v8/src/bench-state.hh @@ -0,0 +1,75 @@ +// 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; + // std::optional store; + // std::optional wasm_module; + // std::optional 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..3e563e60 --- /dev/null +++ b/engines/v8/src/link.cc @@ -0,0 +1,82 @@ + +#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_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)); + wasm::Func::callback fn = [](auto p, auto r) -> wasm::own { + return nullptr; + }; + available_functions[ImportName("wasi_snapshot_preview1", "proc_exit")] = + std::move(wasm::Func::make(store, fn_type_i32_none.get(), fn)); + available_functions[ImportName("wasi_snapshot_preview1", "fd_write")] = + std::move(wasm::Func::make(store, fn_type_4xi32_i32.get(), fn)); + + // 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(available_functions[i].get()); + } else { + std::cerr << "Unable to find a function for import: " << i.first << " " + << i.second << std::endl; + exit(1); + } + } + + return imports; +} diff --git a/engines/v8/src/link.hh b/engines/v8/src/link.hh new file mode 100644 index 00000000..e867c3f1 --- /dev/null +++ b/engines/v8/src/link.hh @@ -0,0 +1,11 @@ +#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); diff --git a/engines/v8/src/wasi.cc b/engines/v8/src/wasi.cc new file mode 100644 index 00000000..66b86885 --- /dev/null +++ b/engines/v8/src/wasi.cc @@ -0,0 +1,17 @@ +#include "wasi.hh" +#include +#include + +auto proc_exit(const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + // std::cerr << "proc_exit" << std::endl; + // exit(args[0].i32()); + return nullptr; +} + +auto fd_write(const wasm::Val args[], wasm::Val results[]) + -> wasm::own { + // std::cerr << "fd_write" << std::endl; + // results[0] = args[0].copy(); + return nullptr; +} diff --git a/engines/v8/src/wasi.hh b/engines/v8/src/wasi.hh new file mode 100644 index 00000000..9abe6abb --- /dev/null +++ b/engines/v8/src/wasi.hh @@ -0,0 +1,6 @@ +#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; 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; + } +} From 1289606ef3fa6a7ac8a4c2db7f09389feced1c01 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Wed, 20 Apr 2022 11:18:53 -0700 Subject: [PATCH 3/6] Switch to using C++ API --- engines/v8/.gitignore | 2 +- engines/v8/Makefile | 7 ++- engines/v8/patch.diff | 40 ------------ engines/v8/src/bench-api.cc | 58 +++++++----------- engines/v8/src/bench-api.h | 8 --- engines/v8/src/bench-state.hh | 3 - engines/v8/src/link.cc | 35 ++++++++--- engines/v8/src/link.hh | 8 ++- engines/v8/src/wasi.cc | 9 +-- engines/v8/wasm-bench.cc | 111 ---------------------------------- 10 files changed, 63 insertions(+), 218 deletions(-) delete mode 100644 engines/v8/patch.diff delete mode 100644 engines/v8/wasm-bench.cc diff --git a/engines/v8/.gitignore b/engines/v8/.gitignore index 902fb41d..3d0f7c20 100644 --- a/engines/v8/.gitignore +++ b/engines/v8/.gitignore @@ -1,3 +1,3 @@ build v8 -wasm-c-api +archive diff --git a/engines/v8/Makefile b/engines/v8/Makefile index 6b4e947f..ed1f21fe 100644 --- a/engines/v8/Makefile +++ b/engines/v8/Makefile @@ -9,6 +9,7 @@ all: v8-checkout test # - `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 @@ -38,7 +39,7 @@ ${V8_V8}: .PHONY: wee8 wee8: ${V8_LIBWEE8} -${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) @@ -65,6 +66,8 @@ 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): @@ -88,7 +91,7 @@ $(BUILD_DIR)/main.o: $(TEST_DIR)/main.c $(SRC_DIR)/bench-api.h test: $(BUILD_DIR)/test $< ../../benchmarks-next/noop/benchmark.wasm $(BUILD_DIR)/test: $(BUILD_DIR) $(OBJS) $(BUILD_DIR)/main.o - $(CXX) -g $(LDFLAGS) -o $@ $(OBJS) $(BUILD_DIR)/main.o $(LDLIBS) + $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(BUILD_DIR)/main.o $(LDLIBS) .PHONY: test clean: diff --git a/engines/v8/patch.diff b/engines/v8/patch.diff deleted file mode 100644 index 9a8b728c..00000000 --- a/engines/v8/patch.diff +++ /dev/null @@ -1,40 +0,0 @@ -diff --git a/Makefile b/Makefile -index 3d9707f..0d755a8 100644 ---- a/Makefile -+++ b/Makefile -@@ -8,9 +8,9 @@ V8_ARCH = x64 - V8_MODE = release - - WASM_FLAGS = -DWASM_API_DEBUG # -DWASM_API_DEBUG_LOG --C_FLAGS = ${WASM_FLAGS} -Wall -Werror -ggdb -O -fsanitize=address -+C_FLAGS = ${WASM_FLAGS} -Wall -Werror -ggdb -O -fPIC - CC_FLAGS = -std=c++11 ${C_FLAGS} --LD_FLAGS = -fsanitize-memory-track-origins -fsanitize-memory-use-after-dtor -+LD_FLAGS = - - C_COMP = clang - -@@ -45,7 +45,7 @@ EXAMPLES = \ - WASM_INCLUDE = ${WASM_DIR}/include - WASM_SRC = ${WASM_DIR}/src - WASM_OUT = ${OUT_DIR}/${WASM_DIR} --WASM_C_LIBS = wasm-bin wasm-c -+WASM_C_LIBS = wasm-bin wasm-c wasm-bench - WASM_CC_LIBS = wasm-bin wasm-v8 - WASM_C_O = ${WASM_C_LIBS:%=${WASM_OUT}/%.o} - WASM_CC_O = ${WASM_CC_LIBS:%=${WASM_OUT}/%.o} -@@ -306,3 +306,14 @@ v8-gn-args: - .PHONY: docker - docker: - docker build -t wasm:Dockerfile . -+ -+ -+############################################################################### -+# Benchmarking Engine -+${OUT_DIR}/libengine.so: ${WASM_OUT}/wasm-bench.o ${WASM_C_O} -+ ${CC_COMP} ${CC_FLAGS} ${LD_FLAGS} -shared $< -o $@ \ -+ ${WASM_OUT}/wasm-bin.o ${WASM_OUT}/wasm-c.o \ -+ ${LD_GROUP_START} \ -+ ${V8_LIBS:%=${V8_OUT}/obj/libv8_%.a} \ -+ ${LD_GROUP_END} \ -+ -ldl -pthread diff --git a/engines/v8/src/bench-api.cc b/engines/v8/src/bench-api.cc index 13a81150..55dd00cc 100644 --- a/engines/v8/src/bench-api.cc +++ b/engines/v8/src/bench-api.cc @@ -56,16 +56,22 @@ int wasm_bench_instantiate(void* state_) { 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); - auto instance = - wasm::Instance::make(st->store.get(), st->module.get(), &imports[0]); - if (!instance) { + 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->instance = wasm::Instance::make(st->store.get(), st->module.get(), + imports_as_extern); + if (!st->instance) { std::cerr << "> Error instantiating module!" << std::endl; return 1; } else { return 0; } - return 0; } int wasm_bench_execute(void* state_) { @@ -73,41 +79,21 @@ int wasm_bench_execute(void* state_) { assert(st != nullptr); assert(st->module != nullptr); assert(st->instance != nullptr); - // RETURN_ON_INVALID_POINTER(state, state); - // RETURN_ON_INVALID_POINTER(state->module, module); - // RETURN_ON_INVALID_POINTER(state->instance, instance); - // const wasm_func_t* run_func = find_start_fn(state->module, - // state->instance); RETURN_ON_INVALID_POINTER(run_func, run_func); if - // (wasm_func_call(run_func, NULL, NULL)) { - // printf("> Error calling function!\n"); - // return 1; - // } + // 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; } -// wasm_func_t* find_start_fn(const wasm_module_t* module, -// const wasm_instance_t* instance) { -// wasm_exporttype_vec_t export_types; -// wasm_extern_vec_t exports; -// wasm_module_exports(module, &export_types); -// wasm_instance_exports(instance, &exports); -// assert(exports.size == export_types.size); - -// for (size_t i = 0; i < exports.size; ++i) { -// if (wasm_extern_kind(exports.data[i]) == WASM_EXTERN_FUNC) { -// assert(wasm_extern_kind(exports.data[i]) == -// wasm_externtype_kind(wasm_exporttype_type(export_types.data[i]))); -// const wasm_name_t* fn_name = -// wasm_exporttype_name(export_types.data[i]); int len = (fn_name->size > -// 6) ? fn_name->size : 6; if (strncmp("_start", fn_name->data, len) == 0) -// { -// return wasm_extern_as_func(exports.data[i]); -// } -// } -// } -// return NULL; -// } - } // extern "C" diff --git a/engines/v8/src/bench-api.h b/engines/v8/src/bench-api.h index 3f09819f..ad6618c0 100644 --- a/engines/v8/src/bench-api.h +++ b/engines/v8/src/bench-api.h @@ -33,14 +33,6 @@ typedef struct wasm_bench_config_t { wasm_bench_timer_t execution_timer; wasm_bench_callback_t execution_start; wasm_bench_callback_t execution_end; - // void (*compilation_start)(char*); - // void (*compilation_end)(char*); - // wasm_bench_timer_t instantiation_timer; - // void (*instantiation_start)(char*); - // void (*instantiation_end)(char*); - // wasm_bench_timer_t execution_timer; - // void (*execution_start)(char*); - // void (*execution_end)(char*); } wasm_bench_config_t; /// API functions (TODO cite). diff --git a/engines/v8/src/bench-state.hh b/engines/v8/src/bench-state.hh index d7d63a25..16f12ecf 100644 --- a/engines/v8/src/bench-state.hh +++ b/engines/v8/src/bench-state.hh @@ -67,9 +67,6 @@ class BenchState { std::unique_ptr store; std::unique_ptr module; std::unique_ptr instance; - // std::optional store; - // std::optional wasm_module; - // std::optional instance; }; #endif // #ifdef __BENCH_STATE_HH diff --git a/engines/v8/src/link.cc b/engines/v8/src/link.cc index 3e563e60..203cc5a8 100644 --- a/engines/v8/src/link.cc +++ b/engines/v8/src/link.cc @@ -35,9 +35,9 @@ auto bench_end(void* env, const wasm::Val args[], wasm::Val results[]) return nullptr; } -std::vector link(wasm::Store* store, - const wasm::Module* module, - Timer* timer) { +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()); @@ -56,21 +56,18 @@ std::vector link(wasm::Store* store, 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)); - wasm::Func::callback fn = [](auto p, auto r) -> wasm::own { - return nullptr; - }; available_functions[ImportName("wasi_snapshot_preview1", "proc_exit")] = - std::move(wasm::Func::make(store, fn_type_i32_none.get(), fn)); + 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(), fn)); + std::move(wasm::Func::make(store, fn_type_4xi32_i32.get(), fd_write)); // Construct list of import functions. auto import_names = list_imports(module); - std::vector imports = {}; + std::vector> imports = {}; for (ImportName i : import_names) { auto found = available_functions.find(i) != available_functions.end(); if (found) { - imports.push_back(available_functions[i].get()); + imports.push_back(std::move(available_functions[i])); } else { std::cerr << "Unable to find a function for import: " << i.first << " " << i.second << std::endl; @@ -80,3 +77,21 @@ std::vector link(wasm::Store* store, 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 index e867c3f1..e2760063 100644 --- a/engines/v8/src/link.hh +++ b/engines/v8/src/link.hh @@ -6,6 +6,8 @@ using ImportName = std::pair; std::vector list_imports(const wasm::Module* module); -std::vector link(wasm::Store* store, - const wasm::Module* module, - Timer* timer); +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 index 66b86885..b72d69ad 100644 --- a/engines/v8/src/wasi.cc +++ b/engines/v8/src/wasi.cc @@ -4,14 +4,15 @@ auto proc_exit(const wasm::Val args[], wasm::Val results[]) -> wasm::own { - // std::cerr << "proc_exit" << std::endl; - // exit(args[0].i32()); + std::cerr << "proc_exit" << std::endl; + exit(args[0].i32()); return nullptr; } auto fd_write(const wasm::Val args[], wasm::Val results[]) -> wasm::own { - // std::cerr << "fd_write" << std::endl; - // results[0] = args[0].copy(); + std::cerr << "fd_write" << std::endl; + // TODO actually write contents; needs access to linear memory. + results[0] = args[0].copy(); return nullptr; } diff --git a/engines/v8/wasm-bench.cc b/engines/v8/wasm-bench.cc deleted file mode 100644 index e570355b..00000000 --- a/engines/v8/wasm-bench.cc +++ /dev/null @@ -1,111 +0,0 @@ -#include -#include -#include -#include "include/wasm.h" - -extern "C" { - - typedef struct state_t - { - wasm_engine_t* engine; - wasm_store_t* store; - wasm_module_t* module; - wasm_instance_t* instance; - } state_t; - -#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); \ - } - - void* wasm_bench_create() - { - state_t* state = (state_t*)calloc(1, sizeof(state_t)); // TODO initialize to all 0s - state->engine = wasm_engine_new(); - EXIT_ON_INVALID_POINTER(state->engine, engine); - state->store = wasm_store_new(state->engine); - EXIT_ON_INVALID_POINTER(state->store, store); - return state; - } - - void wasm_bench_free(void* state_) - { - state_t* state = (state_t*)state_; - wasm_module_delete(state->module); - wasm_instance_delete(state->instance); - wasm_store_delete(state->store); - wasm_engine_delete(state->engine); - } - - int wasm_bench_compile(void* state_, char* wasm_bytes, size_t wasm_bytes_length) - { - state_t* state = (state_t*)state_; - RETURN_ON_INVALID_POINTER(state, state); - wasm_byte_vec_t binary = { .data = wasm_bytes, .size = wasm_bytes_length }; - RETURN_ON_INVALID_POINTER(state->store, store); - state->module = wasm_module_new(state->store, &binary); - RETURN_ON_INVALID_POINTER(state->module, module); - return 0; - } - - int wasm_bench_instantiate(void* state_, void (*bench_start)(), void (*bench_end)()) - { - state_t* state = (state_t*)state_; - RETURN_ON_INVALID_POINTER(state, state); - RETURN_ON_INVALID_POINTER(state->store, store); - wasm_functype_t* bench_fn_type = wasm_functype_new_0_0(); - wasm_func_t* bench_start_fn = wasm_func_new(state->store, bench_fn_type, - (wasm_func_callback_t)bench_start); - wasm_func_t* bench_end_fn = wasm_func_new(state->store, bench_fn_type, (wasm_func_callback_t)bench_end); - wasm_functype_delete(bench_fn_type); - - wasm_extern_t* externs[] = { wasm_func_as_extern(bench_start_fn), wasm_func_as_extern(bench_end_fn) }; - wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs); - RETURN_ON_INVALID_POINTER(state->module, module); - state->instance = wasm_instance_new(state->store, state->module, &imports, NULL); - RETURN_ON_INVALID_POINTER(state->instance, instance); - wasm_func_delete(bench_start_fn); - wasm_func_delete(bench_end_fn); - - return 0; - } - - int wasm_bench_execute(void* state_) - { - state_t* state = (state_t*)state_; - RETURN_ON_INVALID_POINTER(state, state); - RETURN_ON_INVALID_POINTER(state->instance, instance); - - wasm_extern_vec_t exports; - wasm_instance_exports(state->instance, &exports); - if (exports.size == 0) - { - printf("> Error accessing exports!\n"); - return 1; - } - - // TODO check that this is the _start function. - const wasm_func_t* run_func = wasm_extern_as_func(exports.data[0]); - RETURN_ON_INVALID_POINTER(run_func, run_func); - - wasm_val_vec_t args = WASM_EMPTY_VEC; - wasm_val_vec_t results = WASM_EMPTY_VEC; - if (wasm_func_call(run_func, &args, &results)) - { - printf("> Error calling function!\n"); - return 1; - } - - wasm_extern_vec_delete(&exports); - return 0; - } -} // extern "C" From 238049f680ef26ca3911f1e90c6cd35e9b0ca034 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 21 Apr 2022 09:56:30 -0700 Subject: [PATCH 4/6] Add stubs for more WASI functions --- engines/v8/src/bench-api.cc | 3 +++ engines/v8/src/link.cc | 19 +++++++++++++++++++ engines/v8/src/wasi.cc | 32 +++++++++++++++++++++++++++++++- engines/v8/src/wasi.hh | 6 ++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/engines/v8/src/bench-api.cc b/engines/v8/src/bench-api.cc index 55dd00cc..f40043bf 100644 --- a/engines/v8/src/bench-api.cc +++ b/engines/v8/src/bench-api.cc @@ -64,8 +64,11 @@ int wasm_bench_instantiate(void* state_) { 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; diff --git a/engines/v8/src/link.cc b/engines/v8/src/link.cc index 203cc5a8..2d7b1f78 100644 --- a/engines/v8/src/link.cc +++ b/engines/v8/src/link.cc @@ -44,6 +44,18 @@ std::vector> link(wasm::Store* store, 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), @@ -60,6 +72,13 @@ std::vector> link(wasm::Store* store, 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); diff --git a/engines/v8/src/wasi.cc b/engines/v8/src/wasi.cc index b72d69ad..fee6099a 100644 --- a/engines/v8/src/wasi.cc +++ b/engines/v8/src/wasi.cc @@ -2,6 +2,7 @@ #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; @@ -9,10 +10,39 @@ auto proc_exit(const wasm::Val args[], wasm::Val results[]) 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] = 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 index 9abe6abb..fce315ad 100644 --- a/engines/v8/src/wasi.hh +++ b/engines/v8/src/wasi.hh @@ -4,3 +4,9 @@ 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; From 13cc389b05b42086f361221d984e2869fbeea4ac Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 21 Apr 2022 11:19:02 -0700 Subject: [PATCH 5/6] Add a way to run the noop benchmark --- crates/cli/src/benchmark.rs | 10 +++++++++- engines/v8/Makefile | 25 ++++++++++++++++++------- engines/v8/src/bench-api.cc | 3 ++- engines/v8/src/bench-state.cc | 9 +++++---- engines/v8/src/wasi.cc | 2 +- 5 files changed, 35 insertions(+), 14 deletions(-) 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/Makefile b/engines/v8/Makefile index ed1f21fe..aa23cee7 100644 --- a/engines/v8/Makefile +++ b/engines/v8/Makefile @@ -1,15 +1,17 @@ -# This Makefile builds both the V8 components and the bench API wrapper for Sightglass. +# 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 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. +# 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 @@ -53,9 +55,9 @@ v8-clean: ######### 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`. +# 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 @@ -93,6 +95,15 @@ test: $(BUILD_DIR)/test $(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/src/bench-api.cc b/engines/v8/src/bench-api.cc index f40043bf..a829d57c 100644 --- a/engines/v8/src/bench-api.cc +++ b/engines/v8/src/bench-api.cc @@ -15,7 +15,8 @@ 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(); + 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); diff --git a/engines/v8/src/bench-state.cc b/engines/v8/src/bench-state.cc index 1c3dc042..12f35099 100644 --- a/engines/v8/src/bench-state.cc +++ b/engines/v8/src/bench-state.cc @@ -11,10 +11,11 @@ Config Config::make(wasm_bench_config_t config) { 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.compilation_timer, config.compilation_start, - config.compilation_end); - auto execution = 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); } diff --git a/engines/v8/src/wasi.cc b/engines/v8/src/wasi.cc index fee6099a..0204f951 100644 --- a/engines/v8/src/wasi.cc +++ b/engines/v8/src/wasi.cc @@ -6,7 +6,7 @@ auto proc_exit(const wasm::Val args[], wasm::Val results[]) -> wasm::own { std::cerr << "proc_exit" << std::endl; - exit(args[0].i32()); + // exit(args[0].i32()); // TODO this cannot actually exit here; should trap? return nullptr; } From cc238bf27ea07b3c9ddc58ff5d825d4725a501e4 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Thu, 21 Apr 2022 13:25:05 -0700 Subject: [PATCH 6/6] Update Dockerfile --- engines/v8/.dockerignore | 3 +++ engines/v8/Dockerfile | 44 ++++++++++++++-------------------------- 2 files changed, 18 insertions(+), 29 deletions(-) create mode 100644 engines/v8/.dockerignore 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/Dockerfile b/engines/v8/Dockerfile index 3fbc8ce0..83c1352a 100644 --- a/engines/v8/Dockerfile +++ b/engines/v8/Dockerfile @@ -1,39 +1,25 @@ -FROM ubuntu:bionic +FROM ubuntu:latest ARG REPOSITORY=https://github.com/WebAssembly/wasm-c-api ARG REVISION=branch-heads/9.9 -# The V8 build process untar's an archive; this causes issues inside some container frameworks, like -# `Cannot change ownership to uid 65002, gid 100005: Invalid argument` (see -# https://github.com/containers/buildah/issues/1702). Adding this flag to tar avoids the isue. -ENV TAR_OPTIONS=--no-same-owner - -# Install dependencies for building v8; see -# https://github.com/WebAssembly/wasm-c-api/blob/master/Dockerfile. -RUN apt-get update && apt-get install -y \ +# Install necessary dependencies +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -yqq \ apt-utils \ - clang \ - cmake \ curl \ git \ - libc++-dev \ - libc++abi-dev \ - libglib2.0-dev \ - libgmp-dev \ + g++ \ + make \ ninja-build \ - python + pkg-config \ + python3 -# Setup the wasm-c-api; TODO checkout a specific version of the wasm-c-api here. +# Setup V8 and build the `libwee8.a` library. TODO map REVISION to V8_VERSION. WORKDIR /usr/src -RUN git clone ${REPOSITORY} wasm-c-api -WORKDIR /usr/src/wasm-c-api - -# Build v8 as a static library; override the checked out V8 version using the passed $REVISION. -RUN make v8-checkout V8_VERSION=${REVISION} -RUN make v8 +COPY args.gn Makefile ./ +RUN make v8-checkout +RUN make wee8 -# Create the engine shared library and place it in the location `sightglass` expects. -COPY patch.diff . -RUN git apply patch.diff -COPY wasm-bench.cc ./src -RUN make out/libengine.so -RUN cp out/libengine.so /libengine.so +# Build the Sightglass engine. +COPY src ./src +RUN make build/libengine.so +RUN cp build/libengine.so /libengine.so