From c22a2394ff35aebd28c72b0f16cafd20e956a5d2 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Wed, 31 Jan 2024 13:24:20 +0900 Subject: [PATCH] Experimental setjmp/longjmp support Basically copy-and-paste from: https://github.com/yamt/garbage/tree/master/wasm/longjmp While it seems working well, it's disabled by default because: * It might be controversial if it's a good idea to use this emscripten API for WASI as well. * LLVM produces bytecode for an old version of the EH proposal. * The EH proposal is not widely implemeted in runtimes yet. --- Makefile | 16 +++- libc-top-half/musl/arch/wasm32/bits/setjmp.h | 2 + libc-top-half/musl/include/setjmp.h | 3 +- libc-top-half/musl/src/setjmp/wasm32/rt.c | 90 ++++++++++++++++++++ 4 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 libc-top-half/musl/arch/wasm32/bits/setjmp.h create mode 100644 libc-top-half/musl/src/setjmp/wasm32/rt.c diff --git a/Makefile b/Makefile index b96ba3358..37c2bead6 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,8 @@ WASI_SNAPSHOT ?= preview1 MALLOC_IMPL ?= dlmalloc # yes or no BUILD_LIBC_TOP_HALF ?= yes +# Build setjmp/longjmp support. yes or no +BUILD_SJLJ ?= no # The directory where we will store intermediate artifacts. OBJDIR ?= build/$(TARGET_TRIPLE) # The directory where we store files and tools for generating WASI Preview 2 bindings @@ -309,6 +311,11 @@ LIBC_TOP_HALF_MUSL_SOURCES += \ ) endif +ifeq ($(BUILD_SJLJ),yes) +LIBC_TOP_HALF_MUSL_SOURCES += \ + $(LIBC_TOP_HALF_MUSL_SRC_DIR)/setjmp/wasm32/rt.c +endif + MUSL_PRINTSCAN_SOURCES = \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/internal/floatscan.c \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/stdio/vfprintf.c \ @@ -320,6 +327,8 @@ BULK_MEMORY_SOURCES = \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/string/memcpy.c \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/string/memmove.c \ $(LIBC_TOP_HALF_MUSL_SRC_DIR)/string/memset.c +SJLJ_SOURCES = \ + $(LIBC_TOP_HALF_MUSL_SRC_DIR)/setjmp/wasm32/rt.c LIBC_TOP_HALF_HEADERS_PRIVATE = $(LIBC_TOP_HALF_DIR)/headers/private LIBC_TOP_HALF_SOURCES = $(LIBC_TOP_HALF_DIR)/sources LIBC_TOP_HALF_ALL_SOURCES = \ @@ -402,6 +411,7 @@ MUSL_PRINTSCAN_OBJS = $(call objs,$(MUSL_PRINTSCAN_SOURCES)) MUSL_PRINTSCAN_LONG_DOUBLE_OBJS = $(patsubst %.o,%.long-double.o,$(MUSL_PRINTSCAN_OBJS)) MUSL_PRINTSCAN_NO_FLOATING_POINT_OBJS = $(patsubst %.o,%.no-floating-point.o,$(MUSL_PRINTSCAN_OBJS)) BULK_MEMORY_OBJS = $(call objs,$(BULK_MEMORY_SOURCES)) +SJLJ_OBJS = $(call objs,$(SJLJ_SOURCES)) LIBWASI_EMULATED_MMAN_OBJS = $(call objs,$(LIBWASI_EMULATED_MMAN_SOURCES)) LIBWASI_EMULATED_PROCESS_CLOCKS_OBJS = $(call objs,$(LIBWASI_EMULATED_PROCESS_CLOCKS_SOURCES)) LIBWASI_EMULATED_GETPID_OBJS = $(call objs,$(LIBWASI_EMULATED_GETPID_SOURCES)) @@ -473,7 +483,6 @@ MUSL_OMIT_HEADERS += \ "netdb.h" \ "resolv.h" \ "pty.h" \ - "setjmp.h" \ "ulimit.h" \ "sys/xattr.h" \ "wordexp.h" \ @@ -510,6 +519,7 @@ LIBWASI_EMULATED_SIGNAL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_SIGN LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS)) LIBDL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBDL_OBJS)) BULK_MEMORY_SO_OBJS = $(patsubst %.o,%.pic.o,$(BULK_MEMORY_OBJS)) +SJLJ_SO_OBJS = $(patsubst %.o,%.pic.o,$(SJLJ_OBJS)) DLMALLOC_SO_OBJS = $(patsubst %.o,%.pic.o,$(DLMALLOC_OBJS)) LIBC_BOTTOM_HALF_ALL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBC_BOTTOM_HALF_ALL_OBJS)) LIBC_TOP_HALF_ALL_SO_OBJS = $(patsubst %.o,%.pic.o,$(LIBC_TOP_HALF_ALL_OBJS)) @@ -524,6 +534,7 @@ PIC_OBJS = \ $(LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS) \ $(LIBDL_SO_OBJS) \ $(BULK_MEMORY_SO_OBJS) \ + $(SJLJ_SO_OBJS) \ $(DLMALLOC_SO_OBJS) \ $(LIBC_BOTTOM_HALF_ALL_SO_OBJS) \ $(LIBC_TOP_HALF_ALL_SO_OBJS) \ @@ -597,6 +608,9 @@ $(BULK_MEMORY_OBJS) $(BULK_MEMORY_SO_OBJS): CFLAGS += \ $(BULK_MEMORY_OBJS) $(BULK_MEMORY_SO_OBJS): CFLAGS += \ -DBULK_MEMORY_THRESHOLD=$(BULK_MEMORY_THRESHOLD) +$(SJLJ_OBJS) $(SJLJ_SO_OBJS): CFLAGS += \ + -mllvm -wasm-enable-sjlj + $(LIBWASI_EMULATED_SIGNAL_MUSL_OBJS) $(LIBWASI_EMULATED_SIGNAL_MUSL_SO_OBJS): CFLAGS += \ -D_WASI_EMULATED_SIGNAL diff --git a/libc-top-half/musl/arch/wasm32/bits/setjmp.h b/libc-top-half/musl/arch/wasm32/bits/setjmp.h new file mode 100644 index 000000000..aae8de683 --- /dev/null +++ b/libc-top-half/musl/arch/wasm32/bits/setjmp.h @@ -0,0 +1,2 @@ +/* note: right now, only the first word is actually used */ +typedef unsigned long __jmp_buf[8]; diff --git a/libc-top-half/musl/include/setjmp.h b/libc-top-half/musl/include/setjmp.h index f505f8e4f..33c486116 100644 --- a/libc-top-half/musl/include/setjmp.h +++ b/libc-top-half/musl/include/setjmp.h @@ -7,7 +7,8 @@ extern "C" { #include -#ifdef __wasilibc_unmodified_upstream /* WASI has no setjmp */ +/* WASI has no setjmp */ +#if defined(__wasilibc_unmodified_upstream) || defined(__wasm_exception_handling__) #include typedef struct __jmp_buf_tag { diff --git a/libc-top-half/musl/src/setjmp/wasm32/rt.c b/libc-top-half/musl/src/setjmp/wasm32/rt.c new file mode 100644 index 000000000..1ced420e4 --- /dev/null +++ b/libc-top-half/musl/src/setjmp/wasm32/rt.c @@ -0,0 +1,90 @@ +/* + * https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp + */ + +#include +#include + +struct entry { + uint32_t id; + uint32_t label; +}; + +static _Thread_local struct state { + uint32_t id; + uint32_t size; + struct arg { + void *env; + int val; + } arg; +} g_state; + +/* + * table is allocated at the entry of functions which call setjmp. + * + * table = malloc(40); + * size = 4; + * *(int *)table = 0; + */ +_Static_assert(sizeof(struct entry) * (4 + 1) <= 40, "entry size"); + +void * +saveSetjmp(void *env, uint32_t label, void *table, uint32_t size) +{ + struct state *state = &g_state; + struct entry *e = table; + uint32_t i; + for (i = 0; i < size; i++) { + if (e[i].id == 0) { + uint32_t id = ++state->id; + *(uint32_t *)env = id; + e[i].id = id; + e[i].label = label; + /* + * note: only the first word is zero-initialized + * by the caller. + */ + e[i + 1].id = 0; + goto done; + } + } + size *= 2; + void *p = realloc(table, sizeof(*e) * (size + 1)); + if (p == NULL) { + __builtin_trap(); + } + table = p; +done: + state->size = size; + return table; +} + +uint32_t +testSetjmp(unsigned int id, void *table, uint32_t size) +{ + struct entry *e = table; + uint32_t i; + for (i = 0; i < size; i++) { + if (e[i].id == id) { + return e[i].label; + } + } + return 0; +} + +uint32_t +getTempRet0() +{ + struct state *state = &g_state; + return state->size; +} + +void +__wasm_longjmp(void *env, int val) +{ + struct state *state = &g_state; + struct arg *arg = &state->arg; + arg->env = env; + arg->val = val; + __builtin_wasm_throw(1, arg); +}