[前][次][番号順一覧][スレッド一覧]

ruby-changes:70937

From: Yuta <ko1@a...>
Date: Wed, 19 Jan 2022 11:19:54 +0900 (JST)
Subject: [ruby-changes:70937] f72f01abd8 (master): [wasm] add unit test suite for fiber, register scan, sjlj in platform dir

https://git.ruby-lang.org/ruby.git/commit/?id=f72f01abd8

From f72f01abd89640b083b4067e4be399448f0fb6ce Mon Sep 17 00:00:00 2001
From: Yuta Saito <kateinoigakukun@g...>
Date: Wed, 8 Dec 2021 22:19:52 +0900
Subject: [wasm] add unit test suite for fiber, register scan, sjlj in platform
 dir

---
 .gitignore                |   3 ++
 wasm/GNUmakefile.in       |  12 +++++
 wasm/tests/fiber_test.c   |  66 ++++++++++++++++++++++++++
 wasm/tests/machine_test.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++
 wasm/tests/setjmp_test.c  | 108 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 304 insertions(+)
 create mode 100644 wasm/tests/fiber_test.c
 create mode 100644 wasm/tests/machine_test.c
 create mode 100644 wasm/tests/setjmp_test.c

diff --git a/.gitignore b/.gitignore
index 31bfd787a70..675020fa91e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -232,3 +232,6 @@ lcov*.info https://github.com/ruby/ruby/blob/trunk/.gitignore#L232
 /rb_mjit_header.h
 /mjit_config.h
 /include/ruby-*/*/rb_mjit_min_header-*.h
+
+# /wasm/
+/wasm/tests/*.wasm
diff --git a/wasm/GNUmakefile.in b/wasm/GNUmakefile.in
index 4328dec9c66..18ddd06739a 100644
--- a/wasm/GNUmakefile.in
+++ b/wasm/GNUmakefile.in
@@ -6,6 +6,8 @@ GNUmakefile: $(wasmdir)/GNUmakefile.in https://github.com/ruby/ruby/blob/trunk/wasm/GNUmakefile.in#L6
 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) https://github.com/ruby/ruby/blob/trunk/wasm/GNUmakefile.in#L20
 	@$(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 00000000000..e6b36631ceb
--- /dev/null
+++ b/wasm/tests/fiber_test.c
@@ -0,0 +1,66 @@ https://github.com/ruby/ruby/blob/trunk/wasm/tests/fiber_test.c#L1
+#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 00000000000..f4b62ff580b
--- /dev/null
+++ b/wasm/tests/machine_test.c
@@ -0,0 +1,115 @@ https://github.com/ruby/ruby/blob/trunk/wasm/tests/machine_test.c#L1
+#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 00000000000..f263dcfa3e7
--- /dev/null
+++ b/wasm/tests/setjmp_test.c
@@ -0,0 +1,108 @@ https://github.com/ruby/ruby/blob/trunk/wasm/tests/setjmp_test.c#L1
+#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__ (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]