From c23b603a0f35d0e554e3a85e690474ffde31a332 Mon Sep 17 00:00:00 2001 From: Xiaokang Qin Date: Fri, 18 Sep 2020 15:58:02 +0800 Subject: [PATCH 1/2] Add the tail-call feature support for classic-interp Signed-off-by: Xiaokang Qin --- build-scripts/config_common.cmake | 4 + core/config.h | 4 + core/iwasm/interpreter/wasm_interp_classic.c | 44 +++++++++- core/iwasm/interpreter/wasm_interp_fast.c | 4 +- core/iwasm/interpreter/wasm_loader.c | 91 +++++++++++++++++--- core/iwasm/interpreter/wasm_mini_loader.c | 59 ++++++++++--- core/iwasm/interpreter/wasm_opcode.h | 8 +- 7 files changed, 181 insertions(+), 33 deletions(-) diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 2e2750bb40..8ac8e4de65 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -173,4 +173,8 @@ if (WAMR_BUILD_CUSTOM_NAME_SECTION EQUAL 1) add_definitions (-DWASM_ENABLE_CUSTOM_NAME_SECTION=1) message (" Custom name section enabled") endif () +if (WAMR_BUILD_TAIL_CALL EQUAL 1) + add_definitions (-DWASM_ENABLE_TAIL_CALL=1) + message (" Tail call enabled") +endif () diff --git a/core/config.h b/core/config.h index 1c932a6529..c04024f7fb 100644 --- a/core/config.h +++ b/core/config.h @@ -248,5 +248,9 @@ enum { wasm_runtime_set_max_thread_num */ #define CLUSTER_MAX_THREAD_NUM 4 +#ifndef WASM_ENABLE_TAIL_CALL +#define WASM_ENABLE_TAIL_CALL 0 +#endif + #endif /* end of _CONFIG_H_ */ diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index ec0aca8449..a241f19bee 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -1285,11 +1285,33 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, 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 + read_leb_uint32(frame_ip, frame_ip_end, fidx); +#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 */ + 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 + opcode = *(frame_ip - 1); +#endif #if WASM_ENABLE_THREAD_MGR != 0 CHECK_SUSPEND_FLAGS(); #endif @@ -1352,7 +1374,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, 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; } @@ -3121,8 +3146,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP (WASM_OP_UNUSED_0x08): HANDLE_OP (WASM_OP_UNUSED_0x09): HANDLE_OP (WASM_OP_UNUSED_0x0a): - HANDLE_OP (WASM_OP_UNUSED_0x12): - HANDLE_OP (WASM_OP_UNUSED_0x13): +#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): @@ -3151,6 +3178,15 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, FETCH_OPCODE_AND_DISPATCH (); #endif +#if WASM_ENABLE_TAIL_CALL != 0 + call_func_from_return_call: + POP(cur_func->param_cell_num); + word_copy(frame->lp, frame_sp, cur_func->param_cell_num); + FREE_FRAME(exec_env, frame); + wasm_exec_env_set_cur_frame(exec_env, + (WASMRuntimeFrame *)prev_frame); + goto call_func_from_entry; +#endif call_func_from_interp: /* Only do the copy when it's called from interpreter. */ { diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index 2a6262deb0..c36567f6f6 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -3125,8 +3125,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, HANDLE_OP (WASM_OP_UNUSED_0x08): HANDLE_OP (WASM_OP_UNUSED_0x09): HANDLE_OP (WASM_OP_UNUSED_0x0a): - HANDLE_OP (WASM_OP_UNUSED_0x12): - HANDLE_OP (WASM_OP_UNUSED_0x13): + HANDLE_OP (WASM_OP_RETURN_CALL): + HANDLE_OP (WASM_OP_RETURN_CALL_INDIRECT): HANDLE_OP (WASM_OP_UNUSED_0x14): HANDLE_OP (WASM_OP_UNUSED_0x15): HANDLE_OP (WASM_OP_UNUSED_0x16): diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index e99f04e8aa..14d818cf7a 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -3307,10 +3307,16 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, break; case WASM_OP_CALL: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: +#endif skip_leb_uint32(p, p_end); /* funcidx */ break; case WASM_OP_CALL_INDIRECT: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL_INDIRECT: +#endif skip_leb_uint32(p, p_end); /* typeidx */ CHECK_BUF(p, p_end, 1); u8 = read_uint8(p); /* 0x00 */ @@ -5812,6 +5818,9 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, } case WASM_OP_CALL: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: +#endif { WASMType *func_type; uint32 func_idx; @@ -5844,22 +5853,53 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, } } - for (i = 0; i < func_type->result_count; i++) { - PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_CALL) { +#endif + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); #if WASM_ENABLE_FAST_INTERP != 0 - /* Here we emit each return value's dynamic_offset. But in fact - * these offsets are continuous, so interpreter only need to get - * the first return value's offset. - */ - PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); + /* Here we emit each return value's dynamic_offset. But in fact + * these offsets are continuous, so interpreter only need to get + * the first return value's offset. + */ + PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); #endif + } +#if WASM_ENABLE_TAIL_CALL != 0 } - + else { + char *type_str[] = { "f64", "f32", "i64", "i32" }; + uint8 type; + if (func_type->result_count != func->func_type->result_count) { + set_error_buf_v(error_buf, error_buf_size, + "%s%u%s", "type mismatch: expect ", + func->func_type->result_count, + " return values but got other"); + goto fail; + } + 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 ", + type_str[type - VALUE_TYPE_F64], + " but got other"); + goto fail; + } + } + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + } +#endif func->has_op_func_call = true; break; } case WASM_OP_CALL_INDIRECT: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL_INDIRECT: +#endif { int32 idx; WASMType *func_type; @@ -5904,13 +5944,40 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, } } - for (i = 0; i < func_type->result_count; i++) { - PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_CALL_INDIRECT) { +#endif + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); #if WASM_ENABLE_FAST_INTERP != 0 - PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); + PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); #endif + } +#if WASM_ENABLE_TAIL_CALL != 0 } - + else { + char *type_str[] = { "f64", "f32", "i64", "i32" }; + uint8 type; + if (func_type->result_count != func->func_type->result_count) { + set_error_buf_v(error_buf, error_buf_size, + "%s%u%s", "type mismatch: expect ", + func->func_type->result_count, + " return values but got other"); + goto fail; + } + 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 ", + type_str[type - VALUE_TYPE_F64], + " but got other"); + goto fail; + } + RESET_STACK(); + SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true); + } +#endif func->has_op_func_call = true; break; } diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index 499db1245e..5bfff57713 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -2235,10 +2235,16 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, break; case WASM_OP_CALL: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: +#endif skip_leb_uint32(p, p_end); /* funcidx */ break; case WASM_OP_CALL_INDIRECT: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL_INDIRECT: +#endif skip_leb_uint32(p, p_end); /* typeidx */ CHECK_BUF(p, p_end, 1); u8 = read_uint8(p); /* 0x00 */ @@ -4612,6 +4618,9 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, } case WASM_OP_CALL: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL: +#endif { WASMType *func_type; uint32 func_idx; @@ -4641,22 +4650,37 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, } } - for (i = 0; i < func_type->result_count; i++) { - PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_CALL) { +#endif + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); #if WASM_ENABLE_FAST_INTERP != 0 - /* Here we emit each return value's dynamic_offset. But in fact - * these offsets are continuous, so interpreter only need to get - * the first return value's offset. - */ - PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); + /* Here we emit each return value's dynamic_offset. But in fact + * these offsets are continuous, so interpreter only need to get + * the first return value's offset. + */ + PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); #endif + } +#if WASM_ENABLE_TAIL_CALL != 0 } - + else { + bh_assert(func_type->result_count == func->func_type->result_count); + for (i = 0; i < func_type->result_count; i++) { + bh_assert(func_type->types[func_type->param_count + i] == + func->func_type->types[func->func_type->param_count + i]); + } + } +#endif func->has_op_func_call = true; break; } case WASM_OP_CALL_INDIRECT: +#if WASM_ENABLE_TAIL_CALL != 0 + case WASM_OP_RETURN_CALL_INDIRECT: +#endif { int32 idx; WASMType *func_type; @@ -4690,12 +4714,25 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, } } - for (i = 0; i < func_type->result_count; i++) { - PUSH_TYPE(func_type->types[func_type->param_count + i]); +#if WASM_ENABLE_TAIL_CALL != 0 + if (opcode == WASM_OP_CALL) { +#endif + for (i = 0; i < func_type->result_count; i++) { + PUSH_TYPE(func_type->types[func_type->param_count + i]); #if WASM_ENABLE_FAST_INTERP != 0 - PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); + PUSH_OFFSET_TYPE(func_type->types[func_type->param_count + i]); #endif + } +#if WASM_ENABLE_TAIL_CALL != 0 } + else { + bh_assert(func_type->result_count == func->func_type->result_count); + for (i = 0; i < func_type->result_count; i++) { + bh_assert(func_type->types[func_type->param_count + i] == + func->func_type->types[func->func_type->param_count + i]); + } + } +#endif func->has_op_func_call = true; break; diff --git a/core/iwasm/interpreter/wasm_opcode.h b/core/iwasm/interpreter/wasm_opcode.h index f546088b5b..65c1dbf7e6 100644 --- a/core/iwasm/interpreter/wasm_opcode.h +++ b/core/iwasm/interpreter/wasm_opcode.h @@ -34,9 +34,9 @@ typedef enum WASMOpcode { WASM_OP_RETURN = 0x0f, /* return */ WASM_OP_CALL = 0x10, /* call */ WASM_OP_CALL_INDIRECT = 0x11, /* call_indirect */ + WASM_OP_RETURN_CALL = 0x12, /* return_call */ + WASM_OP_RETURN_CALL_INDIRECT = 0x13, /* return_call_indirect */ - WASM_OP_UNUSED_0x12 = 0x12, - WASM_OP_UNUSED_0x13 = 0x13, WASM_OP_UNUSED_0x14 = 0x14, WASM_OP_UNUSED_0x15 = 0x15, WASM_OP_UNUSED_0x16 = 0x16, @@ -403,8 +403,8 @@ static type _name[WASM_INSTRUCTION_NUM] = { \ HANDLE_OPCODE (WASM_OP_RETURN), /* 0x0f */ \ HANDLE_OPCODE (WASM_OP_CALL), /* 0x10 */ \ HANDLE_OPCODE (WASM_OP_CALL_INDIRECT), /* 0x11 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x12), /* 0x12 */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x13), /* 0x13 */ \ + HANDLE_OPCODE (WASM_OP_RETURN_CALL), /* 0x12 */ \ + HANDLE_OPCODE (WASM_OP_RETURN_CALL_INDIRECT), /* 0x13 */ \ HANDLE_OPCODE (WASM_OP_UNUSED_0x14), /* 0x14 */ \ HANDLE_OPCODE (WASM_OP_UNUSED_0x15), /* 0x15 */ \ HANDLE_OPCODE (WASM_OP_UNUSED_0x16), /* 0x16 */ \ From ec4c965f0cb1af52ef06567e4d234e7a5f3326a9 Mon Sep 17 00:00:00 2001 From: Xu Jun <693788454@qq.com> Date: Thu, 24 Sep 2020 11:41:01 +0800 Subject: [PATCH 2/2] add CI for tail call and custom name section (#9) * add CI for tail call and custom name section * update CI for mac --- .github/workflows/linux.yml | 14 ++++++++++++++ .github/workflows/mac.yml | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fcb3a2ebfe..128af62e89 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -70,6 +70,20 @@ jobs: cmake .. -DWAMR_BUILD_MEMORY_PROFILING=1 make cd .. && rm -rf build + - name: Build iwasm [tail call] + run: | + cd product-mini/platforms/linux + mkdir build && cd build + cmake .. -DWAMR_BUILD_TAIL_CALL=1 + make + cd .. && rm -rf build + - name: Build iwasm [custom name section] + run: | + cd product-mini/platforms/linux + mkdir build && cd build + cmake .. -DWAMR_BUILD_CUSTOM_NAME_SECTION=1 + make + cd .. && rm -rf build - name: download wasi-sdk run: | cd /opt diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index f81e46f62b..361a9c2e4d 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -70,6 +70,20 @@ jobs: cmake .. -DWAMR_BUILD_MEMORY_PROFILING=1 make cd .. && rm -rf build + - name: Build iwasm [tail call] + run: | + cd product-mini/platforms/darwin + mkdir build && cd build + cmake .. -DWAMR_BUILD_TAIL_CALL=1 + make + cd .. && rm -rf build + - name: Build iwasm [custom name section] + run: | + cd product-mini/platforms/darwin + mkdir build && cd build + cmake .. -DWAMR_BUILD_CUSTOM_NAME_SECTION=1 + make + cd .. && rm -rf build - name: Build Sample [wasm-c-api] run: | cd samples/wasm-c-api