summaryrefslogtreecommitdiff
path: root/wasm
diff options
context:
space:
mode:
authorYuta Saito <kateinoigakukun@gmail.com>2021-12-08 22:19:52 +0900
committerYuta Saito <kateinoigakukun@gmail.com>2022-01-19 11:19:06 +0900
commitf72f01abd89640b083b4067e4be399448f0fb6ce (patch)
treee41bd40aed432a8c6957a6ea03e799e33c4c5c74 /wasm
parent3794ef6f01095a265c299917c244fbb346b56323 (diff)
[wasm] add unit test suite for fiber, register scan, sjlj in platform dir
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/5407
Diffstat (limited to 'wasm')
-rw-r--r--wasm/GNUmakefile.in12
-rw-r--r--wasm/tests/fiber_test.c66
-rw-r--r--wasm/tests/machine_test.c115
-rw-r--r--wasm/tests/setjmp_test.c108
4 files changed, 301 insertions, 0 deletions
diff --git a/wasm/GNUmakefile.in b/wasm/GNUmakefile.in
index 4328dec9c6..18ddd06739 100644
--- a/wasm/GNUmakefile.in
+++ b/wasm/GNUmakefile.in
@@ -6,6 +6,8 @@ GNUmakefile: $(wasmdir)/GNUmakefile.in
WASMOPT = @WASMOPT@
wasmoptflags = @wasmoptflags@
+WASM_TESTRUNNER = wasmtime
+WASM_TESTS = $(wasmdir)/tests/machine_test.wasm $(wasmdir)/tests/setjmp_test.wasm $(wasmdir)/tests/fiber_test.wasm
WASM_OBJS = $(wasmdir)/machine_core.o $(wasmdir)/machine.o $(wasmdir)/setjmp.o $(wasmdir)/setjmp_core.o $(wasmdir)/fiber.o $(wasmdir)/runtime.o
wasm/missing.$(OBJEXT): $(wasmdir)/missing.c $(PLATFORM_D)
@@ -18,3 +20,13 @@ wasm/%.$(OBJEXT): $(wasmdir)/%.S $(PLATFORM_D)
@$(ECHO) compiling $<
$(Q) $(CC) $(CFLAGS) $(COUTFLAG)$@ -c $<
+test-wasm: $(WASM_TESTS)
+ $(foreach x,$(WASM_TESTS), $(WASM_TESTRUNNER) $(x);)
+clean-test-wasm:
+ @$(RM) $(WASM_TESTS)
+
+$(wasmdir)/tests/%.wasm: $(wasmdir)/tests/%.c $(WASM_OBJS)
+ $(Q) $(CC) -g $(XCFLAGS) $(CFLAGS) $^ -o $@
+ $(Q) $(WASMOPT) -g --asyncify --pass-arg=asyncify-ignore-imports -o $@ $@
+
+.PHONY: test-wasm clean-test-wasm
diff --git a/wasm/tests/fiber_test.c b/wasm/tests/fiber_test.c
new file mode 100644
index 0000000000..e6b36631ce
--- /dev/null
+++ b/wasm/tests/fiber_test.c
@@ -0,0 +1,66 @@
+#include "wasm/fiber.h"
+#include "wasm/asyncify.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+static rb_wasm_fiber_context fctx_main, fctx_func1, fctx_func2;
+
+static int counter = 0;
+
+static void func1(void *arg0, void *arg1) {
+ assert(counter == 2);
+ fprintf(stderr, "func1: started\n");
+ fprintf(stderr, "func1: swapcontext(&fctx_func1, &fctx_func2)\n");
+ counter++;
+ rb_wasm_swapcontext(&fctx_func1, &fctx_func2);
+
+ fprintf(stderr, "func1: returning\n");
+}
+
+static void func2(void *arg0, void *arg1) {
+ assert(counter == 1);
+ fprintf(stderr, "func2: started\n");
+ fprintf(stderr, "func2: swapcontext(&fctx_func2, &fctx_func1)\n");
+ counter++;
+ rb_wasm_swapcontext(&fctx_func2, &fctx_func1);
+
+ assert(counter == 3);
+ fprintf(stderr, "func2: swapcontext(&fctx_func2, &fctx_func2)\n");
+ counter++;
+ rb_wasm_swapcontext(&fctx_func2, &fctx_func2);
+
+ assert(counter == 4);
+ fprintf(stderr, "func2: swapcontext(&fctx_func2, &fctx_main)\n");
+ counter++;
+ rb_wasm_swapcontext(&fctx_func2, &fctx_main);
+
+ fprintf(stderr, "func2: returning\n");
+ assert(false && "unreachable");
+}
+
+// top level function should not be inlined to stop unwinding immediately after this function returns
+__attribute__((noinline))
+int start(int argc, char **argv) {
+ rb_wasm_init_context(&fctx_main, NULL, NULL, NULL);
+ fctx_main.is_started = true;
+
+ rb_wasm_init_context(&fctx_func1, func1, NULL, NULL);
+
+ rb_wasm_init_context(&fctx_func2, func2, NULL, NULL);
+
+ counter++;
+ fprintf(stderr, "start: swapcontext(&uctx_main, &fctx_func2)\n");
+ rb_wasm_swapcontext(&fctx_main, &fctx_func2);
+ assert(counter == 5);
+
+ fprintf(stderr, "start: exiting\n");
+ return 42;
+}
+
+int main(int argc, char **argv) {
+ extern int rb_wasm_rt_start(int (main)(int argc, char **argv), int argc, char **argv);
+ int result = rb_wasm_rt_start(start, argc, argv);
+ assert(result == 42);
+ return 0;
+}
diff --git a/wasm/tests/machine_test.c b/wasm/tests/machine_test.c
new file mode 100644
index 0000000000..f4b62ff580
--- /dev/null
+++ b/wasm/tests/machine_test.c
@@ -0,0 +1,115 @@
+#include <stdio.h>
+#include <assert.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "wasm/machine.h"
+#include "wasm/asyncify.h"
+
+void *rb_wasm_get_stack_pointer(void);
+
+static void *base_stack_pointer = NULL;
+
+int __attribute__((constructor)) record_base_sp(void) {
+ base_stack_pointer = rb_wasm_get_stack_pointer();
+ return 0;
+}
+
+void dump_memory(uint8_t *base, uint8_t *end) {
+ size_t chunk_size = 16;
+
+ for (uint8_t *ptr = base; ptr <= end; ptr += chunk_size) {
+ printf("%p", ptr);
+ for (size_t offset = 0; offset < chunk_size; offset++) {
+ printf(" %02x", *(ptr + offset));
+ }
+ printf("\n");
+ }
+}
+
+bool find_in_stack(uint32_t target, void *base, void *end) {
+ for (uint32_t *ptr = base; ptr <= (uint32_t *)end; ptr++) {
+ if (*ptr == target) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void *_rb_wasm_stack_mem[2];
+void rb_wasm_mark_mem_range(void *start, void *end) {
+ _rb_wasm_stack_mem[0] = start;
+ _rb_wasm_stack_mem[1] = end;
+}
+
+#define check_live(target, ctx) do { \
+ rb_wasm_scan_stack(rb_wasm_mark_mem_range); \
+ _check_live(target, ctx); \
+ } while (0);
+
+void _check_live(uint32_t target, const char *ctx) {
+ printf("checking %#x ... ", target);
+ bool found_in_locals = false, found_in_stack = false;
+ if (find_in_stack(target, _rb_wasm_stack_mem[0], _rb_wasm_stack_mem[1])) {
+ found_in_stack = true;
+ }
+ rb_wasm_scan_locals(rb_wasm_mark_mem_range);
+ if (find_in_stack(target, _rb_wasm_stack_mem[0], _rb_wasm_stack_mem[1])) {
+ found_in_locals = true;
+ }
+ if (found_in_locals && found_in_stack) {
+ printf("ok (found in C stack and Wasm locals)\n");
+ } else if (found_in_stack) {
+ printf("ok (found in C stack)\n");
+ } else if (found_in_locals) {
+ printf("ok (found in Wasm locals)\n");
+ } else {
+ printf("not found: %s\n", ctx);
+ assert(false);
+ }
+}
+
+void new_frame(uint32_t val, uint32_t depth) {
+ if (depth == 0) {
+ dump_memory(rb_wasm_get_stack_pointer(), base_stack_pointer);
+ for (uint32_t i = 0; i < 5; i++) {
+ check_live(0x00bab10c + i, "argument value");
+ }
+ } else {
+ new_frame(val, depth - 1);
+ }
+}
+
+uint32_t return_value(void) {
+ return 0xabadbabe;
+}
+
+uint32_t check_return_value(void) {
+ check_live(0xabadbabe, "returned value");
+ return 0;
+}
+
+void take_two_args(uint32_t a, uint32_t b) {
+}
+
+__attribute__((noinline))
+int start(int argc, char **argv) {
+
+ uint32_t deadbeef;
+ register uint32_t facefeed;
+ deadbeef = 0xdeadbeef;
+ facefeed = 0xfacefeed;
+
+ check_live(0xdeadbeef, "local variable");
+ check_live(0xfacefeed, "local reg variable");
+
+ new_frame(0x00bab10c, 5);
+
+ take_two_args(return_value(), check_return_value());
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ extern int rb_wasm_rt_start(int (main)(int argc, char **argv), int argc, char **argv);
+ return rb_wasm_rt_start(start, argc, argv);
+}
diff --git a/wasm/tests/setjmp_test.c b/wasm/tests/setjmp_test.c
new file mode 100644
index 0000000000..f263dcfa3e
--- /dev/null
+++ b/wasm/tests/setjmp_test.c
@@ -0,0 +1,108 @@
+#include "wasm/setjmp.h"
+#include "wasm/asyncify.h"
+#include "wasm/machine.h"
+#include <stdio.h>
+#include <assert.h>
+
+void check_direct(void) {
+ rb_wasm_jmp_buf buf;
+ int val;
+ printf("[%s] start\n", __func__);
+ printf("[%s] call rb_wasm_setjmp\n", __func__);
+ if ((val = rb_wasm_setjmp(buf)) == 0) {
+ printf("[%s] rb_wasm_setjmp(buf) == 0\n", __func__);
+ printf("[%s] call rb_wasm_longjmp(buf, 2)\n", __func__);
+ rb_wasm_longjmp(buf, 2);
+ assert(0 && "unreachable after longjmp");
+ } else {
+ printf("[%s] rb_wasm_setjmp(buf) == %d\n", __func__, val);
+ printf("[%s] sp = %p\n", __func__, rb_wasm_get_stack_pointer());
+ assert(val == 2 && "unexpected returned value");
+ }
+ printf("[%s] end\n", __func__);
+}
+
+void jump_to_dst(rb_wasm_jmp_buf *dst) {
+ rb_wasm_jmp_buf buf;
+ printf("[%s] start sp = %p\n", __func__, rb_wasm_get_stack_pointer());
+ printf("[%s] call rb_wasm_setjmp\n", __func__);
+ if (rb_wasm_setjmp(buf) == 0) {
+ printf("[%s] rb_wasm_setjmp(buf) == 0\n", __func__);
+ printf("[%s] call rb_wasm_longjmp(dst, 4)\n", __func__);
+ rb_wasm_longjmp(*dst, 4);
+ assert(0 && "unreachable after longjmp");
+ } else {
+ assert(0 && "unreachable");
+ }
+ printf("[%s] end\n", __func__);
+}
+
+void check_jump_two_level(void) {
+ rb_wasm_jmp_buf buf;
+ int val;
+ printf("[%s] start\n", __func__);
+ printf("[%s] call rb_wasm_setjmp\n", __func__);
+ if ((val = rb_wasm_setjmp(buf)) == 0) {
+ printf("[%s] rb_wasm_setjmp(buf) == 0\n", __func__);
+ printf("[%s] call jump_to_dst(&buf)\n", __func__);
+ jump_to_dst(&buf);
+ assert(0 && "unreachable after longjmp");
+ } else {
+ printf("[%s] rb_wasm_setjmp(buf) == %d\n", __func__, val);
+ assert(val == 4 && "unexpected returned value");
+ }
+ printf("[%s] end\n", __func__);
+}
+
+void check_reuse(void) {
+ rb_wasm_jmp_buf buf;
+ int val;
+ printf("[%s] start\n", __func__);
+ printf("[%s] call rb_wasm_setjmp\n", __func__);
+ if ((val = rb_wasm_setjmp(buf)) == 0) {
+ printf("[%s] rb_wasm_setjmp(buf) == 0\n", __func__);
+ printf("[%s] call rb_wasm_longjmp(buf, 2)\n", __func__);
+ rb_wasm_longjmp(buf, 2);
+ assert(0 && "unreachable after longjmp");
+ } else {
+ printf("[%s] rb_wasm_setjmp(buf) == %d\n", __func__, val);
+ if (val < 5) {
+ printf("[%s] re-call rb_wasm_longjmp(buf, %d)\n", __func__, val + 1);
+ rb_wasm_longjmp(buf, val + 1);
+ }
+ }
+ printf("[%s] end\n", __func__);
+}
+
+void check_stack_ptr(void) {
+ static void *normal_sp;
+ rb_wasm_jmp_buf buf;
+ normal_sp = rb_wasm_get_stack_pointer();
+
+ printf("[%s] start sp = %p\n", __func__, normal_sp);
+ printf("[%s] call rb_wasm_setjmp\n", __func__);
+ if (rb_wasm_setjmp(buf) == 0) {
+ printf("[%s] call jump_to_dst(&buf)\n", __func__);
+ jump_to_dst(&buf);
+ assert(0 && "unreachable after longjmp");
+ } else {
+ printf("[%s] sp = %p\n", __func__, rb_wasm_get_stack_pointer());
+ assert(rb_wasm_get_stack_pointer() == normal_sp);
+ }
+ printf("[%s] end\n", __func__);
+}
+
+// top level function should not be inlined to stop unwinding immediately after this function returns
+__attribute__((noinline))
+int start(int argc, char **argv) {
+ check_direct();
+ check_jump_two_level();
+ check_reuse();
+ check_stack_ptr();
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ extern int rb_wasm_rt_start(int (main)(int argc, char **argv), int argc, char **argv);
+ return rb_wasm_rt_start(start, argc, argv);
+}