From 65f95f26ff0e7b4be4704fedc52344a26d22a4e2 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Sat, 15 Jan 2022 23:10:48 +0900 Subject: [wasm] add asyncify based setjmp, fiber, register scan emulation configure.ac: setup build tools and register objects main.c: wrap main with rb_wasm_rt_start to handle asyncify unwinds tool/m4/ruby_wasm_tools.m4: setup default command based on WASI_SDK_PATH environment variable. checks wasm-opt which is used for asyncify. tool/wasm-clangw wasm/wasm-opt: a clang wrapper which replaces real wasm-opt with do-nothing wasm-opt to avoid misoptimization before asyncify. asyncify is performed at POSTLINK, but clang linker driver tries to run optimization by wasm-opt unconditionally. inlining pass at wasm level breaks asyncify's assumption, so should not optimize before POSTLIK. wasm/GNUmakefile.in: wasm specific rules to compile objects --- wasm/fiber.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 wasm/fiber.c (limited to 'wasm/fiber.c') diff --git a/wasm/fiber.c b/wasm/fiber.c new file mode 100644 index 0000000000..ecc481b0ee --- /dev/null +++ b/wasm/fiber.c @@ -0,0 +1,83 @@ +/* + This is a ucontext-like userland context switching API for WebAssembly based on Binaryen's Asyncify. + + * NOTE: + * This mechanism doesn't take care of stack state. Just save and restore program counter and + * registers (rephrased as locals by Wasm term). So use-site need to save and restore the C stack pointer. + * This Asyncify based implementation is not much efficient and will be replaced with future stack-switching feature. + */ + +#include +#include "wasm/fiber.h" +#include "wasm/asyncify.h" + +#ifdef RB_WASM_ENABLE_DEBUG_LOG +# include +# define RB_WASM_DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__) +#else +# define RB_WASM_DEBUG_LOG(...) +#endif + +void +rb_wasm_init_context(rb_wasm_fiber_context *fcp, void (*func)(void *, void *), void *arg0, void *arg1) +{ + fcp->asyncify_buf.top = &fcp->asyncify_buf.buffer[0]; + fcp->asyncify_buf.end = &fcp->asyncify_buf.buffer[WASM_FIBER_STACK_BUFFER_SIZE]; + fcp->is_rewinding = false; + fcp->is_started = false; + fcp->entry_point = func; + fcp->arg0 = arg0; + fcp->arg1 = arg1; + RB_WASM_DEBUG_LOG("[%s] fcp->asyncify_buf %p\n", __func__, &fcp->asyncify_buf); +} + +static rb_wasm_fiber_context *_rb_wasm_active_next_fiber; + +void +rb_wasm_swapcontext(rb_wasm_fiber_context *ofcp, rb_wasm_fiber_context *fcp) +{ + RB_WASM_DEBUG_LOG("[%s] enter ofcp = %p fcp = %p\n", __func__, ofcp, fcp); + if (ofcp->is_rewinding) { + asyncify_stop_rewind(); + ofcp->is_rewinding = false; + return; + } + _rb_wasm_active_next_fiber = fcp; + RB_WASM_DEBUG_LOG("[%s] start unwinding asyncify_buf = %p\n", __func__, &ofcp->asyncify_buf); + asyncify_start_unwind(&ofcp->asyncify_buf); +} + +void * +rb_wasm_handle_fiber_unwind(void (**new_fiber_entry)(void *, void *), + void **arg0, void **arg1, bool *is_new_fiber_started) +{ + rb_wasm_fiber_context *next_fiber; + if (!_rb_wasm_active_next_fiber) { + RB_WASM_DEBUG_LOG("[%s] no next fiber\n", __func__); + *is_new_fiber_started = false; + return NULL; + } + + next_fiber = _rb_wasm_active_next_fiber; + _rb_wasm_active_next_fiber = NULL; + + RB_WASM_DEBUG_LOG("[%s] next_fiber->asyncify_buf = %p\n", __func__, &next_fiber->asyncify_buf); + + *new_fiber_entry = next_fiber->entry_point; + *arg0 = next_fiber->arg0; + *arg1 = next_fiber->arg1; + + if (!next_fiber->is_started) { + RB_WASM_DEBUG_LOG("[%s] new fiber started\n", __func__); + // start a new fiber if not started yet. + next_fiber->is_started = true; + *is_new_fiber_started = true; + return NULL; + } else { + RB_WASM_DEBUG_LOG("[%s] resume a fiber\n", __func__); + // resume a fiber again + next_fiber->is_rewinding = true; + *is_new_fiber_started = false; + return &next_fiber->asyncify_buf; + } +} -- cgit v1.2.3