diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fcb3a2eb..128af62e 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 f81e46f6..361a9c2e 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 diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 2e2750bb..8ac8e4de 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 26aa715a..d21396aa 100644 --- a/core/config.h +++ b/core/config.h @@ -244,5 +244,9 @@ 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 ec0aca84..a241f19b 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -1285,11 +1285,33 @@ label_pop_csp_n: 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 @@ label_pop_csp_n: 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 @@ label_pop_csp_n: 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 @@ label_pop_csp_n: 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 2a6262de..c36567f6 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -3125,8 +3125,8 @@ recover_br_info: 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 ac8d1550..483b4607 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 @@ handle_op_block_and_loop: } 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 @@ handle_op_block_and_loop: } } - 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]); +#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]); +#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 @@ handle_op_block_and_loop: } } - 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]); +#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]); +#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 499db124..5bfff577 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 @@ handle_op_block_and_loop: } 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 @@ handle_op_block_and_loop: } } - 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]); +#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]); +#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 @@ handle_op_block_and_loop: } } - 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]); +#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]); +#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 f546088b..65c1dbf7 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 */ \