Skip to content

Commit

Permalink
wasm2c: implement the tail-call proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
keithw committed Jul 30, 2023
1 parent 88d5298 commit 8049069
Show file tree
Hide file tree
Showing 17 changed files with 849 additions and 80 deletions.
497 changes: 448 additions & 49 deletions src/c-writer.cc

Large diffs are not rendered by default.

32 changes: 29 additions & 3 deletions src/prebuilt/wasm2c_source_declarations.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ R"w2c_template( TRAP(CALL_INDIRECT), \
R"w2c_template( ((t)table.data[x].func)(__VA_ARGS__))
)w2c_template"
R"w2c_template(
#define RETURN_CALL_INDIRECT(table, ft, x) \
)w2c_template"
R"w2c_template( (LIKELY((x) < table.size && table.data[x].func_tailcallee.fn && \
)w2c_template"
R"w2c_template( func_types_eq(ft, table.data[x].func_type)) || \
)w2c_template"
R"w2c_template( TRAP(CALL_INDIRECT), \
)w2c_template"
R"w2c_template( (next->fn = table.data[x].func_tailcallee.fn, \
)w2c_template"
R"w2c_template( *instance_ptr = table.data[x].module_instance))
)w2c_template"
R"w2c_template(
#ifdef SUPPORT_MEMORY64
)w2c_template"
R"w2c_template(#define RANGE_CHECK(mem, offset, len) \
Expand Down Expand Up @@ -922,6 +935,8 @@ R"w2c_template( wasm_rt_func_type_t type;
)w2c_template"
R"w2c_template( wasm_rt_function_ptr_t func;
)w2c_template"
R"w2c_template( wasm_rt_tailcallee_t func_tailcallee;
)w2c_template"
R"w2c_template( size_t module_offset;
)w2c_template"
R"w2c_template(} wasm_elem_segment_expr_t;
Expand Down Expand Up @@ -953,11 +968,11 @@ R"w2c_template( for (u32 i = 0; i < n; i++) {
)w2c_template"
R"w2c_template( const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
)w2c_template"
R"w2c_template( dest->data[dest_addr + i] =
R"w2c_template( dest->data[dest_addr + i] = (wasm_rt_funcref_t){
)w2c_template"
R"w2c_template( (wasm_rt_funcref_t){src_expr->type, src_expr->func,
R"w2c_template( src_expr->type, src_expr->func, src_expr->func_tailcallee,
)w2c_template"
R"w2c_template( (char*)module_instance + src_expr->module_offset};
R"w2c_template( (char*)module_instance + src_expr->module_offset};
)w2c_template"
R"w2c_template( }
)w2c_template"
Expand Down Expand Up @@ -1107,4 +1122,15 @@ R"w2c_template(#define FUNC_TYPE_T(x) static const char x[]
)w2c_template"
R"w2c_template(#endif
)w2c_template"
R"w2c_template(
#if (__STDC_VERSION__ >= 201112L) || defined(_Static_assert)
)w2c_template"
R"w2c_template(#define wasm_static_assert(X) _Static_assert(X, "assertion failure")
)w2c_template"
R"w2c_template(#else
)w2c_template"
R"w2c_template(#define wasm_static_assert assert
)w2c_template"
R"w2c_template(#endif
)w2c_template"
;
20 changes: 17 additions & 3 deletions src/template/wasm2c.declarations.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a,
TRAP(CALL_INDIRECT), \
((t)table.data[x].func)(__VA_ARGS__))

#define RETURN_CALL_INDIRECT(table, ft, x) \
(LIKELY((x) < table.size && table.data[x].func_tailcallee.fn && \
func_types_eq(ft, table.data[x].func_type)) || \
TRAP(CALL_INDIRECT), \
(next->fn = table.data[x].func_tailcallee.fn, \
*instance_ptr = table.data[x].module_instance))

#ifdef SUPPORT_MEMORY64
#define RANGE_CHECK(mem, offset, len) \
do { \
Expand Down Expand Up @@ -493,6 +500,7 @@ static inline void memory_init(wasm_rt_memory_t* dest,
typedef struct {
wasm_rt_func_type_t type;
wasm_rt_function_ptr_t func;
wasm_rt_tailcallee_t func_tailcallee;
size_t module_offset;
} wasm_elem_segment_expr_t;

Expand All @@ -509,9 +517,9 @@ static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
TRAP(OOB);
for (u32 i = 0; i < n; i++) {
const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
dest->data[dest_addr + i] =
(wasm_rt_funcref_t){src_expr->type, src_expr->func,
(char*)module_instance + src_expr->module_offset};
dest->data[dest_addr + i] = (wasm_rt_funcref_t){
src_expr->type, src_expr->func, src_expr->func_tailcallee,
(char*)module_instance + src_expr->module_offset};
}
}

Expand Down Expand Up @@ -591,3 +599,9 @@ DEFINE_TABLE_FILL(externref)
#define FUNC_TYPE_EXTERN_T(x) const char x[]
#define FUNC_TYPE_T(x) static const char x[]
#endif

#if (__STDC_VERSION__ >= 201112L) || defined(_Static_assert)
#define wasm_static_assert(X) _Static_assert(X, "assertion failure")
#else
#define wasm_static_assert assert
#endif
2 changes: 1 addition & 1 deletion src/tools/wasm2c.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ static const char s_description[] =
static const std::string supported_features[] = {
"multi-memory", "multi-value", "sign-extension", "saturating-float-to-int",
"exceptions", "memory64", "extended-const", "simd",
"threads"};
"threads", "tail-call"};

static bool IsFeatureSupported(const std::string& feature) {
return std::find(std::begin(supported_features), std::end(supported_features),
Expand Down
3 changes: 3 additions & 0 deletions test/run-spec-wasm2c.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,7 @@ def main(args):
parser.add_argument('--enable-memory64', action='store_true')
parser.add_argument('--enable-extended-const', action='store_true')
parser.add_argument('--enable-threads', action='store_true')
parser.add_argument('--enable-tail-call', action='store_true')
parser.add_argument('--disable-bulk-memory', action='store_true')
parser.add_argument('--disable-reference-types', action='store_true')
parser.add_argument('--debug-names', action='store_true')
Expand All @@ -553,6 +554,7 @@ def main(args):
'--enable-memory64': options.enable_memory64,
'--enable-extended-const': options.enable_extended_const,
'--enable-threads': options.enable_threads,
'--enable-tail-call': options.enable_tail_call,
'--enable-multi-memory': options.enable_multi_memory,
'--disable-bulk-memory': options.disable_bulk_memory,
'--disable-reference-types': options.disable_reference_types,
Expand All @@ -571,6 +573,7 @@ def main(args):
'--enable-memory64': options.enable_memory64,
'--enable-extended-const': options.enable_extended_const,
'--enable-threads': options.enable_threads,
'--enable-tail-call': options.enable_tail_call,
'--enable-multi-memory': options.enable_multi_memory})

options.cflags += shlex.split(os.environ.get('WASM2C_CFLAGS', ''))
Expand Down
55 changes: 52 additions & 3 deletions test/wasm2c/add.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ wasm_rt_func_type_t wasm2c_test_get_func_type(uint32_t param_count, uint32_t res
/* export: 'add' */
u32 w2c_test_add(w2c_test*, u32, u32);
/* export for tail-call of 'add' */
void wasm2c_tailcall_1024_w2c_test_add(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next);
#ifdef __cplusplus
}
#endif
Expand Down Expand Up @@ -93,6 +96,13 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a,
TRAP(CALL_INDIRECT), \
((t)table.data[x].func)(__VA_ARGS__))
#define RETURN_CALL_INDIRECT(table, ft, x) \
(LIKELY((x) < table.size && table.data[x].func_tailcallee.fn && \
func_types_eq(ft, table.data[x].func_type)) || \
TRAP(CALL_INDIRECT), \
(next->fn = table.data[x].func_tailcallee.fn, \
*instance_ptr = table.data[x].module_instance))
#ifdef SUPPORT_MEMORY64
#define RANGE_CHECK(mem, offset, len) \
do { \
Expand Down Expand Up @@ -560,6 +570,7 @@ static inline void memory_init(wasm_rt_memory_t* dest,
typedef struct {
wasm_rt_func_type_t type;
wasm_rt_function_ptr_t func;
wasm_rt_tailcallee_t func_tailcallee;
size_t module_offset;
} wasm_elem_segment_expr_t;
Expand All @@ -576,9 +587,9 @@ static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
TRAP(OOB);
for (u32 i = 0; i < n; i++) {
const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
dest->data[dest_addr + i] =
(wasm_rt_funcref_t){src_expr->type, src_expr->func,
(char*)module_instance + src_expr->module_offset};
dest->data[dest_addr + i] = (wasm_rt_funcref_t){
src_expr->type, src_expr->func, src_expr->func_tailcallee,
(char*)module_instance + src_expr->module_offset};
}
}
Expand Down Expand Up @@ -659,7 +670,22 @@ DEFINE_TABLE_FILL(externref)
#define FUNC_TYPE_T(x) static const char x[]
#endif
#if (__STDC_VERSION__ >= 201112L) || defined(_Static_assert)
#define wasm_static_assert(X) _Static_assert(X, "assertion failure")
#else
#define wasm_static_assert assert
#endif
static u32 w2c_test_add_0(w2c_test*, u32, u32);
static void wasm2c_tailcall_1024_w2c_test_add_0(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next);
#ifndef wasm_multi_ii
#define wasm_multi_ii wasm_multi_ii
struct wasm_multi_ii {
u32 i0;
u32 i1;
};
#endif /* wasm_multi_ii */
FUNC_TYPE_T(w2c_test_t0) = "\x92\xfb\x6a\xdf\x49\x07\x0a\x83\xbe\x08\x02\x68\xcd\xf6\x95\x27\x4a\xc2\xf3\xe5\xe4\x7d\x29\x49\xe8\xed\x42\x92\x6a\x9d\xda\xf0";
Expand All @@ -668,6 +694,11 @@ u32 w2c_test_add(w2c_test* instance, u32 var_p0, u32 var_p1) {
return w2c_test_add_0(instance, var_p0, var_p1);
}
/* export for tail-call of 'add' */
void wasm2c_tailcall_1024_w2c_test_add(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next) {
wasm2c_tailcall_1024_w2c_test_add_0(instance_ptr, tail_call_stack, next);
}
void wasm2c_test_instantiate(w2c_test* instance) {
assert(wasm_rt_is_initialized());
}
Expand Down Expand Up @@ -699,4 +730,22 @@ u32 w2c_test_add_0(w2c_test* instance, u32 var_p0, u32 var_p1) {
FUNC_EPILOGUE;
return var_i0;
}
void wasm2c_tailcall_1024_w2c_test_add_0(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next) {
wasm_static_assert(sizeof(struct wasm_multi_ii) <= 1024);
wasm_static_assert(sizeof(u32) <= 1024);
w2c_test* instance = *instance_ptr;
u32 var_p0, var_p1;
{
struct wasm_multi_ii *tmp = tail_call_stack;
var_p0 = tmp->i0;
var_p1 = tmp->i1;
}
u32 var_i0, var_i1;
var_i0 = var_p0;
var_i1 = var_p1;
var_i0 += var_i1;
wasm_rt_memcpy(tail_call_stack, &var_i0, sizeof(var_i0));
next->fn = NULL;
}
;;; STDOUT ;;)
71 changes: 67 additions & 4 deletions test/wasm2c/check-imports.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a,
TRAP(CALL_INDIRECT), \
((t)table.data[x].func)(__VA_ARGS__))
#define RETURN_CALL_INDIRECT(table, ft, x) \
(LIKELY((x) < table.size && table.data[x].func_tailcallee && \
func_types_eq(ft, table.data[x].func_type)) || \
TRAP(CALL_INDIRECT), \
(next->fn = table.data[x].func_tailcallee, \
*instance_ptr = table.data[x].module_instance))
#ifdef SUPPORT_MEMORY64
#define RANGE_CHECK(mem, offset, len) \
do { \
Expand Down Expand Up @@ -583,6 +590,7 @@ static inline void memory_init(wasm_rt_memory_t* dest,
typedef struct {
wasm_rt_func_type_t type;
wasm_rt_function_ptr_t func;
wasm_rt_tailcallee_t func_tailcallee;
size_t module_offset;
} wasm_elem_segment_expr_t;
Expand All @@ -599,9 +607,9 @@ static inline void funcref_table_init(wasm_rt_funcref_table_t* dest,
TRAP(OOB);
for (u32 i = 0; i < n; i++) {
const wasm_elem_segment_expr_t* src_expr = &src[src_addr + i];
dest->data[dest_addr + i] =
(wasm_rt_funcref_t){src_expr->type, src_expr->func,
(char*)module_instance + src_expr->module_offset};
dest->data[dest_addr + i] = (wasm_rt_funcref_t){
src_expr->type, src_expr->func, src_expr->func_tailcallee,
(char*)module_instance + src_expr->module_offset};
}
}
Expand Down Expand Up @@ -682,9 +690,26 @@ DEFINE_TABLE_FILL(externref)
#define FUNC_TYPE_T(x) static const char x[]
#endif
#if (__STDC_VERSION__ >= 201112L) || defined(_Static_assert)
#define wasm_static_assert(X) _Static_assert(X, "assertion failure")
#else
#define wasm_static_assert assert
#endif
static u32 w2c_test_f0(w2c_test*, u32);
static void wasm2c_tailcall_1024_w2c_test_f0(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next);
static u32 w2c_test_f1(w2c_test*);
static void wasm2c_tailcall_1024_w2c_test_f1(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next);
static u32 w2c_test_f2(w2c_test*, u32, u32);
static void wasm2c_tailcall_1024_w2c_test_f2(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next);
#ifndef wasm_multi_ii
#define wasm_multi_ii wasm_multi_ii
struct wasm_multi_ii {
u32 i0;
u32 i1;
};
#endif /* wasm_multi_ii */
FUNC_TYPE_T(w2c_test_t0) = "\x07\x80\x96\x7a\x42\xf7\x3e\xe6\x70\x5c\x2f\xac\x83\xf5\x67\xd2\xa2\xa0\x69\x41\x5f\xf8\xe7\x96\x7f\x23\xab\x00\x03\x5f\x4a\x3c";
FUNC_TYPE_T(w2c_test_t1) = "\x72\xab\x00\xdf\x20\x3d\xce\xa1\xf2\x29\xc7\x9d\x13\x40\x7e\x98\xac\x7d\x41\x4a\x53\x2e\x42\x42\x61\x55\x2e\xaa\xeb\xbe\xc6\x35";
Expand All @@ -694,7 +719,7 @@ static void init_memories(w2c_test* instance) {
}
static const wasm_elem_segment_expr_t elem_segment_exprs_w2c_test_e0[] = {
{w2c_test_t1, (wasm_rt_function_ptr_t)w2c_test_f1, 0},
{w2c_test_t1, (wasm_rt_function_ptr_t)w2c_test_f1, {wasm2c_tailcall_1024_w2c_test_f1}, 0},
};
static void init_tables(w2c_test* instance) {
Expand Down Expand Up @@ -768,6 +793,17 @@ u32 w2c_test_f0(w2c_test* instance, u32 var_p0) {
return var_i0;
}
void wasm2c_tailcall_1024_w2c_test_f0(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next) {
wasm_static_assert(sizeof(u32) <= 1024);
w2c_test* instance = *instance_ptr;
u32 var_p0 = *(u32*)tail_call_stack;
u32 var_i0;
var_i0 = var_p0;
var_i0 = CALL_INDIRECT((*instance->w2c_env_0x5F_indirect_function_table), u32 (*)(void*), w2c_test_t1, var_i0, (*instance->w2c_env_0x5F_indirect_function_table).data[var_i0].module_instance);
wasm_rt_memcpy(tail_call_stack, &var_i0, sizeof(var_i0));
next->fn = NULL;
}
u32 w2c_test_f1(w2c_test* instance) {
FUNC_PROLOGUE;
u32 var_i0;
Expand All @@ -777,6 +813,16 @@ u32 w2c_test_f1(w2c_test* instance) {
return var_i0;
}
void wasm2c_tailcall_1024_w2c_test_f1(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next) {
wasm_static_assert(sizeof(u32) <= 1024);
w2c_test* instance = *instance_ptr;
u32 var_i0;
var_i0 = 16u;
var_i0 = i32_load(instance->w2c_env_0x5F_linear_memory, (u64)(var_i0));
wasm_rt_memcpy(tail_call_stack, &var_i0, sizeof(var_i0));
next->fn = NULL;
}
u32 w2c_test_f2(w2c_test* instance, u32 var_p0, u32 var_p1) {
FUNC_PROLOGUE;
u32 var_i0;
Expand All @@ -785,4 +831,21 @@ u32 w2c_test_f2(w2c_test* instance, u32 var_p0, u32 var_p1) {
FUNC_EPILOGUE;
return var_i0;
}
void wasm2c_tailcall_1024_w2c_test_f2(void **instance_ptr, void *tail_call_stack, wasm_rt_tailcallee_t *next) {
wasm_static_assert(sizeof(struct wasm_multi_ii) <= 1024);
wasm_static_assert(sizeof(u32) <= 1024);
w2c_test* instance = *instance_ptr;
u32 var_p0, var_p1;
{
struct wasm_multi_ii *tmp = tail_call_stack;
var_p0 = tmp->i0;
var_p1 = tmp->i1;
}
u32 var_i0;
var_i0 = 1u;
var_i0 = w2c_test_f0(instance, var_i0);
wasm_rt_memcpy(tail_call_stack, &var_i0, sizeof(var_i0));
next->fn = NULL;
}
;;; STDOUT ;;)
Loading

0 comments on commit 8049069

Please sign in to comment.