diff --git a/README.md b/README.md index c20640596..3cd807e86 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ iwasm VM core - Choices of WASM application libc support: the built-in libc subset for the embedded environment or [WASI](https://github.com/WebAssembly/WASI) for standard libc - [Embeddable with the supporting C API's](./doc/embed_wamr.md) - [The mechanism for exporting native API's to WASM applications](./doc/export_native_api.md) -- [Multiple modules as dependencies](./doc/multi_module.md) -- [Thread management and pthread library](./doc/pthread_library.md) +- [Multiple modules as dependencies](./doc/multi_module.md), ref to [sample](samples/multi-module) +- [Thread management and pthread library](./doc/pthread_library.md), ref to [sample](samples/multi-thread) ### post-MVP features - [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions) @@ -34,9 +34,10 @@ iwasm VM core - [Bulk memory operations](https://github.com/WebAssembly/bulk-memory-operations) - [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) +- [wasm-c-api](https://github.com/WebAssembly/wasm-c-api), ref to [document](doc/wasm_c_api.md) and [sample](samples/wasm-c-api) - [Tail-call](https://github.com/WebAssembly/tail-call) -- [128-bit SIMD](https://github.com/WebAssembly/simd) +- [128-bit SIMD](https://github.com/WebAssembly/simd), ref to [samples/workload](samples/workload) +- [Reference Types](https://github.com/WebAssembly/reference-types), ref to [document](doc/ref_types.md) and [sample](samples/ref-types) ### Supported architectures and platforms @@ -124,6 +125,7 @@ The WAMR [samples](./samples) integrate the iwasm VM core, application manager a - **[multi-thread](./samples/multi-thread/)**: Demonstrating how to run wasm application which creates multiple threads to execute wasm functions concurrently, and uses mutex/cond by calling pthread related API's. - **[spawn-thread](./samples/spawn-thread)**: Demonstrating how to execute wasm functions of the same wasm application concurrently, in threads created by host embedder or runtime, but not the wasm application itself. - **[multi-module](./samples/multi-module)**: Demonstrating the [multiple modules as dependencies](./doc/multi_module.md) feature which implements the [load-time dynamic linking](https://webassembly.org/docs/dynamic-linking/). +- **[ref-types](./samples/ref-types)**: Demonstrating how to call wasm functions with argument of externref type introduced by [reference types proposal](https://github.com/WebAssembly/reference-types). - **[wasm-c-api](./samples/wasm-c-api/README.md)**: Demonstrating how to run some samples from [wasm-c-api proposal](https://github.com/WebAssembly/wasm-c-api) and showing the supported API's. - **[workload](./samples/workload/README.md)**: Demonstrating how to build and run some complex workloads, e.g. tensorflow-lite, XNNPACK, wasm-av1, meshoptimizer and bwa. diff --git a/build-scripts/config_common.cmake b/build-scripts/config_common.cmake index 03a4b23bd..816471633 100644 --- a/build-scripts/config_common.cmake +++ b/build-scripts/config_common.cmake @@ -206,6 +206,12 @@ if (WAMR_BUILD_TAIL_CALL EQUAL 1) add_definitions (-DWASM_ENABLE_TAIL_CALL=1) message (" Tail call enabled") endif () +if (WAMR_BUILD_REF_TYPES EQUAL 1) + add_definitions (-DWASM_ENABLE_REF_TYPES=1) + message (" Reference types enabled") +else () + message (" Reference types disabled") +endif () if (DEFINED WAMR_BH_VPRINTF) add_definitions (-DBH_VPRINTF=${WAMR_BH_VPRINTF}) endif () diff --git a/core/config.h b/core/config.h index 02ceb680f..878a42280 100644 --- a/core/config.h +++ b/core/config.h @@ -66,7 +66,7 @@ #endif #define AOT_MAGIC_NUMBER 0x746f6100 -#define AOT_CURRENT_VERSION 2 +#define AOT_CURRENT_VERSION 3 #ifndef WASM_ENABLE_JIT #define WASM_ENABLE_JIT 0 @@ -289,5 +289,9 @@ #define WASM_ENABLE_CUSTOM_NAME_SECTION 0 #endif +#ifndef WASM_ENABLE_REF_TYPES +#define WASM_ENABLE_REF_TYPES 0 +#endif + #endif /* end of _CONFIG_H_ */ diff --git a/core/iwasm/aot/aot_loader.c b/core/iwasm/aot/aot_loader.c index fac0c4856..eba9a9dfb 100644 --- a/core/iwasm/aot/aot_loader.c +++ b/core/iwasm/aot/aot_loader.c @@ -506,6 +506,39 @@ destroy_table_init_data_list(AOTTableInitData **data_list, uint32 count, } } +static bool +load_import_table_list(const uint8 **p_buf, + const uint8 *buf_end, + AOTModule *module, + char *error_buf, + uint32 error_buf_size) +{ + const uint8 *buf = *p_buf; + AOTImportTable *import_table; + uint64 size; + uint32 i, possible_grow; + + /* Allocate memory */ + size = sizeof(AOTImportTable) * (uint64)module->import_table_count; + if (!(module->import_tables = import_table = + loader_malloc(size, error_buf, error_buf_size))) { + return false; + } + + /* keep sync with aot_emit_table_info() aot_emit_aot_file */ + for (i = 0; i < module->import_table_count; i++, import_table++) { + read_uint32(buf, buf_end, import_table->table_init_size); + read_uint32(buf, buf_end, import_table->table_max_size); + read_uint32(buf, buf_end, possible_grow); + import_table->possible_grow = (possible_grow & 0x1); + } + + *p_buf = buf; + return true; +fail: + return false; +} + static bool load_table_list(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module, char *error_buf, uint32 error_buf_size) @@ -513,7 +546,7 @@ load_table_list(const uint8 **p_buf, const uint8 *buf_end, const uint8 *buf = *p_buf; AOTTable *table; uint64 size; - uint32 i; + uint32 i, possible_grow; /* Allocate memory */ size = sizeof(AOTTable) * (uint64)module->table_count; @@ -528,6 +561,8 @@ load_table_list(const uint8 **p_buf, const uint8 *buf_end, read_uint32(buf, buf_end, table->table_flags); read_uint32(buf, buf_end, table->table_init_size); read_uint32(buf, buf_end, table->table_max_size); + read_uint32(buf, buf_end, possible_grow); + table->possible_grow = (possible_grow & 0x1); } *p_buf = buf; @@ -555,9 +590,12 @@ load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end, /* Create each table data segment */ for (i = 0; i < module->table_init_data_count; i++) { + uint32 mode, elem_type; uint32 table_index, init_expr_type, func_index_count; uint64 init_expr_value, size1; + read_uint32(buf, buf_end, mode); + read_uint32(buf, buf_end, elem_type); read_uint32(buf, buf_end, table_index); read_uint32(buf, buf_end, init_expr_type); read_uint64(buf, buf_end, init_expr_value); @@ -570,6 +608,9 @@ load_table_init_data_list(const uint8 **p_buf, const uint8 *buf_end, return false; } + data_list[i]->mode = mode; + data_list[i]->elem_type = elem_type; + data_list[i]->is_dropped = false; data_list[i]->table_index = table_index; data_list[i]->offset.init_expr_type = (uint8)init_expr_type; data_list[i]->offset.u.i64 = (int64)init_expr_value; @@ -591,13 +632,14 @@ load_table_info(const uint8 **p_buf, const uint8 *buf_end, const uint8 *buf = *p_buf; read_uint32(buf, buf_end, module->import_table_count); - /* We don't support import_table_count > 0 currently */ - bh_assert(module->import_table_count == 0); + if (module->import_table_count > 0 + && !load_import_table_list(&buf, buf_end, module, error_buf, + error_buf_size)) + return false; read_uint32(buf, buf_end, module->table_count); if (module->table_count > 0 - && !load_table_list(&buf, buf_end, module, - error_buf, error_buf_size)) + && !load_table_list(&buf, buf_end, module, error_buf, error_buf_size)) return false; read_uint32(buf, buf_end, module->table_init_data_count); @@ -2457,11 +2499,15 @@ aot_convert_wasm_module(WASMModule *wasm_module, #endif #if WASM_ENABLE_SIMD != 0 option.enable_simd = true; +#endif +#if WASM_ENABLE_REF_TYPES != 0 + option.enable_ref_types = true; #endif option.enable_aux_stack_check = true; #if (WASM_ENABLE_PERF_PROFILING != 0) || (WASM_ENABLE_DUMP_CALL_STACK != 0) option.enable_aux_stack_frame = true; #endif + comp_ctx = aot_create_comp_context(comp_data, &option); if (!comp_ctx) { aot_last_error = aot_get_last_error(); diff --git a/core/iwasm/aot/aot_reloc.h b/core/iwasm/aot/aot_reloc.h index 27fecf00e..7c1673cf7 100644 --- a/core/iwasm/aot/aot_reloc.h +++ b/core/iwasm/aot/aot_reloc.h @@ -29,6 +29,17 @@ typedef struct { #define REG_ATOMIC_WAIT_SYM() #endif +#if WASM_ENABLE_REF_TYPES != 0 +#define REG_REF_TYPES_SYM() \ + REG_SYM(aot_drop_table_seg), \ + REG_SYM(aot_table_init), \ + REG_SYM(aot_table_copy), \ + REG_SYM(aot_table_fill), \ + REG_SYM(aot_table_grow), +#else +#define REG_REF_TYPES_SYM() +#endif + #if (WASM_ENABLE_PERF_PROFILING != 0) || (WASM_ENABLE_DUMP_CALL_STACK != 0) #define REG_AOT_TRACE_SYM() \ REG_SYM(aot_alloc_frame), \ @@ -41,8 +52,8 @@ typedef struct { REG_SYM(aot_set_exception_with_id), \ REG_SYM(aot_invoke_native), \ REG_SYM(aot_call_indirect), \ - REG_SYM(wasm_runtime_enlarge_memory), \ - REG_SYM(wasm_runtime_set_exception), \ + REG_SYM(aot_enlarge_memory), \ + REG_SYM(aot_set_exception), \ {"memset", (void*)aot_memset}, \ {"memmove", (void*)aot_memmove}, \ REG_SYM(fmin), \ @@ -59,6 +70,7 @@ typedef struct { REG_SYM(rintf), \ REG_BULK_MEMORY_SYM() \ REG_ATOMIC_WAIT_SYM() \ + REG_REF_TYPES_SYM() \ REG_AOT_TRACE_SYM() #define CHECK_RELOC_OFFSET(data_size) do { \ diff --git a/core/iwasm/aot/aot_runtime.c b/core/iwasm/aot/aot_runtime.c index 74a710324..139c0d034 100644 --- a/core/iwasm/aot/aot_runtime.c +++ b/core/iwasm/aot/aot_runtime.c @@ -89,6 +89,10 @@ init_global_data(uint8 *global_data, uint8 type, switch (type) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif *(int32*)global_data = initial_value->i32; break; case VALUE_TYPE_I64: @@ -143,6 +147,13 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module, .global_data_linked); break; } +#if WASM_ENABLE_REF_TYPES != 0 + case INIT_EXPR_TYPE_REFNULL_CONST: + { + *(uint32 *)p = NULL_REF; + break; + } +#endif default: { init_global_data(p, global->type, &init_expr->u); @@ -157,22 +168,86 @@ global_instantiate(AOTModuleInstance *module_inst, AOTModule *module, return true; } +AOTTableInstance * +aot_next_tbl_inst(const AOTTableInstance *tbl_inst) +{ + uint32 offset = offsetof(AOTTableInstance, data); + offset += tbl_inst->max_size * sizeof(uint32); + return (AOTTableInstance *)((uint8 *)tbl_inst + offset); +} + +static inline AOTTableInstance * +aot_get_table_inst(const AOTModuleInstance *module_inst, uint32 tbl_idx) +{ + uint32 i = 0; + AOTTableInstance *tbl_inst = (AOTTableInstance*)module_inst->tables.ptr; + + while (i != tbl_idx) { + tbl_inst = aot_next_tbl_inst(tbl_inst); + ++i; + } + + return tbl_inst; +} + static bool table_instantiate(AOTModuleInstance *module_inst, AOTModule *module, char *error_buf, uint32 error_buf_size) { uint32 i, global_index, global_data_offset, base_offset, length; AOTTableInitData *table_seg; + AOTTableInstance *tbl_inst = (AOTTableInstance*)module_inst->tables.ptr; + /* + * treat import table like a local one until we enable module linking + * in AOT mode + */ + for (i = 0; i != module_inst->table_count; ++i) { + if (i < module->import_table_count) { + AOTImportTable *import_table = module->import_tables + i; + tbl_inst->cur_size = import_table->table_init_size; + tbl_inst->max_size = aot_get_imp_tbl_data_slots(import_table); + } + else { + AOTTable *table = + module->tables + (i - module->import_table_count); + tbl_inst->cur_size = table->table_init_size; + tbl_inst->max_size = aot_get_tbl_data_slots(table); + } + + tbl_inst = aot_next_tbl_inst(tbl_inst); + } + + /* fill table with element segment content */ for (i = 0; i < module->table_init_data_count; i++) { + AOTTableInstance *tbl_inst; + table_seg = module->table_init_data_list[i]; - bh_assert(table_seg->offset.init_expr_type == - INIT_EXPR_TYPE_I32_CONST - || table_seg->offset.init_expr_type == - INIT_EXPR_TYPE_GET_GLOBAL); + +#if WASM_ENABLE_REF_TYPES != 0 + if (!wasm_elem_is_active(table_seg->mode)) + continue; +#endif + + bh_assert(table_seg->table_index < module_inst->table_count); + + tbl_inst = aot_get_table_inst(module_inst, table_seg->table_index); + bh_assert(tbl_inst); + + bh_assert( + table_seg->offset.init_expr_type == INIT_EXPR_TYPE_I32_CONST + || table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL +#if WASM_ENABLE_REF_TYPES != 0 + || table_seg->offset.init_expr_type + == INIT_EXPR_TYPE_FUNCREF_CONST + || table_seg->offset.init_expr_type + == INIT_EXPR_TYPE_REFNULL_CONST +#endif + ); /* Resolve table data base offset */ - if (table_seg->offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { + if (table_seg->offset.init_expr_type + == INIT_EXPR_TYPE_GET_GLOBAL) { global_index = table_seg->offset.u.global_index; if (!check_global_init_expr(module, global_index, @@ -182,36 +257,42 @@ table_instantiate(AOTModuleInstance *module_inst, AOTModule *module, if (global_index < module->import_global_count) global_data_offset = - module->import_globals[global_index].data_offset; + module->import_globals[global_index].data_offset; else global_data_offset = - module->globals[global_index - module->import_global_count] - .data_offset; + module + ->globals[global_index - module->import_global_count] + .data_offset; - base_offset = *(uint32*) - ((uint8*)module_inst->global_data.ptr + global_data_offset); + base_offset = *(uint32 *)((uint8 *)module_inst->global_data.ptr + + global_data_offset); } else base_offset = (uint32)table_seg->offset.u.i32; /* Copy table data */ - bh_assert(module_inst->table_data.ptr); /* base_offset only since length might negative */ - if (base_offset > module_inst->table_size) { - LOG_DEBUG("base_offset(%d) > table_size(%d)", base_offset, - module_inst->table_size); + if (base_offset > tbl_inst->cur_size) { +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); +#else set_error_buf(error_buf, error_buf_size, "elements segment does not fit"); +#endif return false; } /* base_offset + length(could be zero) */ length = table_seg->func_index_count; - if (base_offset + length > module_inst->table_size) { - LOG_DEBUG("base_offset(%d) + length(%d) > table_size(%d)", - base_offset, length, module_inst->table_size); + if (base_offset + length > tbl_inst->cur_size) { +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); +#else set_error_buf(error_buf, error_buf_size, "elements segment does not fit"); +#endif return false; } @@ -219,9 +300,9 @@ table_instantiate(AOTModuleInstance *module_inst, AOTModule *module, * Check function index in the current module inst for now. * will check the linked table inst owner in future */ - memcpy((uint32 *)module_inst->table_data.ptr + base_offset, - table_seg->func_indexes, - length * sizeof(uint32)); + bh_memcpy_s((uint32 *)tbl_inst->data + base_offset, + (tbl_inst->max_size - base_offset) * sizeof(uint32), + table_seg->func_indexes, length * sizeof(uint32)); } return true; @@ -595,8 +676,13 @@ memories_instantiate(AOTModuleInstance *module_inst, AOTModule *module, if (base_offset > memory_inst->memory_data_size) { LOG_DEBUG("base_offset(%d) > memory_data_size(%d)", base_offset, memory_inst->memory_data_size); +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds memory access"); +#else set_error_buf(error_buf, error_buf_size, "data segment does not fit"); +#endif return false; } @@ -605,8 +691,13 @@ memories_instantiate(AOTModuleInstance *module_inst, AOTModule *module, if (base_offset + length > memory_inst->memory_data_size) { LOG_DEBUG("base_offset(%d) + length(%d) > memory_data_size(%d)", base_offset, length, memory_inst->memory_data_size); +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds memory access"); +#else set_error_buf(error_buf, error_buf_size, "data segment does not fit"); +#endif return false; } @@ -820,24 +911,39 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, char *error_buf, uint32 error_buf_size) { AOTModuleInstance *module_inst; - uint32 module_inst_struct_size = + const uint32 module_inst_struct_size = offsetof(AOTModuleInstance, global_table_data.bytes); - uint64 module_inst_mem_inst_size = + const uint64 module_inst_mem_inst_size = (uint64)module->memory_count * sizeof(AOTMemoryInstance); - uint32 table_size = module->table_count > 0 ? - module->tables[0].table_init_size : 0; - uint64 table_data_size = (uint64)table_size * sizeof(uint32); - uint64 total_size = (uint64)module_inst_struct_size - + module_inst_mem_inst_size - + module->global_data_size - + table_data_size; + uint64 total_size, table_size = 0; uint8 *p; + uint32 i; /* Check heap size */ heap_size = align_uint(heap_size, 8); if (heap_size > APP_HEAP_SIZE_MAX) heap_size = APP_HEAP_SIZE_MAX; + total_size = (uint64)module_inst_struct_size + module_inst_mem_inst_size + + module->global_data_size; + + /* + * calculate size of table data + */ + for (i = 0; i != module->import_table_count; ++i) { + table_size += offsetof(AOTTableInstance, data); + table_size += + (uint64)sizeof(uint32) + * (uint64)aot_get_imp_tbl_data_slots(module->import_tables + i); + } + + for (i = 0; i != module->table_count; ++i) { + table_size += offsetof(AOTTableInstance, data); + table_size += (uint64)sizeof(uint32) + * (uint64)aot_get_tbl_data_slots(module->tables + i); + } + total_size += table_size; + /* Allocate module instance, global data, table data and heap data */ if (!(module_inst = runtime_malloc(total_size, error_buf, error_buf_size))) { @@ -857,10 +963,11 @@ aot_instantiate(AOTModule *module, bool is_sub_inst, /* Initialize table info */ p += module->global_data_size; - module_inst->table_data.ptr = p; - module_inst->table_size = table_size; + module_inst->tables.ptr = p; + module_inst->table_count = + module->table_count + module->import_table_count; /* Set all elements to -1 to mark them as uninitialized elements */ - memset(module_inst->table_data.ptr, -1, (uint32)table_data_size); + memset(module_inst->tables.ptr, 0xff, (uint32)table_size); if (!table_instantiate(module_inst, module, error_buf, error_buf_size)) goto fail; @@ -1254,12 +1361,21 @@ aot_call_function(WASMExecEnv *exec_env, switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif argv_ret++; break; case VALUE_TYPE_I64: case VALUE_TYPE_F64: argv_ret += 2; break; +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + argv_ret += 4; + break; +#endif - default: bh_assert(0); break; @@ -1326,8 +1442,16 @@ aot_create_exec_env_and_call_function(AOTModuleInstance *module_inst, } #endif +#if WASM_ENABLE_REF_TYPES != 0 + wasm_runtime_prepare_call_function(exec_env, func); +#endif + ret = aot_call_function(exec_env, func, argc, argv); +#if WASM_ENABLE_REF_TYPES != 0 + wasm_runtime_finalize_call_function(exec_env, func, ret, argv); +#endif + #if WASM_ENABLE_THREAD_MGR != 0 /* don't destroy the exec_env if it's searched from the cluster */ if (!existing_exec_env) @@ -1399,6 +1523,9 @@ aot_set_exception_with_id(AOTModuleInstance *module_inst, case EXCE_AUX_STACK_UNDERFLOW: aot_set_exception(module_inst, "wasm auxiliary stack underflow"); break; + case EXCE_OUT_OF_BOUNDS_TABLE_ACCESS: + aot_set_exception(module_inst, "out of bounds table access"); + break; default: break; } @@ -2009,17 +2136,16 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, bool aot_call_indirect(WASMExecEnv *exec_env, - uint32 table_elem_idx, + uint32 tbl_idx, uint32 table_elem_idx, uint32 argc, uint32 *argv) { AOTModuleInstance *module_inst = (AOTModuleInstance*) wasm_runtime_get_module_inst(exec_env); AOTModule *aot_module = (AOTModule*)module_inst->aot_module.ptr; uint32 *func_type_indexes = (uint32*)module_inst->func_type_indexes.ptr; - uint32 *table_data = (uint32*)module_inst->table_data.ptr; + AOTTableInstance *tbl_inst; AOTFuncType *func_type; void **func_ptrs = (void**)module_inst->func_ptrs.ptr, *func_ptr; - uint32 table_size = module_inst->table_size; uint32 func_type_idx, func_idx, ext_ret_count; AOTImportFunc *import_func; const char *signature = NULL; @@ -2036,12 +2162,15 @@ aot_call_indirect(WASMExecEnv *exec_env, return false; } - if (table_elem_idx >= table_size) { + tbl_inst = aot_get_table_inst(module_inst, tbl_idx); + bh_assert(tbl_inst); + + if (table_elem_idx >= tbl_inst->cur_size) { aot_set_exception_with_id(module_inst, EXCE_UNDEFINED_ELEMENT); return false; } - func_idx = table_data[table_elem_idx]; + func_idx = ((uint32*)tbl_inst->data)[table_elem_idx]; if (func_idx == (uint32)-1) { aot_set_exception_with_id(module_inst, EXCE_UNINITIALIZED_ELEMENT); return false; @@ -2122,12 +2251,21 @@ aot_call_indirect(WASMExecEnv *exec_env, switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif argv_ret++; break; case VALUE_TYPE_I64: case VALUE_TYPE_F64: argv_ret += 2; break; +#if WASM_ENABLE_SIMD != 0 + case VALUE_TYPE_V128: + argv_ret += 4; + break; +#endif - default: bh_assert(0); break; @@ -2281,16 +2419,17 @@ aot_get_aux_stack(WASMExecEnv *exec_env, } return false; } - #endif #if (WASM_ENABLE_MEMORY_PROFILING != 0) || (WASM_ENABLE_MEMORY_TRACING != 0) -static uint32 const_string_size; - -void const_string_node_size_cb(void *key, void *value) +static void +const_string_node_size_cb(void *key, void *value, + void *p_const_string_size) { + uint32 const_string_size = *(uint32*)p_const_string_size; const_string_size += bh_hash_map_get_elem_struct_size(); const_string_size += strlen((const char *)value) + 1; + *(uint32*)p_const_string_size += const_string_size; } void @@ -2343,12 +2482,14 @@ aot_get_module_mem_consumption(const AOTModule *module, } if (module->const_str_set) { + uint32 const_string_size = 0; + mem_conspn->const_strs_size = bh_hash_map_get_struct_size(module->const_str_set); - const_string_size = 0; bh_hash_map_traverse(module->const_str_set, - const_string_node_size_cb); + const_string_node_size_cb, + (void*)&const_string_size); mem_conspn->const_strs_size += const_string_size; } @@ -2378,6 +2519,7 @@ void aot_get_module_inst_mem_consumption(const AOTModuleInstance *module_inst, WASMModuleInstMemConsumption *mem_conspn) { + AOTTableInstance *tbl_inst; uint32 i; memset(mem_conspn, 0, sizeof(*mem_conspn)); @@ -2399,7 +2541,12 @@ aot_get_module_inst_mem_consumption(const AOTModuleInstance *module_inst, mem_allocator_get_heap_struct_size(); } - mem_conspn->tables_size = sizeof(uint32) * module_inst->table_size; + tbl_inst = module_inst->tables.ptr; + for (i = 0; i < module_inst->table_count; i++) { + mem_conspn->tables_size += offsetof(AOTTableInstance, data); + mem_conspn->tables_size += sizeof(uint32) * tbl_inst->max_size; + tbl_inst = aot_next_tbl_inst(tbl_inst); + } /* func_ptrs and func_type_indexes */ mem_conspn->functions_size = (sizeof(void *) + sizeof(uint32)) * @@ -2421,6 +2568,144 @@ aot_get_module_inst_mem_consumption(const AOTModuleInstance *module_inst, #endif /* end of (WASM_ENABLE_MEMORY_PROFILING != 0) || (WASM_ENABLE_MEMORY_TRACING != 0) */ +#if WASM_ENABLE_REF_TYPES != 0 +void +aot_drop_table_seg(AOTModuleInstance *module_inst, uint32 tbl_seg_idx) +{ + AOTModule *module = (AOTModule *)module_inst->aot_module.ptr; + AOTTableInitData *tbl_seg = module->table_init_data_list[tbl_seg_idx]; + tbl_seg->is_dropped = true; +} + +void +aot_table_init(AOTModuleInstance *module_inst, + uint32 tbl_idx, uint32 tbl_seg_idx, + uint32 length, uint32 src_offset, uint32 dst_offset) +{ + AOTTableInstance *tbl_inst; + AOTTableInitData *tbl_seg; + const AOTModule *module = module_inst->aot_module.ptr; + + tbl_inst = aot_get_table_inst(module_inst, tbl_idx); + bh_assert(tbl_inst); + + tbl_seg = module->table_init_data_list[tbl_seg_idx]; + bh_assert(tbl_seg); + + if (!length) { + return; + } + + if (length + src_offset > tbl_seg->func_index_count + || dst_offset + length > tbl_inst->cur_size) { + aot_set_exception_with_id(module_inst, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + if (tbl_seg->is_dropped) { + aot_set_exception_with_id(module_inst, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + if (!wasm_elem_is_passive(tbl_seg->mode)) { + aot_set_exception_with_id(module_inst, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + bh_memcpy_s((uint8 *)tbl_inst + offsetof(AOTTableInstance, data) + + dst_offset * sizeof(uint32), + (tbl_inst->cur_size - dst_offset) * sizeof(uint32), + tbl_seg->func_indexes + src_offset, length * sizeof(uint32)); +} + +void +aot_table_copy(AOTModuleInstance *module_inst, + uint32 src_tbl_idx, uint32 dst_tbl_idx, + uint32 length, uint32 src_offset, uint32 dst_offset) +{ + AOTTableInstance *src_tbl_inst, *dst_tbl_inst; + + src_tbl_inst = aot_get_table_inst(module_inst, src_tbl_idx); + bh_assert(src_tbl_inst); + + dst_tbl_inst = aot_get_table_inst(module_inst, dst_tbl_idx); + bh_assert(dst_tbl_inst); + + if ((uint64)src_offset + length > dst_tbl_inst->cur_size + || (uint64)dst_offset + length > src_tbl_inst->cur_size) { + aot_set_exception_with_id(module_inst, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + /* if src_offset >= dst_offset, copy from front to back */ + /* if src_offset < dst_offset, copy from back to front */ + /* merge all together */ + bh_memcpy_s((uint8 *)(dst_tbl_inst) + offsetof(AOTTableInstance, data) + + dst_offset * sizeof(uint32), + (dst_tbl_inst->cur_size - dst_offset) * sizeof(uint32), + (uint8 *)(src_tbl_inst) + offsetof(AOTTableInstance, data) + + src_offset * sizeof(uint32), + length * sizeof(uint32)); +} + +void +aot_table_fill(AOTModuleInstance *module_inst, uint32 tbl_idx, + uint32 length, uint32 val, uint32 data_offset) +{ + AOTTableInstance *tbl_inst; + + tbl_inst = aot_get_table_inst(module_inst, tbl_idx); + bh_assert(tbl_inst); + + if (data_offset + length > tbl_inst->cur_size) { + aot_set_exception_with_id(module_inst, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS); + return; + } + + for (; length != 0; data_offset++, length--) { + tbl_inst->data[data_offset] = val; + } +} + +uint32 +aot_table_grow(AOTModuleInstance *module_inst, uint32 tbl_idx, + uint32 inc_entries, uint32 init_val) +{ + uint32 entry_count, i, orig_tbl_sz; + AOTTableInstance *tbl_inst; + + tbl_inst = aot_get_table_inst(module_inst, tbl_idx); + if (!tbl_inst) { + return (uint32)-1; + } + + orig_tbl_sz = tbl_inst->cur_size; + + if (!inc_entries) { + return orig_tbl_sz; + } + + entry_count = tbl_inst->cur_size + inc_entries; + /* prevent from integer overflow */ + if (entry_count < tbl_inst->cur_size || entry_count > tbl_inst->max_size) { + return (uint32)-1; + } + + /* fill in */ + for (i = 0; i < inc_entries; ++i) { + tbl_inst->data[tbl_inst->cur_size + i] = init_val; + } + + tbl_inst->cur_size = entry_count; + return orig_tbl_sz; +} +#endif /* WASM_ENABLE_REF_TYPES != 0 */ + #if (WASM_ENABLE_DUMP_CALL_STACK != 0) || (WASM_ENABLE_PERF_PROFILING != 0) static const char * get_func_name_from_index(const AOTModuleInstance *module_inst, @@ -2551,3 +2836,4 @@ aot_dump_perf_profiling(const AOTModuleInstance *module_inst) } } #endif /* end of WASM_ENABLE_PERF_PROFILING */ + diff --git a/core/iwasm/aot/aot_runtime.h b/core/iwasm/aot/aot_runtime.h index 990523934..f93e8f0c6 100644 --- a/core/iwasm/aot/aot_runtime.h +++ b/core/iwasm/aot/aot_runtime.h @@ -34,6 +34,7 @@ typedef enum AOTExceptionID { EXCE_UNALIGNED_ATOMIC, EXCE_AUX_STACK_OVERFLOW, EXCE_AUX_STACK_UNDERFLOW, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, EXCE_NUM, } AOTExceptionID; @@ -251,6 +252,22 @@ typedef struct AOTMemoryInstance { MemBound mem_bound_check_16bytes; } AOTMemoryInstance; +typedef struct AOTTableInstance { + uint32 cur_size; + /* + * only grow in the range of [:max_size) + * if the table is growable, max_size equals to its declared maximum size + * otherwise, max_size equals to its declared minimum size + */ + uint32 max_size; + /* + * +------------------------------+ <--- data + * | ref.func[] or ref.extern[] + * +------------------------------+ + */ + uint32 data[1]; +} AOTTableInstance; + typedef struct AOTModuleInstance { uint32 module_type; @@ -260,9 +277,17 @@ typedef struct AOTModuleInstance { /* global and table info */ uint32 global_data_size; - uint32 table_size; + /* + * the count of AOTTableInstance. + * it includes imported tables and local tables. + * + * TODO: for now we treate imported table like a local table + */ + uint32 table_count; + /* points to global_data */ AOTPointer global_data; - AOTPointer table_data; + /* points to AOTTableInstance[] */ + AOTPointer tables; /* funciton pointer array */ AOTPointer func_ptrs; @@ -288,20 +313,26 @@ typedef struct AOTModuleInstance { AOTPointer aot_module; /* WASI context */ AOTPointer wasi_ctx; + /* function performance profiling info list */ + AOTPointer func_perf_profilings; /* others */ uint32 temp_ret; uint32 llvm_stack; uint32 default_wasm_stack_size; - uint32 __padding; - - /* function performance profiling info list */ - AOTPointer func_perf_profilings; - /* reserved */ - uint32 reserved[8]; + uint32 reserved[11]; + /* + * +------------------------------+ <-- memories.ptr + * | #0 AOTMemoryInstance + * +------------------------------+ <-- global_data.ptr + * | global data + * +------------------------------+ <-- tables.ptr + * | AOTTableInstance[table_count] + * +------------------------------+ + */ union { uint64 _make_it_8_byte_aligned_; AOTMemoryInstance memory_instances[1]; @@ -561,7 +592,7 @@ aot_invoke_native(WASMExecEnv *exec_env, uint32 func_idx, bool aot_call_indirect(WASMExecEnv *exec_env, - uint32 table_elem_idx, + uint32 tbl_idx, uint32 table_elem_idx, uint32 argc, uint32 *argv); uint32 @@ -608,6 +639,32 @@ void aot_get_module_inst_mem_consumption(const AOTModuleInstance *module_inst, WASMModuleInstMemConsumption *mem_conspn); +#if WASM_ENABLE_REF_TYPES != 0 +void +aot_drop_table_seg(AOTModuleInstance *module_inst, uint32 tbl_seg_idx); + +void +aot_table_init(AOTModuleInstance *module_inst, + uint32 tbl_idx, uint32 tbl_seg_idx, + uint32 length, uint32 src_offset, uint32 dst_offset); + +void +aot_table_copy(AOTModuleInstance *module_inst, + uint32 src_tbl_idx, uint32 dst_tbl_idx, + uint32 length, uint32 src_offset, uint32 dst_offset); + +void +aot_table_fill(AOTModuleInstance *module_inst, uint32 tbl_idx, + uint32 length, uint32 val, uint32 data_offset); + +uint32 +aot_table_grow(AOTModuleInstance *module_inst, uint32 tbl_idx, + uint32 inc_entries, uint32 init_val); +#endif + +AOTTableInstance * +aot_next_tbl_inst(const AOTTableInstance *tbl_inst); + bool aot_alloc_frame(WASMExecEnv *exec_env, uint32 func_index); diff --git a/core/iwasm/common/wasm_exec_env.h b/core/iwasm/common/wasm_exec_env.h index df6131209..f101504dd 100644 --- a/core/iwasm/common/wasm_exec_env.h +++ b/core/iwasm/common/wasm_exec_env.h @@ -116,6 +116,10 @@ typedef struct WASMExecEnv { WASMJmpBuf *jmpbuf_stack_top; #endif +#if WASM_ENABLE_REF_TYPES != 0 + uint16 nested_calling_depth; +#endif + #if WASM_ENABLE_MEMORY_PROFILING != 0 uint32 max_wasm_stack_used; #endif diff --git a/core/iwasm/common/wasm_native.c b/core/iwasm/common/wasm_native.c index fb64c6495..1522b95e7 100644 --- a/core/iwasm/common/wasm_native.c +++ b/core/iwasm/common/wasm_native.c @@ -74,7 +74,13 @@ check_symbol_signature(const WASMType *type, const char *signature) for (i = 0; i < type->param_count; i++) { sig = *p++; - if (sig == sig_map[type->types[i] - VALUE_TYPE_F64]) + if ((type->types[i] >= VALUE_TYPE_F64 + && type->types[i] <= VALUE_TYPE_I32 + && sig == sig_map[type->types[i] - VALUE_TYPE_F64]) +#if WASM_ENABLE_REF_TYPES != 0 + || (sig == 'i' && type->types[i] == VALUE_TYPE_EXTERNREF) +#endif + ) /* normal parameter */ continue; diff --git a/core/iwasm/common/wasm_runtime_common.c b/core/iwasm/common/wasm_runtime_common.c index e2721301c..82f8a0f8c 100644 --- a/core/iwasm/common/wasm_runtime_common.c +++ b/core/iwasm/common/wasm_runtime_common.c @@ -49,6 +49,16 @@ static void wasm_runtime_destroy_registered_module_list(); #endif /* WASM_ENABLE_MULTI_MODULE */ +#if WASM_ENABLE_REF_TYPES != 0 +/* Initialize externref hashmap */ +static bool +wasm_externref_map_init(); + +/* Destroy externref hashmap */ +static void +wasm_externref_map_destroy(); +#endif /* WASM_ENABLE_REF_TYPES */ + static void set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) { @@ -119,10 +129,20 @@ wasm_runtime_env_init() #endif #endif +#if WASM_ENABLE_REF_TYPES != 0 + if (!wasm_externref_map_init()) { + goto fail7; + } +#endif + return true; +#if WASM_ENABLE_REF_TYPES != 0 +fail7: +#endif #if WASM_ENABLE_AOT != 0 #ifdef OS_ENABLE_HW_BOUND_CHECK + aot_signal_destroy(); fail6: #endif #endif @@ -175,6 +195,10 @@ wasm_runtime_init() void wasm_runtime_destroy() { +#if WASM_ENABLE_REF_TYPES != 0 + wasm_externref_map_destroy(); +#endif + #if WASM_ENABLE_AOT != 0 #ifdef OS_ENABLE_HW_BOUND_CHECK aot_signal_destroy(); @@ -997,10 +1021,35 @@ wasm_runtime_get_user_data(WASMExecEnv *exec_env) return exec_env->user_data; } +WASMType * +wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, + uint32 module_type) +{ + WASMType *type = NULL; + +#if WASM_ENABLE_INTERP != 0 + if (module_type == Wasm_Module_Bytecode) { + WASMFunctionInstance *wasm_func = (WASMFunctionInstance *)function; + type = wasm_func->is_import_func + ? wasm_func->u.func_import->func_type + : wasm_func->u.func->func_type; + } +#endif +#if WASM_ENABLE_AOT != 0 + if (module_type == Wasm_Module_AoT) { + AOTFunctionInstance *aot_func = (AOTFunctionInstance *)function; + type = aot_func->is_import_func + ? aot_func->u.func_import->func_type + : aot_func->u.func.func_type; + } +#endif + + return type; +} + WASMFunctionInstanceCommon * wasm_runtime_lookup_function(WASMModuleInstanceCommon * const module_inst, - const char *name, - const char *signature) + const char *name, const char *signature) { #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) @@ -1017,11 +1066,57 @@ wasm_runtime_lookup_function(WASMModuleInstanceCommon * const module_inst, return NULL; } +#if WASM_ENABLE_REF_TYPES != 0 +static void +wasm_runtime_reclaim_externref(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + uint32 *argv) +{ + uint32 i = 0, cell_num = 0; + WASMType *func_type = wasm_runtime_get_function_type( + function, exec_env->module_inst->module_type); + bh_assert(func_type); + + while (i < func_type->result_count) { + uint8 result_type = func_type->types[func_type->param_count + i]; + if (result_type == VALUE_TYPE_EXTERNREF && argv[i] != NULL_REF) { + /* Retain the externref returned to runtime embedder */ + (void)wasm_externref_retain(argv[i]); + } + + cell_num += wasm_value_type_cell_num(result_type); + i++; + } + + wasm_externref_reclaim(exec_env->module_inst); +} + +void +wasm_runtime_prepare_call_function(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function) +{ + exec_env->nested_calling_depth++; +} + +void +wasm_runtime_finalize_call_function(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + bool ret, uint32 *argv) +{ + exec_env->nested_calling_depth--; + if (!exec_env->nested_calling_depth && ret) { + wasm_runtime_reclaim_externref(exec_env, function, argv); + } +} +#endif + bool wasm_runtime_call_wasm(WASMExecEnv *exec_env, WASMFunctionInstanceCommon *function, uint32 argc, uint32 argv[]) { + bool ret = false; + if (!wasm_runtime_exec_env_check(exec_env)) { LOG_ERROR("Invalid exec env stack info."); return false; @@ -1030,19 +1125,28 @@ wasm_runtime_call_wasm(WASMExecEnv *exec_env, /* set thread handle and stack boundary */ wasm_exec_env_set_thread_info(exec_env); +#if WASM_ENABLE_REF_TYPES != 0 + wasm_runtime_prepare_call_function(exec_env, function); +#endif + #if WASM_ENABLE_INTERP != 0 if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) - return wasm_call_function(exec_env, + ret = wasm_call_function(exec_env, (WASMFunctionInstance*)function, argc, argv); #endif #if WASM_ENABLE_AOT != 0 if (exec_env->module_inst->module_type == Wasm_Module_AoT) - return aot_call_function(exec_env, + ret = aot_call_function(exec_env, (AOTFunctionInstance*)function, argc, argv); #endif - return false; + +#if WASM_ENABLE_REF_TYPES != 0 + wasm_runtime_finalize_call_function(exec_env, function, ret, argv); +#endif + + return ret; } static uint32 @@ -1142,32 +1246,21 @@ wasm_runtime_call_wasm_a(WASMExecEnv *exec_env, uint32 num_results, wasm_val_t results[], uint32 num_args, wasm_val_t args[]) { - uint32 argc, *argv, ret_num, cell_num, total_size; + uint32 argc, *argv, ret_num, cell_num, total_size, module_type; + WASMType *type; bool ret = false; - WASMType *type = NULL; -#if WASM_ENABLE_INTERP != 0 - if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) { - WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)function; - type = wasm_func->u.func->func_type; - argc = wasm_func->param_cell_num; - cell_num = argc > wasm_func->ret_cell_num ? - argc : wasm_func->ret_cell_num; - } -#endif -#if WASM_ENABLE_AOT != 0 - if (exec_env->module_inst->module_type == Wasm_Module_AoT) { - type = ((AOTFunctionInstance*)function)->u.func.func_type; - argc = type->param_cell_num; - cell_num = argc > type->ret_cell_num ? - argc : type->ret_cell_num; - } -#endif + module_type = exec_env->module_inst->module_type; + type = wasm_runtime_get_function_type(function, module_type); + if (!type) { LOG_ERROR("Function type get failed, WAMR Interpreter and AOT must be enabled at least one."); goto fail1; } + argc = type->param_cell_num; + cell_num = (argc > type->ret_cell_num) ? argc : type->ret_cell_num; + if (num_results != type->result_count) { LOG_ERROR("The result value number does not match the function declaration."); goto fail1; @@ -1207,27 +1300,21 @@ wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, wasm_val_t *args = NULL; WASMType *type = NULL; bool ret = false; - uint32 i = 0; + uint32 i = 0, module_type; va_list vargs; -#if WASM_ENABLE_INTERP != 0 - if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) { - WASMFunctionInstance *wasm_func = (WASMFunctionInstance*)function; - type = wasm_func->u.func->func_type; - } -#endif -#if WASM_ENABLE_AOT != 0 - if (exec_env->module_inst->module_type == Wasm_Module_AoT) { - type = ((AOTFunctionInstance*)function)->u.func.func_type; - } -#endif + module_type = exec_env->module_inst->module_type; + type = wasm_runtime_get_function_type(function, module_type); + if (!type) { - LOG_ERROR("Function type get failed, WAMR Interpreter and AOT must be enabled at least one."); + LOG_ERROR("Function type get failed, WAMR Interpreter and AOT " + "must be enabled at least one."); goto fail1; } if (num_args != type->param_count) { - LOG_ERROR("The argument value number does not match the function declaration."); + LOG_ERROR("The argument value number does not match the " + "function declaration."); goto fail1; } if (!(args = runtime_malloc(sizeof(wasm_val_t) * num_args, NULL, NULL, 0))) { @@ -1260,7 +1347,8 @@ wasm_runtime_call_wasm_v(WASMExecEnv *exec_env, } } va_end(vargs); - ret = wasm_runtime_call_wasm_a(exec_env, function, num_results, results, num_args, args); + ret = wasm_runtime_call_wasm_a(exec_env, function, num_results, results, + num_args, args); wasm_runtime_free(args); fail1: @@ -1272,21 +1360,21 @@ wasm_runtime_create_exec_env_and_call_wasm(WASMModuleInstanceCommon *module_inst WASMFunctionInstanceCommon *function, uint32 argc, uint32 argv[]) { + bool ret = false; + #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) - return wasm_create_exec_env_and_call_function( - (WASMModuleInstance*)module_inst, - (WASMFunctionInstance*)function, - argc, argv); + ret = wasm_create_exec_env_and_call_function( + (WASMModuleInstance *)module_inst, (WASMFunctionInstance *)function, + argc, argv); #endif #if WASM_ENABLE_AOT != 0 if (module_inst->module_type == Wasm_Module_AoT) - return aot_create_exec_env_and_call_function( - (AOTModuleInstance*)module_inst, - (AOTFunctionInstance*)function, - argc, argv); + ret = aot_create_exec_env_and_call_function( + (AOTModuleInstance *)module_inst, (AOTFunctionInstance *)function, + argc, argv); #endif - return false; + return ret; } void @@ -2192,8 +2280,8 @@ wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, uint32 argv_buf_offset = 0; int32 i; char *argv_buf, *p, *p_end; - uint32 *argv_offsets; - bool ret; + uint32 *argv_offsets, module_type; + bool ret, is_import_func = true; #if WASM_ENABLE_LIBC_WASI != 0 if (wasm_runtime_is_wasi_mode(module_inst)) { @@ -2219,19 +2307,24 @@ wasm_application_execute_main(WASMModuleInstanceCommon *module_inst, #if WASM_ENABLE_INTERP != 0 if (module_inst->module_type == Wasm_Module_Bytecode) { - if (((WASMFunctionInstance*)func)->is_import_func) { - wasm_runtime_set_exception(module_inst, - "lookup main function failed"); - return false; - } - func_type = ((WASMFunctionInstance*)func)->u.func->func_type; + is_import_func = ((WASMFunctionInstance*)func)->is_import_func; } #endif #if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) - func_type = ((AOTFunctionInstance*)func)->u.func.func_type; + if (module_inst->module_type == Wasm_Module_AoT) { + is_import_func = ((AOTFunctionInstance*)func)->is_import_func; + } #endif + if (is_import_func) { + wasm_runtime_set_exception(module_inst, + "lookup main function failed"); + return false; + } + + module_type = module_inst->module_type; + func_type = wasm_runtime_get_function_type(func, module_type); + if (!func_type) { LOG_ERROR("invalid module instance type"); return false; @@ -2462,7 +2555,7 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, WASMFunctionInstanceCommon *func; WASMType *type = NULL; uint32 argc1, *argv1 = NULL, cell_num = 0, j, k = 0; - int32 i, p; + int32 i, p, module_type; uint64 total_size; const char *exception; char buf[128]; @@ -2489,22 +2582,12 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, wasm_runtime_set_exception(module_inst, buf); goto fail; } - type = wasm_func->is_import_func ? wasm_func->u.func_import->func_type - : wasm_func->u.func->func_type; - argc1 = wasm_func->param_cell_num; - cell_num = argc1 > wasm_func->ret_cell_num ? - argc1 : wasm_func->ret_cell_num; - } -#endif -#if WASM_ENABLE_AOT != 0 - if (module_inst->module_type == Wasm_Module_AoT) { - type = ((AOTFunctionInstance*)func)->u.func.func_type; - argc1 = type->param_cell_num; - cell_num = argc1 > type->ret_cell_num ? - argc1 : type->ret_cell_num; } #endif + module_type = module_inst->module_type; + type = wasm_runtime_get_function_type(func, module_type); + if (!type) { LOG_ERROR("invalid module instance type"); return false; @@ -2516,6 +2599,9 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, goto fail; } + argc1 = type->param_cell_num; + cell_num = (argc1 > type->ret_cell_num) ? argc1 : type->ret_cell_num; + total_size = sizeof(uint32) * (uint64)(cell_num > 2 ? cell_num : 2); if ((!(argv1 = runtime_malloc((uint32)total_size, module_inst, NULL, 0)))) { @@ -2619,6 +2705,38 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, break; } #endif /* WASM_ENABLE_SIMD != 0 */ +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + { + if (strncasecmp(argv[i], "null", 4) == 0) { + argv1[p++] = NULL_REF; + } + else { + argv1[p++] = (uint32)strtoul(argv[i], &endptr, 0); + } + break; + } + case VALUE_TYPE_EXTERNREF: + { + if (strncasecmp(argv[i], "null", 4) == 0) { + argv1[p++] = NULL_REF; + } + else { + uint64 val = strtoull(argv[i], &endptr, 0); + void *extern_obj = (void *)(uintptr_t)val; + uint32 externref_idx; + + if (!wasm_externref_obj2ref(module_inst, extern_obj, + &externref_idx)) { + wasm_runtime_set_exception( + module_inst, "map extern object to ref failed"); + goto fail; + } + argv1[p++] = externref_idx; + } + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ default: bh_assert(0); break; @@ -2666,9 +2784,11 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, break; } case VALUE_TYPE_F32: + { os_printf("%.7g:f32", *(float32*)(argv1 + k)); k++; break; + } case VALUE_TYPE_F64: { union { float64 val; uint32 parts[2]; } u; @@ -2678,6 +2798,31 @@ wasm_application_execute_func(WASMModuleInstanceCommon *module_inst, os_printf("%.7g:f64", u.val); break; } +#if WASM_ENABLE_REF_TYPES + case VALUE_TYPE_FUNCREF: + { + if (argv1[k] != NULL_REF) + os_printf("%u:ref.func", argv1[k]); + else + os_printf("func:ref.null"); + k++; + break; + } + case VALUE_TYPE_EXTERNREF: + { + if (argv1[k] != NULL_REF) { + void *extern_obj = NULL; + bool ret = wasm_externref_ref2obj(argv1[k], &extern_obj); + bh_assert(ret); + (void)ret; + os_printf("%p:ref.extern", extern_obj); + } + else + os_printf("extern:ref.null"); + k++; + break; + } +#endif #if WASM_ENABLE_SIMD != 0 case VALUE_TYPE_V128: { @@ -2802,6 +2947,12 @@ wasm_runtime_invoke_native_raw(WASMExecEnv *exec_env, void *func_ptr, case VALUE_TYPE_F32: *(float32*)argv_dst = *(float32*)argv_src++; break; +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + *(uint32*)argv_dst = *argv_src++; + break; +#endif default: bh_assert(0); break; @@ -2815,6 +2966,10 @@ wasm_runtime_invoke_native_raw(WASMExecEnv *exec_env, void *func_ptr, if (func_type->result_count > 0) { switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif argv_ret[0] = *(uint32*)argv1; break; case VALUE_TYPE_F32: @@ -2899,6 +3054,10 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, for (i = 0; i < func_type->param_count; i++) { switch (func_type->types[i]) { case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif if (n_ints < MAX_REG_INTS) n_ints++; else @@ -3067,6 +3226,17 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, stacks[n_stacks++] = arg_i32; break; } +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + { + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = *argv_src++; + else + stacks[n_stacks++] = *argv_src++; + break; + } +#endif case VALUE_TYPE_I64: { if (n_ints < MAX_REG_INTS - 1) { @@ -3204,6 +3374,10 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, else { switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); break; case VALUE_TYPE_I64: @@ -3341,6 +3515,10 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, argv1[j++] = *argv++; break; case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif argv1[j++] = *argv++; break; default: @@ -3360,6 +3538,10 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, else { switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, argc1); break; case VALUE_TYPE_I64: @@ -3582,6 +3764,15 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, } argv_src += 2; break; +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + if (n_ints < MAX_REG_INTS) + ints[n_ints++] = *argv_src++; + else + stacks[n_stacks++] = *argv_src++; + break; +#endif #if WASM_ENABLE_SIMD != 0 case VALUE_TYPE_V128: if (n_fps < MAX_REG_FLOATS) { @@ -3617,6 +3808,10 @@ wasm_runtime_invoke_native(WASMExecEnv *exec_env, void *func_ptr, /* Invoke the native function and get the first result value */ switch (func_type->types[func_type->param_count]) { case VALUE_TYPE_I32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif argv_ret[0] = (uint32)invokeNative_Int32(func_ptr, argv1, n_stacks); break; case VALUE_TYPE_I64: @@ -3670,11 +3865,11 @@ wasm_runtime_call_indirect(WASMExecEnv *exec_env, #if WASM_ENABLE_INTERP != 0 if (exec_env->module_inst->module_type == Wasm_Module_Bytecode) - return wasm_call_indirect(exec_env, element_indices, argc, argv); + return wasm_call_indirect(exec_env, 0, element_indices, argc, argv); #endif #if WASM_ENABLE_AOT != 0 if (exec_env->module_inst->module_type == Wasm_Module_AoT) - return aot_call_indirect(exec_env, element_indices, argc, argv); + return aot_call_indirect(exec_env, 0, element_indices, argc, argv); #endif return false; } @@ -3795,8 +3990,318 @@ wasm_runtime_join_thread(wasm_thread_t tid, void **retval) return os_thread_join((korp_tid)tid, retval); } +#endif /* end of WASM_ENABLE_THREAD_MGR */ + +#if WASM_ENABLE_REF_TYPES != 0 + +static korp_mutex externref_lock; +static uint32 externref_global_id = 1; +static HashMap *externref_map; + +typedef struct ExternRefMapNode { + /* The extern object from runtime embedder */ + void *extern_obj; + /* The module instance it belongs to */ + WASMModuleInstanceCommon *module_inst; + /* Whether it is retained */ + bool retained; + /* Whether it is marked by runtime */ + bool marked; +} ExternRefMapNode; + +static uint32 +wasm_externref_hash(const void *key) +{ + uint32 externref_idx = (uint32)(uintptr_t)key; + return externref_idx; +} + +static bool +wasm_externref_equal(void *key1, void *key2) +{ + uint32 externref_idx1 = (uint32)(uintptr_t)key1; + uint32 externref_idx2 = (uint32)(uintptr_t)key2; + return externref_idx1 == externref_idx2 ? true : false; +} + +static bool +wasm_externref_map_init() +{ + if (os_mutex_init(&externref_lock) != 0) + return false; + + if (!(externref_map = bh_hash_map_create(32, false, + wasm_externref_hash, + wasm_externref_equal, + NULL, + wasm_runtime_free))) { + os_mutex_destroy(&externref_lock); + return false; + } + + externref_global_id = 1; + return true; +} + +static void +wasm_externref_map_destroy() +{ + bh_hash_map_destroy(externref_map); + os_mutex_destroy(&externref_lock); +} + +typedef struct LookupExtObj_UserData { + ExternRefMapNode node; + bool found; + uint32 externref_idx; +} LookupExtObj_UserData; + +static void +lookup_extobj_callback(void *key, void *value, void *user_data) +{ + uint32 externref_idx = (uint32)(uintptr_t)key; + ExternRefMapNode *node = (ExternRefMapNode *)value; + LookupExtObj_UserData *user_data_lookup = (LookupExtObj_UserData *) + user_data; + + if (node->extern_obj == user_data_lookup->node.extern_obj + && node->module_inst == user_data_lookup->node.module_inst) { + user_data_lookup->found = true; + user_data_lookup->externref_idx = externref_idx; + } +} + +bool +wasm_externref_obj2ref(WASMModuleInstanceCommon *module_inst, + void *extern_obj, uint32 *p_externref_idx) +{ + LookupExtObj_UserData lookup_user_data; + ExternRefMapNode *node; + uint32 externref_idx; + + lookup_user_data.node.extern_obj = extern_obj; + lookup_user_data.node.module_inst = module_inst; + lookup_user_data.found = false; + + os_mutex_lock(&externref_lock); + + /* Lookup hashmap firstly */ + bh_hash_map_traverse(externref_map, lookup_extobj_callback, + (void*)&lookup_user_data); + if (lookup_user_data.found) { + *p_externref_idx = lookup_user_data.externref_idx; + os_mutex_unlock(&externref_lock); + return true; + } + + /* Not found in hashmap */ + if (externref_global_id == NULL_REF + || externref_global_id == 0) { + goto fail1; + } + + if (!(node = wasm_runtime_malloc(sizeof(ExternRefMapNode)))) { + goto fail1; + } + + memset(node, 0, sizeof(ExternRefMapNode)); + node->extern_obj = extern_obj; + node->module_inst = module_inst; + + externref_idx = externref_global_id; + + if (!bh_hash_map_insert(externref_map, + (void*)(uintptr_t)externref_idx, + (void*)node)) { + goto fail2; + } + + externref_global_id++; + *p_externref_idx = externref_idx; + os_mutex_unlock(&externref_lock); + return true; +fail2: + wasm_runtime_free(node); +fail1: + os_mutex_unlock(&externref_lock); + return false; +} + +bool +wasm_externref_ref2obj(uint32 externref_idx, void **p_extern_obj) +{ + ExternRefMapNode *node; + + if (externref_idx == NULL_REF) { + return false; + } + + os_mutex_lock(&externref_lock); + node = bh_hash_map_find(externref_map, + (void*)(uintptr_t)externref_idx); + os_mutex_unlock(&externref_lock); + + if (!node) + return false; + + *p_extern_obj = node->extern_obj; + return true; +} + +static void +reclaim_extobj_callback(void *key, void *value, void *user_data) +{ + ExternRefMapNode *node = (ExternRefMapNode *)value; + WASMModuleInstanceCommon *module_inst = (WASMModuleInstanceCommon *) + user_data; + + if (node->module_inst == module_inst) { + if (!node->marked && !node->retained) { + bh_hash_map_remove(externref_map, key, NULL, NULL); + wasm_runtime_free(value); + } + else { + node->marked = false; + } + } +} + +static void +mark_externref(uint32 externref_idx) +{ + ExternRefMapNode *node; + + if (externref_idx != NULL_REF) { + node = bh_hash_map_find(externref_map, + (void*)(uintptr_t)externref_idx); + if (node) { + node->marked = true; + } + } +} + +#if WASM_ENABLE_INTERP != 0 +static void +interp_mark_all_externrefs(WASMModuleInstance *module_inst) +{ + uint32 i, j, externref_idx, *table_data; + uint8 *global_data = module_inst->global_data; + WASMGlobalInstance *global; + WASMTableInstance *table; + + global = module_inst->globals; + for (i = 0; i < module_inst->global_count; i++, global++) { + if (global->type == VALUE_TYPE_EXTERNREF) { + externref_idx = *(uint32*)(global_data + global->data_offset); + mark_externref(externref_idx); + } + } + + for (i = 0; i < module_inst->table_count; i++) { + table = wasm_get_table_inst(module_inst, i); + if (table->elem_type == VALUE_TYPE_EXTERNREF) { + table_data = (uint32 *)table->base_addr; + for (j = 0; j < table->cur_size; j++) { + externref_idx = table_data[j]; + mark_externref(externref_idx); + } + } + } +} #endif +#if WASM_ENABLE_AOT != 0 +static void +aot_mark_all_externrefs(AOTModuleInstance *module_inst) +{ + uint32 i = 0, j = 0; + const AOTModule *module = (AOTModule *)(module_inst->aot_module.ptr); + const AOTTable *table = module->tables; + const AOTGlobal *global = module->globals; + const AOTTableInstance *table_inst = + (AOTTableInstance *)module_inst->tables.ptr; + + for (i = 0; i < module->global_count; i++, global++) { + if (global->type == VALUE_TYPE_EXTERNREF) { + mark_externref(*(uint32 *)((uint8 *)module_inst->global_data.ptr + + global->data_offset)); + } + } + + for (i = 0; i < module->table_count; + i++, table_inst = aot_next_tbl_inst(table_inst)) { + + if ((table + i)->elem_type == VALUE_TYPE_EXTERNREF) { + while (j < table_inst->cur_size) { + mark_externref(table_inst->data[j++]); + } + } + } +} +#endif + +void +wasm_externref_reclaim(WASMModuleInstanceCommon *module_inst) +{ + os_mutex_lock(&externref_lock); +#if WASM_ENABLE_INTERP != 0 + if (module_inst->module_type == Wasm_Module_Bytecode) + interp_mark_all_externrefs((WASMModuleInstance*)module_inst); +#endif +#if WASM_ENABLE_AOT != 0 + if (module_inst->module_type == Wasm_Module_AoT) + aot_mark_all_externrefs((AOTModuleInstance*)module_inst); +#endif + + bh_hash_map_traverse(externref_map, reclaim_extobj_callback, + (void*)module_inst); + os_mutex_unlock(&externref_lock); +} + +static void +cleanup_extobj_callback(void *key, void *value, void *user_data) +{ + ExternRefMapNode *node = (ExternRefMapNode *)value; + WASMModuleInstanceCommon *module_inst = (WASMModuleInstanceCommon *) + user_data; + + if (node->module_inst == module_inst) { + bh_hash_map_remove(externref_map, key, NULL, NULL); + wasm_runtime_free(value); + } +} + +void +wasm_externref_cleanup(WASMModuleInstanceCommon *module_inst) +{ + os_mutex_lock(&externref_lock); + bh_hash_map_traverse(externref_map, cleanup_extobj_callback, + (void*)module_inst); + os_mutex_unlock(&externref_lock); +} + +bool +wasm_externref_retain(uint32 externref_idx) +{ + ExternRefMapNode *node; + + os_mutex_lock(&externref_lock); + + if (externref_idx != NULL_REF) { + node = bh_hash_map_find(externref_map, + (void*)(uintptr_t)externref_idx); + if (node) { + node->retained = true; + os_mutex_unlock(&externref_lock); + return true; + } + } + + os_mutex_unlock(&externref_lock); + return false; +} +#endif /* end of WASM_ENABLE_REF_TYPES */ + #if WASM_ENABLE_DUMP_CALL_STACK != 0 void wasm_runtime_dump_call_stack(WASMExecEnv *exec_env) @@ -3815,3 +4320,4 @@ wasm_runtime_dump_call_stack(WASMExecEnv *exec_env) #endif } #endif /* end of WASM_ENABLE_DUMP_CALL_STACK */ + diff --git a/core/iwasm/common/wasm_runtime_common.h b/core/iwasm/common/wasm_runtime_common.h index d61df2751..01a0c21be 100644 --- a/core/iwasm/common/wasm_runtime_common.h +++ b/core/iwasm/common/wasm_runtime_common.h @@ -352,7 +352,6 @@ wasm_runtime_destroy(void); WASM_RUNTIME_API_EXTERN PackageType get_package_type(const uint8 *buf, uint32 size); - /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN WASMModuleCommon * wasm_runtime_load(const uint8 *buf, uint32 size, @@ -393,6 +392,11 @@ WASM_RUNTIME_API_EXTERN WASMFunctionInstanceCommon * wasm_runtime_lookup_function(WASMModuleInstanceCommon * const module_inst, const char *name, const char *signature); +/* Internal API */ +WASMType * +wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function, + uint32 module_type); + /* See wasm_export.h for description */ WASM_RUNTIME_API_EXTERN WASMExecEnv * wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst, @@ -651,6 +655,34 @@ wasm_runtime_get_wasi_ctx(WASMModuleInstanceCommon *module_inst); #endif /* end of WASM_ENABLE_LIBC_WASI */ +#if WASM_ENABLE_REF_TYPES != 0 +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_obj2ref(WASMModuleInstanceCommon *module_inst, + void *extern_obj, uint32 *p_externref_idx); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_ref2obj(uint32 externref_idx, void **p_extern_obj); + +/* See wasm_export.h for description */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_retain(uint32 externref_idx); + +/** + * Reclaim the externref objects/indexes which are not used by + * module instance + */ +void +wasm_externref_reclaim(WASMModuleInstanceCommon *module_inst); + +/** + * Cleanup the externref objects/indexes of the module instance + */ +void +wasm_externref_cleanup(WASMModuleInstanceCommon *module_inst); +#endif /* end of WASM_ENABLE_REF_TYPES */ + /* Get module of the current exec_env */ WASMModuleCommon* wasm_exec_env_get_module(WASMExecEnv *exec_env); @@ -702,6 +734,16 @@ wasm_runtime_dump_module_inst_mem_consumption(const WASMModuleInstanceCommon void wasm_runtime_dump_exec_env_mem_consumption(const WASMExecEnv *exec_env); +#if WASM_ENABLE_REF_TYPES != 0 +void +wasm_runtime_prepare_call_function(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function); +void +wasm_runtime_finalize_call_function(WASMExecEnv *exec_env, + WASMFunctionInstanceCommon *function, + bool ret, uint32 *argv); +#endif + #ifdef __cplusplus } #endif diff --git a/core/iwasm/compilation/aot.c b/core/iwasm/compilation/aot.c index fd127c817..1bb67f3f1 100644 --- a/core/iwasm/compilation/aot.c +++ b/core/iwasm/compilation/aot.c @@ -125,8 +125,18 @@ aot_create_table_init_data_list(const WASMModule *module) data_list[i]->offset = module->table_segments[i].base_offset; data_list[i]->func_index_count = module->table_segments[i].function_count; - memcpy(data_list[i]->func_indexes, module->table_segments[i].func_indexes, - sizeof(uint32) * module->table_segments[i].function_count); + data_list[i]->mode = module->table_segments[i].mode; + data_list[i]->elem_type = module->table_segments[i].elem_type; + /* runtime control it */ + data_list[i]->is_dropped = false; + data_list[i]->table_index = module->table_segments[i].table_index; + bh_memcpy_s(&data_list[i]->offset, sizeof(AOTInitExpr), + &module->table_segments[i].base_offset, sizeof(AOTInitExpr)); + data_list[i]->func_index_count = module->table_segments[i].function_count; + bh_memcpy_s(data_list[i]->func_indexes, + sizeof(uint32) * module->table_segments[i].function_count, + module->table_segments[i].func_indexes, + sizeof(uint32) * module->table_segments[i].function_count); } return data_list; @@ -424,8 +434,6 @@ aot_create_comp_data(WASMModule *module) aot_create_mem_init_data_list(module))) goto fail; - /* TODO: create import tables */ - /* Create tables */ comp_data->table_count = module->import_table_count + module->table_count; @@ -447,6 +455,8 @@ aot_create_comp_data(WASMModule *module) module->import_tables[i].u.table.init_size; comp_data->tables[i].table_max_size = module->import_tables[i].u.table.max_size; + comp_data->tables[i].possible_grow = + module->import_tables[i].u.table.possible_grow; } else { j = i - module->import_table_count; @@ -454,6 +464,7 @@ aot_create_comp_data(WASMModule *module) comp_data->tables[i].table_flags = module->tables[i].flags; comp_data->tables[i].table_init_size = module->tables[i].init_size; comp_data->tables[i].table_max_size = module->tables[i].max_size; + comp_data->tables[i].possible_grow = module->tables[i].possible_grow; } } } diff --git a/core/iwasm/compilation/aot.h b/core/iwasm/compilation/aot.h index 32f8da2c7..015c84a7d 100644 --- a/core/iwasm/compilation/aot.h +++ b/core/iwasm/compilation/aot.h @@ -71,6 +71,7 @@ typedef struct AOTImportTable { uint32 table_flags; uint32 table_init_size; uint32 table_max_size; + bool possible_grow; } AOTImportTable; /** @@ -81,12 +82,19 @@ typedef struct AOTTable { uint32 table_flags; uint32 table_init_size; uint32 table_max_size; + bool possible_grow; } AOTTable; /** * A segment of table init data */ typedef struct AOTTableInitData { + /* 0 to 7 */ + uint32 mode; + /* funcref or externref, elemkind will be considered as funcref */ + uint32 elem_type; + bool is_dropped; + /* optional, only for active */ uint32 table_index; /* Start address of init data */ AOTInitExpr offset; @@ -245,6 +253,18 @@ aot_set_last_error_v(const char *format, ...); } while (0) #endif +static inline uint32 +aot_get_tbl_data_slots(const AOTTable *tbl) +{ + return tbl->possible_grow ? tbl->table_max_size : tbl->table_init_size; +} + +static inline uint32 +aot_get_imp_tbl_data_slots(const AOTImportTable *tbl) +{ + return tbl->possible_grow ? tbl->table_max_size : tbl->table_init_size; +} + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/core/iwasm/compilation/aot_compiler.c b/core/iwasm/compilation/aot_compiler.c index 766ce24d6..781e1f9fb 100644 --- a/core/iwasm/compilation/aot_compiler.c +++ b/core/iwasm/compilation/aot_compiler.c @@ -14,6 +14,7 @@ #include "aot_emit_control.h" #include "aot_emit_function.h" #include "aot_emit_parametric.h" +#include "aot_emit_table.h" #include "simd/simd_access_lanes.h" #include "simd/simd_bitmask_extracts.h" #include "simd/simd_bit_shifts.h" @@ -176,7 +177,9 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) || value_type == VALUE_TYPE_F32 || value_type == VALUE_TYPE_F64 || value_type == VALUE_TYPE_V128 - || value_type == VALUE_TYPE_VOID) { + || value_type == VALUE_TYPE_VOID + || value_type == VALUE_TYPE_FUNCREF + || value_type == VALUE_TYPE_EXTERNREF) { param_count = 0; param_types = NULL; if (value_type == VALUE_TYPE_VOID) { @@ -258,11 +261,27 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; case WASM_OP_CALL_INDIRECT: + { + uint32 tbl_idx; + read_leb_uint32(frame_ip, frame_ip_end, type_idx); - frame_ip++; /* skip 0x00 */ - if (!aot_compile_op_call_indirect(comp_ctx, func_ctx, type_idx)) + +#if WASM_ENABLE_REF_TYPES != 0 + if (comp_ctx->enable_ref_types) { + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + } + else +#endif + { + frame_ip++; + tbl_idx = 0; + } + + if (!aot_compile_op_call_indirect(comp_ctx, func_ctx, type_idx, + tbl_idx)) return false; break; + } #if WASM_ENABLE_TAIL_CALL != 0 case WASM_OP_RETURN_CALL: @@ -278,17 +297,33 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; case WASM_OP_RETURN_CALL_INDIRECT: + { + uint32 tbl_idx; + if (!comp_ctx->enable_tail_call) { aot_set_last_error("unsupported opcode"); return false; } + read_leb_uint32(frame_ip, frame_ip_end, type_idx); - frame_ip++; /* skip 0x00 */ - if (!aot_compile_op_call_indirect(comp_ctx, func_ctx, type_idx)) +#if WASM_ENABLE_REF_TYPES != 0 + if (comp_ctx->enable_ref_types) { + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + } + else +#endif + { + frame_ip++; + tbl_idx = 0; + } + + if (!aot_compile_op_call_indirect(comp_ctx, func_ctx, type_idx, + tbl_idx)) return false; if (!aot_compile_op_return(comp_ctx, func_ctx, &frame_ip)) return false; break; + } #endif /* end of WASM_ENABLE_TAIL_CALL */ case WASM_OP_DROP: @@ -311,6 +346,93 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) return false; break; +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_SELECT_T: + { + uint32 vec_len; + + if (!comp_ctx->enable_ref_types) { + goto unsupport_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, vec_len); + bh_assert(vec_len == 1); + vec_len = vec_len; + + type_idx = *frame_ip++; + if (!aot_compile_op_select(comp_ctx, func_ctx, + (type_idx != VALUE_TYPE_I64) + && (type_idx != VALUE_TYPE_F64))) + return false; + break; + } + case WASM_OP_TABLE_GET: + { + uint32 tbl_idx; + + if (!comp_ctx->enable_ref_types) { + goto unsupport_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_get(comp_ctx, func_ctx, tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_SET: + { + uint32 tbl_idx; + + if (!comp_ctx->enable_ref_types) { + goto unsupport_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_set(comp_ctx, func_ctx, tbl_idx)) + return false; + break; + } + case WASM_OP_REF_NULL: + { + uint32 type; + + if (!comp_ctx->enable_ref_types) { + goto unsupport_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, type); + + if (!aot_compile_op_ref_null(comp_ctx, func_ctx)) + return false; + + (void)type; + break; + } + case WASM_OP_REF_IS_NULL: + { + if (!comp_ctx->enable_ref_types) { + goto unsupport_ref_types; + } + + if (!aot_compile_op_ref_is_null(comp_ctx, func_ctx)) + return false; + break; + } + case WASM_OP_REF_FUNC: + { + uint32 func_idx; + + if (!comp_ctx->enable_ref_types) { + goto unsupport_ref_types; + } + + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + if (!aot_compile_op_ref_func(comp_ctx, func_ctx, func_idx)) + return false; + break; + } +#endif + case WASM_OP_GET_LOCAL: read_leb_uint32(frame_ip, frame_ip_end, local_idx); if (!aot_compile_op_get_local(comp_ctx, func_ctx, local_idx)) @@ -828,6 +950,15 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) read_leb_uint32(frame_ip, frame_ip_end, opcode1); opcode = (uint32)opcode1; + //TODO: --enable-bulk-memory ? + +#if WASM_ENABLE_REF_TYPES != 0 + if (WASM_OP_TABLE_INIT <= opcode && opcode <= WASM_OP_TABLE_FILL + && !comp_ctx->enable_ref_types) { + goto unsupport_ref_types; + } +#endif + switch (opcode) { case WASM_OP_I32_TRUNC_SAT_S_F32: case WASM_OP_I32_TRUNC_SAT_U_F32: @@ -886,11 +1017,74 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index) break; } #endif /* WASM_ENABLE_BULK_MEMORY */ - default: +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + { + uint32 tbl_idx, tbl_seg_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_seg_idx); + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_init(comp_ctx, func_ctx, tbl_idx, + tbl_seg_idx)) + return false; break; + } + case WASM_OP_ELEM_DROP: + { + uint32 tbl_seg_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_seg_idx); + if (!aot_compile_op_elem_drop(comp_ctx, func_ctx, tbl_seg_idx)) + return false; + break; + } + case WASM_OP_TABLE_COPY: + { + uint32 src_tbl_idx, dst_tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, dst_tbl_idx); + read_leb_uint32(frame_ip, frame_ip_end, src_tbl_idx); + if (!aot_compile_op_table_copy(comp_ctx, func_ctx, src_tbl_idx, + dst_tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_GROW: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_grow(comp_ctx, func_ctx, tbl_idx)) + return false; + break; + } + + case WASM_OP_TABLE_SIZE: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_size(comp_ctx, func_ctx, tbl_idx)) + return false; + break; + } + case WASM_OP_TABLE_FILL: + { + uint32 tbl_idx; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + if (!aot_compile_op_table_fill(comp_ctx, func_ctx, tbl_idx)) + return false; + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ + default: + aot_set_last_error("unsupported opcode"); + return false; } break; } + #if WASM_ENABLE_SHARED_MEMORY != 0 case WASM_OP_ATOMIC_PREFIX: { @@ -1030,7 +1224,8 @@ build_atomic_rmw: break; default: - break; + aot_set_last_error("unsupported opcode"); + return false; } break; } @@ -1040,9 +1235,7 @@ build_atomic_rmw: case WASM_OP_SIMD_PREFIX: { if (!comp_ctx->enable_simd) { - aot_set_last_error("SIMD instruction was found, " - "try adding --enable-simd option?"); - return false; + goto unsupport_simd; } opcode = *frame_ip++; @@ -1795,7 +1988,8 @@ build_atomic_rmw: } default: - break; + aot_set_last_error("unsupported opcode"); + return false; } break; } @@ -1803,29 +1997,43 @@ build_atomic_rmw: default: aot_set_last_error("unsupported opcode"); - break; + return false; } } /* Move func_return block to the bottom */ if (func_ctx->func_return_block) { - LLVMBasicBlockRef last_block = - LLVMGetLastBasicBlock(func_ctx->func); - if (last_block != func_ctx->func_return_block) - LLVMMoveBasicBlockAfter(func_ctx->func_return_block, - last_block); + LLVMBasicBlockRef last_block = + LLVMGetLastBasicBlock(func_ctx->func); + if (last_block != func_ctx->func_return_block) + LLVMMoveBasicBlockAfter(func_ctx->func_return_block, + last_block); } /* Move got_exception block to the bottom */ if (func_ctx->got_exception_block) { - LLVMBasicBlockRef last_block = - LLVMGetLastBasicBlock(func_ctx->func); - if (last_block != func_ctx->got_exception_block) - LLVMMoveBasicBlockAfter(func_ctx->got_exception_block, - last_block); + LLVMBasicBlockRef last_block = + LLVMGetLastBasicBlock(func_ctx->func); + if (last_block != func_ctx->got_exception_block) + LLVMMoveBasicBlockAfter(func_ctx->got_exception_block, + last_block); } return true; +#if WASM_ENABLE_SIMD != 0 +unsupport_simd: + aot_set_last_error("SIMD instruction was found, " + "try adding --enable-simd option?"); + return false; +#endif + +#if WASM_ENABLE_REF_TYPES != 0 +unsupport_ref_types: + aot_set_last_error("reference type instruction was found, " + "try adding --enable-ref-types option?"); + return false; +#endif + fail: return false; } diff --git a/core/iwasm/compilation/aot_compiler.h b/core/iwasm/compilation/aot_compiler.h index c1c05cd6c..182e180db 100644 --- a/core/iwasm/compilation/aot_compiler.h +++ b/core/iwasm/compilation/aot_compiler.h @@ -102,6 +102,34 @@ typedef enum FloatArithmetic { FLOAT_MAX } FloatArithmetic; +static inline bool +check_type_compatible(uint8 src_type, uint8 dst_type) +{ + if (src_type == dst_type) { + return true; + } + + /* ext i1 to i32 */ + if (src_type == VALUE_TYPE_I1 && dst_type == VALUE_TYPE_I32) { + return true; + } + + /* i32 <==> func.ref, i32 <==> extern.ref */ + if (src_type == VALUE_TYPE_I32 + && (dst_type == VALUE_TYPE_EXTERNREF + || dst_type == VALUE_TYPE_FUNCREF)) { + return true; + } + + if (dst_type == VALUE_TYPE_I32 + && (src_type == VALUE_TYPE_FUNCREF + || src_type == VALUE_TYPE_EXTERNREF)) { + return true; + } + + return false; +} + #define CHECK_STACK() do { \ if (!func_ctx->block_stack.block_list_end) { \ aot_set_last_error("WASM block stack underflow."); \ @@ -119,11 +147,8 @@ typedef enum FloatArithmetic { CHECK_STACK(); \ aot_value = aot_value_stack_pop \ (&func_ctx->block_stack.block_list_end->value_stack); \ - if ((value_type != VALUE_TYPE_I32 \ - && aot_value->type != value_type) \ - || (value_type == VALUE_TYPE_I32 \ - && (aot_value->type != VALUE_TYPE_I32 \ - && aot_value->type != VALUE_TYPE_I1))) { \ + if (!check_type_compatible(aot_value->type, \ + value_type)) { \ aot_set_last_error("invalid WASM stack data type."); \ wasm_runtime_free(aot_value); \ goto fail; \ @@ -131,12 +156,23 @@ typedef enum FloatArithmetic { if (aot_value->type == value_type) \ llvm_value = aot_value->value; \ else { \ - bh_assert(aot_value->type == VALUE_TYPE_I1); \ - if (!(llvm_value = LLVMBuildZExt(comp_ctx->builder, \ - aot_value->value, I32_TYPE, "i1toi32"))) { \ - aot_set_last_error("invalid WASM stack data type.");\ - wasm_runtime_free(aot_value); \ - goto fail; \ + if (aot_value->type == VALUE_TYPE_I1) { \ + if (!(llvm_value = LLVMBuildZExt(comp_ctx->builder, \ + aot_value->value, I32_TYPE, "i1toi32"))) { \ + aot_set_last_error("invalid WASM stack " \ + "data type."); \ + wasm_runtime_free(aot_value); \ + goto fail; \ + } \ + } \ + else { \ + bh_assert(aot_value->type == VALUE_TYPE_I32 \ + || aot_value->type == VALUE_TYPE_FUNCREF \ + || aot_value->type == VALUE_TYPE_EXTERNREF); \ + bh_assert(value_type == VALUE_TYPE_I32 \ + || value_type == VALUE_TYPE_FUNCREF \ + || value_type == VALUE_TYPE_EXTERNREF); \ + llvm_value = aot_value->value; \ } \ } \ wasm_runtime_free(aot_value); \ @@ -147,6 +183,8 @@ typedef enum FloatArithmetic { #define POP_F32(v) POP(v, VALUE_TYPE_F32) #define POP_F64(v) POP(v, VALUE_TYPE_F64) #define POP_V128(v) POP(v, VALUE_TYPE_V128) +#define POP_FUNCREF(v) POP(v, VALUE_TYPE_FUNCREF) +#define POP_EXTERNREF(v) POP(v, VALUE_TYPE_EXTERNREF) #define POP_COND(llvm_value) do { \ AOTValue *aot_value; \ @@ -198,6 +236,8 @@ typedef enum FloatArithmetic { #define PUSH_F64(v) PUSH(v, VALUE_TYPE_F64) #define PUSH_V128(v) PUSH(v, VALUE_TYPE_V128) #define PUSH_COND(v) PUSH(v, VALUE_TYPE_I1) +#define PUSH_FUNCREF(v) PUSH(v, VALUE_TYPE_FUNCREF) +#define PUSH_EXTERNREF(v) PUSH(v, VALUE_TYPE_EXTERNREF) #define TO_LLVM_TYPE(wasm_type) \ wasm_type_to_llvm_type(&comp_ctx->basic_types, wasm_type) @@ -217,6 +257,8 @@ typedef enum FloatArithmetic { #define INT64_PTR_TYPE comp_ctx->basic_types.int64_ptr_type #define F32_PTR_TYPE comp_ctx->basic_types.float32_ptr_type #define F64_PTR_TYPE comp_ctx->basic_types.float64_ptr_type +#define FUNC_REF_TYPE comp_ctx->basic_types.funcref_type +#define EXTERN_REF_TYPE comp_ctx->basic_types.externref_type #define I32_CONST(v) LLVMConstInt(I32_TYPE, v, true) #define I64_CONST(v) LLVMConstInt(I64_TYPE, v, true) @@ -263,6 +305,8 @@ typedef enum FloatArithmetic { #define V128_f32x4_ZERO (comp_ctx->llvm_consts.f32x4_vec_zero) #define V128_f64x2_ZERO (comp_ctx->llvm_consts.f64x2_vec_zero) +#define REF_NULL (comp_ctx->llvm_consts.i32_neg_one) + #define TO_V128_i8x16(v) LLVMBuildBitCast(comp_ctx->builder, v, \ V128_i8x16_TYPE, "i8x16_val") #define TO_V128_i16x8(v) LLVMBuildBitCast(comp_ctx->builder, v, \ @@ -283,6 +327,36 @@ typedef enum FloatArithmetic { } \ } while (0) +#define GET_AOT_FUNCTION(name, argc) do { \ + if (!(func_type = LLVMFunctionType(ret_type, param_types, \ + argc, false))) { \ + aot_set_last_error("llvm add function type failed."); \ + return false; \ + } \ + if (comp_ctx->is_jit_mode) { \ + /* JIT mode, call the function directly */ \ + if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { \ + aot_set_last_error("llvm add pointer type failed."); \ + return false; \ + } \ + if (!(value = I64_CONST((uint64)(uintptr_t)name)) \ + || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { \ + aot_set_last_error("create LLVM value failed."); \ + return false; \ + } \ + } \ + else { \ + char *func_name = #name; \ + /* AOT mode, delcare the function */ \ + if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) \ + && !(func = LLVMAddFunction(comp_ctx->module, \ + func_name, func_type))) { \ + aot_set_last_error("llvm add function failed."); \ + return false; \ + } \ + } \ + } while (0) + bool aot_compile_wasm(AOTCompContext *comp_ctx); diff --git a/core/iwasm/compilation/aot_emit_aot_file.c b/core/iwasm/compilation/aot_emit_aot_file.c index 58cc4b4a9..8102f846c 100644 --- a/core/iwasm/compilation/aot_emit_aot_file.c +++ b/core/iwasm/compilation/aot_emit_aot_file.c @@ -183,9 +183,13 @@ get_mem_info_size(AOTCompData *comp_data) static uint32 get_table_init_data_size(AOTTableInitData *table_init_data) { - /* table_index + init expr type (4 bytes) + init expr value (8 bytes) - + func index count (4 bytes) + func indexes */ - return (uint32)(sizeof(uint32) + sizeof(uint32) + /* + * mode (4 bytes), elem_type (4 bytes), do not need is_dropped field + * + * table_index(4 bytes) + init expr type (4 bytes) + init expr value (8 bytes) + * + func index count (4 bytes) + func indexes + */ + return (uint32)(sizeof(uint32) * 2 + sizeof(uint32) + sizeof(uint32) + sizeof(uint64) + sizeof(uint32) + sizeof(uint32) * table_init_data->func_index_count); } @@ -194,9 +198,24 @@ static uint32 get_table_init_data_list_size(AOTTableInitData **table_init_data_list, uint32 table_init_data_count) { + /* + * ------------------------------ + * | table_init_data_count + * ------------------------------ + * | | U32 mode + * | AOTTableInitData[N] | U32 elem_type + * | | U32 table_index + * | | U32 offset.init_expr_type + * | | U64 offset.u.i64 + * | | U32 func_index_count + * | | U32[func_index_count] + * ------------------------------ + */ AOTTableInitData **table_init_data = table_init_data_list; uint32 size = 0, i; + size = (uint32)sizeof(uint32); + for (i = 0; i < table_init_data_count; i++, table_init_data++) { size = align_uint(size, 4); size += get_table_init_data_size(*table_init_data); @@ -207,27 +226,66 @@ get_table_init_data_list_size(AOTTableInitData **table_init_data_list, static uint32 get_import_table_size(AOTCompData *comp_data) { - /* currently we only emit import_table_count = 0 */ - return sizeof(uint32); + /* + * ------------------------------ + * | import_table_count + * ------------------------------ + * | | U32 table_init_size + * | | ---------------------- + * | AOTImpotTable[N] | U32 table_init_size + * | | ---------------------- + * | | U32 possible_grow (convenient than U8) + * ------------------------------ + */ + return (uint32)(sizeof(uint32) + + comp_data->import_table_count + * (sizeof(uint32) * 3)); } static uint32 get_table_size(AOTCompData *comp_data) { - /* table_count + table_count * (elem_type + table_flags - * + init_size + max_size) */ + /* + * ------------------------------ + * | table_count + * ------------------------------ + * | | U32 elem_type + * | AOTTable[N] | U32 table_flags + * | | U32 table_init_size + * | | U32 table_max_size + * | | U32 possible_grow (convenient than U8) + * ------------------------------ + */ return (uint32)(sizeof(uint32) - + comp_data->table_count * sizeof(uint32) * 4); + + comp_data->table_count + * (sizeof(uint32) * 5)); } static uint32 get_table_info_size(AOTCompData *comp_data) { - /* import_table size + table_size - + init data count + init data list */ - return get_import_table_size(comp_data) - + get_table_size(comp_data) - + (uint32)sizeof(uint32) + /* + * ------------------------------ + * | import_table_count + * ------------------------------ + * | + * | AOTImportTable[import_table_count] + * | + * ------------------------------ + * | table_count + * ------------------------------ + * | + * | AOTTable[table_count] + * | + * ------------------------------ + * | table_init_data_count + * ------------------------------ + * | + * | AOTTableInitData*[table_init_data_count] + * | + * ------------------------------ + */ + return get_import_table_size(comp_data) + get_table_size(comp_data) + get_table_init_data_list_size(comp_data->table_init_data_list, comp_data->table_init_data_count); } @@ -1014,10 +1072,18 @@ aot_emit_table_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, *p_offset = offset = align_uint(offset, 4); - /* Emit import table count, only emit 0 currently. - TODO: emit the actual import table count and - the full import table info. */ - EMIT_U32(0); + /* Emit import table count */ + EMIT_U32(comp_data->import_table_count); + /* Emit table items */ + for (i = 0; i < comp_data->import_table_count; i++) { + /* TODO: + * EMIT_STR(comp_data->import_tables[i].module_name ); + * EMIT_STR(comp_data->import_tables[i].table_name); + */ + EMIT_U32(comp_data->import_tables[i].table_init_size); + EMIT_U32(comp_data->import_tables[i].table_max_size); + EMIT_U32(comp_data->import_tables[i].possible_grow & 0x000000FF); + } /* Emit table count */ EMIT_U32(comp_data->table_count); @@ -1027,6 +1093,7 @@ aot_emit_table_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, EMIT_U32(comp_data->tables[i].table_flags); EMIT_U32(comp_data->tables[i].table_init_size); EMIT_U32(comp_data->tables[i].table_max_size); + EMIT_U32(comp_data->tables[i].possible_grow & 0x000000FF); } /* Emit table init data count */ @@ -1034,6 +1101,8 @@ aot_emit_table_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset, /* Emit table init data items */ for (i = 0; i < comp_data->table_init_data_count; i++) { offset = align_uint(offset, 4); + EMIT_U32(init_datas[i]->mode); + EMIT_U32(init_datas[i]->elem_type); EMIT_U32(init_datas[i]->table_index); EMIT_U32(init_datas[i]->offset.init_expr_type); EMIT_U64(init_datas[i]->offset.u.i64); diff --git a/core/iwasm/compilation/aot_emit_control.c b/core/iwasm/compilation/aot_emit_control.c index ca2cc75a6..39f882d41 100644 --- a/core/iwasm/compilation/aot_emit_control.c +++ b/core/iwasm/compilation/aot_emit_control.c @@ -616,8 +616,10 @@ aot_compile_op_end(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* Handle block result values */ CREATE_RESULT_VALUE_PHIS(block); for (i = 0; i < block->result_count; i++) { + value = NULL; result_index = block->result_count - 1 - i; POP(value, block->result_types[result_index]); + bh_assert(value); ADD_TO_RESULT_PHIS(block, value, result_index); } diff --git a/core/iwasm/compilation/aot_emit_function.c b/core/iwasm/compilation/aot_emit_function.c index 32cb40158..a0fe2412f 100644 --- a/core/iwasm/compilation/aot_emit_function.c +++ b/core/iwasm/compilation/aot_emit_function.c @@ -6,38 +6,9 @@ #include "aot_emit_function.h" #include "aot_emit_exception.h" #include "aot_emit_control.h" +#include "aot_emit_table.h" #include "../aot/aot_runtime.h" -#define GET_AOT_FUNCTION(name, argc) do { \ - if (!(func_type = LLVMFunctionType(ret_type, param_types, \ - argc, false))) { \ - aot_set_last_error("llvm add function type failed."); \ - return false; \ - } \ - if (comp_ctx->is_jit_mode) { \ - /* JIT mode, call the function directly */ \ - if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { \ - aot_set_last_error("llvm add pointer type failed."); \ - return false; \ - } \ - if (!(value = I64_CONST((uint64)(uintptr_t)name)) \ - || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { \ - aot_set_last_error("create LLVM value failed."); \ - return false; \ - } \ - } \ - else { \ - char *func_name = #name; \ - /* AOT mode, delcare the function */ \ - if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) \ - && !(func = LLVMAddFunction(comp_ctx->module, \ - func_name, func_type))) { \ - aot_set_last_error("llvm add function failed."); \ - return false; \ - } \ - } \ - } while (0) - #define ADD_BASIC_BLOCK(block, name) do { \ if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context, \ func_ctx->func, \ @@ -640,8 +611,8 @@ fail: static bool call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - AOTFuncType *aot_func_type, - LLVMValueRef func_type_idx, LLVMValueRef table_elem_idx, + AOTFuncType *aot_func_type, LLVMValueRef func_type_idx, + LLVMValueRef table_idx, LLVMValueRef table_elem_idx, LLVMTypeRef *param_types, LLVMValueRef *param_values, uint32 param_count, uint32 param_cell_num, uint32 result_count, uint8 *wasm_ret_types, @@ -656,10 +627,11 @@ call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, /* prepare function type of aot_call_indirect */ func_param_types[0] = comp_ctx->exec_env_type; /* exec_env */ - func_param_types[1] = I32_TYPE; /* table_elem_idx */ - func_param_types[2] = I32_TYPE; /* argc */ - func_param_types[3] = INT32_PTR_TYPE; /* argv */ - if (!(func_type = LLVMFunctionType(INT8_TYPE, func_param_types, 4, false))) { + func_param_types[1] = I32_TYPE; /* table_idx */ + func_param_types[2] = I32_TYPE; /* table_elem_idx */ + func_param_types[3] = I32_TYPE; /* argc */ + func_param_types[4] = INT32_PTR_TYPE; /* argv */ + if (!(func_type = LLVMFunctionType(INT8_TYPE, func_param_types, 5, false))) { aot_set_last_error("llvm add function type failed."); return false; } @@ -722,18 +694,19 @@ call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, } func_param_values[0] = func_ctx->exec_env; - func_param_values[1] = table_elem_idx; - func_param_values[2] = I32_CONST(param_cell_num); - func_param_values[3] = func_ctx->argv_buf; + func_param_values[1] = table_idx; + func_param_values[2] = table_elem_idx; + func_param_values[3] = I32_CONST(param_cell_num); + func_param_values[4] = func_ctx->argv_buf; - if (!func_param_values[2]) { + if (!func_param_values[3]) { aot_set_last_error("llvm create const failed."); return false; } /* call aot_call_indirect() function */ if (!(res = LLVMBuildCall(comp_ctx->builder, func, - func_param_values, 4, "res"))) { + func_param_values, 5, "res"))) { aot_set_last_error("llvm build call failed."); return false; } @@ -771,10 +744,10 @@ call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, bool aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 type_idx) + uint32 type_idx, uint32 tbl_idx) { AOTFuncType *func_type; - LLVMValueRef elem_idx, table_elem, func_idx; + LLVMValueRef tbl_idx_value, elem_idx, table_elem, func_idx; LLVMValueRef ftype_idx_ptr, ftype_idx, ftype_idx_const; LLVMValueRef cmp_elem_idx, cmp_func_idx, cmp_ftype_idx; LLVMValueRef func, func_ptr, table_size_const; @@ -787,8 +760,9 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMBasicBlockRef check_elem_idx_succ, check_ftype_idx_succ; LLVMBasicBlockRef check_func_idx_succ, block_return, block_curr; LLVMBasicBlockRef block_call_import, block_call_non_import; + LLVMValueRef offset; uint32 total_param_count, func_param_count, func_result_count; - uint32 table_init_size = 0, ext_cell_num, param_cell_num, i, j; + uint32 ext_cell_num, param_cell_num, i, j; uint8 wasm_ret_type, *wasm_ret_types; uint64 total_size; char buf[32]; @@ -818,20 +792,31 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, POP_I32(elem_idx); - if (comp_ctx->comp_data->import_table_count > 0) { - table_init_size = comp_ctx->comp_data->import_tables[0] - .table_init_size; - } - else if (comp_ctx->comp_data->table_count > 0) { - table_init_size = comp_ctx->comp_data->tables[0] - .table_init_size; - } - else { - aot_set_last_error("table index out of range"); + /* get the cur size of the table instance */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, cur_size)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(table_size_const = + LLVMBuildGEP(comp_ctx->builder, func_ctx->aot_inst, &offset, 1, + "cur_size_i8p"))) { + HANDLE_FAILURE("LLVMBuildGEP"); + goto fail; + } + + if (!(table_size_const = + LLVMBuildBitCast(comp_ctx->builder, table_size_const, + INT32_PTR_TYPE, "cur_siuze_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(table_size_const = LLVMBuildLoad(comp_ctx->builder, table_size_const, "cur_size"))) { + HANDLE_FAILURE("LLVMBuildLoad"); goto fail; } - table_size_const = I32_CONST(table_init_size); - CHECK_LLVM_CONST(table_size_const); /* Check if (uint32)elem index >= table size */ if (!(cmp_elem_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, @@ -857,14 +842,32 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, true, cmp_elem_idx, check_elem_idx_succ))) goto fail; - /* Load function index */ - if (!(table_elem = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->table_base, - &elem_idx, 1, "table_elem"))) { + /* load data as i32* */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, data)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(table_elem = LLVMBuildGEP(comp_ctx->builder, func_ctx->aot_inst, + &offset, 1, "table_elem_i8p"))) { aot_set_last_error("llvm build add failed."); goto fail; } + if (!(table_elem = LLVMBuildBitCast(comp_ctx->builder, table_elem, + INT32_PTR_TYPE, "table_elem_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + /* Load function index */ + if (!(table_elem = LLVMBuildGEP(comp_ctx->builder, table_elem, &elem_idx, + 1, "table_elem"))) { + HANDLE_FAILURE("LLVMBuildNUWAdd"); + goto fail; + } + if (!(func_idx = LLVMBuildLoad(comp_ctx->builder, table_elem, "func_idx"))) { aot_set_last_error("llvm build load failed."); @@ -1118,8 +1121,15 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, param_cell_num = func_type->param_cell_num; wasm_ret_types = func_type->types + func_type->param_count; + tbl_idx_value = I32_CONST(tbl_idx); + if (!tbl_idx_value) { + aot_set_last_error("llvm create const failed."); + goto fail; + } + if (!call_aot_call_indirect_func(comp_ctx, func_ctx, - func_type, ftype_idx, elem_idx, + func_type, ftype_idx, + tbl_idx_value, elem_idx, param_types + 1, param_values + 1, func_param_count, param_cell_num, func_result_count, wasm_ret_types, @@ -1240,3 +1250,56 @@ fail: return ret; } +bool +aot_compile_op_ref_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + PUSH_I32(REF_NULL); + + return true; +fail: + return false; +} + +bool +aot_compile_op_ref_is_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) +{ + LLVMValueRef lhs, res; + + POP_I32(lhs); + + if (!(res = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, lhs, REF_NULL, + "cmp_w_null"))) { + HANDLE_FAILURE("LLVMBuildICmp"); + goto fail; + } + + if (!(res = LLVMBuildZExt(comp_ctx->builder, res, I32_TYPE, "r_i"))) { + HANDLE_FAILURE("LLVMBuildZExt"); + goto fail; + } + + PUSH_I32(res); + + return true; +fail: + return false; +} + +bool +aot_compile_op_ref_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 func_idx) +{ + LLVMValueRef ref_idx; + + if (!(ref_idx = I32_CONST(func_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + PUSH_I32(ref_idx); + + return true; +fail: + return false; +} diff --git a/core/iwasm/compilation/aot_emit_function.h b/core/iwasm/compilation/aot_emit_function.h index 70aa4c5d1..23a384da8 100644 --- a/core/iwasm/compilation/aot_emit_function.h +++ b/core/iwasm/compilation/aot_emit_function.h @@ -17,9 +17,21 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, uint32 func_idx, bool tail_call); bool -aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, - uint32 type_idx); +aot_compile_op_call_indirect(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 type_idx, + uint32 tbl_idx); +bool +aot_compile_op_ref_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_ref_is_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx); + +bool +aot_compile_op_ref_func(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 func_idx); #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/core/iwasm/compilation/aot_emit_memory.c b/core/iwasm/compilation/aot_emit_memory.c index 883f4b9df..0a506143c 100644 --- a/core/iwasm/compilation/aot_emit_memory.c +++ b/core/iwasm/compilation/aot_emit_memory.c @@ -657,7 +657,7 @@ aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) POP_I32(delta); - /* Function type of wasm_runtime_enlarge_memory() */ + /* Function type of aot_enlarge_memory() */ param_types[0] = INT8_PTR_TYPE; param_types[1] = I32_TYPE; ret_type = INT8_TYPE; @@ -673,14 +673,14 @@ aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) aot_set_last_error("llvm add pointer type failed."); return false; } - if (!(value = I64_CONST((uint64)(uintptr_t)wasm_runtime_enlarge_memory)) + if (!(value = I64_CONST((uint64)(uintptr_t)aot_enlarge_memory)) || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { aot_set_last_error("create LLVM value failed."); return false; } } else { - char *func_name = "wasm_runtime_enlarge_memory"; + char *func_name = "aot_enlarge_memory"; /* AOT mode, delcare the function */ if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) && !(func = LLVMAddFunction(comp_ctx->module, @@ -690,7 +690,7 @@ aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) } } - /* Call function wasm_runtime_enlarge_memory() */ + /* Call function aot_enlarge_memory() */ param_values[0] = func_ctx->aot_inst; param_values[1] = delta; if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func, @@ -715,35 +715,6 @@ fail: return false; } -#define GET_AOT_FUNCTION(name, argc) do { \ - if (!(func_type = LLVMFunctionType(ret_type, param_types, \ - argc, false))) { \ - aot_set_last_error("llvm add function type failed."); \ - return false; \ - } \ - if (comp_ctx->is_jit_mode) { \ - /* JIT mode, call the function directly */ \ - if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { \ - aot_set_last_error("llvm add pointer type failed."); \ - return false; \ - } \ - if (!(value = I64_CONST((uint64)(uintptr_t)name)) \ - || !(func = LLVMConstIntToPtr(value, func_ptr_type))) { \ - aot_set_last_error("create LLVM value failed."); \ - return false; \ - } \ - } \ - else { \ - char *func_name = #name; \ - /* AOT mode, delcare the function */ \ - if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) \ - && !(func = LLVMAddFunction(comp_ctx->module, \ - func_name, func_type))) { \ - aot_set_last_error("llvm add function failed."); \ - return false; \ - } \ - } \ - } while (0) #if WASM_ENABLE_BULK_MEMORY != 0 diff --git a/core/iwasm/compilation/aot_emit_parametric.c b/core/iwasm/compilation/aot_emit_parametric.c index 498e9e8a6..fba89e273 100644 --- a/core/iwasm/compilation/aot_emit_parametric.c +++ b/core/iwasm/compilation/aot_emit_parametric.c @@ -45,11 +45,18 @@ pop_value_from_wasm_stack(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, wasm_runtime_free(aot_value); - if ((is_32 - && (type != VALUE_TYPE_I32 && type != VALUE_TYPE_F32 - && type != VALUE_TYPE_V128)) - || (!is_32 - && (type != VALUE_TYPE_I64 && type != VALUE_TYPE_F64))) { + /* is_32: i32, f32, ref.func, ref.extern, v128 */ + if (is_32 + && !(type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32 + || type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF + || type == VALUE_TYPE_V128)) { + aot_set_last_error("invalid WASM stack data type."); + return false; + } + + /* !is_32: i64, f64 */ + if (!is_32 + && !(type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64)) { aot_set_last_error("invalid WASM stack data type."); return false; } diff --git a/core/iwasm/compilation/aot_emit_table.c b/core/iwasm/compilation/aot_emit_table.c new file mode 100644 index 000000000..e84cad5d4 --- /dev/null +++ b/core/iwasm/compilation/aot_emit_table.c @@ -0,0 +1,487 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "aot_emit_table.h" +#include "aot_emit_exception.h" +#include "../aot/aot_runtime.h" + + +uint64 +get_tbl_inst_offset(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + uint64 offset = 0, i = 0; + AOTImportTable *imp_tbls = comp_ctx->comp_data->import_tables; + AOTTable *tbls = comp_ctx->comp_data->tables; + + /* from the head of AOTModuleInstance */ + offset = + offsetof(AOTModuleInstance, global_table_data.bytes) + + (uint64)comp_ctx->comp_data->memory_count * sizeof(AOTMemoryInstance) + + comp_ctx->comp_data->global_data_size; + + while (i < tbl_idx && i < comp_ctx->comp_data->import_table_count) { + offset += offsetof(AOTTableInstance, data); + offset += sizeof(uint32) * aot_get_imp_tbl_data_slots(imp_tbls + i); + ++i; + } + + if (i == tbl_idx) { + return offset; + } + + tbl_idx -= comp_ctx->comp_data->import_table_count; + i -= comp_ctx->comp_data->import_table_count; + while (i < tbl_idx && i < comp_ctx->comp_data->table_count) { + offset += offsetof(AOTTableInstance, data); + offset += sizeof(uint32) * aot_get_tbl_data_slots(tbls + i); + ++i; + } + + return offset; +} + +#if WASM_ENABLE_REF_TYPES != 0 + +LLVMValueRef +aot_compile_get_tbl_inst(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMValueRef offset, tbl_inst; + + if (!(offset = + I64_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(tbl_inst = LLVMBuildGEP(comp_ctx->builder, func_ctx->aot_inst, + &offset, 1, "tbl_inst"))) { + HANDLE_FAILURE("LLVMBuildGEP"); + goto fail; + } + + return tbl_inst; +fail: + return NULL; +} + +bool +aot_compile_op_elem_drop(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_seg_idx) +{ + LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type; + LLVMValueRef param_values[2], ret_value, func, value; + + /* void aot_drop_table_seg(AOTModuleInstance *, uint32 ) */ + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + ret_type = VOID_TYPE; + + GET_AOT_FUNCTION(aot_drop_table_seg, 2); + + param_values[0] = func_ctx->aot_inst; + if (!(param_values[1] = I32_CONST(tbl_seg_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* "" means return void */ + if (!(ret_value = + LLVMBuildCall(comp_ctx->builder, func, param_values, 2, ""))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + return true; +fail: + return false; +} + +static bool +aot_check_table_access(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx, + LLVMValueRef elem_idx) +{ + LLVMValueRef offset, tbl_sz, cmp_elem_idx; + LLVMBasicBlockRef check_elem_idx_succ; + + /* get the cur size of the table instance */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, cur_size)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildGEP(comp_ctx->builder, func_ctx->aot_inst, &offset, + 1, "cur_size_i8p"))) { + HANDLE_FAILURE("LLVMBuildGEP"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildBitCast(comp_ctx->builder, tbl_sz, INT32_PTR_TYPE, + "cur_siuze_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildLoad(comp_ctx->builder, tbl_sz, "cur_size"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + + /* Check if (uint32)elem index >= table size */ + if (!(cmp_elem_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, elem_idx, + tbl_sz, "cmp_elem_idx"))) { + aot_set_last_error("llvm build icmp failed."); + goto fail; + } + + /* Throw exception if elem index >= table size */ + if (!(check_elem_idx_succ = LLVMAppendBasicBlockInContext( + comp_ctx->context, func_ctx->func, "check_elem_idx_succ"))) { + aot_set_last_error("llvm add basic block failed."); + goto fail; + } + + LLVMMoveBasicBlockAfter(check_elem_idx_succ, + LLVMGetInsertBlock(comp_ctx->builder)); + + if (!(aot_emit_exception(comp_ctx, func_ctx, + EXCE_OUT_OF_BOUNDS_TABLE_ACCESS, true, + cmp_elem_idx, check_elem_idx_succ))) + goto fail; + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_get(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMValueRef elem_idx, offset, table_elem, func_idx; + + POP_I32(elem_idx); + + if (!aot_check_table_access(comp_ctx, func_ctx, tbl_idx, elem_idx)) { + goto fail; + } + + /* load data as i32* */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, data)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(table_elem = LLVMBuildGEP(comp_ctx->builder, func_ctx->aot_inst, + &offset, 1, "table_elem_i8p"))) { + aot_set_last_error("llvm build add failed."); + goto fail; + } + + if (!(table_elem = LLVMBuildBitCast(comp_ctx->builder, table_elem, + INT32_PTR_TYPE, "table_elem_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + /* Load function index */ + if (!(table_elem = LLVMBuildGEP(comp_ctx->builder, table_elem, &elem_idx, + 1, "table_elem"))) { + HANDLE_FAILURE("LLVMBuildNUWAdd"); + goto fail; + } + + if (!(func_idx = + LLVMBuildLoad(comp_ctx->builder, table_elem, "func_idx"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + + PUSH_I32(func_idx); + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_set(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMValueRef val, elem_idx, offset, table_elem; + + POP_I32(val); + POP_I32(elem_idx); + + if (!aot_check_table_access(comp_ctx, func_ctx, tbl_idx, elem_idx)) { + goto fail; + } + + /* load data as i32* */ + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, data)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(table_elem = LLVMBuildGEP(comp_ctx->builder, func_ctx->aot_inst, + &offset, 1, "table_elem_i8p"))) { + HANDLE_FAILURE("LLVMBuildGEP"); + goto fail; + } + + if (!(table_elem = LLVMBuildBitCast(comp_ctx->builder, table_elem, + INT32_PTR_TYPE, "table_elem_i32p"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + /* Load function index */ + if (!(table_elem = LLVMBuildGEP(comp_ctx->builder, table_elem, &elem_idx, + 1, "table_elem"))) { + HANDLE_FAILURE("LLVMBuildGEP"); + goto fail; + } + + if (!(LLVMBuildStore(comp_ctx->builder, val, table_elem))) { + HANDLE_FAILURE("LLVMBuildStore"); + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_init(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx, + uint32 tbl_seg_idx) + +{ + LLVMValueRef func, param_values[6], value; + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = I32_TYPE; + param_types[5] = I32_TYPE; + ret_type = VOID_TYPE; + + GET_AOT_FUNCTION(aot_table_init, 6); + + param_values[0] = func_ctx->aot_inst; + + if (!(param_values[1] = I32_CONST(tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(param_values[2] = I32_CONST(tbl_seg_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* n */ + POP_I32(param_values[3]); + /* s */ + POP_I32(param_values[4]); + /* d */ + POP_I32(param_values[5]); + + /* "" means return void */ + if (!(LLVMBuildCall(comp_ctx->builder, func, param_values, 6, ""))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_copy(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 src_tbl_idx, + uint32 dst_tbl_idx) +{ + LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type; + LLVMValueRef func, param_values[6], value; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = I32_TYPE; + param_types[5] = I32_TYPE; + ret_type = VOID_TYPE; + + GET_AOT_FUNCTION(aot_table_copy, 6); + + param_values[0] = func_ctx->aot_inst; + + if (!(param_values[1] = I32_CONST(src_tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(param_values[2] = I32_CONST(dst_tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* n */ + POP_I32(param_values[3]); + /* s */ + POP_I32(param_values[4]); + /* d */ + POP_I32(param_values[5]); + + /* "" means return void */ + if (!(LLVMBuildCall(comp_ctx->builder, func, param_values, 6, ""))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_size(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMValueRef offset, tbl_sz; + + if (!(offset = I32_CONST(get_tbl_inst_offset(comp_ctx, func_ctx, tbl_idx) + + offsetof(AOTTableInstance, cur_size)))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildGEP(comp_ctx->builder, func_ctx->aot_inst, &offset, + 1, "tbl_sz_ptr_i8"))) { + HANDLE_FAILURE("LLVMBuildGEP"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildBitCast(comp_ctx->builder, tbl_sz, INT32_PTR_TYPE, + "tbl_sz_ptr"))) { + HANDLE_FAILURE("LLVMBuildBitCast"); + goto fail; + } + + if (!(tbl_sz = LLVMBuildLoad(comp_ctx->builder, tbl_sz, "tbl_sz"))) { + HANDLE_FAILURE("LLVMBuildLoad"); + goto fail; + } + + PUSH_I32(tbl_sz); + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_grow(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMTypeRef param_types[4], ret_type, func_type, func_ptr_type; + LLVMValueRef func, param_values[4], ret, value; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + ret_type = I32_TYPE; + + GET_AOT_FUNCTION(aot_table_grow, 4); + + param_values[0] = func_ctx->aot_inst; + + if (!(param_values[1] = I32_CONST(tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* n */ + POP_I32(param_values[2]); + /* v */ + POP_I32(param_values[3]); + + if (!(ret = LLVMBuildCall(comp_ctx->builder, func, param_values, 4, + "table_grow"))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + PUSH_I32(ret); + + return true; +fail: + return false; +} + +bool +aot_compile_op_table_fill(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx) +{ + LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type; + LLVMValueRef func, param_values[5], value; + + param_types[0] = INT8_PTR_TYPE; + param_types[1] = I32_TYPE; + param_types[2] = I32_TYPE; + param_types[3] = I32_TYPE; + param_types[4] = I32_TYPE; + ret_type = VOID_TYPE; + + GET_AOT_FUNCTION(aot_table_fill, 5); + + param_values[0] = func_ctx->aot_inst; + + if (!(param_values[1] = I32_CONST(tbl_idx))) { + HANDLE_FAILURE("LLVMConstInt"); + goto fail; + } + + /* n */ + POP_I32(param_values[2]); + /* v */ + POP_I32(param_values[3]); + /* i */ + POP_I32(param_values[4]); + + /* "" means return void */ + if (!(LLVMBuildCall(comp_ctx->builder, func, param_values, 5, ""))) { + HANDLE_FAILURE("LLVMBuildCall"); + goto fail; + } + + return true; +fail: + return false; +} + +#endif /* WASM_ENABLE_REF_TYPES != 0 */ \ No newline at end of file diff --git a/core/iwasm/compilation/aot_emit_table.h b/core/iwasm/compilation/aot_emit_table.h new file mode 100644 index 000000000..78c5b90c3 --- /dev/null +++ b/core/iwasm/compilation/aot_emit_table.h @@ -0,0 +1,71 @@ + +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifndef _AOT_EMIT_TABLE_H_ +#define _AOT_EMIT_TABLE_H_ + +#include "aot_compiler.h" + +#ifdef __cplusplus +extern "C" { +#endif + +bool +aot_compile_op_elem_drop(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_seg_idx); + +bool +aot_compile_op_table_get(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx); + +bool +aot_compile_op_table_set(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx); + +bool +aot_compile_op_table_init(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx, + uint32 tbl_seg_idx); + +bool +aot_compile_op_table_copy(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 src_tbl_idx, + uint32 dst_tbl_idx); + +bool +aot_compile_op_table_size(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx); + +bool +aot_compile_op_table_grow(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx); + +bool +aot_compile_op_table_fill(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx); + +uint64 +get_tbl_inst_offset(const AOTCompContext *comp_ctx, + const AOTFuncContext *func_ctx, + uint32 tbl_idx); + +LLVMValueRef +aot_compile_get_tbl_inst(AOTCompContext *comp_ctx, + AOTFuncContext *func_ctx, + uint32 tbl_idx); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif +#endif \ No newline at end of file diff --git a/core/iwasm/compilation/aot_emit_variable.c b/core/iwasm/compilation/aot_emit_variable.c index f79588cda..c3ddc356f 100644 --- a/core/iwasm/compilation/aot_emit_variable.c +++ b/core/iwasm/compilation/aot_emit_variable.c @@ -143,6 +143,8 @@ compile_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, switch (global_type) { case VALUE_TYPE_I32: + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: ptr_type = comp_ctx->basic_types.int32_ptr_type; break; case VALUE_TYPE_I64: @@ -158,7 +160,7 @@ compile_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, ptr_type = comp_ctx->basic_types.v128_ptr_type; break; default: - bh_assert(0); + bh_assert("unknown type"); break; } diff --git a/core/iwasm/compilation/aot_llvm.c b/core/iwasm/compilation/aot_llvm.c index 0044a3b40..0784e87e9 100644 --- a/core/iwasm/compilation/aot_llvm.c +++ b/core/iwasm/compilation/aot_llvm.c @@ -14,6 +14,8 @@ wasm_type_to_llvm_type(AOTLLVMTypes *llvm_types, uint8 wasm_type) { switch (wasm_type) { case VALUE_TYPE_I32: + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: return llvm_types->int32_type; case VALUE_TYPE_I64: return llvm_types->int64_type; @@ -21,12 +23,12 @@ wasm_type_to_llvm_type(AOTLLVMTypes *llvm_types, uint8 wasm_type) return llvm_types->float32_type; case VALUE_TYPE_F64: return llvm_types->float64_type; -#if WASM_ENABLE_SIMD != 0 case VALUE_TYPE_V128: return llvm_types->i64x2_vec_type; -#endif case VALUE_TYPE_VOID: return llvm_types->void_type; + default: + break; } return NULL; } @@ -491,34 +493,6 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, return true; } -static bool -create_table_base(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) -{ - AOTCompData *comp_data = comp_ctx->comp_data; - uint64 module_inst_mem_inst_size = - (uint64)comp_data->memory_count * sizeof(AOTMemoryInstance); - LLVMValueRef offset; - - offset = I32_CONST(offsetof(AOTModuleInstance, global_table_data.bytes) - + module_inst_mem_inst_size - + comp_ctx->comp_data->global_data_size); - func_ctx->table_base = LLVMBuildInBoundsGEP(comp_ctx->builder, - func_ctx->aot_inst, - &offset, 1, - "table_base_tmp"); - if (!func_ctx->table_base) { - aot_set_last_error("llvm build in bounds gep failed."); - return false; - } - func_ctx->table_base = LLVMBuildBitCast(comp_ctx->builder, func_ctx->table_base, - INT32_PTR_TYPE, "table_base"); - if (!func_ctx->table_base) { - aot_set_last_error("llvm build bit cast failed."); - return false; - } - return true; -} - static bool create_cur_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx) { @@ -810,11 +784,13 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, case VALUE_TYPE_F64: local_value = F64_ZERO; break; -#if WASM_ENABLE_SIMD != 0 case VALUE_TYPE_V128: local_value = V128_ZERO; break; -#endif + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + local_value = REF_NULL; + break; default: bh_assert(0); break; @@ -853,10 +829,6 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx, if (!create_memory_info(comp_ctx, func_ctx, int8_ptr_type, func_index)) goto fail; - /* Load table base */ - if (!create_table_base(comp_ctx, func_ctx)) - goto fail; - /* Load current exception */ if (!create_cur_exception(comp_ctx, func_ctx)) goto fail; @@ -943,6 +915,12 @@ aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context) basic_types->meta_data_type = LLVMMetadataTypeInContext(context); basic_types->int8_ptr_type = LLVMPointerType(basic_types->int8_type, 0); + + if (basic_types->int8_ptr_type) { + basic_types->int8_pptr_type = + LLVMPointerType(basic_types->int8_ptr_type, 0); + } + basic_types->int16_ptr_type = LLVMPointerType(basic_types->int16_type, 0); basic_types->int32_ptr_type = LLVMPointerType(basic_types->int32_type, 0); basic_types->int64_ptr_type = LLVMPointerType(basic_types->int64_type, 0); @@ -959,7 +937,11 @@ aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context) basic_types->v128_type = basic_types->i64x2_vec_type; basic_types->v128_ptr_type = LLVMPointerType(basic_types->v128_type, 0); + basic_types->funcref_type = LLVMInt32TypeInContext(context); + basic_types->externref_type = LLVMInt32TypeInContext(context); + return (basic_types->int8_ptr_type + && basic_types->int8_pptr_type && basic_types->int16_ptr_type && basic_types->int32_ptr_type && basic_types->int64_ptr_type @@ -971,7 +953,9 @@ aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context) && basic_types->i64x2_vec_type && basic_types->f32x4_vec_type && basic_types->f64x2_vec_type - && basic_types->meta_data_type) ? true : false; + && basic_types->meta_data_type + && basic_types->funcref_type + && basic_types->externref_type) ? true : false; } static bool @@ -1014,6 +998,7 @@ aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx) consts->i32_32 = I32_CONST(32); consts->i64_63 = I64_CONST(63); consts->i64_64 = I64_CONST(64); + consts->ref_null = I32_CONST(NULL_REF); return (consts->i8_zero && consts->i32_zero @@ -1041,7 +1026,8 @@ aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx) && consts->i32_31 && consts->i32_32 && consts->i64_63 - && consts->i64_64) ? true : false; + && consts->i64_64 + && consts->ref_null) ? true : false; } typedef struct ArchItem { @@ -1245,6 +1231,9 @@ aot_create_comp_context(AOTCompData *comp_data, if (option->enable_tail_call) comp_ctx->enable_tail_call = true; + if (option->enable_ref_types) + comp_ctx->enable_ref_types = true; + if (option->enable_aux_stack_frame) comp_ctx->enable_aux_stack_frame = true; @@ -1585,10 +1574,7 @@ aot_create_comp_context(AOTCompData *comp_data, } /* set exec_env data type to int8** */ - if (!(comp_ctx->exec_env_type = LLVMPointerType(INT8_PTR_TYPE, 0))) { - aot_set_last_error("llvm get pointer type failed."); - goto fail; - } + comp_ctx->exec_env_type = comp_ctx->basic_types.int8_pptr_type; /* set aot_inst data type to int8* */ comp_ctx->aot_inst_type = INT8_PTR_TYPE; @@ -1846,11 +1832,13 @@ aot_build_zero_function_ret(AOTCompContext *comp_ctx, case VALUE_TYPE_F64: ret = LLVMBuildRet(comp_ctx->builder, F64_ZERO); break; -#if WASM_ENABLE_SIMD != 0 case VALUE_TYPE_V128: ret = LLVMBuildRet(comp_ctx->builder, V128_ZERO); break; -#endif + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: + ret = LLVMBuildRet(comp_ctx->builder, REF_NULL); + break; default: bh_assert(0); } diff --git a/core/iwasm/compilation/aot_llvm.h b/core/iwasm/compilation/aot_llvm.h index 83ed0e781..95d0e8f5c 100644 --- a/core/iwasm/compilation/aot_llvm.h +++ b/core/iwasm/compilation/aot_llvm.h @@ -119,7 +119,6 @@ typedef struct AOTFuncContext { LLVMValueRef exec_env; LLVMValueRef aot_inst; - LLVMValueRef table_base; LLVMValueRef argv_buf; LLVMValueRef native_stack_bound; LLVMValueRef aux_stack_bound; @@ -152,6 +151,7 @@ typedef struct AOTLLVMTypes { LLVMTypeRef void_type; LLVMTypeRef int8_ptr_type; + LLVMTypeRef int8_pptr_type; LLVMTypeRef int16_ptr_type; LLVMTypeRef int32_ptr_type; LLVMTypeRef int64_ptr_type; @@ -168,6 +168,9 @@ typedef struct AOTLLVMTypes { LLVMTypeRef f64x2_vec_type; LLVMTypeRef meta_data_type; + + LLVMTypeRef funcref_type; + LLVMTypeRef externref_type; } AOTLLVMTypes; typedef struct AOTLLVMConsts { @@ -199,6 +202,7 @@ typedef struct AOTLLVMConsts { LLVMValueRef i32_32; LLVMValueRef i64_63; LLVMValueRef i64_64; + LLVMValueRef ref_null; } AOTLLVMConsts; /** @@ -241,6 +245,9 @@ typedef struct AOTCompContext { /* Tail Call */ bool enable_tail_call; + /* Reference Types */ + bool enable_ref_types; + /* Whether optimize the JITed code */ bool optimize; @@ -283,6 +290,7 @@ typedef struct AOTCompOption{ bool enable_thread_mgr; bool enable_tail_call; bool enable_simd; + bool enable_ref_types; bool enable_aux_stack_check; bool enable_aux_stack_frame; bool is_sgx_platform; diff --git a/core/iwasm/include/aot_export.h b/core/iwasm/include/aot_export.h index 8edabeb31..8adf663a3 100644 --- a/core/iwasm/include/aot_export.h +++ b/core/iwasm/include/aot_export.h @@ -43,6 +43,7 @@ typedef struct AOTCompOption{ bool enable_thread_mgr; bool enable_tail_call; bool enable_simd; + bool enable_ref_types; bool enable_aux_stack_check; bool enable_aux_stack_frame; bool is_sgx_platform; diff --git a/core/iwasm/include/wasm_export.h b/core/iwasm/include/wasm_export.h index 137692911..e20404bf0 100644 --- a/core/iwasm/include/wasm_export.h +++ b/core/iwasm/include/wasm_export.h @@ -849,6 +849,45 @@ wasm_runtime_spawn_thread(wasm_exec_env_t exec_env, wasm_thread_t *tid, WASM_RUNTIME_API_EXTERN int32_t wasm_runtime_join_thread(wasm_thread_t tid, void **retval); +/** + * Map external object to an internal externref index: if the index + * has been created, return it, otherwise create the index. + * + * @param module_inst the WASM module instance that the extern object + * belongs to + * @param extern_obj the external object to be mapped + * @param p_externref_idx return externref index of the external object + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_obj2ref(wasm_module_inst_t module_inst, + void *extern_obj, uint32_t *p_externref_idx); + +/** + * Retrieve the external object from an internal externref index + * + * @param externref_idx the externref index to retrieve + * @param p_extern_obj return the mapped external object of + * the externref index + * + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_ref2obj(uint32_t externref_idx, void **p_extern_obj); + +/** + * Retain an extern object which is mapped to the internal externref + * so that the object won't be cleaned during extern object reclaim + * if it isn't used. + * + * @param externref_idx the externref index of an external object + * to retain + * @return true if success, false otherwise + */ +WASM_RUNTIME_API_EXTERN bool +wasm_externref_retain(uint32_t externref_idx); + /** * dump the call stack * diff --git a/core/iwasm/interpreter/wasm.h b/core/iwasm/interpreter/wasm.h index ab3476c0f..d99036e5e 100644 --- a/core/iwasm/interpreter/wasm.h +++ b/core/iwasm/interpreter/wasm.h @@ -20,22 +20,29 @@ extern "C" { #define VALUE_TYPE_F32 0x7D #define VALUE_TYPE_F64 0x7C #define VALUE_TYPE_V128 0x7B +#define VALUE_TYPE_FUNCREF 0x70 +#define VALUE_TYPE_EXTERNREF 0x6F #define VALUE_TYPE_VOID 0x40 /* Used by AOT */ #define VALUE_TYPE_I1 0x41 /* Used by loader to represent any type of i32/i64/f32/f64 */ #define VALUE_TYPE_ANY 0x42 -/* Table Element Type */ -#define TABLE_ELEM_TYPE_ANY_FUNC 0x70 - #define DEFAULT_NUM_BYTES_PER_PAGE 65536 +#define NULL_REF (0xFFFFFFFF) + +#define TABLE_MAX_SIZE (1024) + #define INIT_EXPR_TYPE_I32_CONST 0x41 #define INIT_EXPR_TYPE_I64_CONST 0x42 #define INIT_EXPR_TYPE_F32_CONST 0x43 #define INIT_EXPR_TYPE_F64_CONST 0x44 #define INIT_EXPR_TYPE_V128_CONST 0xFD +/* = WASM_OP_REF_FUNC */ +#define INIT_EXPR_TYPE_FUNCREF_CONST 0xD2 +/* = WASM_OP_REF_NULL */ +#define INIT_EXPR_TYPE_REFNULL_CONST 0xD0 #define INIT_EXPR_TYPE_GET_GLOBAL 0x23 #define INIT_EXPR_TYPE_ERROR 0xff @@ -105,6 +112,7 @@ typedef union WASMValue { typedef struct InitializerExpression { /* type of INIT_EXPR_TYPE_XXX */ + /* it actually is instr, in some places, requires constant only */ uint8 init_expr_type; WASMValue u; } InitializerExpression; @@ -124,6 +132,7 @@ typedef struct WASMTable { uint32 init_size; /* specified if (flags & 1), else it is 0x10000 */ uint32 max_size; + bool possible_grow; } WASMTable; typedef struct WASMMemory { @@ -141,6 +150,7 @@ typedef struct WASMTableImport { uint32 init_size; /* specified if (flags & 1), else it is 0x10000 */ uint32 max_size; + bool possible_grow; #if WASM_ENABLE_MULTI_MODULE != 0 WASMModule *import_module; WASMTable *import_table_linked; @@ -257,6 +267,12 @@ typedef struct WASMExport { } WASMExport; typedef struct WASMTableSeg { + /* 0 to 7 */ + uint32 mode; + /* funcref or externref, elemkind will be considered as funcref */ + uint32 elem_type; + bool is_dropped; + /* optional, only for active */ uint32 table_index; InitializerExpression base_offset; uint32 function_count; @@ -456,6 +472,10 @@ wasm_value_type_size(uint8 value_type) switch (value_type) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif return sizeof(int32); case VALUE_TYPE_I64: case VALUE_TYPE_F64: @@ -475,11 +495,14 @@ wasm_value_type_cell_num(uint8 value_type) { if (value_type == VALUE_TYPE_VOID) return 0; - else if (value_type == VALUE_TYPE_I32 - || value_type == VALUE_TYPE_F32) + else if (value_type == VALUE_TYPE_I32 || value_type == VALUE_TYPE_F32 +#if WASM_ENABLE_REF_TYPES != 0 + || value_type == VALUE_TYPE_FUNCREF + || value_type == VALUE_TYPE_EXTERNREF +#endif + ) return 1; - else if (value_type == VALUE_TYPE_I64 - || value_type == VALUE_TYPE_F64) + else if (value_type == VALUE_TYPE_I64 || value_type == VALUE_TYPE_F64) return 2; #if WASM_ENABLE_SIMD != 0 else if (value_type == VALUE_TYPE_V128) diff --git a/core/iwasm/interpreter/wasm_interp_classic.c b/core/iwasm/interpreter/wasm_interp_classic.c index 8f37c0d77..3861376a5 100644 --- a/core/iwasm/interpreter/wasm_interp_classic.c +++ b/core/iwasm/interpreter/wasm_interp_classic.c @@ -871,7 +871,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 num_bytes_per_page = memory ? memory->num_bytes_per_page : 0; uint8 *global_data = module->global_data; uint32 linear_mem_size = memory ? num_bytes_per_page * memory->cur_page_count : 0; - WASMTableInstance *table = module->default_table; WASMType **wasm_types = module->module->types; WASMGlobalInstance *globals = module->globals, *global; uint8 opcode_IMPDEP = WASM_OP_IMPDEP; @@ -1099,7 +1098,8 @@ label_pop_csp_n: #endif { WASMType *cur_type, *cur_func_type; - WASMTableInstance *cur_table_inst; + WASMTableInstance *tbl_inst; + uint32 tbl_idx; #if WASM_ENABLE_TAIL_CALL != 0 opcode = *(frame_ip - 1); #endif @@ -1114,41 +1114,35 @@ label_pop_csp_n: * no matter it is used or not */ read_leb_uint32(frame_ip, frame_ip_end, tidx); - if (tidx >= module->module->type_count) { - wasm_set_exception(module, "unknown type"); - goto got_exception; - } + bh_assert(tidx < module->module->type_count); cur_type = wasm_types[tidx]; - /* to skip 0x00 here */ - frame_ip++; + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + val = POP_I32(); - - /* careful, it might be a table in another module */ - cur_table_inst = table; -#if WASM_ENABLE_MULTI_MODULE != 0 - if (table->table_inst_linked) { - cur_table_inst = table->table_inst_linked; - } -#endif - - if (val < 0 || val >= (int32)cur_table_inst->cur_size) { + if (val < 0 || val >= (int32)tbl_inst->cur_size) { wasm_set_exception(module, "undefined element"); goto got_exception; } - fidx = ((uint32*)cur_table_inst->base_addr)[val]; + fidx = ((uint32*)tbl_inst->base_addr)[val]; if (fidx == (uint32)-1) { wasm_set_exception(module, "uninitialized element"); goto got_exception; } -#if WASM_ENABLE_MULTI_MODULE != 0 - if (fidx >= module->function_count) { + /* + * we might be using a table injected by host or + * another module. In that case, we don't validate + * the elem value while loading + */ + if (fidx >= module->function_count) { wasm_set_exception(module, "unknown function"); goto got_exception; } -#endif /* always call module own functions */ cur_func = module->functions + fidx; @@ -1201,6 +1195,99 @@ label_pop_csp_n: HANDLE_OP_END (); } +#if WASM_ENABLE_REF_TYPES != 0 + HANDLE_OP (WASM_OP_SELECT_T): + { + uint32 vec_len; + uint8 type; + + read_leb_uint32(frame_ip, frame_ip_end, vec_len); + type = *frame_ip++; + + cond = (uint32)POP_I32(); + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) { + frame_sp -= 2; + if (!cond) { + *(frame_sp - 2) = *frame_sp; + *(frame_sp - 1) = *(frame_sp + 1); + } + } + else { + frame_sp--; + if (!cond) + *(frame_sp - 1) = *frame_sp; + } + + (void)vec_len; + HANDLE_OP_END (); + } + HANDLE_OP (WASM_OP_TABLE_GET): + { + uint32 tbl_idx, elem_idx; + WASMTableInstance *tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + elem_idx = POP_I32(); + if (elem_idx >= tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + PUSH_I32(((uint32 *)tbl_inst->base_addr)[elem_idx]); + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_TABLE_SET): + { + uint32 tbl_idx, elem_idx, val; + WASMTableInstance *tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + val = POP_I32(); + elem_idx = POP_I32(); + if (elem_idx >= tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + ((uint32 *)(tbl_inst->base_addr))[elem_idx] = val; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_REF_NULL): + { + uint32 ref_type; + read_leb_uint32(frame_ip, frame_ip_end, ref_type); + PUSH_I32(NULL_REF); + (void)ref_type; + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_REF_IS_NULL): + { + uint32 val; + val = POP_I32(); + PUSH_I32(val == NULL_REF ? 1 : 0); + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_REF_FUNC): + { + uint32 func_idx; + read_leb_uint32(frame_ip, frame_ip_end, func_idx); + PUSH_I32(func_idx); + HANDLE_OP_END(); + } +#endif /* WASM_ENABLE_REF_TYPES */ + /* variable instructions */ HANDLE_OP (WASM_OP_GET_LOCAL): { @@ -1209,6 +1296,10 @@ label_pop_csp_n: switch (local_type) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif PUSH_I32(*(int32*)(frame_lp + local_offset)); break; case VALUE_TYPE_I64: @@ -1240,6 +1331,10 @@ label_pop_csp_n: switch (local_type) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif *(int32*)(frame_lp + local_offset) = POP_I32(); break; case VALUE_TYPE_I64: @@ -1271,6 +1366,10 @@ label_pop_csp_n: switch (local_type) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif *(int32*)(frame_lp + local_offset) = *(int32*)(frame_sp - 1); break; case VALUE_TYPE_I64: @@ -2586,10 +2685,173 @@ label_pop_csp_n: break; } #endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + { + uint32 tbl_idx, elem_idx; + uint64 n, s, d; + WASMTableInstance *tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, elem_idx); + bh_assert(elem_idx < module->module->table_seg_count); + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + n = (uint32)POP_I32(); + s = (uint32)POP_I32(); + d = (uint32)POP_I32(); + + /* TODO: what if the element is not passive? */ + + if (!n) { + break; + } + + if (n + s > module->module->table_segments[elem_idx].function_count + || d + n > tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + if (module->module->table_segments[elem_idx].is_dropped) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + if (!wasm_elem_is_passive( + module->module->table_segments[elem_idx].mode)) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + bh_memcpy_s( + (uint8 *)(tbl_inst) + + offsetof(WASMTableInstance, base_addr) + d * sizeof(uint32), + (tbl_inst->cur_size - d) * sizeof(uint32), + module->module->table_segments[elem_idx].func_indexes + s, + n * sizeof(uint32)); + + break; + } + case WASM_OP_ELEM_DROP: + { + uint32 elem_idx; + read_leb_uint32(frame_ip, frame_ip_end, elem_idx); + bh_assert(elem_idx < module->module->table_seg_count); + + module->module->table_segments[elem_idx].is_dropped = true; + break; + } + case WASM_OP_TABLE_COPY: + { + uint32 src_tbl_idx, dst_tbl_idx; + uint64 n, s, d; + WASMTableInstance *src_tbl_inst, *dst_tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, dst_tbl_idx); + bh_assert(dst_tbl_idx < module->table_count); + + dst_tbl_inst = wasm_get_table_inst(module, dst_tbl_idx); + + read_leb_uint32(frame_ip, frame_ip_end, src_tbl_idx); + bh_assert(src_tbl_idx < module->table_count); + + src_tbl_inst = wasm_get_table_inst(module, src_tbl_idx); + + n = (uint32)POP_I32(); + s = (uint32)POP_I32(); + d = (uint32)POP_I32(); + + if (s + n > dst_tbl_inst->cur_size + || d + n > src_tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + /* if s >= d, copy from front to back */ + /* if s < d, copy from back to front */ + /* merge all together */ + bh_memcpy_s( + (uint8 *)(dst_tbl_inst) + offsetof(WASMTableInstance, base_addr) + + d * sizeof(uint32), + (dst_tbl_inst->cur_size - d) * sizeof(uint32), + (uint8 *)(src_tbl_inst) + offsetof(WASMTableInstance, base_addr) + + s * sizeof(uint32), + n * sizeof(uint32)); + break; + } + case WASM_OP_TABLE_GROW: + { + uint32 tbl_idx, n, init_val, orig_tbl_sz; + WASMTableInstance *tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + orig_tbl_sz = tbl_inst->cur_size; + + n = POP_I32(); + init_val = POP_I32(); + + if (!wasm_enlarge_table(module, tbl_idx, n, init_val)) { + PUSH_I32(-1); + } + else { + PUSH_I32(orig_tbl_sz); + } + break; + } + case WASM_OP_TABLE_SIZE: + { + uint32 tbl_idx; + WASMTableInstance *tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + PUSH_I32(tbl_inst->cur_size); + break; + } + case WASM_OP_TABLE_FILL: + { + uint32 tbl_idx, n, val, i; + WASMTableInstance *tbl_inst; + + read_leb_uint32(frame_ip, frame_ip_end, tbl_idx); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + n = POP_I32(); + val = POP_I32(); + i = POP_I32(); + + /* TODO: what if the element is not passive? */ + /* TODO: what if the element is dropped? */ + + if (i + n > tbl_inst->cur_size) { + /* TODO: verify warning content */ + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + for (; n != 0; i++, n--) { + ((uint32 *)(tbl_inst->base_addr))[i] = val; + } + + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ default: wasm_set_exception(module, "unsupported opcode"); - goto got_exception; - break; + goto got_exception; } HANDLE_OP_END (); } @@ -2946,6 +3208,17 @@ label_pop_csp_n: #if WASM_ENABLE_TAIL_CALL == 0 HANDLE_OP (WASM_OP_RETURN_CALL): HANDLE_OP (WASM_OP_RETURN_CALL_INDIRECT): +#endif +#if WASM_ENABLE_SHARED_MEMORY == 0 + HANDLE_OP (WASM_OP_ATOMIC_PREFIX): +#endif +#if WASM_ENABLE_REF_TYPES == 0 + HANDLE_OP (WASM_OP_SELECT_T): + HANDLE_OP (WASM_OP_TABLE_GET): + HANDLE_OP (WASM_OP_TABLE_SET): + HANDLE_OP (WASM_OP_REF_NULL): + HANDLE_OP (WASM_OP_REF_IS_NULL): + HANDLE_OP (WASM_OP_REF_FUNC): #endif HANDLE_OP (WASM_OP_UNUSED_0x14): HANDLE_OP (WASM_OP_UNUSED_0x15): @@ -2953,10 +3226,7 @@ label_pop_csp_n: HANDLE_OP (WASM_OP_UNUSED_0x17): HANDLE_OP (WASM_OP_UNUSED_0x18): HANDLE_OP (WASM_OP_UNUSED_0x19): - HANDLE_OP (WASM_OP_UNUSED_0x1c): - HANDLE_OP (WASM_OP_UNUSED_0x1d): - HANDLE_OP (WASM_OP_UNUSED_0x1e): - HANDLE_OP (WASM_OP_UNUSED_0x1f): + HANDLE_OP (WASM_OP_UNUSED_0x27): /* Used by fast interpreter */ HANDLE_OP (EXT_OP_SET_LOCAL_FAST_I64): HANDLE_OP (EXT_OP_TEE_LOCAL_FAST_I64): diff --git a/core/iwasm/interpreter/wasm_interp_fast.c b/core/iwasm/interpreter/wasm_interp_fast.c index e98ec760d..786ba7b12 100644 --- a/core/iwasm/interpreter/wasm_interp_fast.c +++ b/core/iwasm/interpreter/wasm_interp_fast.c @@ -991,7 +991,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module, uint32 num_bytes_per_page = memory ? memory->num_bytes_per_page : 0; uint8 *global_data = module->global_data; uint32 linear_mem_size = memory ? num_bytes_per_page * memory->cur_page_count : 0; - WASMTableInstance *table = module->default_table; WASMGlobalInstance *globals = module->globals, *global; uint8 opcode_IMPDEP = WASM_OP_IMPDEP; WASMInterpFrame *frame = NULL; @@ -1150,7 +1149,8 @@ recover_br_info: #endif { WASMType *cur_type, *cur_func_type; - WASMTableInstance *cur_table_inst; + WASMTableInstance *tbl_inst; + uint32 tbl_idx; #if WASM_ENABLE_TAIL_CALL != 0 GET_OPCODE(); @@ -1160,40 +1160,36 @@ recover_br_info: #endif tidx = read_uint32(frame_ip); + cur_type = module->module->types[tidx]; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + val = GET_OPERAND(uint32, I32, 0); frame_ip += 2; - if (tidx >= module->module->type_count) { - wasm_set_exception(module, "type index is overflow"); - goto got_exception; - } - cur_type = module->module->types[tidx]; - - /* careful, it might be a table in another module */ - cur_table_inst = table; -#if WASM_ENABLE_MULTI_MODULE != 0 - if (table->table_inst_linked) { - cur_table_inst = table->table_inst_linked; - } -#endif - - if (val < 0 || val >= (int32)cur_table_inst->cur_size) { + if (val < 0 || val >= (int32)tbl_inst->cur_size) { wasm_set_exception(module, "undefined element"); goto got_exception; } - fidx = ((uint32*)cur_table_inst->base_addr)[val]; + fidx = ((uint32*)tbl_inst->base_addr)[val]; if (fidx == (uint32)-1) { wasm_set_exception(module, "uninitialized element"); goto got_exception; } -#if WASM_ENABLE_MULTI_MODULE != 0 + /* + * we might be using a table injected by host or + * another module. in that case, we don't validate + * the elem value while loading + */ if (fidx >= module->function_count) { wasm_set_exception(module, "unknown function"); goto got_exception; } -#endif /* always call module own functions */ cur_func = module->functions + fidx; @@ -1252,6 +1248,70 @@ recover_br_info: HANDLE_OP_END (); } +#if WASM_ENABLE_REF_TYPES != 0 + HANDLE_OP (WASM_OP_TABLE_GET): + { + uint32 tbl_idx, elem_idx; + WASMTableInstance *tbl_inst; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + elem_idx = POP_I32(); + if (elem_idx >= tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + PUSH_I32(((uint32 *)tbl_inst->base_addr)[elem_idx]); + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_TABLE_SET): + { + uint32 tbl_idx, elem_idx, val; + WASMTableInstance *tbl_inst; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + val = POP_I32(); + elem_idx = POP_I32(); + if (elem_idx >= tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + ((uint32 *)tbl_inst->base_addr)[elem_idx] = val; + HANDLE_OP_END (); + } + + HANDLE_OP (WASM_OP_REF_NULL): + { + PUSH_I32(NULL_REF); + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_REF_IS_NULL): + { + uint32 val; + val = POP_I32(); + PUSH_I32(val == NULL_REF ? 1 : 0); + HANDLE_OP_END(); + } + + HANDLE_OP (WASM_OP_REF_FUNC): + { + uint32 func_idx = read_uint32(frame_ip); + PUSH_I32(func_idx); + HANDLE_OP_END(); + } +#endif /* WASM_ENABLE_REF_TYPES */ + /* variable instructions */ HANDLE_OP (EXT_OP_SET_LOCAL_FAST): HANDLE_OP (EXT_OP_TEE_LOCAL_FAST): @@ -2605,10 +2665,165 @@ recover_br_info: break; } #endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + { + uint32 tbl_idx, elem_idx; + uint64 n, s, d; + WASMTableInstance *tbl_inst; + + elem_idx = read_uint32(frame_ip); + bh_assert(elem_idx < module->module->table_seg_count); + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + n = (uint32)POP_I32(); + s = (uint32)POP_I32(); + d = (uint32)POP_I32(); + + if (!n) { + break; + } + + if (n + s > module->module->table_segments[elem_idx].function_count + || d + n > tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + if (module->module->table_segments[elem_idx].is_dropped) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + if (!wasm_elem_is_passive( + module->module->table_segments[elem_idx].mode)) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + bh_memcpy_s( + (uint8 *)tbl_inst + offsetof(WASMTableInstance, base_addr) + + d * sizeof(uint32), + (tbl_inst->cur_size - d) * sizeof(uint32), + module->module->table_segments[elem_idx].func_indexes + s, + n * sizeof(uint32)); + break; + } + case WASM_OP_ELEM_DROP: + { + uint32 elem_idx = read_uint32(frame_ip); + bh_assert(elem_idx < module->module->table_seg_count); + + module->module->table_segments[elem_idx].is_dropped = true; + break; + } + case WASM_OP_TABLE_COPY: + { + uint32 src_tbl_idx, dst_tbl_idx; + uint64 n, s, d; + WASMTableInstance *src_tbl_inst, *dst_tbl_inst; + + dst_tbl_idx = read_uint32(frame_ip); + bh_assert(dst_tbl_idx < module->table_count); + + dst_tbl_inst = wasm_get_table_inst(module, dst_tbl_idx); + + src_tbl_idx = read_uint32(frame_ip); + bh_assert(src_tbl_idx < module->table_count); + + src_tbl_inst = wasm_get_table_inst(module, src_tbl_idx); + + n = (uint32)POP_I32(); + s = (uint32)POP_I32(); + d = (uint32)POP_I32(); + + if (s + n > dst_tbl_inst->cur_size + || d + n > src_tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + /* if s >= d, copy from front to back */ + /* if s < d, copy from back to front */ + /* merge all together */ + bh_memcpy_s( + (uint8 *)dst_tbl_inst + offsetof(WASMTableInstance, base_addr) + + d * sizeof(uint32), + (dst_tbl_inst->cur_size - d) * sizeof(uint32), + (uint8 *)src_tbl_inst + + offsetof(WASMTableInstance, base_addr) + s * sizeof(uint32), + n * sizeof(uint32)); + break; + } + case WASM_OP_TABLE_GROW: + { + uint32 tbl_idx, n, init_val, orig_tbl_sz; + WASMTableInstance *tbl_inst; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + orig_tbl_sz = tbl_inst->cur_size; + + n = POP_I32(); + init_val = POP_I32(); + + if (!wasm_enlarge_table(module, tbl_idx, n, init_val)) { + PUSH_I32(-1); + } else { + PUSH_I32(orig_tbl_sz); + } + + break; + } + case WASM_OP_TABLE_SIZE: + { + uint32 tbl_idx; + WASMTableInstance *tbl_inst; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + PUSH_I32(tbl_inst->cur_size); + break; + } + case WASM_OP_TABLE_FILL: + { + uint32 tbl_idx, n, val, i; + WASMTableInstance *tbl_inst; + + tbl_idx = read_uint32(frame_ip); + bh_assert(tbl_idx < module->table_count); + + tbl_inst = wasm_get_table_inst(module, tbl_idx); + + n = POP_I32(); + val = POP_I32(); + i = POP_I32(); + + if (i + n > tbl_inst->cur_size) { + wasm_set_exception(module, "out of bounds table access"); + goto got_exception; + } + + for (; n != 0; i++, n--) { + ((uint32 *)(tbl_inst->base_addr))[i] = val; + } + + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ default: wasm_set_exception(module, "unsupported opcode"); - goto got_exception; - break; + goto got_exception; } HANDLE_OP_END (); } @@ -2992,16 +3207,25 @@ recover_br_info: HANDLE_OP (WASM_OP_RETURN_CALL): HANDLE_OP (WASM_OP_RETURN_CALL_INDIRECT): #endif +#if WASM_ENABLE_SHARED_MEMORY == 0 + HANDLE_OP (WASM_OP_ATOMIC_PREFIX): +#endif +#if WASM_ENABLE_REF_TYPES == 0 + HANDLE_OP (WASM_OP_TABLE_GET): + HANDLE_OP (WASM_OP_TABLE_SET): + HANDLE_OP (WASM_OP_REF_NULL): + HANDLE_OP (WASM_OP_REF_IS_NULL): + HANDLE_OP (WASM_OP_REF_FUNC): +#endif + /* SELECT_T is converted to SELECT or SELECT_64 */ + HANDLE_OP (WASM_OP_SELECT_T): HANDLE_OP (WASM_OP_UNUSED_0x14): HANDLE_OP (WASM_OP_UNUSED_0x15): HANDLE_OP (WASM_OP_UNUSED_0x16): HANDLE_OP (WASM_OP_UNUSED_0x17): HANDLE_OP (WASM_OP_UNUSED_0x18): HANDLE_OP (WASM_OP_UNUSED_0x19): - HANDLE_OP (WASM_OP_UNUSED_0x1c): - HANDLE_OP (WASM_OP_UNUSED_0x1d): - HANDLE_OP (WASM_OP_UNUSED_0x1e): - HANDLE_OP (WASM_OP_UNUSED_0x1f): + HANDLE_OP (WASM_OP_UNUSED_0x27): /* optimized op code */ HANDLE_OP (WASM_OP_F32_STORE): HANDLE_OP (WASM_OP_F64_STORE): diff --git a/core/iwasm/interpreter/wasm_loader.c b/core/iwasm/interpreter/wasm_loader.c index 2ab8aa53d..c1af8a8fd 100644 --- a/core/iwasm/interpreter/wasm_loader.c +++ b/core/iwasm/interpreter/wasm_loader.c @@ -96,6 +96,7 @@ read_leb(uint8 **p_buf, const uint8 *buf_end, uint64 byte; while (true) { + /* uN or SN must not exceed ceil(N/7) bytes */ if (bcnt + 1 > (maxbits + 6) / 7) { set_error_buf(error_buf, error_buf_size, "integer representation too long"); @@ -188,6 +189,67 @@ fail: res = (int32)res64; \ } while (0) +static char * +type2str(uint8 type) +{ + char *type_str[] = { "v128", "f64", "f32", "i64", "i32" }; + + if (type >= VALUE_TYPE_V128 && type <= VALUE_TYPE_I32) + return type_str[type - VALUE_TYPE_V128]; + else if (type == VALUE_TYPE_FUNCREF) + return "funcref"; + else if (type == VALUE_TYPE_EXTERNREF) + return "externref"; + else + return "unknown type"; +} + +static bool +is_32bit_type(uint8 type) +{ + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32 +#if WASM_ENABLE_REF_TYPES != 0 + || (wasm_get_ref_types_flag() + && (type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF)) +#endif + ) + return true; + return false; +} + +static bool +is_64bit_type(uint8 type) +{ + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) + return true; + return false; +} + +static bool +is_value_type(uint8 type) +{ + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_I64 + || type == VALUE_TYPE_F32 || type == VALUE_TYPE_F64 +#if WASM_ENABLE_REF_TYPES != 0 + || (wasm_get_ref_types_flag() + && (type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF)) +#endif +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) + || type == VALUE_TYPE_V128 +#endif +#endif + ) + return true; + return false; +} + +static bool +is_byte_a_type(uint8 type) +{ + return is_value_type(type) || (type == VALUE_TYPE_VOID); +} + #if WASM_ENABLE_SIMD != 0 #if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) static V128 @@ -394,7 +456,7 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, uint64 high, low; if (type != VALUE_TYPE_V128) - goto fail; + goto fail_type_mismatch; flag = read_uint8(p); (void)flag; @@ -409,20 +471,57 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, } #endif /* end of (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) */ #endif /* end of WASM_ENABLE_SIMD */ +#if WASM_ENABLE_REF_TYPES != 0 + case INIT_EXPR_TYPE_FUNCREF_CONST: + { + if (!wasm_get_ref_types_flag()) { + goto illegal_opcode; + } + + if (type != VALUE_TYPE_FUNCREF) + goto fail_type_mismatch; + read_leb_uint32(p, p_end, init_expr->u.ref_index); + break; + } + case INIT_EXPR_TYPE_REFNULL_CONST: + { + uint8 reftype; + + if (!wasm_get_ref_types_flag()) { + goto illegal_opcode; + } + + CHECK_BUF(p, p_end, 1); + reftype = read_uint8(p); + if (reftype != type) + goto fail_type_mismatch; + + init_expr->u.ref_index = NULL_REF; + break; + } +#endif /* WASM_ENABLE_REF_TYPES != 0 */ /* get_global */ case INIT_EXPR_TYPE_GET_GLOBAL: read_leb_uint32(p, p_end, init_expr->u.global_index); break; - default: - goto fail_type_mismatch; + default: { +#if WASM_ENABLE_REF_TYPES != 0 +illegal_opcode: +#endif + set_error_buf(error_buf, error_buf_size, + "illegal opcode " + "or constant expression required " + "or type mismatch"); + goto fail; + } } CHECK_BUF(p, p_end, 1); end_byte = read_uint8(p); if (end_byte != 0x0b) goto fail_type_mismatch; *p_buf = p; - return true; + fail_type_mismatch: set_error_buf(error_buf, error_buf_size, "type mismatch or constant expression required"); @@ -520,6 +619,27 @@ fail: return false; } +static void +adjust_table_max_size(uint32 init_size, uint32 max_size_flag, uint32 *max_size) +{ + uint32 default_max_size = + init_size * 2 > TABLE_MAX_SIZE ? init_size * 2 : TABLE_MAX_SIZE; + + if (max_size_flag) { + /* module defines the table limitation */ + bh_assert(init_size <= *max_size); + + if (init_size < *max_size) { + *max_size = + *max_size < default_max_size ? *max_size : default_max_size; + } + } + else { + /* partial defined table limitation, gives a default value */ + *max_size = default_max_size; + } +} + #if WASM_ENABLE_MULTI_MODULE != 0 /** * Find export item of a module with export info: @@ -752,7 +872,8 @@ wasm_loader_resolve_global(const char *module_name, if (export->index < module->import_global_count) { global = module->import_globals[export->index].u.global.import_global_linked; - } else { + } + else { global = &(module->globals[export->index - module->import_global_count]); } @@ -1021,33 +1142,44 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end, char *error_buf, uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end; - uint32 declare_elem_type = 0; - uint32 declare_max_size_flag = 0; - uint32 declare_init_size = 0; - uint32 declare_max_size = 0; + uint32 declare_elem_type = 0, declare_max_size_flag = 0, + declare_init_size = 0, declare_max_size = 0; #if WASM_ENABLE_MULTI_MODULE != 0 WASMModule *sub_module = NULL; WASMTable *linked_table = NULL; #endif CHECK_BUF(p, p_end, 1); - /* 0x70 */ + /* 0x70 or 0x6F */ declare_elem_type = read_uint8(p); - if (TABLE_ELEM_TYPE_ANY_FUNC != declare_elem_type) { + if (VALUE_TYPE_FUNCREF != declare_elem_type +#if WASM_ENABLE_REF_TYPES != 0 + && (wasm_get_ref_types_flag() + && VALUE_TYPE_EXTERNREF != declare_elem_type) +#endif + ) { set_error_buf(error_buf, error_buf_size, "incompatible import type"); return false; } read_leb_uint32(p, p_end, declare_max_size_flag); + if (declare_max_size_flag > 1) { + set_error_buf(error_buf, error_buf_size, "integer too large"); + return false; + } + read_leb_uint32(p, p_end, declare_init_size); - if (declare_max_size_flag & 1) { + + if (declare_max_size_flag) { read_leb_uint32(p, p_end, declare_max_size); - if (!check_table_max_size(table->init_size, table->max_size, + if (!check_table_max_size(declare_init_size, declare_max_size, error_buf, error_buf_size)) return false; - } else { - declare_max_size = 0x10000; } + + adjust_table_max_size(declare_init_size, declare_max_size_flag, + &declare_max_size); + *p_buf = p; #if WASM_ENABLE_MULTI_MODULE != 0 @@ -1074,12 +1206,13 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end, table->import_table_linked = linked_table; table->import_module = sub_module; } -#endif +#endif /* WASM_ENABLE_MULTI_MODULE != 0 */ /* (table (export "table") 10 20 funcref) */ + /* we need this section working in wamrc */ if (!strcmp("spectest", sub_module_name)) { - uint32 spectest_table_init_size = 10; - uint32 spectest_table_max_size = 20; + const uint32 spectest_table_init_size = 10; + const uint32 spectest_table_max_size = 20; if (strcmp("table", table_name)) { set_error_buf(error_buf, error_buf_size, @@ -1323,9 +1456,14 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, const uint8 *p = *p_buf, *p_end = buf_end, *p_org; CHECK_BUF(p, p_end, 1); - /* 0x70 */ + /* 0x70 or 0x6F */ table->elem_type = read_uint8(p); - if (TABLE_ELEM_TYPE_ANY_FUNC != table->elem_type) { + if (VALUE_TYPE_FUNCREF != table->elem_type +#if WASM_ENABLE_REF_TYPES != 0 + && (wasm_get_ref_types_flag() + && VALUE_TYPE_EXTERNREF != table->elem_type) +#endif + ) { set_error_buf(error_buf, error_buf_size, "incompatible import type"); return false; } @@ -1358,16 +1496,16 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, #endif read_leb_uint32(p, p_end, table->init_size); - if (table->flags == 0) { - table->max_size = 0x10000; - } - else if (table->flags == 1) { + + if (table->flags) { read_leb_uint32(p, p_end, table->max_size); - if (!check_table_max_size(table->init_size, table->max_size, - error_buf, error_buf_size)) + if (!check_table_max_size(table->init_size, table->max_size, error_buf, + error_buf_size)) return false; } + adjust_table_max_size(table->init_size, table->flags, &table->max_size); + *p_buf = p; return true; fail: @@ -1506,7 +1644,13 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, if (flags & 1) read_leb_uint32(p, p_end, u32); module->import_table_count++; - if (module->import_table_count > 1) { + + if ( +#if WASM_ENABLE_REF_TYPES != 0 + !wasm_get_ref_types_flag() && + +#endif + module->import_table_count > 1) { set_error_buf(error_buf, error_buf_size, "multiple tables"); return false; @@ -1722,7 +1866,8 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, if (func_count != code_count) { set_error_buf(error_buf, error_buf_size, - "function and code section have inconsistent lengths"); + "function and code section have inconsistent lengths or " + "unexpected end"); return false; } @@ -1822,10 +1967,19 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, && type != VALUE_TYPE_V128 #endif #endif - ) { +#if WASM_ENABLE_REF_TYPES != 0 + && (wasm_get_ref_types_flag() && type != VALUE_TYPE_FUNCREF + && type != VALUE_TYPE_EXTERNREF) +#endif + ) { if (type == VALUE_TYPE_V128) set_error_buf(error_buf, error_buf_size, "v128 value type requires simd feature"); + else if (type == VALUE_TYPE_FUNCREF + || type == VALUE_TYPE_EXTERNREF) + set_error_buf(error_buf, error_buf_size, + "ref value type requires " + "reference types feature"); else set_error_buf_v(error_buf, error_buf_size, "invalid local type 0x%02X", type); @@ -1859,6 +2013,21 @@ fail: return false; } +static bool +check_function_index(const WASMModule *module, + uint32 function_index, + char *error_buf, + uint32 error_buf_size) +{ + if (function_index + >= module->import_function_count + module->function_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown function %d", + function_index); + return false; + } + return true; +} + static bool load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, char *error_buf, uint32 error_buf_size) @@ -1869,8 +2038,12 @@ load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, WASMTable *table; read_leb_uint32(p, p_end, table_count); - /* a total of one table is allowed */ - if (module->import_table_count + table_count > 1) { + if ( +#if WASM_ENABLE_REF_TYPES != 0 + !wasm_get_ref_types_flag() && +#endif + module->import_table_count + table_count > 1) { + /* a total of one table is allowed */ set_error_buf(error_buf, error_buf_size, "multiple tables"); return false; } @@ -1993,6 +2166,13 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, return false; } } + else if (INIT_EXPR_TYPE_FUNCREF_CONST == global->init_expr.init_expr_type) { + if (!check_function_index(module, + global->init_expr.u.ref_index, + error_buf, error_buf_size)) { + return false; + } + } } } @@ -2117,12 +2297,140 @@ fail: return false; } +static bool +check_table_index(const WASMModule *module, uint32 table_index, + char *error_buf, uint32 error_buf_size) +{ + if ( +#if WASM_ENABLE_REF_TYPES != 0 + !wasm_get_ref_types_flag() && +#endif + table_index != 0) { + set_error_buf(error_buf, error_buf_size, "zero flag expected"); + return false; + } + + if (table_index >= module->import_table_count + module->table_count) { + set_error_buf_v(error_buf, error_buf_size, "unknown table %d", + table_index); + return false; + } + return true; +} + +static bool +load_table_index(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, uint32 *p_table_index, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 table_index; + + read_leb_uint32(p, p_end, table_index); + if (!check_table_index(module, table_index, error_buf, error_buf_size)) { + return false; + } + + *p_table_index = table_index; + *p_buf = p; + return true; +fail: + return false; +} + +#if WASM_ENABLE_REF_TYPES != 0 +static bool +load_elem_type(const uint8 **p_buf, const uint8 *buf_end, + uint32 *p_elem_type, bool elemkind_zero, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 elem_type; + + CHECK_BUF(p, p_end, 1); + elem_type = read_uint8(p); + if ((elemkind_zero && elem_type != 0) + || (!elemkind_zero && elem_type != VALUE_TYPE_FUNCREF + && elem_type != VALUE_TYPE_EXTERNREF)) { + set_error_buf(error_buf, error_buf_size, "invalid reference type"); + return false; + } + + if (elemkind_zero) + *p_elem_type = VALUE_TYPE_FUNCREF; + else + *p_elem_type = elem_type; + *p_buf = p; + return true; +fail: + return false; +} +#endif /* WASM_ENABLE_REF_TYPES != 0*/ + +static bool +load_func_index_vec(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, WASMTableSeg *table_segment, + bool use_init_expr, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 function_count, function_index = 0, i; + uint64 total_size; + + read_leb_uint32(p, p_end, function_count); + table_segment->function_count = function_count; + total_size = sizeof(uint32) * (uint64)function_count; + if (total_size > 0 + && !(table_segment->func_indexes = (uint32 *) + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < function_count; i++) { + InitializerExpression init_expr = { 0 }; + +#if WASM_ENABLE_REF_TYPES != 0 + if (!wasm_get_ref_types_flag()) { + read_leb_uint32(p, p_end, function_index); + } + else { + if (!use_init_expr) { + read_leb_uint32(p, p_end, function_index); + } + else { + if (!load_init_expr(&p, p_end, &init_expr, + table_segment->elem_type, error_buf, + error_buf_size)) + return false; + + function_index = init_expr.u.ref_index; + } + } +#else + read_leb_uint32(p, p_end, function_index); +#endif + + /* since we are using -1 to indicate ref.null */ + if (init_expr.init_expr_type != INIT_EXPR_TYPE_REFNULL_CONST + && !check_function_index(module, function_index, error_buf, + error_buf_size)) { + return false; + } + table_segment->func_indexes[i] = function_index; + } + + *p_buf = p; + return true; +fail: + return false; +} + static bool load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, char *error_buf, uint32 error_buf_size) { const uint8 *p = buf, *p_end = buf_end; - uint32 table_segment_count, i, j, table_index, function_count, function_index; + uint32 table_segment_count, i; uint64 total_size; WASMTableSeg *table_segment; @@ -2139,41 +2447,106 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *m table_segment = module->table_segments; for (i = 0; i < table_segment_count; i++, table_segment++) { if (p >= p_end) { - set_error_buf(error_buf, error_buf_size, "unexpected end"); - return false; - } - read_leb_uint32(p, p_end, table_index); - if (table_index - >= module->import_table_count + module->table_count) { - LOG_DEBUG("table#%d does not exist", table_index); - set_error_buf(error_buf, error_buf_size, "unknown table"); + set_error_buf(error_buf, error_buf_size, + "invalid value type or " + "invalid elements segment kind"); return false; } - table_segment->table_index = table_index; +#if WASM_ENABLE_REF_TYPES != 0 + if (wasm_get_ref_types_flag()) { + read_leb_uint32(p, p_end, table_segment->mode); + /* last three bits */ + table_segment->mode = table_segment->mode & 0x07; + switch (table_segment->mode) { + /* elemkind/elemtype + active */ + case 0: + case 4: + table_segment->elem_type = VALUE_TYPE_FUNCREF; + table_segment->table_index = 0; - /* initialize expression */ - if (!load_init_expr(&p, p_end, &(table_segment->base_offset), - VALUE_TYPE_I32, error_buf, error_buf_size)) - return false; - - read_leb_uint32(p, p_end, function_count); - table_segment->function_count = function_count; - total_size = sizeof(uint32) * (uint64)function_count; - if (total_size > 0 - && !(table_segment->func_indexes = (uint32 *) - loader_malloc(total_size, error_buf, error_buf_size))) { - return false; - } - for (j = 0; j < function_count; j++) { - read_leb_uint32(p, p_end, function_index); - if (function_index >= module->import_function_count - + module->function_count) { - set_error_buf(error_buf, error_buf_size, - "unknown function"); - return false; + if (!check_table_index(module, + table_segment->table_index, + error_buf, error_buf_size)) + return false; + if (!load_init_expr( + &p, p_end, &table_segment->base_offset, + VALUE_TYPE_I32, error_buf, error_buf_size)) + return false; + if (!load_func_index_vec( + &p, p_end, module, table_segment, + table_segment->mode == 0 ? false : true, + error_buf, error_buf_size)) + return false; + break; + /* elemkind + passive/declarative */ + case 1: + case 3: + if (!load_elem_type(&p, p_end, + &table_segment->elem_type, true, + error_buf, error_buf_size)) + return false; + if (!load_func_index_vec(&p, p_end, module, + table_segment, false, + error_buf, error_buf_size)) + return false; + break; + /* elemkind/elemtype + table_idx + active */ + case 2: + case 6: + if (!load_table_index(&p, p_end, module, + &table_segment->table_index, + error_buf, error_buf_size)) + return false; + if (!load_init_expr( + &p, p_end, &table_segment->base_offset, + VALUE_TYPE_I32, error_buf, error_buf_size)) + return false; + if (!load_elem_type( + &p, p_end, &table_segment->elem_type, + table_segment->mode == 2 ? true : false, + error_buf, error_buf_size)) + return false; + if (!load_func_index_vec( + &p, p_end, module, table_segment, + table_segment->mode == 2 ? false : true, + error_buf, error_buf_size)) + return false; + break; + case 5: + case 7: + if (!load_elem_type(&p, p_end, + &table_segment->elem_type, false, + error_buf, error_buf_size)) + return false; + if (!load_func_index_vec(&p, p_end, module, + table_segment, true, + error_buf, error_buf_size)) + return false; + break; + default: + set_error_buf(error_buf, error_buf_size, + "unknown element segment kind"); + return false; } - table_segment->func_indexes[j] = function_index; + } + else +#endif /* WASM_ENABLE_REF_TYPES != 0 */ + { + /* + * like: 00 41 05 0b 04 00 01 00 01 + * for: (elem 0 (offset (i32.const 5)) $f1 $f2 $f1 $f2) + */ + if (!load_table_index(&p, p_end, module, + &table_segment->table_index, error_buf, + error_buf_size)) + return false; + if (!load_init_expr(&p, p_end, &table_segment->base_offset, + VALUE_TYPE_I32, error_buf, error_buf_size)) + return false; + if (!load_func_index_vec(&p, p_end, module, table_segment, + false, error_buf, error_buf_size)) + return false; } } } @@ -2242,8 +2615,8 @@ load_data_segment_section(const uint8 *buf, const uint8 *buf_end, check_mem_index: if (mem_index >= module->import_memory_count + module->memory_count) { - LOG_DEBUG("memory#%d does not exist", mem_index); - set_error_buf(error_buf, error_buf_size, "unknown memory"); + set_error_buf_v(error_buf, error_buf_size, + "unknown memory %d", mem_index); return false; } break; @@ -2256,8 +2629,8 @@ check_mem_index: #else if (mem_index >= module->import_memory_count + module->memory_count) { - LOG_DEBUG("memory#%d does not exist", mem_index); - set_error_buf(error_buf, error_buf_size, "unknown memory"); + set_error_buf_v(error_buf, error_buf_size, "unknown memory %d", + mem_index); return false; } #endif /* WASM_ENABLE_BULK_MEMORY */ @@ -2530,7 +2903,8 @@ fail: } static bool -wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, +wasm_loader_prepare_bytecode(WASMModule *module, + WASMFunction *func, uint32 cur_func_idx, char *error_buf, uint32 error_buf_size); #if WASM_ENABLE_FAST_INTERP != 0 && WASM_ENABLE_LABELS_AS_VALUES != 0 @@ -2842,7 +3216,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, for (i = 0; i < module->function_count; i++) { WASMFunction *func = module->functions[i]; - if (!wasm_loader_prepare_bytecode(module, func, + if (!wasm_loader_prepare_bytecode(module, func, i, error_buf, error_buf_size)) { return false; } @@ -3378,6 +3752,46 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, case WASM_OP_SELECT_64: break; +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_SELECT_T: + if (!wasm_get_ref_types_flag()) { + return false; + } + + skip_leb_uint32(p, p_end); /* vec length */ + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); /* typeidx */ + break; + case WASM_OP_TABLE_GET: + case WASM_OP_TABLE_SET: + if (!wasm_get_ref_types_flag()) { + return false; + } + + skip_leb_uint32(p, p_end); /* table index */ + break; + case WASM_OP_REF_NULL: + if (!wasm_get_ref_types_flag()) { + return false; + } + + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); /* type */ + break; + case WASM_OP_REF_IS_NULL: + if (!wasm_get_ref_types_flag()) { + return false; + } + + break; + case WASM_OP_REF_FUNC: + if (!wasm_get_ref_types_flag()) { + return false; + } + + skip_leb_uint32(p, p_end); /* func index */ + break; +#endif /* WASM_ENABLE_REF_TYPES */ case WASM_OP_GET_LOCAL: case WASM_OP_SET_LOCAL: case WASM_OP_TEE_LOCAL: @@ -3386,7 +3800,7 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, case WASM_OP_GET_GLOBAL_64: case WASM_OP_SET_GLOBAL_64: case WASM_OP_SET_GLOBAL_AUX_STACK: - skip_leb_uint32(p, p_end); /* localidx */ + skip_leb_uint32(p, p_end); /* local index */ break; case EXT_OP_GET_LOCAL_FAST: @@ -3603,7 +4017,34 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, /* skip memory idx */ p++; break; -#endif +#endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + case WASM_OP_TABLE_COPY: + if (!wasm_get_ref_types_flag()) { + return false; + } + /* tableidx */ + skip_leb_uint32(p, p_end); + /* elemidx */ + skip_leb_uint32(p, p_end); + break; + case WASM_OP_ELEM_DROP: + if (!wasm_get_ref_types_flag()) { + return false; + } + /* elemidx */ + skip_leb_uint32(p, p_end); + break; + case WASM_OP_TABLE_SIZE: + case WASM_OP_TABLE_GROW: + case WASM_OP_TABLE_FILL: + if (!wasm_get_ref_types_flag()) { + return false; + } + skip_leb_uint32(p, p_end); /* table idx */ + break; +#endif /* WASM_ENABLE_REF_TYPES */ default: return false; } @@ -3708,6 +4149,7 @@ fail: return false; } +#define REF_ANY VALUE_TYPE_ANY #define REF_I32 VALUE_TYPE_I32 #define REF_F32 VALUE_TYPE_F32 #define REF_I64_1 VALUE_TYPE_I64 @@ -3718,7 +4160,8 @@ fail: #define REF_V128_2 VALUE_TYPE_V128 #define REF_V128_3 VALUE_TYPE_V128 #define REF_V128_4 VALUE_TYPE_V128 -#define REF_ANY VALUE_TYPE_ANY +#define REF_FUNCREF VALUE_TYPE_FUNCREF +#define REF_EXTERNREF VALUE_TYPE_EXTERNREF #if WASM_ENABLE_FAST_INTERP != 0 @@ -3905,7 +4348,7 @@ free_all_label_patch_lists(BranchBlock *frame_csp, uint32 csp_num) } } -#endif +#endif /* end of WASM_ENABLE_FAST_INTERP */ static bool check_stack_push(WASMLoaderContext *ctx, @@ -3928,12 +4371,8 @@ static bool check_stack_top_values(uint8 *frame_ref, int32 stack_cell_num, uint8 type, char *error_buf, uint32 error_buf_size) { - char *type_str[] = { "v128", "f64", "f32", "i64", "i32" }; - - if (((type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) - && stack_cell_num < 1) - || ((type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) - && stack_cell_num < 2) + if ((is_32bit_type(type) && stack_cell_num < 1) + || (is_64bit_type(type) && stack_cell_num < 2) #if WASM_ENABLE_SIMD != 0 #if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) || (type == VALUE_TYPE_V128 && stack_cell_num < 4) @@ -3945,14 +4384,9 @@ check_stack_top_values(uint8 *frame_ref, int32 stack_cell_num, uint8 type, return false; } - if ((type == VALUE_TYPE_I32 && *(frame_ref - 1) != REF_I32) - || (type == VALUE_TYPE_F32 && *(frame_ref - 1) != REF_F32) - || (type == VALUE_TYPE_I64 - && (*(frame_ref - 2) != REF_I64_1 - || *(frame_ref - 1) != REF_I64_2)) - || (type == VALUE_TYPE_F64 - && (*(frame_ref - 2) != REF_F64_1 - || *(frame_ref - 1) != REF_F64_2)) + if ((is_32bit_type(type) && *(frame_ref - 1) != type) + || (is_64bit_type(type) + && (*(frame_ref - 2) != type || *(frame_ref - 1) != type)) #if WASM_ENABLE_SIMD != 0 #if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) || (type == VALUE_TYPE_V128 @@ -3962,10 +4396,10 @@ check_stack_top_values(uint8 *frame_ref, int32 stack_cell_num, uint8 type, || *(frame_ref - 1) != REF_V128_4)) #endif #endif - ) { + ) { set_error_buf_v(error_buf, error_buf_size, "%s%s%s", "type mismatch: expect ", - type_str[type - VALUE_TYPE_V128], + type2str(type), " but got other"); return false; } @@ -4077,20 +4511,14 @@ wasm_loader_push_frame_ref(WASMLoaderContext *ctx, uint8 type, *ctx->frame_ref++ = type; ctx->stack_cell_num++; - if (ctx->stack_cell_num > ctx->max_stack_cell_num) - ctx->max_stack_cell_num = ctx->stack_cell_num; - - if (type == VALUE_TYPE_I32 - || type == VALUE_TYPE_F32 - || type == VALUE_TYPE_ANY) - return true; + if (is_32bit_type(type) || type == VALUE_TYPE_ANY) + goto check_stack_and_return; if (!check_stack_push(ctx, error_buf, error_buf_size)) return false; + *ctx->frame_ref++ = type; ctx->stack_cell_num++; - if (ctx->stack_cell_num > ctx->max_stack_cell_num) - ctx->max_stack_cell_num = ctx->stack_cell_num; #if WASM_ENABLE_SIMD != 0 #if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) @@ -4103,11 +4531,13 @@ wasm_loader_push_frame_ref(WASMLoaderContext *ctx, uint8 type, return false; *ctx->frame_ref++ = type; ctx->stack_cell_num++; - if (ctx->stack_cell_num > ctx->max_stack_cell_num) - ctx->max_stack_cell_num = ctx->stack_cell_num; } #endif #endif + +check_stack_and_return: + if (ctx->stack_cell_num > ctx->max_stack_cell_num) + ctx->max_stack_cell_num = ctx->stack_cell_num; return true; } @@ -4133,9 +4563,7 @@ wasm_loader_pop_frame_ref(WASMLoaderContext *ctx, uint8 type, ctx->frame_ref--; ctx->stack_cell_num--; - if (type == VALUE_TYPE_I32 - || type == VALUE_TYPE_F32 - || *ctx->frame_ref == VALUE_TYPE_ANY) + if (is_32bit_type(type) || *ctx->frame_ref == VALUE_TYPE_ANY) return true; ctx->frame_ref--; @@ -4480,8 +4908,7 @@ preserve_referenced_local(WASMLoaderContext *loader_ctx, uint8 opcode, if (loader_ctx->p_code_compiled) { bh_assert(preserved_offset != (int16)local_index); } - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) { + if (is_32bit_type(local_type)) { /* Only increase preserve offset in the second traversal */ if (loader_ctx->p_code_compiled) loader_ctx->preserved_local_offset++; @@ -4499,7 +4926,7 @@ preserve_referenced_local(WASMLoaderContext *loader_ctx, uint8 opcode, loader_ctx->frame_offset_bottom[i] = preserved_offset; } - if (cur_type == VALUE_TYPE_I32 || cur_type == VALUE_TYPE_F32) + if (is_32bit_type(cur_type)) i++; else i += 2; @@ -4535,7 +4962,7 @@ preserve_local_for_block(WASMLoaderContext *loader_ctx, uint8 opcode, return false; } - if (cur_type == VALUE_TYPE_I32 || cur_type == VALUE_TYPE_F32) { + if (is_32bit_type(cur_type)) { i++; } else { @@ -4696,7 +5123,7 @@ wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, ctx->max_dynamic_offset = ctx->dynamic_offset; } - if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + if (is_32bit_type(type)) return true; if (ctx->p_code_compiled == NULL) { @@ -4735,7 +5162,7 @@ wasm_loader_pop_frame_offset(WASMLoaderContext *ctx, uint8 type, if (type == VALUE_TYPE_VOID) return true; - if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) { + if (is_32bit_type(type)) { /* Check the offset stack bottom to ensure the frame offset stack will not go underflow. But we don't thrown error and return true here, because the error msg should be @@ -4767,7 +5194,9 @@ wasm_loader_push_pop_frame_offset(WASMLoaderContext *ctx, uint8 pop_cnt, bool disable_emit, int16 operand_offset, char *error_buf, uint32 error_buf_size) { - for (int i = 0; i < pop_cnt; i++) { + uint8 i; + + for (i = 0; i < pop_cnt; i++) { if (!wasm_loader_pop_frame_offset(ctx, type_pop, error_buf, error_buf_size)) return false; } @@ -4832,21 +5261,25 @@ wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, Const *c; for (c = (Const *)ctx->const_buf; (uint8*)c < ctx->const_buf + ctx->num_const * sizeof(Const); c ++) { + /* TODO: handle v128 type? */ if ((type == c->value_type) && ((type == VALUE_TYPE_I64 && *(int64*)value == c->value.i64) - || (type == VALUE_TYPE_I32 && *(int32*)value == c->value.i32) - || (type == VALUE_TYPE_F64 - && (0 == memcmp(value, &(c->value.f64), sizeof(float64)))) - || (type == VALUE_TYPE_F32 - && (0 == memcmp(value, &(c->value.f32), sizeof(float32)))))) { + || (type == VALUE_TYPE_I32 && *(int32*)value == c->value.i32) +#if WASM_ENABLE_REF_TYPES != 0 + || (type == VALUE_TYPE_FUNCREF && *(int32*)value == c->value.i32) + || (type == VALUE_TYPE_EXTERNREF && *(int32*)value == c->value.i32) +#endif + || (type == VALUE_TYPE_F64 + && (0 == memcmp(value, &(c->value.f64), sizeof(float64)))) + || (type == VALUE_TYPE_F32 + && (0 == memcmp(value, &(c->value.f32), sizeof(float32)))))) { operand_offset = c->slot_index; break; } - if (c->value_type == VALUE_TYPE_I64 - || c->value_type == VALUE_TYPE_F64) - operand_offset += 2; - else + if (is_32bit_type(c->value_type)) operand_offset += 1; + else + operand_offset += 2; } if ((uint8 *)c == ctx->const_buf + ctx->num_const * sizeof(Const)) { if ((uint8 *)c == ctx->const_buf + ctx->const_buf_size) { @@ -4878,13 +5311,20 @@ wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, c->value.i32 = *(int32*)value; ctx->const_cell_num ++; break; +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: + c->value.i32 = *(int32*)value; + ctx->const_cell_num ++; + break; +#endif default: break; } c->slot_index = operand_offset; ctx->num_const ++; LOG_OP("#### new const [%d]: %ld\n", - ctx->num_const, (int64)c->value.i64); + ctx->num_const, (int64)c->value.i64); } /* use negetive index for const */ operand_offset = -(operand_offset + 1); @@ -4910,67 +5350,16 @@ fail: PUSH_XXX(); only push the frame_offset stack, no emit */ -#define PUSH_I32() do { \ - if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ + +#define TEMPLATE_PUSH(Type) do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_##Type,\ disable_emit, operand_offset,\ error_buf, error_buf_size)) \ goto fail; \ } while (0) -#define PUSH_F32() do { \ - if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_F32, \ - disable_emit, operand_offset,\ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) - -#define PUSH_I64() do { \ - if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_I64, \ - disable_emit, operand_offset,\ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) - -#define PUSH_F64() do { \ - if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_F64, \ - disable_emit, operand_offset,\ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) - -#define PUSH_V128() do { \ - if (!(wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_V128,\ - disable_emit, operand_offset,\ - error_buf, error_buf_size)))\ - goto fail; \ - } while (0) - -#define POP_I32() do { \ - if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) - -#define POP_F32() do { \ - if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_F32, \ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) - -#define POP_I64() do { \ - if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I64, \ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) - -#define POP_F64() do { \ - if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_F64, \ - error_buf, error_buf_size)) \ - goto fail; \ - } while (0) - -#define POP_V128() do { \ - if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_V128, \ +#define TEMPLATE_POP(Type) do { \ + if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_##Type,\ error_buf, error_buf_size)) \ goto fail; \ } while (0) @@ -5007,62 +5396,14 @@ fail: #else /* WASM_ENABLE_FAST_INTERP */ -#define PUSH_I32() do { \ - if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_I32, \ +#define TEMPLATE_PUSH(Type) do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_##Type, \ error_buf, error_buf_size))) \ goto fail; \ } while (0) -#define PUSH_F32() do { \ - if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_F32, \ - error_buf, error_buf_size))) \ - goto fail; \ - } while (0) - -#define PUSH_I64() do { \ - if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_I64, \ - error_buf, error_buf_size))) \ - goto fail; \ - } while (0) - -#define PUSH_F64() do { \ - if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_F64, \ - error_buf, error_buf_size))) \ - goto fail; \ - } while (0) - -#define PUSH_V128() do { \ - if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_V128, \ - error_buf, error_buf_size))) \ - goto fail; \ - } while (0) - -#define POP_I32() do { \ - if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I32, \ - error_buf, error_buf_size))) \ - goto fail; \ - } while (0) - -#define POP_F32() do { \ - if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_F32, \ - error_buf, error_buf_size))) \ - goto fail; \ - } while (0) - -#define POP_I64() do { \ - if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I64, \ - error_buf, error_buf_size))) \ - goto fail; \ - } while (0) - -#define POP_F64() do { \ - if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_F64, \ - error_buf, error_buf_size))) \ - goto fail; \ - } while (0) - -#define POP_V128() do { \ - if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_V128, \ +#define TEMPLATE_POP(Type) do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_##Type, \ error_buf, error_buf_size))) \ goto fail; \ } while (0) @@ -5083,6 +5424,22 @@ fail: } while (0) #endif /* WASM_ENABLE_FAST_INTERP */ +#define PUSH_I32() TEMPLATE_PUSH(I32) +#define PUSH_F32() TEMPLATE_PUSH(F32) +#define PUSH_I64() TEMPLATE_PUSH(I64) +#define PUSH_F64() TEMPLATE_PUSH(F64) +#define PUSH_V128() TEMPLATE_PUSH(V128) +#define PUSH_FUNCREF() TEMPLATE_PUSH(FUNCREF) +#define PUSH_EXTERNREF() TEMPLATE_PUSH(EXTERNREF) + +#define POP_I32() TEMPLATE_POP(I32) +#define POP_F32() TEMPLATE_POP(F32) +#define POP_I64() TEMPLATE_POP(I64) +#define POP_F64() TEMPLATE_POP(F64) +#define POP_V128() TEMPLATE_POP(V128) +#define POP_FUNCREF() TEMPLATE_POP(FUNCREF) +#define POP_EXTERNREF() TEMPLATE_POP(EXTERNREF) + #if WASM_ENABLE_FAST_INTERP != 0 static bool @@ -5433,17 +5790,6 @@ check_memory_align_equal(uint8 opcode, uint32 align, } #endif /* end of WASM_ENABLE_SHARED_MEMORY */ -static bool -is_value_type(uint8 type) -{ - return type == VALUE_TYPE_I32 || - type == VALUE_TYPE_I64 || - type == VALUE_TYPE_F32 || - type == VALUE_TYPE_F64 || - type == VALUE_TYPE_V128 || - type == VALUE_TYPE_VOID; -} - static bool wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, char *error_buf, uint32 error_buf_size) @@ -5741,8 +6087,57 @@ fail: } \ } while (0) +#if WASM_ENABLE_REF_TYPES != 0 static bool -wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, +get_table_elem_type(const WASMModule *module, + uint32 table_idx, uint8 *p_elem_type, + char *error_buf, uint32 error_buf_size) +{ + if (!check_table_index(module, table_idx, error_buf, error_buf_size)) { + return false; + } + + if (p_elem_type) { + if (table_idx < module->import_table_count) + *p_elem_type = module->import_tables[table_idx].u.table.elem_type; + else + *p_elem_type = module->tables[module->import_table_count + + table_idx].elem_type; + } + return true; +} + +static bool +get_table_seg_elem_type(const WASMModule *module, + uint32 table_seg_idx, uint8 *p_elem_type, + char *error_buf, uint32 error_buf_size) +{ + if (table_seg_idx >= module->table_seg_count) { +#if WASM_ENABLE_REF_TYPES != 0 + if (!wasm_get_ref_types_flag()) { + set_error_buf(error_buf, error_buf_size, "unknown table segment"); + } + else { + set_error_buf_v(error_buf, error_buf_size, + "unknown elem segment %u", table_seg_idx); + } +#else + set_error_buf(error_buf, error_buf_size, + "unknown table segment"); +#endif + return false; + } + + if (p_elem_type) { + *p_elem_type = module->table_segments[table_seg_idx].elem_type; + } + return true; +} +#endif + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, + WASMFunction *func, uint32 cur_func_idx, char *error_buf, uint32 error_buf_size) { uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org; @@ -5750,16 +6145,14 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, uint8 *param_types, *local_types, local_type, global_type; BlockType func_type; uint16 *local_offsets, local_offset; - uint32 count, i, local_idx, global_idx, u32, align, mem_offset; - int32 i32, i32_const = 0; + uint32 type_idx, func_idx, local_idx, global_idx, table_idx; + uint32 table_seg_idx, data_seg_idx, count, i, align, mem_offset; + int32 i32_const = 0; int64 i64; - uint8 opcode, u8; + uint8 opcode; bool return_value = false; WASMLoaderContext *loader_ctx; BranchBlock *frame_csp_tmp; -#if WASM_ENABLE_BULK_MEMORY != 0 - uint32 segment_index; -#endif #if WASM_ENABLE_FAST_INTERP != 0 uint8 *func_const_end, *func_const = NULL; int16 operand_offset; @@ -5845,7 +6238,7 @@ handle_op_block_and_loop: BlockType block_type; value_type = read_uint8(p); - if (is_value_type(value_type)) { + if (is_byte_a_type(value_type)) { /* If the first byte is one of these special values: * 0x40/0x7F/0x7E/0x7D/0x7C, take it as the type of * the single return value. */ @@ -5893,7 +6286,8 @@ handle_op_block_and_loop: #if WASM_ENABLE_FAST_INTERP != 0 if (opcode == WASM_OP_BLOCK) { skip_label(); - } else if (opcode == WASM_OP_LOOP) { + } + else if (opcode == WASM_OP_LOOP) { skip_label(); if (BLOCK_HAS_PARAM(block_type)) { /* Make sure params are in dynamic space */ @@ -5905,7 +6299,8 @@ handle_op_block_and_loop: } (loader_ctx->frame_csp - 1)->code_compiled = loader_ctx->p_code_compiled; - } else if (opcode == WASM_OP_IF) { + } + else if (opcode == WASM_OP_IF) { /* If block has parameters, we should make sure they are in * dynamic space. Otherwise, when else branch is missing, * the later opcode may consume incorrect operand offset. @@ -6182,7 +6577,6 @@ handle_op_block_and_loop: #endif { WASMType *func_type; - uint32 func_idx; int32 idx; read_leb_uint32(p, p_end, func_idx); @@ -6191,9 +6585,8 @@ handle_op_block_and_loop: emit_uint32(loader_ctx, func_idx); #endif - if (func_idx >= module->import_function_count + module->function_count) { - set_error_buf(error_buf, error_buf_size, - "unknown function"); + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { goto fail; } @@ -6228,7 +6621,6 @@ handle_op_block_and_loop: #if WASM_ENABLE_TAIL_CALL != 0 } else { - char *type_str[] = { "v128", "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, @@ -6242,7 +6634,7 @@ handle_op_block_and_loop: 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_V128], + type2str(type), " but got other"); goto fail; } @@ -6255,6 +6647,10 @@ handle_op_block_and_loop: break; } + /* + * if disable reference type: call_indirect typeidx, 0x00 + * if enable reference type: call_indirect typeidx, tableidx + */ case WASM_OP_CALL_INDIRECT: #if WASM_ENABLE_TAIL_CALL != 0 case WASM_OP_RETURN_CALL_INDIRECT: @@ -6262,31 +6658,36 @@ handle_op_block_and_loop: { int32 idx; WASMType *func_type; - uint32 type_idx; - if (module->table_count == 0 - && module->import_table_count == 0) { - set_error_buf(error_buf, error_buf_size, - "call indirect with unknown table"); + read_leb_uint32(p, p_end, type_idx); +#if WASM_ENABLE_REF_TYPES != 0 + if (!wasm_get_ref_types_flag()) { + CHECK_BUF(p, p_end, 1); + table_idx = read_uint8(p); + } + else { + read_leb_uint32(p, p_end, table_idx); + } + +#else + CHECK_BUF(p, p_end, 1); + table_idx = read_uint8(p); +#endif + if (!check_table_index(module, table_idx, error_buf, + error_buf_size)) { goto fail; } - read_leb_uint32(p, p_end, type_idx); #if WASM_ENABLE_FAST_INTERP != 0 + /* we need to emit before arguments */ #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); + emit_uint32(loader_ctx, table_idx); #endif - /* reserved byte 0x00 */ - if (*p++ != 0x00) { - set_error_buf(error_buf, error_buf_size, - "zero flag expected"); - goto fail; - } - + /* skip elem idx */ POP_I32(); if (type_idx >= module->type_count) { @@ -6318,7 +6719,6 @@ handle_op_block_and_loop: #if WASM_ENABLE_TAIL_CALL != 0 } else { - char *type_str[] = { "v128", "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, @@ -6332,7 +6732,7 @@ handle_op_block_and_loop: 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_V128], + type2str(type), " but got other"); goto fail; } @@ -6362,7 +6762,12 @@ handle_op_block_and_loop: if (available_stack_cell > 0) { if (*(loader_ctx->frame_ref - 1) == REF_I32 - || *(loader_ctx->frame_ref - 1) == REF_F32) { + || *(loader_ctx->frame_ref - 1) == REF_F32 +#if WASM_ENABLE_REF_TYPES != 0 + || *(loader_ctx->frame_ref - 1) == REF_FUNCREF + || *(loader_ctx->frame_ref - 1) == REF_EXTERNREF +#endif + ) { loader_ctx->frame_ref--; loader_ctx->stack_cell_num--; #if WASM_ENABLE_FAST_INTERP != 0 @@ -6388,10 +6793,19 @@ handle_op_block_and_loop: loader_ctx->dynamic_offset -= 2; #endif } - else { /* V128 */ +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) + else if (*(loader_ctx->frame_ref - 1) == REF_V128_1) { loader_ctx->frame_ref -= 4; loader_ctx->stack_cell_num -= 4; } +#endif +#endif + else { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } } else { #if WASM_ENABLE_FAST_INTERP != 0 @@ -6416,7 +6830,8 @@ handle_op_block_and_loop: if (available_stack_cell <= 0 && !cur_block->is_stack_polymorphic) { set_error_buf(error_buf, error_buf_size, - "type mismatch, opcode select was found " + "type mismatch or invalid result arity, " + "opcode select was found " "but stack was empty"); goto fail; } @@ -6462,24 +6877,30 @@ handle_op_block_and_loop: } #endif /* end of WASM_ENABLE_FAST_INTERP */ break; - default: - bh_assert(0); +#if WASM_ENABLE_SIMD != 0 +#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) + case REF_V128_4: break; +#endif /* (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0) */ +#endif /* WASM_ENABLE_SIMD != 0 */ + default: { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } } ref_type = *(loader_ctx->frame_ref - 1); #if WASM_ENABLE_FAST_INTERP != 0 POP_OFFSET_TYPE(ref_type); -#endif POP_TYPE(ref_type); -#if WASM_ENABLE_FAST_INTERP != 0 POP_OFFSET_TYPE(ref_type); -#endif POP_TYPE(ref_type); -#if WASM_ENABLE_FAST_INTERP != 0 PUSH_OFFSET_TYPE(ref_type); -#endif PUSH_TYPE(ref_type); +#else + POP2_AND_PUSH(ref_type, ref_type); +#endif } else { #if WASM_ENABLE_FAST_INTERP != 0 @@ -6490,6 +6911,223 @@ handle_op_block_and_loop: break; } +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_SELECT_T: + { + uint8 vec_len, ref_type; + + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + read_leb_uint32(p, p_end, vec_len); + if (!vec_len) { + set_error_buf(error_buf, error_buf_size, + "invalid result arity"); + goto fail; + } + + CHECK_BUF(p, p_end, 1); + ref_type = read_uint8(p); + if (!is_value_type(ref_type)) { + set_error_buf(error_buf, error_buf_size, + "unknown value type"); + goto fail; + } + + POP_I32(); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled) { + uint8 opcode_tmp = WASM_OP_SELECT; + uint8 *p_code_compiled_tmp = + loader_ctx->p_code_compiled - 2; + + if (ref_type == VALUE_TYPE_V128) { +#if (WASM_ENABLE_SIMD == 0) \ + || ((WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)) + set_error_buf(error_buf, error_buf_size, + "SIMD v128 type isn't supported"); + goto fail; +#endif + } + else { + if (ref_type == VALUE_TYPE_F64 + || ref_type == VALUE_TYPE_I64) + opcode_tmp = WASM_OP_SELECT_64; +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(void**)(p_code_compiled_tmp - sizeof(void*)) = + handle_table[opcode_tmp]; +#else + int32 offset = (int32) + ((uint8*)handle_table[opcode_tmp] + - (uint8*)handle_table[0]); + if (!(offset >= INT16_MIN && offset < INT16_MAX)) { + set_error_buf(error_buf, error_buf_size, + "pre-compiled label offset out of range"); + goto fail; + } + *(int16*)(p_code_compiled_tmp - sizeof(int16)) = + (int16)offset; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(p_code_compiled_tmp - 1) = opcode_tmp; +#else + *(p_code_compiled_tmp - 2) = opcode_tmp; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + } + } +#endif /* WASM_ENABLE_FAST_INTERP != 0 */ + +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); + POP_TYPE(ref_type); + POP_OFFSET_TYPE(ref_type); + POP_TYPE(ref_type); + PUSH_OFFSET_TYPE(ref_type); + PUSH_TYPE(ref_type); +#else + POP2_AND_PUSH(ref_type, ref_type); +#endif /* WASM_ENABLE_FAST_INTERP != 0 */ + + (void)vec_len; + break; + } + + /* table.get x. tables[x]. [i32] -> [t] */ + /* table.set x. tables[x]. [i32 t] -> [] */ + case WASM_OP_TABLE_GET: + case WASM_OP_TABLE_SET: + { + uint8 decl_ref_type; + + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + read_leb_uint32(p, p_end, table_idx); + if (!get_table_elem_type(module, table_idx, &decl_ref_type, + error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + + if (opcode == WASM_OP_TABLE_GET) { + POP_I32(); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(decl_ref_type); +#endif + PUSH_TYPE(decl_ref_type); + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(decl_ref_type); +#endif + POP_TYPE(decl_ref_type); + POP_I32(); + } + break; + } + case WASM_OP_REF_NULL: + { + uint8 ref_type; + + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + CHECK_BUF(p, p_end, 1); + ref_type = read_uint8(p); + if (ref_type != VALUE_TYPE_FUNCREF + && ref_type != VALUE_TYPE_EXTERNREF) { + set_error_buf(error_buf, error_buf_size, + "unknown value type"); + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(ref_type); +#endif + PUSH_TYPE(ref_type); + break; + } + case WASM_OP_REF_IS_NULL: + { + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!wasm_loader_pop_frame_ref_offset( + loader_ctx, VALUE_TYPE_FUNCREF, + error_buf, error_buf_size) + && !wasm_loader_pop_frame_ref_offset( + loader_ctx, VALUE_TYPE_EXTERNREF, + error_buf, error_buf_size)) { + goto fail; + } +#else + if (!wasm_loader_pop_frame_ref( + loader_ctx, VALUE_TYPE_FUNCREF, + error_buf, error_buf_size) + && !wasm_loader_pop_frame_ref( + loader_ctx, VALUE_TYPE_EXTERNREF, + error_buf, error_buf_size)) { + goto fail; + } +#endif + PUSH_I32(); + break; + } + case WASM_OP_REF_FUNC: + { + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + read_leb_uint32(p, p_end, func_idx); + + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { + goto fail; + } + + if (func_idx == cur_func_idx) { + WASMTableSeg *table_seg = module->table_segments; + bool func_declared = false; + uint32 j; + + /* Check whether current function is declared */ + for (i = 0; i < module->table_seg_count; i++, table_seg++) { + if (table_seg->elem_type == VALUE_TYPE_FUNCREF + && wasm_elem_is_declarative(table_seg->mode)) { + for (j =0; j < table_seg->function_count; j++) { + if (table_seg->func_indexes[j] == cur_func_idx) { + func_declared = true; + break; + } + } + } + } + if (!func_declared) { + set_error_buf(error_buf, error_buf_size, + "undeclared function reference"); + goto fail; + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, func_idx); +#endif + PUSH_FUNCREF(); + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ + case WASM_OP_GET_LOCAL: { p_org = p - 1; @@ -6506,8 +7144,7 @@ handle_op_block_and_loop: #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) if (local_offset < 0x80) { *p_org++ = EXT_OP_GET_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) + if (is_32bit_type(local_type)) *p_org++ = (uint8)local_offset; else *p_org++ = (uint8)(local_offset | 0x80); @@ -6546,8 +7183,7 @@ handle_op_block_and_loop: loader_ctx->dynamic_offset -= 2; } else { - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) { + if (is_32bit_type(local_type)) { emit_label(EXT_OP_SET_LOCAL_FAST); emit_byte(loader_ctx, (uint8)local_offset); } @@ -6566,8 +7202,7 @@ handle_op_block_and_loop: #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) if (local_offset < 0x80) { *p_org++ = EXT_OP_SET_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) + if (is_32bit_type(local_type)) *p_org++ = (uint8)local_offset; else *p_org++ = (uint8)(local_offset | 0x80); @@ -6604,8 +7239,7 @@ handle_op_block_and_loop: if (local_offset < 256) { skip_label(); - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) { + if (is_32bit_type(local_type)) { emit_label(EXT_OP_TEE_LOCAL_FAST); emit_byte(loader_ctx, (uint8)local_offset); } @@ -6623,8 +7257,7 @@ handle_op_block_and_loop: #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) if (local_offset < 0x80) { *p_org++ = EXT_OP_TEE_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) + if (is_32bit_type(local_type)) *p_org++ = (uint8)local_offset; else *p_org++ = (uint8)(local_offset | 0x80); @@ -6842,8 +7475,13 @@ handle_op_block_and_loop: CHECK_MEMORY(); /* reserved byte 0x00 */ if (*p++ != 0x00) { +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "zero byte expected"); +#else set_error_buf(error_buf, error_buf_size, "zero flag expected"); +#endif goto fail; } PUSH_I32(); @@ -6855,8 +7493,13 @@ handle_op_block_and_loop: CHECK_MEMORY(); /* reserved byte 0x00 */ if (*p++ != 0x00) { +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "zero byte expected"); +#else set_error_buf(error_buf, error_buf_size, "zero flag expected"); +#endif goto fail; } POP_AND_PUSH(VALUE_TYPE_I32, VALUE_TYPE_I32); @@ -7162,9 +7805,10 @@ handle_op_block_and_loop: break; #if WASM_ENABLE_BULK_MEMORY != 0 case WASM_OP_MEMORY_INIT: - read_leb_uint32(p, p_end, segment_index); + { + read_leb_uint32(p, p_end, data_seg_idx); #if WASM_ENABLE_FAST_INTERP != 0 - emit_uint32(loader_ctx, segment_index); + emit_uint32(loader_ctx, data_seg_idx); #endif if (module->import_memory_count == 0 && module->memory_count == 0) goto fail_unknown_memory; @@ -7172,9 +7816,9 @@ handle_op_block_and_loop: if (*p++ != 0x00) goto fail_zero_flag_expected; - if (segment_index >= module->data_seg_count) { - set_error_buf(error_buf, error_buf_size, - "unknown data segment"); + if (data_seg_idx >= module->data_seg_count) { + set_error_buf_v(error_buf, error_buf_size, + "unknown data segment %d", data_seg_idx); goto fail; } @@ -7185,12 +7829,14 @@ handle_op_block_and_loop: POP_I32(); POP_I32(); break; + } case WASM_OP_DATA_DROP: - read_leb_uint32(p, p_end, segment_index); + { + read_leb_uint32(p, p_end, data_seg_idx); #if WASM_ENABLE_FAST_INTERP != 0 - emit_uint32(loader_ctx, segment_index); + emit_uint32(loader_ctx, data_seg_idx); #endif - if (segment_index >= module->data_seg_count) { + if (data_seg_idx >= module->data_seg_count) { set_error_buf(error_buf, error_buf_size, "unknown data segment"); goto fail; @@ -7200,7 +7846,9 @@ handle_op_block_and_loop: goto fail_data_cnt_sec_require; break; + } case WASM_OP_MEMORY_COPY: + { /* both src and dst memory index should be 0 */ if (*(int16*)p != 0x0000) goto fail_zero_flag_expected; @@ -7213,7 +7861,9 @@ handle_op_block_and_loop: POP_I32(); POP_I32(); break; + } case WASM_OP_MEMORY_FILL: + { if (*p++ != 0x00) { goto fail_zero_flag_expected; } @@ -7238,8 +7888,155 @@ fail_data_cnt_sec_require: set_error_buf(error_buf, error_buf_size, "data count section required"); goto fail; - /* TODO: to support bulk table operation */ + } #endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + { + uint8 seg_ref_type, tbl_ref_type; + + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + read_leb_uint32(p, p_end, table_seg_idx); + read_leb_uint32(p, p_end, table_idx); + + if (!get_table_elem_type(module, table_idx, &tbl_ref_type, + error_buf, error_buf_size)) + goto fail; + + if (!get_table_seg_elem_type(module, table_seg_idx, + &seg_ref_type, error_buf, + error_buf_size)) + goto fail; + + if (seg_ref_type != tbl_ref_type) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_seg_idx); + emit_uint32(loader_ctx, table_idx); +#endif + POP_I32(); + POP_I32(); + POP_I32(); + break; + } + case WASM_OP_ELEM_DROP: + { + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + read_leb_uint32(p, p_end, table_seg_idx); + if (!get_table_seg_elem_type(module, table_seg_idx, NULL, + error_buf, error_buf_size)) + goto fail; +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_seg_idx); +#endif + break; + } + case WASM_OP_TABLE_COPY: + { + uint8 src_ref_type, dst_ref_type; + uint32 src_tbl_idx, dst_tbl_idx; + + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + read_leb_uint32(p, p_end, src_tbl_idx); + if (!get_table_elem_type(module, src_tbl_idx, &src_ref_type, + error_buf, error_buf_size)) + goto fail; + + read_leb_uint32(p, p_end, dst_tbl_idx); + if (!get_table_elem_type(module, dst_tbl_idx, &dst_ref_type, + error_buf, error_buf_size)) + goto fail; + + if (src_ref_type != dst_ref_type) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, src_tbl_idx); + emit_uint32(loader_ctx, dst_tbl_idx); +#endif + POP_I32(); + POP_I32(); + POP_I32(); + break; + } + case WASM_OP_TABLE_SIZE: + { + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + read_leb_uint32(p, p_end, table_idx); + /* TODO: shall we create a new function to check + table idx instead of using below function? */ + if (!get_table_elem_type(module, table_idx, NULL, + error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + + PUSH_I32(); + break; + } + case WASM_OP_TABLE_GROW: + case WASM_OP_TABLE_FILL: + { + uint8 decl_ref_type; + + if (!wasm_get_ref_types_flag()) { + goto unsupported_opcode; + } + + read_leb_uint32(p, p_end, table_idx); + if (!get_table_elem_type(module, table_idx, + &decl_ref_type, error_buf, + error_buf_size)) + goto fail; + + if (opcode1 == WASM_OP_TABLE_GROW) { + if (table_idx < module->import_table_count) { + module->import_tables[table_idx] + .u.table.possible_grow = true; + } + else { + module->tables[table_idx - module->import_table_count] + .possible_grow = true; + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + + POP_I32(); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(decl_ref_type); +#endif + POP_TYPE(decl_ref_type); + if (opcode1 == WASM_OP_TABLE_GROW) + PUSH_I32(); + else + POP_I32(); + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ default: set_error_buf_v(error_buf, error_buf_size, "%s %02x %02x", @@ -7770,6 +8567,9 @@ fail_data_cnt_sec_require: #endif /* end of WASM_ENABLE_SHARED_MEMORY */ default: +#if WASM_ENABLE_REF_TYPES != 0 +unsupported_opcode: +#endif set_error_buf_v(error_buf, error_buf_size, "%s %02x", "unsupported opcode", opcode); @@ -7807,7 +8607,8 @@ fail_data_cnt_sec_require: bh_memcpy_s(func_const, (uint32)(func_const_end - func_const), &(c->value.f64), (uint32)sizeof(int64)); func_const += sizeof(int64); - } else { + } + else { bh_memcpy_s(func_const, (uint32)(func_const_end - func_const), &(c->value.f32), (uint32)sizeof(int32)); func_const += sizeof(int32); @@ -7825,9 +8626,9 @@ fail_data_cnt_sec_require: fail: wasm_loader_ctx_destroy(loader_ctx); - (void)u8; - (void)u32; - (void)i32; + (void)table_idx; + (void)table_seg_idx; + (void)data_seg_idx; (void)i64; (void)local_offset; (void)p_org; @@ -7835,3 +8636,20 @@ fail: (void)align; return return_value; } + +#if WASM_ENABLE_REF_TYPES != 0 +static bool ref_types_flag = true; + +void +wasm_set_ref_types_flag(bool enable) +{ + ref_types_flag = enable; +} + +bool +wasm_get_ref_types_flag() +{ + return ref_types_flag; +} +#endif + diff --git a/core/iwasm/interpreter/wasm_loader.h b/core/iwasm/interpreter/wasm_loader.h index 0e9c48888..13daeaf65 100644 --- a/core/iwasm/interpreter/wasm_loader.h +++ b/core/iwasm/interpreter/wasm_loader.h @@ -70,6 +70,14 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, uint8 **p_else_addr, uint8 **p_end_addr); +#if WASM_ENABLE_REF_TYPES != 0 +void +wasm_set_ref_types_flag(bool enable); + +bool +wasm_get_ref_types_flag(); +#endif + #ifdef __cplusplus } #endif diff --git a/core/iwasm/interpreter/wasm_mini_loader.c b/core/iwasm/interpreter/wasm_mini_loader.c index b1854899a..cc2cd7119 100644 --- a/core/iwasm/interpreter/wasm_mini_loader.c +++ b/core/iwasm/interpreter/wasm_mini_loader.c @@ -38,6 +38,27 @@ set_error_buf(char *error_buf, uint32 error_buf_size, const char *string) #define skip_leb_uint32(p, p_end) skip_leb(p) #define skip_leb_int32(p, p_end) skip_leb(p) +static bool +is_32bit_type(uint8 type) +{ + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32 +#if WASM_ENABLE_REF_TYPES != 0 + || (wasm_get_ref_types_flag() + && (type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF)) +#endif + ) + return true; + return false; +} + +static bool +is_64bit_type(uint8 type) +{ + if (type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) + return true; + return false; +} + static void read_leb(uint8 **p_buf, const uint8 *buf_end, uint32 maxbits, bool sign, uint64 *p_result, @@ -229,6 +250,35 @@ load_init_expr(const uint8 **p_buf, const uint8 *buf_end, for (i = 0; i < sizeof(float64); i++) *p_float++ = *p++; break; +#if WASM_ENABLE_REF_TYPES != 0 + case INIT_EXPR_TYPE_FUNCREF_CONST: + { + if (!wasm_get_ref_types_flag()) { + return false; + } + + bh_assert(type == VALUE_TYPE_FUNCREF); + read_leb_uint32(p, p_end, init_expr->u.ref_index); + break; + } + case INIT_EXPR_TYPE_REFNULL_CONST: + { + uint8 reftype; + + if (!wasm_get_ref_types_flag()) { + return false; + } + + CHECK_BUF(p, p_end, 1); + reftype = read_uint8(p); + + bh_assert(type == reftype); + + init_expr->u.ref_index = NULL_REF; + (void)reftype; + break; + } +#endif /* WASM_ENABLE_REF_TYPES != 0 */ /* get_global */ case INIT_EXPR_TYPE_GET_GLOBAL: read_leb_uint32(p, p_end, init_expr->u.global_index); @@ -319,6 +369,27 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, return true; } +static void +adjust_table_max_size(uint32 init_size, uint32 max_size_flag, uint32 *max_size) +{ + uint32 default_max_size = + init_size * 2 > TABLE_MAX_SIZE ? init_size * 2 : TABLE_MAX_SIZE; + + if (max_size_flag) { + /* module defines the table limitation */ + bh_assert(init_size <= *max_size); + + if (init_size < *max_size) { + *max_size = + *max_size < default_max_size ? *max_size : default_max_size; + } + } + else { + /* partial defined table limitation, gives a default value */ + *max_size = default_max_size; + } +} + static bool load_function_import(const uint8 **p_buf, const uint8 *buf_end, const WASMModule *parent_module, @@ -370,24 +441,28 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end, char *error_buf, uint32 error_buf_size) { const uint8 *p = *p_buf, *p_end = buf_end; - uint32 declare_elem_type = 0; - uint32 declare_max_size_flag = 0; - uint32 declare_init_size = 0; - uint32 declare_max_size = 0; + uint32 declare_elem_type = 0, declare_max_size_flag = 0, + declare_init_size = 0, declare_max_size = 0; CHECK_BUF(p, p_end, 1); - /* 0x70 */ + /* 0x70 or 0x6F */ declare_elem_type = read_uint8(p); - bh_assert(TABLE_ELEM_TYPE_ANY_FUNC == declare_elem_type); + bh_assert(VALUE_TYPE_FUNCREF == declare_elem_type +#if WASM_ENABLE_REF_TYPES != 0 + || (wasm_get_ref_types_flag() && + VALUE_TYPE_EXTERNREF == declare_elem_type) +#endif + ); read_leb_uint32(p, p_end, declare_max_size_flag); read_leb_uint32(p, p_end, declare_init_size); if (declare_max_size_flag & 1) { read_leb_uint32(p, p_end, declare_max_size); bh_assert(table->init_size <= table->max_size); - } else { - declare_max_size = 0x10000; } + + adjust_table_max_size(declare_init_size, declare_max_size_flag, + &declare_max_size); *p_buf = p; bh_assert(!((declare_max_size_flag & 1) @@ -499,9 +574,14 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, const uint8 *p = *p_buf, *p_end = buf_end, *p_org; CHECK_BUF(p, p_end, 1); - /* 0x70 */ + /* 0x70 or 0x6F */ table->elem_type = read_uint8(p); - bh_assert(TABLE_ELEM_TYPE_ANY_FUNC == table->elem_type); + bh_assert((VALUE_TYPE_FUNCREF == table->elem_type) +#if WASM_ENABLE_REF_TYPES != 0 + || (wasm_get_ref_types_flag() && + VALUE_TYPE_EXTERNREF == table->elem_type) +#endif + ); p_org = p; read_leb_uint32(p, p_end, table->flags); @@ -510,14 +590,13 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMTable *table, (void)p_org; read_leb_uint32(p, p_end, table->init_size); - if (table->flags == 0) { - table->max_size = 0x10000; - } - else if (table->flags == 1) { + if (table->flags == 1) { read_leb_uint32(p, p_end, table->max_size); bh_assert(table->init_size <= table->max_size); } + adjust_table_max_size(table->init_size, table->flags, &table->max_size); + *p_buf = p; return true; } @@ -631,7 +710,12 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, if (flags & 1) read_leb_uint32(p, p_end, u32); module->import_table_count++; - bh_assert(module->import_table_count <= 1); + bh_assert( +#if WASM_ENABLE_REF_TYPES != 0 + wasm_get_ref_types_flag() || +#endif + module->import_table_count <= 1); + break; case IMPORT_KIND_MEMORY: /* import memory */ @@ -903,7 +987,13 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, CHECK_BUF(p_code, buf_code_end, 1); /* 0x7F/0x7E/0x7D/0x7C */ type = read_uint8(p_code); - bh_assert(type >= VALUE_TYPE_F64 && type <= VALUE_TYPE_I32); + bh_assert((type >= VALUE_TYPE_F64 && type <= VALUE_TYPE_I32) +#if WASM_ENABLE_REF_TYPES != 0 + || (wasm_get_ref_types_flag() + && (type == VALUE_TYPE_FUNCREF + || type == VALUE_TYPE_EXTERNREF)) +#endif + ); for (k = 0; k < sub_local_count; k++) { func->local_types[local_type_index++] = type; } @@ -927,6 +1017,16 @@ load_function_section(const uint8 *buf, const uint8 *buf_end, return true; } +static bool +check_function_index(const WASMModule *module, + uint32 function_index, + char *error_buf, + uint32 error_buf_size) +{ + return (function_index + < module->import_function_count + module->function_count); +} + static bool load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, char *error_buf, uint32 error_buf_size) @@ -937,7 +1037,11 @@ load_table_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, WASMTable *table; read_leb_uint32(p, p_end, table_count); - bh_assert(module->import_table_count + table_count <= 1); + bh_assert( +#if WASM_ENABLE_REF_TYPES != 0 + wasm_get_ref_types_flag() || +#endif + module->import_table_count + table_count <= 1); if (table_count) { module->table_count = table_count; @@ -1036,6 +1140,12 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, bh_assert(target_global_index < module->import_global_count); (void)target_global_index; } + else if (INIT_EXPR_TYPE_FUNCREF_CONST + == global->init_expr.init_expr_type) { + bh_assert(global->init_expr.u.ref_index + < module->import_function_count + + module->function_count); + } } } @@ -1121,12 +1231,133 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, return true; } +static bool +check_table_index(const WASMModule *module, uint32 table_index, + char *error_buf, uint32 error_buf_size) +{ + if ( +#if WASM_ENABLE_REF_TYPES != 0 + !wasm_get_ref_types_flag() && +#endif + table_index != 0) { + return false; + } + + if (table_index >= module->import_table_count + module->table_count) { + return false; + } + return true; +} + +#if WASM_ENABLE_REF_TYPES != 0 +static bool +load_table_index(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, uint32 *p_table_index, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 table_index; + + read_leb_uint32(p, p_end, table_index); + if (!check_table_index(module, table_index, error_buf, error_buf_size)) { + return false; + } + + *p_table_index = table_index; + *p_buf = p; + return true; +} + +static bool +load_elem_type(const uint8 **p_buf, const uint8 *buf_end, + uint32 *p_elem_type, bool elemkind_zero, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint8 elem_type; + + CHECK_BUF(p, p_end, 1); + elem_type = read_uint8(p); + if ((elemkind_zero && elem_type != 0) + || (!elemkind_zero && elem_type != VALUE_TYPE_FUNCREF + && elem_type != VALUE_TYPE_EXTERNREF)) { + set_error_buf(error_buf, error_buf_size, "invalid reference type"); + return false; + } + + if (elemkind_zero) + *p_elem_type = VALUE_TYPE_FUNCREF; + else + *p_elem_type = elem_type; + *p_buf = p; + + (void)p_end; + return true; +} +#endif /* WASM_ENABLE_REF_TYPES != 0*/ + +static bool +load_func_index_vec(const uint8 **p_buf, const uint8 *buf_end, + WASMModule *module, WASMTableSeg *table_segment, + bool use_init_expr, + char *error_buf, uint32 error_buf_size) +{ + const uint8 *p = *p_buf, *p_end = buf_end; + uint32 function_count, function_index = 0, i; + uint64 total_size; + + read_leb_uint32(p, p_end, function_count); + table_segment->function_count = function_count; + total_size = sizeof(uint32) * (uint64)function_count; + if (total_size > 0 + && !(table_segment->func_indexes = (uint32 *) + loader_malloc(total_size, error_buf, error_buf_size))) { + return false; + } + + for (i = 0; i < function_count; i++) { + InitializerExpression init_expr = { 0 }; + +#if WASM_ENABLE_REF_TYPES != 0 + if (!wasm_get_ref_types_flag()) { + read_leb_uint32(p, p_end, function_index); + } + else { + if (!use_init_expr) { + read_leb_uint32(p, p_end, function_index); + } + else { + if (!load_init_expr(&p, p_end, &init_expr, + table_segment->elem_type, error_buf, + error_buf_size)) + return false; + + function_index = init_expr.u.ref_index; + } + } +#else + read_leb_uint32(p, p_end, function_index); +#endif + + /* since we are using -1 to indicate ref.null */ + if (init_expr.init_expr_type != INIT_EXPR_TYPE_REFNULL_CONST + && !check_function_index(module, function_index, error_buf, + error_buf_size)) { + return false; + } + table_segment->func_indexes[i] = function_index; + } + + *p_buf = p; + return true; +} + static bool load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, char *error_buf, uint32 error_buf_size) { const uint8 *p = buf, *p_end = buf_end; - uint32 table_segment_count, i, j, table_index, function_count, function_index; + uint32 table_segment_count, i, table_index, function_count; uint64 total_size; WASMTableSeg *table_segment; @@ -1143,31 +1374,112 @@ load_table_segment_section(const uint8 *buf, const uint8 *buf_end, WASMModule *m table_segment = module->table_segments; for (i = 0; i < table_segment_count; i++, table_segment++) { bh_assert(p < p_end); - read_leb_uint32(p, p_end, table_index); - bh_assert(table_index < module->import_table_count - + module->table_count); - table_segment->table_index = table_index; +#if WASM_ENABLE_REF_TYPES != 0 + if (wasm_get_ref_types_flag()) { + read_leb_uint32(p, p_end, table_segment->mode); + /* last three bits */ + table_segment->mode = table_segment->mode & 0x07; + switch (table_segment->mode) { + /* elemkind/elemtype + active */ + case 0: + case 4: + table_segment->elem_type = VALUE_TYPE_FUNCREF; + table_segment->table_index = 0; - /* initialize expression */ - if (!load_init_expr(&p, p_end, &(table_segment->base_offset), - VALUE_TYPE_I32, error_buf, error_buf_size)) - return false; + if (!check_table_index(module, + table_segment->table_index, + error_buf, error_buf_size)) + return false; - read_leb_uint32(p, p_end, function_count); - table_segment->function_count = function_count; - total_size = sizeof(uint32) * (uint64)function_count; - if (total_size > 0 - && !(table_segment->func_indexes = (uint32 *) - loader_malloc(total_size, error_buf, error_buf_size))) { - return false; + if (!load_init_expr( + &p, p_end, &table_segment->base_offset, + VALUE_TYPE_I32, error_buf, error_buf_size)) + return false; + + if (!load_func_index_vec( + &p, p_end, module, table_segment, + table_segment->mode == 0 ? false : true, + error_buf, error_buf_size)) + return false; + break; + /* elemkind + passive/declarative */ + case 1: + case 3: + if (!load_elem_type(&p, p_end, + &table_segment->elem_type, true, + error_buf, error_buf_size)) + return false; + if (!load_func_index_vec(&p, p_end, module, + table_segment, false, + error_buf, error_buf_size)) + return false; + break; + /* elemkind/elemtype + table_idx + active */ + case 2: + case 6: + if (!load_table_index(&p, p_end, module, + &table_segment->table_index, + error_buf, error_buf_size)) + return false; + if (!load_init_expr( + &p, p_end, &table_segment->base_offset, + VALUE_TYPE_I32, error_buf, error_buf_size)) + return false; + if (!load_elem_type( + &p, p_end, &table_segment->elem_type, + table_segment->mode == 2 ? true : false, + error_buf, error_buf_size)) + return false; + if (!load_func_index_vec( + &p, p_end, module, table_segment, + table_segment->mode == 2 ? false : true, + error_buf, error_buf_size)) + return false; + break; + case 5: + case 7: + if (!load_elem_type(&p, p_end, + &table_segment->elem_type, false, + error_buf, error_buf_size)) + return false; + if (!load_func_index_vec(&p, p_end, module, + table_segment, true, + error_buf, error_buf_size)) + return false; + break; + default: + return false; + } } + else +#endif /* WASM_ENABLE_REF_TYPES != 0 */ + { + read_leb_uint32(p, p_end, table_index); + bh_assert(table_index + < module->import_table_count + module->table_count); - for (j = 0; j < function_count; j++) { - read_leb_uint32(p, p_end, function_index); - bh_assert(function_index < module->import_function_count - + module->function_count); - table_segment->func_indexes[j] = function_index; + table_segment->table_index = table_index; + + /* initialize expression */ + if (!load_init_expr(&p, p_end, &(table_segment->base_offset), + VALUE_TYPE_I32, error_buf, error_buf_size)) + return false; + + read_leb_uint32(p, p_end, function_count); + table_segment->function_count = function_count; + total_size = sizeof(uint32) * (uint64)function_count; + if (total_size > 0 + && !(table_segment->func_indexes = (uint32 *)loader_malloc( + total_size, error_buf, error_buf_size))) { + return false; + } + + if (!load_func_index_vec(&p, p_end, module, table_segment, + table_segment->mode == 0 ? false + : true, + error_buf, error_buf_size)) + return false; } } } @@ -1437,9 +1749,48 @@ load_user_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module, return true; } +#if WASM_ENABLE_REF_TYPES != 0 static bool -wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, - char *error_buf, uint32 error_buf_size); +get_table_elem_type(const WASMModule *module, + uint32 table_idx, uint8 *p_elem_type, + char *error_buf, uint32 error_buf_size) +{ + if (!check_table_index(module, table_idx, error_buf, error_buf_size)) { + return false; + } + + if (p_elem_type) { + if (table_idx < module->import_table_count) + *p_elem_type = module->import_tables[table_idx].u.table.elem_type; + else + *p_elem_type = module->tables[module->import_table_count + + table_idx].elem_type; + } + return true; +} + +static bool +get_table_seg_elem_type(const WASMModule *module, + uint32 table_seg_idx, uint8 *p_elem_type, + char *error_buf, uint32 error_buf_size) +{ + if (table_seg_idx >= module->table_seg_count) { + return false; + } + + if (p_elem_type) { + *p_elem_type = module->table_segments[table_seg_idx].elem_type; + } + return true; +} +#endif + +static bool +wasm_loader_prepare_bytecode(WASMModule *module, + WASMFunction *func, + uint32 cur_func_idx, + char *error_buf, + uint32 error_buf_size); #if WASM_ENABLE_FAST_INTERP != 0 && WASM_ENABLE_LABELS_AS_VALUES != 0 void ** @@ -1738,7 +2089,7 @@ load_from_sections(WASMModule *module, WASMSection *sections, for (i = 0; i < module->function_count; i++) { WASMFunction *func = module->functions[i]; - if (!wasm_loader_prepare_bytecode(module, func, + if (!wasm_loader_prepare_bytecode(module, func, i, error_buf, error_buf_size)) { return false; } @@ -2236,7 +2587,46 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, case WASM_OP_DROP_64: case WASM_OP_SELECT_64: break; +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_SELECT_T: + if (!wasm_get_ref_types_flag()) { + return false; + } + skip_leb_uint32(p, p_end); /* vec length */ + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); /* typeidx */ + break; + case WASM_OP_TABLE_GET: + case WASM_OP_TABLE_SET: + if (!wasm_get_ref_types_flag()) { + return false; + } + + skip_leb_uint32(p, p_end); /* table index */ + break; + case WASM_OP_REF_NULL: + if (!wasm_get_ref_types_flag()) { + return false; + } + + CHECK_BUF(p, p_end, 1); + u8 = read_uint8(p); /* type */ + break; + case WASM_OP_REF_IS_NULL: + if (!wasm_get_ref_types_flag()) { + return false; + } + + break; + case WASM_OP_REF_FUNC: + if (!wasm_get_ref_types_flag()) { + return false; + } + + skip_leb_uint32(p, p_end); /* func index */ + break; +#endif /* WASM_ENABLE_REF_TYPES */ case WASM_OP_GET_LOCAL: case WASM_OP_SET_LOCAL: case WASM_OP_TEE_LOCAL: @@ -2463,6 +2853,33 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache, p++; break; #endif +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + case WASM_OP_TABLE_COPY: + if (!wasm_get_ref_types_flag()) { + return false; + } + /* tableidx */ + skip_leb_uint32(p, p_end); + /* elemidx */ + skip_leb_uint32(p, p_end); + break; + case WASM_OP_ELEM_DROP: + if (!wasm_get_ref_types_flag()) { + return false; + } + /* elemidx */ + skip_leb_uint32(p, p_end); + break; + case WASM_OP_TABLE_SIZE: + case WASM_OP_TABLE_GROW: + case WASM_OP_TABLE_FILL: + if (!wasm_get_ref_types_flag()) { + return false; + } + skip_leb_uint32(p, p_end); /* table idx */ + break; +#endif /* WASM_ENABLE_REF_TYPES */ default: bh_assert(0); break; @@ -2708,10 +3125,8 @@ static bool check_stack_top_values(uint8 *frame_ref, int32 stack_cell_num, uint8 type, char *error_buf, uint32 error_buf_size) { - bh_assert(!(((type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) - && stack_cell_num < 1) - || ((type == VALUE_TYPE_I64 || type == VALUE_TYPE_F64) - && stack_cell_num < 2))); + bh_assert(!((is_32bit_type(type) && stack_cell_num < 1) + || (is_64bit_type(type) && stack_cell_num < 2))); bh_assert(!((type == VALUE_TYPE_I32 && *(frame_ref - 1) != REF_I32) || (type == VALUE_TYPE_F32 && *(frame_ref - 1) != REF_F32) @@ -2831,9 +3246,7 @@ wasm_loader_push_frame_ref(WASMLoaderContext *ctx, uint8 type, if (ctx->stack_cell_num > ctx->max_stack_cell_num) ctx->max_stack_cell_num = ctx->stack_cell_num; - if (type == VALUE_TYPE_I32 - || type == VALUE_TYPE_F32 - || type == VALUE_TYPE_ANY) + if (is_32bit_type(type)) return true; if (!check_stack_push(ctx, error_buf, error_buf_size)) @@ -2867,9 +3280,7 @@ wasm_loader_pop_frame_ref(WASMLoaderContext *ctx, uint8 type, ctx->frame_ref--; ctx->stack_cell_num--; - if (type == VALUE_TYPE_I32 - || type == VALUE_TYPE_F32 - || *ctx->frame_ref == VALUE_TYPE_ANY) + if (is_32bit_type(type) || *ctx->frame_ref == VALUE_TYPE_ANY) return true; ctx->frame_ref--; @@ -3202,8 +3613,7 @@ preserve_referenced_local(WASMLoaderContext *loader_ctx, uint8 opcode, if (loader_ctx->p_code_compiled) { bh_assert(preserved_offset != (int16)local_index); } - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) { + if (is_32bit_type(local_type)) { /* Only increase preserve offset in the second traversal */ if (loader_ctx->p_code_compiled) loader_ctx->preserved_local_offset++; @@ -3221,7 +3631,7 @@ preserve_referenced_local(WASMLoaderContext *loader_ctx, uint8 opcode, loader_ctx->frame_offset_bottom[i] = preserved_offset; } - if (cur_type == VALUE_TYPE_I32 || cur_type == VALUE_TYPE_F32) + if (is_32bit_type(cur_type)) i++; else i += 2; @@ -3258,7 +3668,7 @@ preserve_local_for_block(WASMLoaderContext *loader_ctx, uint8 opcode, return false; } - if (cur_type == VALUE_TYPE_I32 || cur_type == VALUE_TYPE_F32) { + if (is_32bit_type(cur_type == VALUE_TYPE_I32)) { i++; } else { @@ -3420,7 +3830,7 @@ wasm_loader_push_frame_offset(WASMLoaderContext *ctx, uint8 type, ctx->max_dynamic_offset = ctx->dynamic_offset; } - if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) + if (is_32bit_type(type)) return true; if (ctx->p_code_compiled == NULL) { @@ -3459,7 +3869,7 @@ wasm_loader_pop_frame_offset(WASMLoaderContext *ctx, uint8 type, if (type == VALUE_TYPE_VOID) return true; - if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32) { + if (is_32bit_type(type)) { /* Check the offset stack bottom to ensure the frame offset stack will not go underflow. But we don't thrown error and return true here, because the error msg should be @@ -3559,6 +3969,10 @@ wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, if ((type == c->value_type) && ((type == VALUE_TYPE_I64 && *(int64*)value == c->value.i64) || (type == VALUE_TYPE_I32 && *(int32*)value == c->value.i32) +#if WASM_ENABLE_REF_TYPES != 0 + || (type == VALUE_TYPE_FUNCREF && *(int32*)value == c->value.i32) + || (type == VALUE_TYPE_EXTERNREF && *(int32*)value == c->value.i32) +#endif || (type == VALUE_TYPE_F64 && (0 == memcmp(value, &(c->value.f64), sizeof(float64)))) || (type == VALUE_TYPE_F32 @@ -3602,6 +4016,13 @@ wasm_loader_get_const_offset(WASMLoaderContext *ctx, uint8 type, c->value.i32 = *(int32*)value; ctx->const_cell_num ++; break; +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_EXTERNREF: + case VALUE_TYPE_FUNCREF: + c->value.i32 = *(int32*)value; + ctx->const_cell_num ++; + break; +#endif default: break; } @@ -3662,6 +4083,13 @@ fail: goto fail; \ } while (0) +#define PUSH_FUNCREF() do { \ + if (!wasm_loader_push_frame_ref_offset(loader_ctx, VALUE_TYPE_FUNCREF,\ + disable_emit, operand_offset,\ + error_buf, error_buf_size)) \ + goto fail; \ + } while (0) + #define POP_I32() do { \ if (!wasm_loader_pop_frame_ref_offset(loader_ctx, VALUE_TYPE_I32, \ error_buf, error_buf_size)) \ @@ -3742,6 +4170,12 @@ fail: goto fail; \ } while (0) +#define PUSH_FUNCREF() do { \ + if (!(wasm_loader_push_frame_ref(loader_ctx, VALUE_TYPE_FUNCREF,\ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + #define POP_I32() do { \ if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_I32, \ error_buf, error_buf_size))) \ @@ -3766,6 +4200,12 @@ fail: goto fail; \ } while (0) +#define POP_FUNCREF() do { \ + if (!(wasm_loader_pop_frame_ref(loader_ctx, VALUE_TYPE_FUNCREF, \ + error_buf, error_buf_size))) \ + goto fail; \ + } while (0) + #define POP_AND_PUSH(type_pop, type_push) do { \ if (!(wasm_loader_push_pop_frame_ref(loader_ctx, 1, \ type_push, type_pop, \ @@ -3983,17 +4423,27 @@ fail: + module->memory_count > 0); \ } while (0) - static bool is_value_type(uint8 type) { - return type == VALUE_TYPE_I32 || - type == VALUE_TYPE_I64 || - type == VALUE_TYPE_F32 || - type == VALUE_TYPE_F64 || - type == VALUE_TYPE_VOID; + if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_I64 + || type == VALUE_TYPE_F32 || type == VALUE_TYPE_F64 +#if WASM_ENABLE_REF_TYPES != 0 + || (wasm_get_ref_types_flag() + && (type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF)) +#endif + ) + return true; + return false; } +static bool +is_byte_a_type(uint8 type) +{ + return is_value_type(type) || (type == VALUE_TYPE_VOID); +} + + static bool wasm_loader_check_br(WASMLoaderContext *loader_ctx, uint32 depth, char *error_buf, uint32 error_buf_size) @@ -4285,8 +4735,11 @@ fail: } while (0) static bool -wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func, - char *error_buf, uint32 error_buf_size) +wasm_loader_prepare_bytecode(WASMModule *module, + WASMFunction *func, + uint32 cur_func_idx, + char *error_buf, + uint32 error_buf_size) { uint8 *p = func->code, *p_end = func->code + func->code_size, *p_org; uint32 param_count, local_count, global_count; @@ -4388,7 +4841,7 @@ handle_op_block_and_loop: BlockType block_type; value_type = read_uint8(p); - if (is_value_type(value_type)) { + if (is_byte_a_type(value_type)) { /* If the first byte is one of these special values: * 0x40/0x7F/0x7E/0x7D/0x7C, take it as the type of * the single return value. */ @@ -4432,7 +4885,8 @@ handle_op_block_and_loop: #if WASM_ENABLE_FAST_INTERP != 0 if (opcode == WASM_OP_BLOCK) { skip_label(); - } else if (opcode == WASM_OP_LOOP) { + } + else if (opcode == WASM_OP_LOOP) { skip_label(); if (BLOCK_HAS_PARAM(block_type)) { /* Make sure params are in dynamic space */ @@ -4444,7 +4898,8 @@ handle_op_block_and_loop: } (loader_ctx->frame_csp - 1)->code_compiled = loader_ctx->p_code_compiled; - } else if (opcode == WASM_OP_IF) { + } + else if (opcode == WASM_OP_IF) { /* If block has parameters, we should make sure they are in * dynamic space. Otherwise, when else branch is missing, * the later opcode may consume incorrect operand offset. @@ -4755,21 +5210,39 @@ handle_op_block_and_loop: { int32 idx; WASMType *func_type; - uint32 type_idx; + uint32 type_idx, table_idx; bh_assert(module->import_table_count + module->table_count > 0); read_leb_uint32(p, p_end, type_idx); + +#if WASM_ENABLE_REF_TYPES != 0 + if (!wasm_get_ref_types_flag()) { + CHECK_BUF(p, p_end, 1); + table_idx = read_uint8(p); + } + else { + read_leb_uint32(p, p_end, table_idx); + } + +#else + CHECK_BUF(p, p_end, 1); + table_idx = read_uint8(p); +#endif + if (!check_table_index(module, table_idx, error_buf, + error_buf_size)) { + goto fail; + } + + #if WASM_ENABLE_FAST_INTERP != 0 - /* we need to emit func_idx before arguments */ + /* we need to emit before arguments */ emit_uint32(loader_ctx, type_idx); + emit_uint32(loader_ctx, table_idx); #endif - /* reserved byte 0x00 */ - bh_assert(*p == 0x00); - p++; - + /* skip elem idx */ POP_I32(); bh_assert(type_idx < module->type_count); @@ -4936,6 +5409,216 @@ handle_op_block_and_loop: break; } +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_SELECT_T: + { + uint8 vec_len, ref_type; + + if (!wasm_get_ref_types_flag()) { + goto fail; + } + + read_leb_uint32(p, p_end, vec_len); + if (!vec_len) { + set_error_buf(error_buf, error_buf_size, + "invalid result arity"); + goto fail; + } + + CHECK_BUF(p, p_end, 1); + ref_type = read_uint8(p); + if (!is_value_type(ref_type)) { + set_error_buf(error_buf, error_buf_size, + "unknown value type"); + goto fail; + } + + POP_I32(); + +#if WASM_ENABLE_FAST_INTERP != 0 + if (loader_ctx->p_code_compiled) { + uint8 opcode_tmp = WASM_OP_SELECT; + uint8 *p_code_compiled_tmp = + loader_ctx->p_code_compiled - 2; + + if (ref_type == VALUE_TYPE_F64 + || ref_type == VALUE_TYPE_I64) + opcode_tmp = WASM_OP_SELECT_64; + +#if WASM_ENABLE_LABELS_AS_VALUES != 0 +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(void**)(p_code_compiled_tmp - sizeof(void*)) = + handle_table[opcode_tmp]; +#else + int32 offset = (int32) + ((uint8*)handle_table[opcode_tmp] + - (uint8*)handle_table[0]); + if (!(offset >= INT16_MIN && offset < INT16_MAX)) { + set_error_buf(error_buf, error_buf_size, + "pre-compiled label offset out of range"); + goto fail; + } + *(int16*)(p_code_compiled_tmp - sizeof(int16)) = + (int16)offset; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#else /* else of WASM_ENABLE_LABELS_AS_VALUES */ +#if WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS != 0 + *(p_code_compiled_tmp - 1) = opcode_tmp; +#else + *(p_code_compiled_tmp - 2) = opcode_tmp; +#endif /* end of WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS */ +#endif /* end of WASM_ENABLE_LABELS_AS_VALUES */ + } +#endif /* WASM_ENABLE_FAST_INTERP != 0 */ + +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(ref_type); + POP_TYPE(ref_type); + POP_OFFSET_TYPE(ref_type); + POP_TYPE(ref_type); + PUSH_OFFSET_TYPE(ref_type); + PUSH_TYPE(ref_type); +#else + POP2_AND_PUSH(ref_type, ref_type); +#endif /* WASM_ENABLE_FAST_INTERP != 0 */ + + (void)vec_len; + break; + } + + /* table.get x. tables[x]. [i32] -> [t] */ + /* table.set x. tables[x]. [i32 t] -> [] */ + case WASM_OP_TABLE_GET: + case WASM_OP_TABLE_SET: + { + uint8 decl_ref_type; + uint32 table_idx; + + if (!wasm_get_ref_types_flag()) { + goto fail;; + } + + read_leb_uint32(p, p_end, table_idx); + if (!get_table_elem_type(module, table_idx, &decl_ref_type, + error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + + if (opcode == WASM_OP_TABLE_GET) { + POP_I32(); +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(decl_ref_type); +#endif + PUSH_TYPE(decl_ref_type); + } + else { +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(decl_ref_type); +#endif + POP_TYPE(decl_ref_type); + POP_I32(); + } + break; + } + case WASM_OP_REF_NULL: + { + uint8 ref_type; + + if (!wasm_get_ref_types_flag()) { + goto fail;; + } + + CHECK_BUF(p, p_end, 1); + ref_type = read_uint8(p); + if (ref_type != VALUE_TYPE_FUNCREF + && ref_type != VALUE_TYPE_EXTERNREF) { + set_error_buf(error_buf, error_buf_size, + "unknown value type"); + goto fail; + } +#if WASM_ENABLE_FAST_INTERP != 0 + PUSH_OFFSET_TYPE(ref_type); +#endif + PUSH_TYPE(ref_type); + break; + } + case WASM_OP_REF_IS_NULL: + { + if (!wasm_get_ref_types_flag()) { + goto fail;; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + if (!wasm_loader_pop_frame_ref_offset( + loader_ctx, VALUE_TYPE_FUNCREF, + error_buf, error_buf_size) + && !wasm_loader_pop_frame_ref_offset( + loader_ctx, VALUE_TYPE_EXTERNREF, + error_buf, error_buf_size)) { + goto fail; + } +#else + if (!wasm_loader_pop_frame_ref( + loader_ctx, VALUE_TYPE_FUNCREF, + error_buf, error_buf_size) + && !wasm_loader_pop_frame_ref( + loader_ctx, VALUE_TYPE_EXTERNREF, + error_buf, error_buf_size)) { + goto fail; + } +#endif + PUSH_I32(); + break; + } + case WASM_OP_REF_FUNC: + { + uint32 func_idx = 0; + if (!wasm_get_ref_types_flag()) { + goto fail;; + } + + read_leb_uint32(p, p_end, func_idx); + + if (!check_function_index(module, func_idx, error_buf, + error_buf_size)) { + goto fail; + } + + if (func_idx == cur_func_idx) { + WASMTableSeg *table_seg = module->table_segments; + bool func_declared = false; + uint32 j; + + /* Check whether current function is declared */ + for (i = 0; i < module->table_seg_count; i++, table_seg++) { + if (table_seg->elem_type == VALUE_TYPE_FUNCREF + && wasm_elem_is_declarative(table_seg->mode)) { + for (j =0; j < table_seg->function_count; j++) { + if (table_seg->func_indexes[j] == cur_func_idx) { + func_declared = true; + break; + } + } + } + } + if (!func_declared) { + set_error_buf(error_buf, error_buf_size, + "undeclared function reference"); + goto fail; + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, func_idx); +#endif + PUSH_FUNCREF(); + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ + case WASM_OP_GET_LOCAL: { p_org = p - 1; @@ -4952,8 +5635,7 @@ handle_op_block_and_loop: #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) if (local_offset < 0x80) { *p_org++ = EXT_OP_GET_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) + if (is_32bit_type(local_type)) *p_org++ = (uint8)local_offset; else *p_org++ = (uint8)(local_offset | 0x80); @@ -4992,8 +5674,7 @@ handle_op_block_and_loop: loader_ctx->dynamic_offset -= 2; } else { - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) { + if (is_32bit_type(local_type)) { emit_label(EXT_OP_SET_LOCAL_FAST); emit_byte(loader_ctx, (uint8)local_offset); } @@ -5012,8 +5693,7 @@ handle_op_block_and_loop: #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) if (local_offset < 0x80) { *p_org++ = EXT_OP_SET_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) + if (is_32bit_type(local_type)) *p_org++ = (uint8)local_offset; else *p_org++ = (uint8)(local_offset | 0x80); @@ -5050,8 +5730,7 @@ handle_op_block_and_loop: if (local_offset < 256) { skip_label(); - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) { + if (is_32bit_type(local_type)) { emit_label(EXT_OP_TEE_LOCAL_FAST); emit_byte(loader_ctx, (uint8)local_offset); } @@ -5069,8 +5748,7 @@ handle_op_block_and_loop: #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) if (local_offset < 0x80) { *p_org++ = EXT_OP_TEE_LOCAL_FAST; - if (local_type == VALUE_TYPE_I32 - || local_type == VALUE_TYPE_F32) + if (is_32bit_type(local_type)) *p_org++ = (uint8)local_offset; else *p_org++ = (uint8)(local_offset | 0x80); @@ -5104,8 +5782,7 @@ handle_op_block_and_loop: } #endif #else /* else of WASM_ENABLE_FAST_INTERP */ - if (global_type == VALUE_TYPE_I64 - || global_type == VALUE_TYPE_F64) { + if (is_64bit_type(global_type)) { skip_label(); emit_label(WASM_OP_GET_GLOBAL_64); } @@ -5140,8 +5817,7 @@ handle_op_block_and_loop: #if WASM_ENABLE_FAST_INTERP == 0 #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0) - if (global_type == VALUE_TYPE_I64 - || global_type == VALUE_TYPE_F64) { + if (is_64bit_type(global_type)) { *p_org = WASM_OP_SET_GLOBAL_64; } else if (module->aux_stack_size > 0 @@ -5150,8 +5826,7 @@ handle_op_block_and_loop: } #endif #else /* else of WASM_ENABLE_FAST_INTERP */ - if (global_type == VALUE_TYPE_I64 - || global_type == VALUE_TYPE_F64) { + if (is_64bit_type(global_type)) { skip_label(); emit_label(WASM_OP_SET_GLOBAL_64); } @@ -5637,8 +6312,158 @@ handle_op_block_and_loop: POP_I32(); POP_I32(); break; - /* TODO: to support bulk table operation */ #endif /* WASM_ENABLE_BULK_MEMORY */ +#if WASM_ENABLE_REF_TYPES != 0 + case WASM_OP_TABLE_INIT: + { + uint8 seg_ref_type, tbl_ref_type; + uint32 table_seg_idx, table_idx; + + if (!wasm_get_ref_types_flag()) { + goto fail; + } + + read_leb_uint32(p, p_end, table_seg_idx); + read_leb_uint32(p, p_end, table_idx); + + if (!get_table_elem_type(module, table_idx, &tbl_ref_type, + error_buf, error_buf_size)) + goto fail; + + if (!get_table_seg_elem_type(module, table_seg_idx, + &seg_ref_type, error_buf, + error_buf_size)) + goto fail; + + if (seg_ref_type != tbl_ref_type) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_seg_idx); + emit_uint32(loader_ctx, table_idx); +#endif + POP_I32(); + POP_I32(); + POP_I32(); + break; + } + case WASM_OP_ELEM_DROP: + { + uint32 table_seg_idx; + if (!wasm_get_ref_types_flag()) { + goto fail; + } + + read_leb_uint32(p, p_end, table_seg_idx); + if (!get_table_seg_elem_type(module, table_seg_idx, NULL, + error_buf, error_buf_size)) + goto fail; +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_seg_idx); +#endif + break; + } + case WASM_OP_TABLE_COPY: + { + uint8 src_ref_type, dst_ref_type; + uint32 src_tbl_idx, dst_tbl_idx; + + if (!wasm_get_ref_types_flag()) { + goto fail; + } + + read_leb_uint32(p, p_end, src_tbl_idx); + if (!get_table_elem_type(module, src_tbl_idx, &src_ref_type, + error_buf, error_buf_size)) + goto fail; + + read_leb_uint32(p, p_end, dst_tbl_idx); + if (!get_table_elem_type(module, dst_tbl_idx, &dst_ref_type, + error_buf, error_buf_size)) + goto fail; + + if (src_ref_type != dst_ref_type) { + set_error_buf(error_buf, error_buf_size, + "type mismatch"); + goto fail; + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, src_tbl_idx); + emit_uint32(loader_ctx, dst_tbl_idx); +#endif + POP_I32(); + POP_I32(); + POP_I32(); + break; + } + case WASM_OP_TABLE_SIZE: + { + uint32 table_idx; + if (!wasm_get_ref_types_flag()) { + goto fail; + } + + read_leb_uint32(p, p_end, table_idx); + /* TODO: shall we create a new function to check + table idx instead of using below function? */ + if (!get_table_elem_type(module, table_idx, NULL, + error_buf, error_buf_size)) + goto fail; + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + + PUSH_I32(); + break; + } + case WASM_OP_TABLE_GROW: + case WASM_OP_TABLE_FILL: + { + uint8 decl_ref_type; + uint32 table_idx; + + if (!wasm_get_ref_types_flag()) { + goto fail; + } + + read_leb_uint32(p, p_end, table_idx); + if (!get_table_elem_type(module, table_idx, + &decl_ref_type, error_buf, + error_buf_size)) + goto fail; + + if (opcode1 == WASM_OP_TABLE_GROW) { + if (table_idx < module->import_table_count) { + module->import_tables[table_idx] + .u.table.possible_grow = true; + } + else { + module->tables[table_idx - module->import_table_count] + .possible_grow = true; + } + } + +#if WASM_ENABLE_FAST_INTERP != 0 + emit_uint32(loader_ctx, table_idx); +#endif + + POP_I32(); +#if WASM_ENABLE_FAST_INTERP != 0 + POP_OFFSET_TYPE(decl_ref_type); +#endif + POP_TYPE(decl_ref_type); + if (opcode1 == WASM_OP_TABLE_GROW) + PUSH_I32(); + else + POP_I32(); + break; + } +#endif /* WASM_ENABLE_REF_TYPES */ default: bh_assert(0); break; @@ -5815,7 +6640,8 @@ handle_op_block_and_loop: bh_memcpy_s(func_const, (uint32)(func_const_end - func_const), &(c->value.f64), (uint32)sizeof(int64)); func_const += sizeof(int64); - } else { + } + else { bh_memcpy_s(func_const, (uint32)(func_const_end - func_const), &(c->value.f32), (uint32)sizeof(int32)); func_const += sizeof(int32); @@ -5843,5 +6669,24 @@ fail: (void)p_org; (void)mem_offset; (void)align; +#if WASM_ENABLE_BULK_MEMORY != 0 + (void)segment_index; +#endif return return_value; } + +#if WASM_ENABLE_REF_TYPES != 0 +static bool ref_types_flag = true; + +void +wasm_set_ref_types_flag(bool enable) +{ + ref_types_flag = enable; +} + +bool +wasm_get_ref_types_flag() +{ + return ref_types_flag; +} +#endif diff --git a/core/iwasm/interpreter/wasm_opcode.h b/core/iwasm/interpreter/wasm_opcode.h index fa0fed74a..5f5508230 100644 --- a/core/iwasm/interpreter/wasm_opcode.h +++ b/core/iwasm/interpreter/wasm_opcode.h @@ -47,11 +47,11 @@ typedef enum WASMOpcode { /* parametric instructions */ WASM_OP_DROP = 0x1a, /* drop */ WASM_OP_SELECT = 0x1b, /* select */ + WASM_OP_SELECT_T = 0x1c, /* select t */ - WASM_OP_UNUSED_0x1c = 0x1c, - WASM_OP_UNUSED_0x1d = 0x1d, - WASM_OP_UNUSED_0x1e = 0x1e, - WASM_OP_UNUSED_0x1f = 0x1f, + WASM_OP_GET_GLOBAL_64 = 0x1d, + WASM_OP_SET_GLOBAL_64 = 0x1e, + WASM_OP_SET_GLOBAL_AUX_STACK = 0x1f, /* variable instructions */ WASM_OP_GET_LOCAL = 0x20, /* get_local */ @@ -60,9 +60,9 @@ typedef enum WASMOpcode { WASM_OP_GET_GLOBAL = 0x23, /* get_global */ WASM_OP_SET_GLOBAL = 0x24, /* set_global */ - WASM_OP_GET_GLOBAL_64 = 0x25, - WASM_OP_SET_GLOBAL_64 = 0x26, - WASM_OP_SET_GLOBAL_AUX_STACK = 0x27, + WASM_OP_TABLE_GET = 0x25, /* table.get */ + WASM_OP_TABLE_SET = 0x26, /* table.set */ + WASM_OP_UNUSED_0x27 = 0x27, /* memory instructions */ WASM_OP_I32_LOAD = 0x28, /* i32.load */ @@ -256,10 +256,16 @@ typedef enum WASMOpcode { EXT_OP_COPY_STACK_TOP = 0xcc, EXT_OP_COPY_STACK_TOP_I64 = 0xcd, EXT_OP_COPY_STACK_VALUES = 0xce, - EXT_OP_BLOCK = 0xcf, /* block with blocktype */ - EXT_OP_LOOP = 0xd0, /* loop with blocktype */ - EXT_OP_IF = 0xd1, /* if with blocktype */ - WASM_OP_IMPDEP = 0xd2, + + WASM_OP_IMPDEP = 0xcf, + + WASM_OP_REF_NULL = 0xd0, /* ref.null */ + WASM_OP_REF_IS_NULL = 0xd1, /* ref.is_null */ + WASM_OP_REF_FUNC = 0xd2, /* ref.func */ + + EXT_OP_BLOCK = 0xd3, /* block with blocktype */ + EXT_OP_LOOP = 0xd4, /* loop with blocktype */ + EXT_OP_IF = 0xd5, /* if with blocktype */ /* Post-MVP extend op prefix */ WASM_OP_MISC_PREFIX = 0xfc, @@ -276,15 +282,16 @@ typedef enum WASMMiscEXTOpcode { WASM_OP_I64_TRUNC_SAT_U_F32 = 0x05, WASM_OP_I64_TRUNC_SAT_S_F64 = 0x06, WASM_OP_I64_TRUNC_SAT_U_F64 = 0x07, -#if WASM_ENABLE_BULK_MEMORY != 0 WASM_OP_MEMORY_INIT = 0x08, WASM_OP_DATA_DROP = 0x09, WASM_OP_MEMORY_COPY = 0x0a, WASM_OP_MEMORY_FILL = 0x0b, WASM_OP_TABLE_INIT = 0x0c, WASM_OP_ELEM_DROP = 0x0d, - WASM_OP_TABLE_COPY = 0x0e -#endif + WASM_OP_TABLE_COPY = 0x0e, + WASM_OP_TABLE_GROW = 0x0f, + WASM_OP_TABLE_SIZE = 0x10, + WASM_OP_TABLE_FILL = 0x11, } WASMMiscEXTOpcode; typedef enum WASMSimdEXTOpcode { @@ -594,15 +601,6 @@ typedef enum WASMAtomicEXTOpcode { } #endif -/* Opcode prefix controlled by features */ -#if WASM_ENABLE_SHARED_MEMORY != 0 -#define DEF_ATOMIC_PREFIX_HANDLE(_name) \ - _name[WASM_OP_ATOMIC_PREFIX] = \ - HANDLE_OPCODE (WASM_OP_ATOMIC_PREFIX); /* 0xfe */ -#else -#define DEF_ATOMIC_PREFIX_HANDLE(_name) -#endif - /* * Macro used to generate computed goto tables for the C interpreter. */ @@ -638,18 +636,18 @@ static type _name[WASM_INSTRUCTION_NUM] = { \ HANDLE_OPCODE (WASM_OP_UNUSED_0x19), /* 0x19 */ \ HANDLE_OPCODE (WASM_OP_DROP), /* 0x1a */ \ HANDLE_OPCODE (WASM_OP_SELECT), /* 0x1b */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x1c), /* 0x1c */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x1d), /* 0x1d */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x1e), /* 0x1e */ \ - HANDLE_OPCODE (WASM_OP_UNUSED_0x1f), /* 0x1f */ \ + HANDLE_OPCODE (WASM_OP_SELECT_T), /* 0x1c */ \ + HANDLE_OPCODE (WASM_OP_GET_GLOBAL_64), /* 0x1d */ \ + HANDLE_OPCODE (WASM_OP_SET_GLOBAL_64), /* 0x1e */ \ + HANDLE_OPCODE (WASM_OP_SET_GLOBAL_AUX_STACK), /* 0x1f */ \ HANDLE_OPCODE (WASM_OP_GET_LOCAL), /* 0x20 */ \ HANDLE_OPCODE (WASM_OP_SET_LOCAL), /* 0x21 */ \ HANDLE_OPCODE (WASM_OP_TEE_LOCAL), /* 0x22 */ \ HANDLE_OPCODE (WASM_OP_GET_GLOBAL), /* 0x23 */ \ HANDLE_OPCODE (WASM_OP_SET_GLOBAL), /* 0x24 */ \ - HANDLE_OPCODE (WASM_OP_GET_GLOBAL_64), /* 0x25 */ \ - HANDLE_OPCODE (WASM_OP_SET_GLOBAL_64), /* 0x26 */ \ - HANDLE_OPCODE (WASM_OP_SET_GLOBAL_AUX_STACK), /* 0x27 */ \ + HANDLE_OPCODE (WASM_OP_TABLE_GET), /* 0x25 */ \ + HANDLE_OPCODE (WASM_OP_TABLE_SET), /* 0x26 */ \ + HANDLE_OPCODE (WASM_OP_UNUSED_0x27), /* 0x27 */ \ HANDLE_OPCODE (WASM_OP_I32_LOAD), /* 0x28 */ \ HANDLE_OPCODE (WASM_OP_I64_LOAD), /* 0x29 */ \ HANDLE_OPCODE (WASM_OP_F32_LOAD), /* 0x2a */ \ @@ -817,14 +815,19 @@ static type _name[WASM_INSTRUCTION_NUM] = { \ HANDLE_OPCODE (EXT_OP_COPY_STACK_TOP), /* 0xcc */ \ HANDLE_OPCODE (EXT_OP_COPY_STACK_TOP_I64), /* 0xcd */ \ HANDLE_OPCODE (EXT_OP_COPY_STACK_VALUES), /* 0xce */ \ - HANDLE_OPCODE (EXT_OP_BLOCK), /* 0xcf */ \ - HANDLE_OPCODE (EXT_OP_LOOP), /* 0xd0 */ \ - HANDLE_OPCODE (EXT_OP_IF), /* 0xd1 */ \ - HANDLE_OPCODE (WASM_OP_IMPDEP), /* 0xd2 */ \ + HANDLE_OPCODE (WASM_OP_IMPDEP), /* 0xcf */ \ + HANDLE_OPCODE (WASM_OP_REF_NULL), /* 0xd0 */ \ + HANDLE_OPCODE (WASM_OP_REF_IS_NULL), /* 0xd1 */ \ + HANDLE_OPCODE (WASM_OP_REF_FUNC), /* 0xd2 */ \ + HANDLE_OPCODE (EXT_OP_BLOCK), /* 0xd3 */ \ + HANDLE_OPCODE (EXT_OP_LOOP), /* 0xd4 */ \ + HANDLE_OPCODE (EXT_OP_IF), /* 0xd5 */ \ }; \ do { \ _name[WASM_OP_MISC_PREFIX] = \ HANDLE_OPCODE (WASM_OP_MISC_PREFIX); /* 0xfc */ \ - DEF_ATOMIC_PREFIX_HANDLE(_name) \ + _name[WASM_OP_ATOMIC_PREFIX] = \ + HANDLE_OPCODE (WASM_OP_ATOMIC_PREFIX); /* 0xfe */ \ } while (0) #endif /* end of _WASM_OPCODE_H */ + diff --git a/core/iwasm/interpreter/wasm_runtime.c b/core/iwasm/interpreter/wasm_runtime.c index 360958cb0..aa4176fc9 100644 --- a/core/iwasm/interpreter/wasm_runtime.c +++ b/core/iwasm/interpreter/wasm_runtime.c @@ -499,9 +499,12 @@ tables_instantiate(const WASMModule *module, else #endif { - /* it is a built-in table */ - total_size = offsetof(WASMTableInstance, base_addr) - + sizeof(uint32) * (uint64)import->u.table.init_size; + /* it is a built-in table, every module has its own */ + total_size = offsetof(WASMTableInstance, base_addr); + total_size += + import->u.table.possible_grow + ? sizeof(uint32) * (uint64)import->u.table.max_size + : sizeof(uint32) * (uint64)import->u.table.init_size; } if (!(table = tables[table_index++] = runtime_malloc @@ -530,8 +533,15 @@ tables_instantiate(const WASMModule *module, /* instantiate tables from table section */ for (i = 0; i < module->table_count; i++) { - total_size = offsetof(WASMTableInstance, base_addr) + - sizeof(uint32) * (uint64)module->tables[i].init_size; + total_size = offsetof(WASMTableInstance, base_addr); +#if WASM_ENABLE_MULTI_MODULE != 0 + /* in case, a module which imports this table will grow it */ + total_size += sizeof(uint32) * (uint64)module->tables[i].max_size; +#else + total_size += module->tables[i].possible_grow + ? sizeof(uint32) * (uint64)module->tables[i].max_size + : sizeof(uint32) * (uint64)module->tables[i].init_size; +#endif if (!(table = tables[table_index++] = runtime_malloc (total_size, error_buf, error_buf_size))) { tables_deinstantiate(tables, table_count); @@ -764,6 +774,11 @@ globals_instantiate(const WASMModule *module, &(globals[init_expr->u.global_index].initial_value), sizeof(globals[init_expr->u.global_index].initial_value)); } +#if WASM_ENABLE_REF_TYPES != 0 + else if (init_expr->init_expr_type == INIT_EXPR_TYPE_REFNULL_CONST) { + global->initial_value.u32 = (uint32)NULL_REF; + } +#endif else { bh_memcpy_s(&(global->initial_value), sizeof(WASMValue), &(init_expr->u), sizeof(init_expr->u)); @@ -1216,6 +1231,10 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, switch (global->type) { case VALUE_TYPE_I32: case VALUE_TYPE_F32: +#if WASM_ENABLE_REF_TYPES != 0 + case VALUE_TYPE_FUNCREF: + case VALUE_TYPE_EXTERNREF: +#endif *(int32*)global_data = global->initial_value.i32; global_data += sizeof(int32); break; @@ -1285,13 +1304,18 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, .initial_value.i32; } - /* check offset since length might negative */ + /* check offset */ base_offset = (uint32)data_seg->base_offset.u.i32; if (base_offset > memory_size) { LOG_DEBUG("base_offset(%d) > memory_size(%d)", base_offset, memory_size); +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds memory access"); +#else set_error_buf(error_buf, error_buf_size, "data segment does not fit"); +#endif goto fail; } @@ -1300,8 +1324,13 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, if (base_offset + length > memory_size) { LOG_DEBUG("base_offset(%d) + length(%d) > memory_size(%d)", base_offset, length, memory_size); +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds memory access"); +#else set_error_buf(error_buf, error_buf_size, "data segment does not fit"); +#endif goto fail; } @@ -1314,12 +1343,23 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, /* Initialize the table data with table segment section */ module_inst->default_table = module_inst->table_count ? module_inst->tables[0] : NULL; - for (i = 0; i < module->table_seg_count; i++) { + /* in case there is no table */ + for (i = 0; module_inst->table_count > 0 && i < module->table_seg_count; + i++) { WASMTableSeg *table_seg = module->table_segments + i; /* has check it in loader */ WASMTableInstance *table = module_inst->tables[table_seg->table_index]; bh_assert(table); +#if WASM_ENABLE_REF_TYPES != 0 + if (table->elem_type != VALUE_TYPE_FUNCREF + && table->elem_type != VALUE_TYPE_EXTERNREF) { + set_error_buf(error_buf, error_buf_size, + "elements segment does not fit"); + goto fail; + } +#endif + uint32 *table_data = (uint32 *)table->base_addr; #if WASM_ENABLE_MULTI_MODULE != 0 table_data = table->table_inst_linked @@ -1328,11 +1368,20 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, #endif bh_assert(table_data); - /* init vec(funcidx) */ - bh_assert(table_seg->base_offset.init_expr_type - == INIT_EXPR_TYPE_I32_CONST - || table_seg->base_offset.init_expr_type - == INIT_EXPR_TYPE_GET_GLOBAL); +#if WASM_ENABLE_REF_TYPES != 0 + if (!wasm_elem_is_active(table_seg->mode)) + continue; +#endif + + /* init vec(funcidx) or vec(expr) */ + bh_assert( + table_seg->base_offset.init_expr_type == INIT_EXPR_TYPE_I32_CONST + || table_seg->base_offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL +#if WASM_ENABLE_REF_TYPES != 0 + || table_seg->base_offset.init_expr_type == INIT_EXPR_TYPE_FUNCREF_CONST + || table_seg->base_offset.init_expr_type == INIT_EXPR_TYPE_REFNULL_CONST +#endif + ); if (table_seg->base_offset.init_expr_type == INIT_EXPR_TYPE_GET_GLOBAL) { @@ -1349,6 +1398,7 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, "elements segment does not fit"); goto fail; } + table_seg->base_offset.u.i32 = globals[table_seg->base_offset.u.global_index].initial_value.i32; } @@ -1357,8 +1407,13 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, if ((uint32)table_seg->base_offset.u.i32 > table->cur_size) { LOG_DEBUG("base_offset(%d) > table->cur_size(%d)", table_seg->base_offset.u.i32, table->cur_size); +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); +#else set_error_buf(error_buf, error_buf_size, "elements segment does not fit"); +#endif goto fail; } @@ -1367,8 +1422,13 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, if ((uint32)table_seg->base_offset.u.i32 + length > table->cur_size) { LOG_DEBUG("base_offset(%d) + length(%d)> table->cur_size(%d)", table_seg->base_offset.u.i32, length, table->cur_size); +#if WASM_ENABLE_REF_TYPES != 0 + set_error_buf(error_buf, error_buf_size, + "out of bounds table access"); +#else set_error_buf(error_buf, error_buf_size, "elements segment does not fit"); +#endif goto fail; } @@ -1510,6 +1570,10 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst) if (module_inst->global_data) wasm_runtime_free(module_inst->global_data); +#if WASM_ENABLE_REF_TYPES != 0 + wasm_externref_cleanup((WASMModuleInstanceCommon*)module_inst); +#endif + wasm_runtime_free(module_inst); } @@ -1616,8 +1680,16 @@ wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst, } #endif +#if WASM_ENABLE_REF_TYPES != 0 + wasm_runtime_prepare_call_function(exec_env, func); +#endif + ret = wasm_call_function(exec_env, func, argc, argv); +#if WASM_ENABLE_REF_TYPES != 0 + wasm_runtime_finalize_call_function(exec_env, func, ret, argv); +#endif + #if WASM_ENABLE_THREAD_MGR != 0 /* don't destroy the exec_env if it's searched from the cluster */ if (!existing_exec_env) @@ -2025,8 +2097,47 @@ wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count) return true; } +#if WASM_ENABLE_REF_TYPES != 0 +bool +wasm_enlarge_table(WASMModuleInstance *module_inst, + uint32 table_idx, uint32 inc_entries, uint32 init_val) +{ + uint32 entry_count, *new_table_data_start, i; + WASMTableInstance *table_inst; + + if (!inc_entries) { + return true; + } + + bh_assert(table_idx < module_inst->table_count); + table_inst = wasm_get_table_inst(module_inst, table_idx); + if (!table_inst) { + return false; + } + + entry_count = table_inst->cur_size + inc_entries; + /* prevent from integer overflow */ + if (entry_count < table_inst->cur_size + || entry_count > table_inst->max_size) { + return false; + } + + /* fill in */ + new_table_data_start = + (uint32 *)((uint8 *)table_inst + offsetof(WASMTableInstance, base_addr)) + + table_inst->cur_size; + for (i = 0; i < inc_entries; ++i) { + new_table_data_start[i] = init_val; + } + + table_inst->cur_size = entry_count; + return true; +} +#endif /* WASM_ENABLE_REF_TYPES != 0 */ + bool wasm_call_indirect(WASMExecEnv *exec_env, + uint32_t tbl_idx, uint32_t element_indices, uint32_t argc, uint32_t argv[]) { @@ -2039,7 +2150,7 @@ wasm_call_indirect(WASMExecEnv *exec_env, (WASMModuleInstance*)exec_env->module_inst; bh_assert(module_inst); - table_inst = module_inst->default_table; + table_inst = module_inst->tables[tbl_idx]; if (!table_inst) { wasm_set_exception(module_inst, "unknown table"); goto got_exception; @@ -2055,7 +2166,7 @@ wasm_call_indirect(WASMExecEnv *exec_env, * to another module's table **/ function_indices = ((uint32_t*)table_inst->base_addr)[element_indices]; - if (function_indices == 0xFFFFFFFF) { + if (function_indices == NULL_REF) { wasm_set_exception(module_inst, "uninitialized element"); goto got_exception; } @@ -2247,8 +2358,16 @@ wasm_get_module_inst_mem_consumption(const WASMModuleInstance *module_inst, * module_inst->table_count; for (i = 0; i < module_inst->table_count; i++) { WASMTableInstance *table = module_inst->tables[i]; - size = offsetof(WASMTableInstance, base_addr) - + sizeof(uint32) * table->cur_size; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (table->table_inst_linked) { + size = offsetof(WASMTableInstance, base_addr); + } + else +#endif + { + size = offsetof(WASMTableInstance, base_addr) + + sizeof(uint32) * table->cur_size; + } mem_conspn->tables_size += size; } diff --git a/core/iwasm/interpreter/wasm_runtime.h b/core/iwasm/interpreter/wasm_runtime.h index 3d7d47c6e..5f45f183c 100644 --- a/core/iwasm/interpreter/wasm_runtime.h +++ b/core/iwasm/interpreter/wasm_runtime.h @@ -60,7 +60,7 @@ struct WASMMemoryInstance { }; struct WASMTableInstance { - /* The element type, TABLE_ELEM_TYPE_ANY_FUNC currently */ + /* The element type, VALUE_TYPE_FUNCREF/EXTERNREF currently */ uint8 elem_type; /* Current size */ uint32 cur_size; @@ -376,6 +376,7 @@ wasm_enlarge_memory(WASMModuleInstance *module, uint32 inc_page_count); bool wasm_call_indirect(WASMExecEnv *exec_env, + uint32_t tbl_idx, uint32_t element_indices, uint32_t argc, uint32_t argv[]); @@ -397,6 +398,45 @@ void wasm_get_module_inst_mem_consumption(const WASMModuleInstance *module, WASMModuleInstMemConsumption *mem_conspn); +#if WASM_ENABLE_REF_TYPES != 0 +static inline bool +wasm_elem_is_active(uint32 mode) +{ + return (mode & 0x1) == 0x0; +} + +static inline bool +wasm_elem_is_passive(uint32 mode) +{ + return (mode & 0x1) == 0x1; +} + +static inline bool +wasm_elem_is_declarative(uint32 mode) +{ + return (mode & 0x3) == 0x3; +} + +bool +wasm_enlarge_table(WASMModuleInstance *module_inst, + uint32 table_idx, uint32 inc_entries, uint32 init_val); +#endif /* WASM_ENABLE_REF_TYPES != 0 */ + +static inline WASMTableInstance * +wasm_get_table_inst(const WASMModuleInstance *module_inst, + const uint32 tbl_idx) +{ + /* careful, it might be a table in another module */ + WASMTableInstance *tbl_inst = module_inst->tables[tbl_idx]; +#if WASM_ENABLE_MULTI_MODULE != 0 + if (tbl_inst->table_inst_linked) { + tbl_inst = tbl_inst->table_inst_linked; + } +#endif + bh_assert(tbl_inst); + return tbl_inst; +} + #if WASM_ENABLE_DUMP_CALL_STACK != 0 void wasm_interp_dump_call_stack(struct WASMExecEnv *exec_env); diff --git a/core/shared/utils/bh_hashmap.c b/core/shared/utils/bh_hashmap.c index f774546a9..7ed036019 100644 --- a/core/shared/utils/bh_hashmap.c +++ b/core/shared/utils/bh_hashmap.c @@ -307,7 +307,8 @@ bh_hash_map_get_elem_struct_size() } bool -bh_hash_map_traverse(HashMap *map, TraverseCallbackFunc callback) +bh_hash_map_traverse(HashMap *map, TraverseCallbackFunc callback, + void *user_data) { uint32 index; HashMapElem *elem, *next; @@ -325,7 +326,7 @@ bh_hash_map_traverse(HashMap *map, TraverseCallbackFunc callback) elem = map->elements[index]; while (elem) { next = elem->next; - callback(elem->key, elem->value); + callback(elem->key, elem->value, user_data); elem = next; } } diff --git a/core/shared/utils/bh_hashmap.h b/core/shared/utils/bh_hashmap.h index 8cb02300c..de4eefcfc 100644 --- a/core/shared/utils/bh_hashmap.h +++ b/core/shared/utils/bh_hashmap.h @@ -34,7 +34,7 @@ typedef void (*ValueDestroyFunc)(void *key); /* traverse callback function: auto called when traverse every hash element */ -typedef void (*TraverseCallbackFunc)(void *key, void *value); +typedef void (*TraverseCallbackFunc)(void *key, void *value, void *user_data); /** * Create a hash map. @@ -150,14 +150,16 @@ bh_hash_map_get_elem_struct_size(); * Traverse the hash map and call the callback function * * @param map the hash map to traverse - * @callback the function to be called for every element + * @param callback the function to be called for every element + * @param user_data the argument to be passed to the callback function * * @return true if success, false otherwise * Note: if the hash map has lock, the map will be locked during traverse, * keep the callback function as simple as possible. */ bool -bh_hash_map_traverse(HashMap *map, TraverseCallbackFunc callback); +bh_hash_map_traverse(HashMap *map, TraverseCallbackFunc callback, + void *user_data); #ifdef __cplusplus } diff --git a/doc/build_wamr.md b/doc/build_wamr.md index 3ea474289..f9692bb81 100644 --- a/doc/build_wamr.md +++ b/doc/build_wamr.md @@ -135,6 +135,9 @@ Currently we only profile the memory consumption of module, module_instance and > > and then use `cmake -DWAMR_BH_VPRINTF=my_vprintf ..` to pass the callback function, or add `BH_VPRINTF=my_vprintf` macro for the compiler, e.g. add line `add_defintions(-DBH_VPRINTF=my_vprintf)` in CMakeListst.txt. +#### **Enable reference types feature** +- **WAMR_BUILD_REF_TYPES**=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: diff --git a/doc/export_native_api.md b/doc/export_native_api.md index 8b21d4a12..e507c5c41 100644 --- a/doc/export_native_api.md +++ b/doc/export_native_api.md @@ -85,7 +85,7 @@ The function signature field in **NativeSymbol** structure is a string for descr Each letter in the "()" represents a parameter type, and the one following after ")" represents the return value type. The meaning of each letter: -- '**i**': i32 +- '**i**': i32 or externref (for externref, developer should use `wasm_externref_obj2ref()` to map host object to externref index firstly) - '**I**': i64 - '**f**': f32 - '**F**': f64 diff --git a/doc/ref_types.md b/doc/ref_types.md new file mode 100644 index 000000000..ed37411f3 --- /dev/null +++ b/doc/ref_types.md @@ -0,0 +1,22 @@ +# WAMR reference-types introduction + +WebAssembly [reference-types](https://github.com/WebAssembly/reference-types) proposal introduces the new type `externref` and makes it easier and more efficient to interoperate with host environment, allowing host references to be represented directly by type externref. And WASM modules can talk about host references directly, rather than requiring external glue code running in the host. + +WAMR implements the reference-types proposal, allowing developer to pass the host object to WASM application and then restore and access the host object in native lib. In WAMR internal, the external host object is represented as externref index with `uint32` type, developer must firstly map the host object of `void *` type to the externref index, and then pass the index to the function to called as the function's externref argument. + +Currently WAMR provides APIs as below: +```C +bool +wasm_externref_obj2ref(wasm_module_inst_t module_inst, + void *extern_obj, uint32_t *p_externref_idx); + +WASM_RUNTIME_API_EXTERN bool +wasm_externref_ref2obj(uint32_t externref_idx, void **p_extern_obj); + +WASM_RUNTIME_API_EXTERN bool +wasm_externref_retain(uint32 externref_idx); +``` + +The `wasm_externref_obj2ref()` API is used to map the host object to the externref index, and the `wasm_externref_ref2obj()` API is used to retrieve the original host object mapped. The `wasm_externref_retain()` API is to retain the host object if we don't want the object to be cleaned when it isn't used during externref object reclaim. + +Please ref to the [sample](../samples/ref-types) for more details. diff --git a/product-mini/platforms/linux/CMakeLists.txt b/product-mini/platforms/linux/CMakeLists.txt index cefb9bff5..231bd0720 100644 --- a/product-mini/platforms/linux/CMakeLists.txt +++ b/product-mini/platforms/linux/CMakeLists.txt @@ -85,6 +85,11 @@ if (NOT DEFINED WAMR_BUILD_SIMD) set (WAMR_BUILD_SIMD 1) endif () +if (NOT DEFINED WAMR_BUILD_REF_TYPES) + # Disable reference types by default + set (WAMR_BUILD_REF_TYPES 0) +endif () + if (COLLECT_CODE_COVERAGE EQUAL 1) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") endif () diff --git a/samples/ref-types/CMakeLists.txt b/samples/ref-types/CMakeLists.txt new file mode 100644 index 000000000..38c1d4750 --- /dev/null +++ b/samples/ref-types/CMakeLists.txt @@ -0,0 +1,108 @@ +# Copyright (C) 2019 Intel Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +cmake_minimum_required (VERSION 2.8) + +if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows") + project(ref-types) +else() + project (ref-types C ASM) + enable_language (ASM_MASM) +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() + +################ runtime settings ################ +string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM) +if (APPLE) + add_definitions(-DBH_PLATFORM_DARWIN) +endif () + +# Resetdefault linker flags +set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") +set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") + +# WAMR features switch +set(WAMR_BUILD_TARGET "X86_64") + +if(NOT DEFINED WAMR_BUILD_INTERP) + set(WAMR_BUILD_INTERP 1) +endif() + +if(NOT DEFINED WAMR_BUILD_AOT) + set(WAMR_BUILD_AOT 0) +endif() + +if(NOT DEFINED WAMR_BUILD_JOT) + set(WAMR_BUILD_JIT 0) +endif() + +if(NOT DEFINED WAMR_BUILD_FAST_INTERP) + set(WAMR_BUILD_FAST_INTERP 1) +endif() + +if(NOT DEFINED WAMR_BUILD_LIBC_BUILTIN) + set(WAMR_BUILD_LIBC_BUILTIN 1) +endif() + +# Enable reference-types feature +set(WAMR_BUILD_REF_TYPES 1) + +if (NOT MSVC) + # compiling and linking flags + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pie -fPIE") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") + endif () + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wformat -Wformat-security") + if (WAMR_BUILD_TARGET MATCHES "X86_.*" OR WAMR_BUILD_TARGET STREQUAL "AMD_64") + if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang")) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mindirect-branch-register") + endif () + endif () +endif() +# build out vmlib +set(WAMR_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +set(WAMRC ${WAMR_ROOT_DIR}/wamr-compiler/build/wamrc) +include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) + +add_library(vmlib STATIC ${WAMR_RUNTIME_LIB_SOURCE}) +if (MSVC) + target_compile_definitions(vmlib PRIVATE WASM_API_EXTERN=) +endif() + +################ application related ################ +## locate wat2wasm +find_program(WAT2WASM + wat2wasm + PATHS /opt/wabt/bin + REQUIRED +) + +if(NOT WAT2WASM) + message(SEND_ERROR "can not find wat2wasm") +endif() + +include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake) + +add_executable(hello src/hello.c ${UNCOMMON_SHARED_SOURCE}) + +target_include_directories(hello PRIVATE ${UNCOMMON_SHARED_DIR}) + +target_link_libraries(hello vmlib -lpthread -lm) + +if (MSVC) + target_compile_definitions(hello PRIVATE WASM_API_EXTERN=) +endif() + +# wat to wasm +file(GLOB WAT_FILE src/hello.wat) +add_custom_target(hello_wasm ALL + COMMAND ${WAT2WASM} ${WAT_FILE} -o ${PROJECT_BINARY_DIR}/hello.wasm + DEPENDS ${WAT_FILE} + BYPRODUCTS ${PROJECT_BINARY_DIR}/hello.wasm + VERBATIM + SOURCES ${WAT_FILE} +) diff --git a/samples/ref-types/src/hello.c b/samples/ref-types/src/hello.c new file mode 100644 index 000000000..a5ec8eb44 --- /dev/null +++ b/samples/ref-types/src/hello.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "bh_platform.h" +#include "bh_read_file.h" +#include "wasm_export.h" + +#define USE_GLOBAL_HEAP_BUF 0 + +#if USE_GLOBAL_HEAP_BUF != 0 +static char global_heap_buf[10 * 1024 * 1024] = { 0 }; +#endif + +static int +test_write_wrapper(wasm_exec_env_t exec_env, + uint32 externref_idx_of_file, + const char *str, int len) +{ + FILE *file; + char buf[16]; + + printf("## retrieve file handle from externref index\n"); + if (!wasm_externref_ref2obj(externref_idx_of_file, (void **)&file)) { + printf("failed to get host object from externref index!\n"); + return -1; + } + + snprintf(buf, sizeof(buf), "%%%ds", len); + + printf("## write string to file: "); + printf(buf, str); + + return fprintf(file, buf, str); +} + +static NativeSymbol native_symbols[] = { + { "test_write", test_write_wrapper, "(i*~)i", NULL } +}; + +int +main(int argc, char *argv[]) +{ + char *wasm_file = "hello.wasm"; + uint8 *wasm_file_buf = NULL; + uint32 wasm_file_size, externref_idx; + uint32 stack_size = 16 * 1024, heap_size = 16 * 1024; + wasm_module_t wasm_module = NULL; + wasm_module_inst_t wasm_module_inst = NULL; + wasm_function_inst_t func_inst = NULL; + wasm_exec_env_t exec_env = NULL; + RuntimeInitArgs init_args; + char error_buf[128] = { 0 }; + const char *exce; + unsigned argv1[8]; +#if WASM_ENABLE_LOG != 0 + int log_verbose_level = 2; +#endif + FILE *file; + + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + +#if USE_GLOBAL_HEAP_BUF != 0 + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = global_heap_buf; + init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf); +#else + init_args.mem_alloc_type = Alloc_With_Allocator; + init_args.mem_alloc_option.allocator.malloc_func = malloc; + init_args.mem_alloc_option.allocator.realloc_func = realloc; + init_args.mem_alloc_option.allocator.free_func = free; +#endif + + init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol); + init_args.native_module_name = "env"; + init_args.native_symbols = native_symbols; + + /* initialize runtime environment */ + if (!wasm_runtime_full_init(&init_args)) { + printf("Init runtime environment failed.\n"); + return -1; + } + +#if WASM_ENABLE_LOG != 0 + bh_log_set_verbose_level(log_verbose_level); +#endif + + /* load WASM byte buffer from WASM bin file */ + if (!(wasm_file_buf = + (uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size))) + goto fail1; + + /* load WASM module */ + if (!(wasm_module = wasm_runtime_load(wasm_file_buf, wasm_file_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail2; + } + + /* instantiate the module */ + if (!(wasm_module_inst = + wasm_runtime_instantiate(wasm_module, stack_size, heap_size, + error_buf, sizeof(error_buf)))) { + printf("%s\n", error_buf); + goto fail3; + } + + /* lookup function instance */ + if (!(func_inst = wasm_runtime_lookup_function(wasm_module_inst, + "test", NULL))) { + printf("%s\n", "lookup function test failed"); + goto fail4; + } + + if (!(exec_env = + wasm_runtime_create_exec_env(wasm_module_inst, stack_size))) { + printf("%s\n", "create exec env failed"); + goto fail4; + } + + printf("## open file test.txt\n"); + if (!(file = fopen("test.txt", "wb+"))) { + printf("%s\n", "open file text.txt failed"); + goto fail5; + } + + printf("## map file handle to externref index\n"); + if (!wasm_externref_obj2ref(wasm_module_inst, file, &externref_idx)) { + printf("%s\n", "map host object to externref index failed"); + goto fail6; + } + + printf("## call wasm function with externref index\n"); + argv1[0] = externref_idx; + wasm_runtime_call_wasm(exec_env, func_inst, 1, argv1); + + if ((exce = wasm_runtime_get_exception(wasm_module_inst))) { + printf("Exception: %s\n", exce); + } + +fail6: + fclose(file); + +fail5: + /* destroy exec env */ + wasm_runtime_destroy_exec_env(exec_env); + +fail4: + /* destroy the module instance */ + wasm_runtime_deinstantiate(wasm_module_inst); + +fail3: + /* unload the module */ + wasm_runtime_unload(wasm_module); + +fail2: + /* free the file buffer */ + wasm_runtime_free(wasm_file_buf); + +fail1: + /* destroy runtime environment */ + wasm_runtime_destroy(); + return 0; +} diff --git a/samples/ref-types/src/hello.wat b/samples/ref-types/src/hello.wat new file mode 100644 index 000000000..1a0f21db3 --- /dev/null +++ b/samples/ref-types/src/hello.wat @@ -0,0 +1,22 @@ +;; Copyright (C) 2019 Intel Corporation. All rights reserved. +;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +(module + ;; import test_write function which is implemented by host + (import "env" "test_write" + (func $test_write (param externref i32 i32) (result i32))) + + ;; memory with one page (64KiB). + (memory (export "memory") 1) + + (data (i32.const 0x8) "Hello, world!\n") + + ;; function that writes string to a given open file handle + (func (export "test") (param externref) + (local.get 0) + (i32.const 0x8) + (i32.const 14) + (call $test_write) + drop + ) +) diff --git a/wamr-compiler/CMakeLists.txt b/wamr-compiler/CMakeLists.txt index e47ce833d..440970cf5 100644 --- a/wamr-compiler/CMakeLists.txt +++ b/wamr-compiler/CMakeLists.txt @@ -31,6 +31,7 @@ add_definitions(-DWASM_ENABLE_SHARED_MEMORY=1) add_definitions(-DWASM_ENABLE_THREAD_MGR=1) add_definitions(-DWASM_ENABLE_TAIL_CALL=1) add_definitions(-DWASM_ENABLE_SIMD=1) +add_definitions(-DWASM_ENABLE_REF_TYPES=1) add_definitions(-DWASM_ENABLE_CUSTOM_NAME_SECTION=1) add_definitions(-DWASM_ENABLE_DUMP_CALL_STACK=1) add_definitions(-DWASM_ENABLE_PERF_PROFILING=1) @@ -90,6 +91,10 @@ if (NOT CMAKE_BUILD_TYPE) endif (NOT CMAKE_BUILD_TYPE) message ("-- CMAKE_BUILD_TYPE = " ${CMAKE_BUILD_TYPE}) +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DBH_DEBUG=1) +endif () + # Enable LLVM set (LLVM_SRC_ROOT "${PROJECT_SOURCE_DIR}/../core/deps/llvm") if (WAMR_BUILD_PLATFORM STREQUAL "windows") diff --git a/wamr-compiler/main.c b/wamr-compiler/main.c index f552e9b2d..4ced209b0 100644 --- a/wamr-compiler/main.c +++ b/wamr-compiler/main.c @@ -9,6 +9,12 @@ #include "wasm_export.h" #include "aot_export.h" + +#if WASM_ENABLE_REF_TYPES != 0 +extern void +wasm_set_ref_types_flag(bool enable); +#endif + static int print_help() { @@ -47,6 +53,7 @@ print_help() printf(" currently 128-bit SIMD is only supported for x86-64 target,\n"); printf(" and by default it is enabled in x86-64 target and disabled\n"); printf(" in other targets\n"); + printf(" --enable-ref-types Enable the post-MVP reference types feature\n"); printf(" --disable-aux-stack-check Disable auxiliary stack overflow/underflow check\n"); printf(" --enable-dump-call-stack Enable stack trace feature\n"); printf(" --enable-perf-profiling Enable function performance profiling\n"); @@ -166,6 +173,9 @@ main(int argc, char *argv[]) else if (!strcmp(argv[0], "--disable-simd")) { option.enable_simd = false; } + else if (!strcmp(argv[0], "--enable-ref-types")) { + option.enable_ref_types = true; + } else if (!strcmp(argv[0], "--disable-aux-stack-check")) { option.enable_aux_stack_check = false; } @@ -187,6 +197,10 @@ main(int argc, char *argv[]) option.is_sgx_platform = true; } +#if WASM_ENABLE_REF_TYPES != 0 + wasm_set_ref_types_flag(option.enable_ref_types); +#endif + wasm_file_name = argv[0]; memset(&init_args, 0, sizeof(RuntimeInitArgs));