summaryrefslogtreecommitdiff
path: root/wasm
diff options
context:
space:
mode:
Diffstat (limited to 'wasm')
-rw-r--r--wasm/README.md60
-rw-r--r--wasm/missing.c7
-rw-r--r--wasm/runtime.c7
-rw-r--r--wasm/setjmp.c77
-rw-r--r--wasm/setjmp.h5
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;
};