mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	[wasm] add unit test suite for fiber, register scan, sjlj in platform dir
This commit is contained in:
		
							parent
							
								
									3794ef6f01
								
							
						
					
					
						commit
						f72f01abd8
					
				
				
				Notes:
				
					git
				
				2022-01-19 11:19:38 +09:00 
				
			
			
			
		
		
					 5 changed files with 304 additions and 0 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -232,3 +232,6 @@ lcov*.info
 | 
			
		|||
/rb_mjit_header.h
 | 
			
		||||
/mjit_config.h
 | 
			
		||||
/include/ruby-*/*/rb_mjit_min_header-*.h
 | 
			
		||||
 | 
			
		||||
# /wasm/
 | 
			
		||||
/wasm/tests/*.wasm
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										66
									
								
								wasm/tests/fiber_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								wasm/tests/fiber_test.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								wasm/tests/machine_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								wasm/tests/machine_test.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										108
									
								
								wasm/tests/setjmp_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								wasm/tests/setjmp_test.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue