diff options
Diffstat (limited to 'wasm')
| -rw-r--r-- | wasm/README.md | 60 | ||||
| -rw-r--r-- | wasm/missing.c | 7 | ||||
| -rw-r--r-- | wasm/runtime.c | 7 | ||||
| -rw-r--r-- | wasm/setjmp.c | 77 | ||||
| -rw-r--r-- | wasm/setjmp.h | 5 |
5 files changed, 92 insertions, 64 deletions
diff --git a/wasm/README.md b/wasm/README.md index 0f9ca1a3d5..c23a9b8a91 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -13,50 +13,58 @@ ### Steps 1. Download a prebuilt WASI SDK package from [WASI SDK release page](https://github.com/WebAssembly/wasi-sdk/releases). + 2. Set `WASI_SDK_PATH` environment variable to the root directory of the WASI SDK package. -```console -$ export WASI_SDK_PATH=/path/to/wasi-sdk-X.Y -``` + + ```console + $ export WASI_SDK_PATH=/path/to/wasi-sdk-X.Y + ``` + 3. Download a prebuilt binaryen from [Binaryen release page](https://github.com/WebAssembly/binaryen/releases) + 4. Set PATH environment variable to lookup binaryen tools -```console -$ export PATH=path/to/binaryen:$PATH -``` -5. Download the latest `config.guess` with WASI support, and run `./autogen.sh` to generate configure when you - are building from the source checked out from Git repository -```console -$ ruby tool/downloader.rb -d tool -e gnu config.guess config.sub -$ ./autogen.sh -``` + + ```console + $ export PATH=path/to/binaryen:$PATH + ``` + +5. Download the latest `config.guess` with WASI support, and run `./autogen.sh` to generate configure when you are building from the source checked out from Git repository + + ```console + $ ruby tool/downloader.rb -d tool -e gnu config.guess config.sub + $ ./autogen.sh + ``` 6. Configure - - You can select which extensions you want to build. - - If you got `Out of bounds memory access` while running the produced ruby, you may need to increase the maximum size of stack. -```console -$ ./configure LDFLAGS="-Xlinker -zstack-size=16777216" \ - --host wasm32-unknown-wasi \ - --with-destdir=./ruby-wasm32-wasi \ - --with-static-linked-ext \ - --with-ext=ripper,monitor -``` + - You can select which extensions you want to build. + - If you got `Out of bounds memory access` while running the produced ruby, you may need to increase the maximum size of stack. + + ```console + $ ./configure LDFLAGS="-Xlinker -zstack-size=16777216" \ + --host wasm32-unknown-wasi \ + --with-destdir=./ruby-wasm32-wasi \ + --with-static-linked-ext \ + --with-ext=ripper,monitor + ``` 7. Make -```console -$ make install -``` + + ```console + $ make install + ``` Now you have a WASI compatible ruby binary. You can run it by any WebAssembly runtime like [`wasmtime`](https://github.com/bytecodealliance/wasmtime), [`wasmer`](https://github.com/wasmerio/wasmer), [Node.js](https://nodejs.org/api/wasi.html), or browser with [WASI polyfill](https://www.npmjs.com/package/@wasmer/wasi). Note: it may take a long time (~20 sec) for the first time for JIT compilation -``` +```console $ wasmtime ruby-wasm32-wasi/usr/local/bin/ruby --mapdir /::./ruby-wasm32-wasi/ -- -e 'puts RUBY_PLATFORM' wasm32-wasi ``` Note: you cannot run the built ruby without a WebAssembly runtime, because of the difference of the binary file type. -``` +```console $ ruby-wasm32-wasi/usr/local/bin/ruby -e 'puts "a"' bash: ruby-wasm32-wasi/usr/local/bin/ruby: cannot execute binary file: Exec format error diff --git a/wasm/missing.c b/wasm/missing.c index 5bbf988642..b9ecf520fd 100644 --- a/wasm/missing.c +++ b/wasm/missing.c @@ -121,13 +121,6 @@ umask(rb_mode_t mask) WASM_MISSING_LIBC_FUNC int -mprotect(const void *addr, size_t len, int prot) -{ - return 0; -} - -WASM_MISSING_LIBC_FUNC -int pclose(FILE *stream) { errno = ENOTSUP; diff --git a/wasm/runtime.c b/wasm/runtime.c index b5b0a1a966..89b06be6ad 100644 --- a/wasm/runtime.c +++ b/wasm/runtime.c @@ -19,6 +19,13 @@ int rb_wasm_rt_start(int (main)(int argc, char **argv), int argc, char **argv) { result = main(argc, argv); } + extern void *rb_asyncify_unwind_buf; + // Exit Asyncify loop if there is no unwound buffer, which + // means that main function has returned normally. + if (rb_asyncify_unwind_buf == NULL) { + break; + } + // NOTE: it's important to call 'asyncify_stop_unwind' here instead in rb_wasm_handle_jmp_unwind // because unless that, Asyncify inserts another unwind check here and it unwinds to the root frame. asyncify_stop_unwind(); diff --git a/wasm/setjmp.c b/wasm/setjmp.c index 1e69cb1c64..32ede68c09 100644 --- a/wasm/setjmp.c +++ b/wasm/setjmp.c @@ -29,10 +29,25 @@ #include "wasm/setjmp.h" #ifdef RB_WASM_ENABLE_DEBUG_LOG -# include <stdio.h> -# define RB_WASM_DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__) +# include <wasi/api.h> +# include <unistd.h> +// NOTE: We can't use printf() and most of library function that are +// Asyncified due to the use of them in the application itself. +// Use of printf() causes "unreachable" error because Asyncified +// function misunderstands Asyncify's internal state during +// start_unwind()...stop_unwind() and start_rewind()...stop_rewind(). +# define RB_WASM_DEBUG_LOG_INTERNAL(msg) do { \ + const uint8_t *msg_start = (uint8_t *)msg; \ + const uint8_t *msg_end = msg_start; \ + for (; *msg_end != '\0'; msg_end++) {} \ + __wasi_ciovec_t iov = {.buf = msg_start, .buf_len = msg_end - msg_start}; \ + size_t nwritten; \ + __wasi_fd_write(STDERR_FILENO, &iov, 1, &nwritten); \ +} while (0) +# define RB_WASM_DEBUG_LOG(msg) \ + RB_WASM_DEBUG_LOG_INTERNAL(__FILE__ ":" STRINGIZE(__LINE__) ": " msg "\n") #else -# define RB_WASM_DEBUG_LOG(...) +# define RB_WASM_DEBUG_LOG(msg) #endif enum rb_wasm_jmp_buf_state { @@ -63,12 +78,13 @@ __attribute__((noinline)) int _rb_wasm_setjmp_internal(rb_wasm_jmp_buf *env) { - RB_WASM_DEBUG_LOG("[%s] env = %p, env->state = %d, _rb_wasm_active_jmpbuf = %p\n", __func__, env, env->state, _rb_wasm_active_jmpbuf); + RB_WASM_DEBUG_LOG("enter _rb_wasm_setjmp_internal"); switch (env->state) { case JMP_BUF_STATE_INITIALIZED: { - RB_WASM_DEBUG_LOG("[%s] JMP_BUF_STATE_INITIALIZED\n", __func__); + RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_INITIALIZED"); env->state = JMP_BUF_STATE_CAPTURING; env->payload = 0; + env->longjmp_buf_ptr = NULL; _rb_wasm_active_jmpbuf = env; async_buf_init(&env->setjmp_buf); asyncify_start_unwind(&env->setjmp_buf); @@ -76,14 +92,14 @@ _rb_wasm_setjmp_internal(rb_wasm_jmp_buf *env) } case JMP_BUF_STATE_CAPTURING: { asyncify_stop_rewind(); - RB_WASM_DEBUG_LOG("[%s] JMP_BUF_STATE_CAPTURING\n", __func__); + RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_CAPTURING"); env->state = JMP_BUF_STATE_CAPTURED; _rb_wasm_active_jmpbuf = NULL; return 0; } case JMP_BUF_STATE_RETURNING: { asyncify_stop_rewind(); - RB_WASM_DEBUG_LOG("[%s] JMP_BUF_STATE_RETURNING\n", __func__); + RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_RETURNING"); env->state = JMP_BUF_STATE_CAPTURED; _rb_wasm_active_jmpbuf = NULL; return env->payload; @@ -97,14 +113,18 @@ _rb_wasm_setjmp_internal(rb_wasm_jmp_buf *env) void _rb_wasm_longjmp(rb_wasm_jmp_buf* env, int value) { - RB_WASM_DEBUG_LOG("[%s] env = %p, env->state = %d, value = %d\n", __func__, env, env->state, value); + RB_WASM_DEBUG_LOG("enter _rb_wasm_longjmp"); assert(env->state == JMP_BUF_STATE_CAPTURED); assert(value != 0); env->state = JMP_BUF_STATE_RETURNING; env->payload = value; + // Asyncify buffer built during unwinding for longjmp will not + // be used to rewind, so re-use static-variable. + static struct __rb_wasm_asyncify_jmp_buf tmp_longjmp_buf; + env->longjmp_buf_ptr = &tmp_longjmp_buf; _rb_wasm_active_jmpbuf = env; - async_buf_init(&env->longjmp_buf); - asyncify_start_unwind(&env->longjmp_buf); + async_buf_init(env->longjmp_buf_ptr); + asyncify_start_unwind(env->longjmp_buf_ptr); } @@ -123,9 +143,11 @@ rb_wasm_try_catch_init(struct rb_wasm_try_catch *try_catch, try_catch->try_f = try_f; try_catch->catch_f = catch_f; try_catch->context = context; + try_catch->stack_pointer = NULL; } // NOTE: This function is not processed by Asyncify due to a call of asyncify_stop_rewind +__attribute__((noinline)) void rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf *target) { @@ -134,29 +156,33 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf target->state = JMP_BUF_STATE_CAPTURED; + if (try_catch->stack_pointer == NULL) { + try_catch->stack_pointer = rb_wasm_get_stack_pointer(); + } + switch ((enum try_catch_phase)try_catch->state) { - case TRY_CATCH_PHASE_MAIN: { + case TRY_CATCH_PHASE_MAIN: // may unwind try_catch->try_f(try_catch->context); break; - } - case TRY_CATCH_PHASE_RESCUE: { + case TRY_CATCH_PHASE_RESCUE: if (try_catch->catch_f) { // may unwind try_catch->catch_f(try_catch->context); } break; } - } - while (1) { + { // catch longjmp with target jmp_buf - if (rb_asyncify_unwind_buf && _rb_wasm_active_jmpbuf == target) { + while (rb_asyncify_unwind_buf && _rb_wasm_active_jmpbuf == target) { // do similar steps setjmp does when JMP_BUF_STATE_RETURNING // stop unwinding // (but call stop_rewind to update the asyncify state to "normal" from "unwind") asyncify_stop_rewind(); + // reset the stack pointer to what it was before the most recent call to try_f or catch_f + rb_wasm_set_stack_pointer(try_catch->stack_pointer); // clear the active jmpbuf because it's already stopped _rb_wasm_active_jmpbuf = NULL; // reset jmpbuf state to be able to unwind again @@ -166,37 +192,30 @@ rb_wasm_try_catch_loop_run(struct rb_wasm_try_catch *try_catch, rb_wasm_jmp_buf if (try_catch->catch_f) { try_catch->catch_f(try_catch->context); } - continue; - } else if (rb_asyncify_unwind_buf /* unrelated unwind */) { - return; } - // no unwind, then exit - break; + // no unwind or unrelated unwind, then exit } - return; } void * rb_wasm_handle_jmp_unwind(void) { - RB_WASM_DEBUG_LOG("[%s] _rb_wasm_active_jmpbuf = %p\n", __func__, _rb_wasm_active_jmpbuf); + RB_WASM_DEBUG_LOG("enter rb_wasm_handle_jmp_unwind"); if (!_rb_wasm_active_jmpbuf) { return NULL; } switch (_rb_wasm_active_jmpbuf->state) { - case JMP_BUF_STATE_CAPTURING: { - RB_WASM_DEBUG_LOG("[%s] JMP_BUF_STATE_CAPTURING\n", __func__); + case JMP_BUF_STATE_CAPTURING: + RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_CAPTURING"); // save the captured Asyncify stack top _rb_wasm_active_jmpbuf->dst_buf_top = _rb_wasm_active_jmpbuf->setjmp_buf.top; break; - } - case JMP_BUF_STATE_RETURNING: { - RB_WASM_DEBUG_LOG("[%s] JMP_BUF_STATE_RETURNING\n", __func__); + case JMP_BUF_STATE_RETURNING: + RB_WASM_DEBUG_LOG(" JMP_BUF_STATE_RETURNING"); // restore the saved Asyncify stack top _rb_wasm_active_jmpbuf->setjmp_buf.top = _rb_wasm_active_jmpbuf->dst_buf_top; break; - } default: assert(0 && "unexpected state"); } diff --git a/wasm/setjmp.h b/wasm/setjmp.h index 65e35c03b3..82cfff1d00 100644 --- a/wasm/setjmp.h +++ b/wasm/setjmp.h @@ -5,7 +5,7 @@ #include <stdbool.h> #ifndef WASM_SETJMP_STACK_BUFFER_SIZE -# define WASM_SETJMP_STACK_BUFFER_SIZE 6144 +# define WASM_SETJMP_STACK_BUFFER_SIZE 8192 #endif struct __rb_wasm_asyncify_jmp_buf { @@ -19,7 +19,7 @@ typedef struct { struct __rb_wasm_asyncify_jmp_buf setjmp_buf; // Internal Asyncify buffer space used while unwinding from longjmp // but never used for rewinding. - struct __rb_wasm_asyncify_jmp_buf longjmp_buf; + struct __rb_wasm_asyncify_jmp_buf *longjmp_buf_ptr; // Used to save top address of Asyncify stack `setjmp_buf`, which is // overwritten during first rewind. void *dst_buf_top; @@ -65,6 +65,7 @@ struct rb_wasm_try_catch { rb_wasm_try_catch_func_t try_f; rb_wasm_try_catch_func_t catch_f; void *context; + void *stack_pointer; int state; }; |
