Skip to content

Commit

Permalink
Experimental setjmp/longjmp support
Browse files Browse the repository at this point in the history
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.
  Maybe it isn't a problem for libc.a. But for libc.so, it would be
  a disaster for runtimes w/o EH.

Tested with `binaryen --translate-eh-old-to-new` and toywasm:

* build wasi-libc with BUILD_SJLJ=yes
* build your app with "-mllvm -wasm-enable-sjlj"
* apply "wasm-opt --translate-eh-old-to-new"
* run with "toywasm --wasi"

Besides that, for libc.so, your embedder needs to provide
"env:__c_longjmp".
  • Loading branch information
yamt committed Feb 2, 2024
1 parent a6489a8 commit a0c169f
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 2 deletions.
16 changes: 15 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 \
Expand All @@ -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 = \
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -473,7 +483,6 @@ MUSL_OMIT_HEADERS += \
"netdb.h" \
"resolv.h" \
"pty.h" \
"setjmp.h" \
"ulimit.h" \
"sys/xattr.h" \
"wordexp.h" \
Expand Down Expand Up @@ -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))
Expand All @@ -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) \
Expand Down Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions expected/wasm32-wasi-preview2/include-all.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
#include <sched.h>
#include <search.h>
#include <semaphore.h>
#include <setjmp.h>
#include <stdalign.h>
#include <stdbool.h>
#include <stdc-predef.h>
Expand Down
1 change: 1 addition & 0 deletions expected/wasm32-wasi-preview2/predefined-macros.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2412,6 +2412,7 @@
#define _SC_XOPEN_XPG4 100
#define _SEARCH_H
#define _SEMAPHORE_H
#define _SETJMP_H
#define _SIZE_T
#define _STDALIGN_H
#define _STDBOOL_H
Expand Down
1 change: 1 addition & 0 deletions expected/wasm32-wasi-threads/include-all.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
#include <sched.h>
#include <search.h>
#include <semaphore.h>
#include <setjmp.h>
#include <stdalign.h>
#include <stdbool.h>
#include <stdc-predef.h>
Expand Down
1 change: 1 addition & 0 deletions expected/wasm32-wasi-threads/predefined-macros.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2352,6 +2352,7 @@
#define _SC_XOPEN_XPG4 100
#define _SEARCH_H
#define _SEMAPHORE_H
#define _SETJMP_H
#define _SIZE_T
#define _STDALIGN_H
#define _STDBOOL_H
Expand Down
1 change: 1 addition & 0 deletions expected/wasm32-wasi/include-all.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
#include <sched.h>
#include <search.h>
#include <semaphore.h>
#include <setjmp.h>
#include <stdalign.h>
#include <stdbool.h>
#include <stdc-predef.h>
Expand Down
1 change: 1 addition & 0 deletions expected/wasm32-wasi/predefined-macros.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2315,6 +2315,7 @@
#define _SC_XOPEN_XPG4 100
#define _SEARCH_H
#define _SEMAPHORE_H
#define _SETJMP_H
#define _SIZE_T
#define _STDALIGN_H
#define _STDBOOL_H
Expand Down
2 changes: 2 additions & 0 deletions libc-top-half/musl/arch/wasm32/bits/setjmp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* note: right now, only the first word is actually used */
typedef unsigned long __jmp_buf[8];
3 changes: 2 additions & 1 deletion libc-top-half/musl/include/setjmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ extern "C" {

#include <features.h>

#ifdef __wasilibc_unmodified_upstream /* WASI has no setjmp */
/* WASI has no setjmp */
#if defined(__wasilibc_unmodified_upstream) || defined(__wasm_exception_handling__)
#include <bits/setjmp.h>

typedef struct __jmp_buf_tag {
Expand Down
90 changes: 90 additions & 0 deletions libc-top-half/musl/src/setjmp/wasm32/rt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* https://github.com/llvm/llvm-project/blob/main/llvm/lib/Target/WebAssembly/WebAssemblyLowerEmscriptenEHSjLj.cpp
*/

#include <stdint.h>
#include <stdlib.h>

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);
}

0 comments on commit a0c169f

Please sign in to comment.