From c83a5713f9b7bc33cbb0f94d75a35e4b2e35ca89 Mon Sep 17 00:00:00 2001 From: Xiaokang Qin Date: Tue, 29 Sep 2020 10:35:10 +0800 Subject: [PATCH] Add the fast-interp tail call support (#409) And also fix one bug in loader for tail-call Signed-off-by: Xiaokang Qin --- README.md | 1 + core/iwasm/interpreter/wasm_interp_fast.c | 62 +++++++++++++++++++++++ core/iwasm/interpreter/wasm_loader.c | 12 +++-- doc/build_wamr.md | 3 ++ 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a3e062a2b..95d07c020 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ iwasm VM core - [Shared memory](https://github.com/WebAssembly/threads/blob/main/proposals/threads/Overview.md#shared-linear-memory) - [Multi-value](https://github.com/WebAssembly/multi-value) - [wasm-c-api](https://github.com/WebAssembly/wasm-c-api) +- [Tail-call](https://github.com/WebAssembly/tail-call) ### Performance and memory usage The WAMR performance, footprint and memory usage data are available at the [performance](../../wiki/Performance) wiki page. diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index bbb39a381..37a10ad2a 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -1301,10 +1301,16 @@ recover_br_info: goto return_func; HANDLE_OP (WASM_OP_CALL_INDIRECT): +#if WASM_ENABLE_TAIL_CALL != 0 + HANDLE_OP (WASM_OP_RETURN_CALL_INDIRECT): +#endif { WASMType *cur_type, *cur_func_type; WASMTableInstance *cur_table_inst; +#if WASM_ENABLE_TAIL_CALL != 0 + GET_OPCODE(); +#endif #if WASM_ENABLE_THREAD_MGR != 0 CHECK_SUSPEND_FLAGS(); #endif @@ -1360,6 +1366,10 @@ recover_br_info: wasm_set_exception(module, "indirect call type mismatch"); goto got_exception; } +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_RETURN_CALL_INDIRECT) + goto call_func_from_return_call; +#endif goto call_func_from_interp; } @@ -3112,6 +3122,22 @@ recover_br_info: cur_func = module->functions + fidx; goto call_func_from_interp; +#if WASM_ENABLE_TAIL_CALL != 0 + HANDLE_OP (WASM_OP_RETURN_CALL): +#if WASM_ENABLE_THREAD_MGR != 0 + CHECK_SUSPEND_FLAGS(); +#endif + fidx = read_uint32(frame_ip); +#if WASM_ENABLE_MULTI_MODULE != 0 + if (fidx >= module->function_count) { + wasm_set_exception(module, "unknown function"); + goto got_exception; + } +#endif + cur_func = module->functions + fidx; + goto call_func_from_return_call; +#endif /* WASM_ENABLE_TAIL_CALL */ + #if WASM_ENABLE_LABELS_AS_VALUES == 0 default: wasm_set_exception(module, "unsupported opcode"); @@ -3125,8 +3151,10 @@ recover_br_info: HANDLE_OP (WASM_OP_UNUSED_0x08): HANDLE_OP (WASM_OP_UNUSED_0x09): HANDLE_OP (WASM_OP_UNUSED_0x0a): +#if WASM_ENABLE_TAIL_CALL == 0 HANDLE_OP (WASM_OP_RETURN_CALL): HANDLE_OP (WASM_OP_RETURN_CALL_INDIRECT): +#endif HANDLE_OP (WASM_OP_UNUSED_0x14): HANDLE_OP (WASM_OP_UNUSED_0x15): HANDLE_OP (WASM_OP_UNUSED_0x16): @@ -3169,6 +3197,40 @@ recover_br_info: FETCH_OPCODE_AND_DISPATCH (); #endif +#if WASM_ENABLE_TAIL_CALL !=0 + call_func_from_return_call: + { + uint32 *lp_base; + uint32 *lp; + int i; + + if (!(lp_base = lp = wasm_runtime_malloc(cur_func->param_cell_num * sizeof(uint32)))) { + wasm_set_exception(module, "allocate memory failed"); + goto got_exception; + } + for (i = 0; i < cur_func->param_count; i++) { + if (cur_func->param_types[i] == VALUE_TYPE_I64 + || cur_func->param_types[i] == VALUE_TYPE_F64) { + *(int64*)(lp) = + GET_OPERAND(int64, (2 * (cur_func->param_count - i - 1))); + lp += 2; + } + else { + *(lp) = GET_OPERAND(int32, (2 * (cur_func->param_count - i - 1))); + lp ++; + } + } + frame->lp = frame->operand + cur_func->const_cell_num; + bh_memcpy_s(frame->lp, (lp - lp_base) * sizeof(uint32), + lp_base, (lp - lp_base) * sizeof(uint32)); + wasm_runtime_free(lp_base); + FREE_FRAME(exec_env, frame); + frame_ip += cur_func->param_count * sizeof(int16); + wasm_exec_env_set_cur_frame(exec_env, + (WASMRuntimeFrame *)prev_frame); + goto call_func_from_entry; + } +#endif /* WASM_ENABLE_TAIL_CALL */ call_func_from_interp: /* Only do the copy when it's called from interpreter. */ { diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 20e204042..a8d54b519 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -5970,6 +5970,9 @@ handle_op_block_and_loop: read_leb_uint32(p, p_end, type_idx); #if WASM_ENABLE_FAST_INTERP != 0 +#if WASM_ENABLE_TAIL_CALL != 0 + emit_byte(loader_ctx, opcode); +#endif /* we need to emit func_idx before arguments */ emit_uint32(loader_ctx, type_idx); #endif @@ -6023,12 +6026,13 @@ handle_op_block_and_loop: } for (i = 0; i < func_type->result_count; i++) { type = func->func_type->types[func->func_type->param_count + i]; - if (func_type->types[func_type->param_count + i] != type) - set_error_buf_v(error_buf, error_buf_size, "%s%s%s", - "type mismatch: expect ", + if (func_type->types[func_type->param_count + i] != type) { + set_error_buf_v(error_buf, error_buf_size, + "%s%s%s", "type mismatch: expect ", type_str[type - VALUE_TYPE_F64], " but got other"); - goto fail; + goto fail; + } } RESET_STACK(); SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 659f3d839..52090e297 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -83,6 +83,9 @@ Currently we only profile the memory consumption of module, module_instance and - **WAMR_APP_THREAD_STACK_SIZE_MAX**=n, default to 8 MB (8388608) if not set > Note: the AOT boundary check with hardware trap mechanism might consume large stack since the OS may lazily grow the stack mapping as a guard page is hit, we may use this configuration to reduce the total stack usage, e.g. -DWAMR_APP_THREAD_STACK_SIZE_MAX=131072 (128 KB). +#### **Enable tail call feature** +- **WAMR_BUILD_TAIL_CALL**=1/0, default to disable if not set + **Combination of configurations:** We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command: