Merge pull request #908 from bytecodealliance/main

Merge bytecodealliance:main into wenyongh:update_release_notes
This commit is contained in:
Wenyong Huang 2024-04-20 09:03:47 +08:00 committed by GitHub
commit b4ae0cfb8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 1599 additions and 565 deletions

View File

@ -134,7 +134,9 @@ endif ()
# Sanitizers
if (NOT DEFINED WAMR_BUILD_SANITIZER)
set(WAMR_BUILD_SANITIZER $ENV{WAMR_BUILD_SANITIZER})
endif ()
if (NOT DEFINED WAMR_BUILD_SANITIZER)
set(WAMR_BUILD_SANITIZER "")
@ -554,3 +556,9 @@ else ()
# Disable aot intrinsics for interp, fast-jit and llvm-jit
add_definitions (-DWASM_ENABLE_AOT_INTRINSICS=0)
endif ()
if (WAMR_BUILD_ALLOC_WITH_USAGE EQUAL 1)
add_definitions(-DWASM_MEM_ALLOC_WITH_USAGE=1)
endif()
if (NOT WAMR_BUILD_SANITIZER STREQUAL "")
message (" Sanitizer ${WAMR_BUILD_SANITIZER} enabled")
endif ()

View File

@ -445,7 +445,29 @@
#endif
/* Reserved bytes to the native thread stack boundary, throw native
stack overflow exception if the guard boudary is reached */
* stack overflow exception if the guard boudary is reached
*
* WASM_STACK_GUARD_SIZE needs to be large enough for:
*
* - native functions
* w/o hw bound check, the overhead (aot_call_function etc) + the native
* function itself. as of writing this, the former is about 1000 bytes
* on macOS amd64.
* with hw bound check, theoretically, only needs to cover the logic to
* set up the jmp_buf stack.
*
* - aot runtime functions
* eg. aot_enlarge_memory.
*
* - w/o hw bound check, the intepreter loop
*
* - stack check wrapper functions generated by the aot compiler
* (--stack-bounds-checks=1)
*
* Note: on platforms with lazy function binding, don't forget to consider
* the symbol resolution overhead on the first call. For example,
* on Ubuntu amd64 20.04, it seems to consume about 1500 bytes.
*/
#ifndef WASM_STACK_GUARD_SIZE
#if WASM_ENABLE_UVWASI != 0
/* UVWASI requires larger native stack */
@ -587,4 +609,8 @@
#define WASM_TABLE_MAX_SIZE 1024
#endif
#ifndef WASM_MEM_ALLOC_WITH_USAGE
#define WASM_MEM_ALLOC_WITH_USAGE 0
#endif
#endif /* end of _CONFIG_H_ */

View File

@ -1526,29 +1526,41 @@ fail:
}
static void
destroy_types(AOTType **types, uint32 count)
destroy_type(AOTType *type)
{
uint32 i;
for (i = 0; i < count; i++) {
if (types[i]) {
#if WASM_ENABLE_GC != 0
if (types[i]->type_flag == WASM_TYPE_FUNC) {
AOTFuncType *func_type = (AOTFuncType *)types[i];
if (type->ref_count > 1) {
/* The type is referenced by other types
of current aot module */
type->ref_count--;
return;
}
if (type->type_flag == WASM_TYPE_FUNC) {
AOTFuncType *func_type = (AOTFuncType *)type;
if (func_type->ref_type_maps != NULL) {
bh_assert(func_type->ref_type_map_count > 0);
wasm_runtime_free(func_type->ref_type_maps);
}
}
else if (types[i]->type_flag == WASM_TYPE_STRUCT) {
AOTStructType *struct_type = (AOTStructType *)types[i];
else if (type->type_flag == WASM_TYPE_STRUCT) {
AOTStructType *struct_type = (AOTStructType *)type;
if (struct_type->ref_type_maps != NULL) {
bh_assert(struct_type->ref_type_map_count > 0);
wasm_runtime_free(struct_type->ref_type_maps);
}
}
#endif
wasm_runtime_free(types[i]);
wasm_runtime_free(type);
}
static void
destroy_types(AOTType **types, uint32 count)
{
uint32 i;
for (i = 0; i < count; i++) {
if (types[i]) {
destroy_type(types[i]);
}
}
wasm_runtime_free(types);
@ -1556,14 +1568,17 @@ destroy_types(AOTType **types, uint32 count)
#if WASM_ENABLE_GC != 0
static void
init_base_type(AOTType *base_type, uint16 type_flag, bool is_sub_final,
uint32 parent_type_idx, uint16 rec_count, uint16 rec_idx)
init_base_type(AOTType *base_type, uint32 type_idx, uint16 type_flag,
bool is_sub_final, uint32 parent_type_idx, uint16 rec_count,
uint16 rec_idx)
{
base_type->type_flag = type_flag;
base_type->ref_count = 1;
base_type->is_sub_final = is_sub_final;
base_type->parent_type_idx = parent_type_idx;
base_type->rec_count = rec_count;
base_type->rec_idx = rec_idx;
base_type->rec_begin_type_idx = type_idx - rec_idx;
}
static bool
@ -1576,7 +1591,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
uint32 i, j;
uint32 type_flag, param_cell_num, ret_cell_num;
uint16 param_count, result_count, ref_type_map_count, rec_count, rec_idx;
bool is_sub_final;
bool is_equivalence_type, is_sub_final;
uint32 parent_type_idx;
WASMRefType ref_type;
@ -1590,12 +1605,31 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
/* Create each type */
for (i = 0; i < module->type_count; i++) {
buf = align_ptr(buf, 4);
/* Read base type info */
read_uint16(buf, buf_end, type_flag);
read_uint16(buf, buf_end, is_sub_final);
read_uint8(buf, buf_end, is_equivalence_type);
/* If there is an equivalence type, re-use it */
if (is_equivalence_type) {
uint8 u8;
/* padding */
read_uint8(buf, buf_end, u8);
(void)u8;
read_uint32(buf, buf_end, j);
if (module->types[j]->ref_count == UINT16_MAX) {
set_error_buf(error_buf, error_buf_size,
"wasm type's ref count too large");
goto fail;
}
module->types[j]->ref_count++;
module->types[i] = module->types[j];
continue;
}
read_uint8(buf, buf_end, is_sub_final);
read_uint32(buf, buf_end, parent_type_idx);
read_uint16(buf, buf_end, rec_count);
read_uint16(buf, buf_end, rec_idx);
@ -1620,7 +1654,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
types[i] = (AOTType *)func_type;
init_base_type((AOTType *)func_type, type_flag, is_sub_final,
init_base_type((AOTType *)func_type, i, type_flag, is_sub_final,
parent_type_idx, rec_count, rec_idx);
func_type->param_count = param_count;
func_type->result_count = result_count;
@ -1726,7 +1760,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
offset = (uint32)offsetof(WASMStructObject, field_data);
types[i] = (AOTType *)struct_type;
init_base_type((AOTType *)struct_type, type_flag, is_sub_final,
init_base_type((AOTType *)struct_type, i, type_flag, is_sub_final,
parent_type_idx, rec_count, rec_idx);
struct_type->field_count = field_count;
struct_type->ref_type_map_count = ref_type_map_count;
@ -1812,7 +1846,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
types[i] = (AOTType *)array_type;
init_base_type((AOTType *)array_type, type_flag, is_sub_final,
init_base_type((AOTType *)array_type, i, type_flag, is_sub_final,
parent_type_idx, rec_count, rec_idx);
read_uint16(buf, buf_end, array_type->elem_flags);
read_uint8(buf, buf_end, array_type->elem_type);
@ -1841,7 +1875,6 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
if (rec_count == 0) {
bh_assert(rec_idx == 0);
}
for (j = i - rec_idx; j <= i; j++) {
AOTType *cur_type = module->types[j];
parent_type_idx = cur_type->parent_type_idx;
@ -1850,6 +1883,11 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
module->types[j]->parent_type = parent_type;
module->types[j]->root_type = parent_type->root_type;
if (parent_type->inherit_depth == UINT16_MAX) {
set_error_buf(error_buf, error_buf_size,
"parent type's inherit depth too large");
goto fail;
}
module->types[j]->inherit_depth =
parent_type->inherit_depth + 1;
}
@ -1867,7 +1905,7 @@ load_types(const uint8 **p_buf, const uint8 *buf_end, AOTModule *module,
AOTType *parent_type = module->types[parent_type_idx];
/* subtyping has been checked during compilation */
bh_assert(wasm_type_is_subtype_of(
module->types[j], parent_type, module->types, i));
module->types[j], parent_type, module->types, i + 1));
(void)parent_type;
}
}
@ -3257,37 +3295,26 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end,
bh_memcpy_s(symbol_name_buf, (uint32)sizeof(symbol_name_buf),
symbol_name, symbol_name_len);
if ((group_name_len == strlen(".text")
|| (module->is_indirect_mode
&& group_name_len == strlen(".text") + 1))
/* aot compiler emits string with '\0' since 2.0.0 */
if (group_name_len == strlen(".text") + 1
&& !strncmp(group_name, ".text", strlen(".text"))) {
if ((symbol_name_len == strlen(YMM_PLT_PREFIX) + 64
|| (module->is_indirect_mode
&& symbol_name_len == strlen(YMM_PLT_PREFIX) + 64 + 1))
/* aot compiler emits string with '\0' since 2.0.0 */
if (symbol_name_len == strlen(YMM_PLT_PREFIX) + 64 + 1
&& !strncmp(symbol_name, YMM_PLT_PREFIX,
strlen(YMM_PLT_PREFIX))) {
module->ymm_plt_count++;
}
else if ((symbol_name_len == strlen(XMM_PLT_PREFIX) + 32
|| (module->is_indirect_mode
&& symbol_name_len
== strlen(XMM_PLT_PREFIX) + 32 + 1))
else if (symbol_name_len == strlen(XMM_PLT_PREFIX) + 32 + 1
&& !strncmp(symbol_name, XMM_PLT_PREFIX,
strlen(XMM_PLT_PREFIX))) {
module->xmm_plt_count++;
}
else if ((symbol_name_len == strlen(REAL_PLT_PREFIX) + 16
|| (module->is_indirect_mode
&& symbol_name_len
== strlen(REAL_PLT_PREFIX) + 16 + 1))
else if (symbol_name_len == strlen(REAL_PLT_PREFIX) + 16 + 1
&& !strncmp(symbol_name, REAL_PLT_PREFIX,
strlen(REAL_PLT_PREFIX))) {
module->real_plt_count++;
}
else if ((symbol_name_len >= strlen(REAL_PLT_PREFIX) + 8
|| (module->is_indirect_mode
&& symbol_name_len
== strlen(REAL_PLT_PREFIX) + 8 + 1))
else if (symbol_name_len >= strlen(REAL_PLT_PREFIX) + 8 + 1
&& !strncmp(symbol_name, REAL_PLT_PREFIX,
strlen(REAL_PLT_PREFIX))) {
module->float_plt_count++;

View File

@ -143,6 +143,7 @@ typedef struct {
REG_SYM(aot_array_init_with_data), \
REG_SYM(aot_create_func_obj), \
REG_SYM(aot_obj_is_instance_of), \
REG_SYM(aot_func_type_is_super_of), \
REG_SYM(aot_rtt_type_new), \
REG_SYM(wasm_array_obj_copy), \
REG_SYM(wasm_array_obj_new), \

View File

@ -1721,6 +1721,7 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent,
bh_assert(table_init_data);
bh_assert(table_init_data->table_index < module_inst->table_count);
table = module_inst->tables[table_init_data->table_index];
bh_assert(table);
@ -1728,8 +1729,9 @@ aot_instantiate(AOTModule *module, AOTModuleInstance *parent,
bh_assert(table_data);
wasm_runtime_get_table_inst_elem_type(
(WASMModuleInstanceCommon *)module_inst, i, &tbl_elem_type,
&tbl_elem_ref_type, &tbl_init_size, &tbl_max_size);
(WASMModuleInstanceCommon *)module_inst,
table_init_data->table_index, &tbl_elem_type, &tbl_elem_ref_type,
&tbl_init_size, &tbl_max_size);
if (!wasm_elem_is_declarative(table_init_data->mode)
&& !wasm_reftype_is_subtype_of(
@ -4487,6 +4489,22 @@ aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj,
return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count);
}
bool
aot_func_type_is_super_of(AOTModuleInstance *module_inst, uint32 type_idx1,
uint32 type_idx2)
{
AOTModule *aot_module = (AOTModule *)module_inst->module;
AOTType **types = aot_module->types;
if (type_idx1 == type_idx2)
return true;
bh_assert(types[type_idx1]->type_flag == WASM_TYPE_FUNC);
bh_assert(types[type_idx2]->type_flag == WASM_TYPE_FUNC);
return wasm_func_type_is_super_of((WASMFuncType *)types[type_idx1],
(WASMFuncType *)types[type_idx2]);
}
WASMRttTypeRef
aot_rtt_type_new(AOTModuleInstance *module_inst, uint32 type_index)
{

View File

@ -752,6 +752,11 @@ bool
aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj,
uint32 type_index);
/* Whether func type1 is one of super types of func type2 */
bool
aot_func_type_is_super_of(AOTModuleInstance *module_inst, uint32 type_idx1,
uint32 type_idx2);
WASMRttTypeRef
aot_rtt_type_new(AOTModuleInstance *module_inst, uint32 type_index);

View File

@ -304,7 +304,12 @@ wasm_defined_type_equal(WASMType *const def_type1, WASMType *const def_type2,
}
#endif
#if WASM_ENABLE_AOT != 0
/* TODO */
if (module->module_type == Wasm_Module_AoT) {
AOTModule *aot_module = (AOTModule *)module;
types = aot_module->types;
type_count = aot_module->type_count;
}
#endif
bh_assert(types);

View File

@ -250,6 +250,51 @@ wasm_value_types_is_subtype_of(const uint8 *types1,
return true;
}
static bool
rec_ref_type_equal(const WASMRefType *ref_type1, const WASMRefType *ref_type2,
uint32 rec_begin_type_idx1, uint32 rec_begin_type_idx2,
uint32 rec_count, const WASMTypePtr *types,
uint32 type_count)
{
uint32 type_idx1, type_idx2;
if (!wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)
|| !wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common))
return ref_type1->ref_ht_common.heap_type
== ref_type2->ref_ht_common.heap_type
? true
: false;
/* Now both ref types are type of (ref type_idx) */
type_idx1 = ref_type1->ref_ht_typeidx.type_idx;
type_idx2 = ref_type2->ref_ht_typeidx.type_idx;
if (type_idx1 >= rec_begin_type_idx1
&& type_idx1 < rec_begin_type_idx1 + rec_count) {
/* The converted iso-recursive types should be the same */
bool ret = (type_idx2 >= rec_begin_type_idx2
&& type_idx2 < rec_begin_type_idx2 + rec_count
&& type_idx1 - rec_begin_type_idx1
== type_idx2 - rec_begin_type_idx2)
? true
: false;
return ret;
}
else if (type_idx2 >= rec_begin_type_idx2
&& type_idx2 < rec_begin_type_idx2 + rec_count) {
/* The converted iso-recursive types should be the same */
bool ret = (type_idx1 >= rec_begin_type_idx1
&& type_idx1 < rec_begin_type_idx1 + rec_count
&& type_idx1 - rec_begin_type_idx1
== type_idx2 - rec_begin_type_idx2)
? true
: false;
return ret;
}
return types[type_idx1] == types[type_idx2] ? true : false;
}
bool
wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2,
const WASMTypePtr *types, uint32 type_count)
@ -277,9 +322,11 @@ wasm_func_type_equal(const WASMFuncType *type1, const WASMFuncType *type2,
ref_type1 = type1->ref_type_maps[j].ref_type;
ref_type2 = type2->ref_type_maps[j].ref_type;
if (!wasm_reftype_equal(ref_type1->ref_type, ref_type1,
ref_type2->ref_type, ref_type2, types,
type_count))
if (!rec_ref_type_equal(
ref_type1, ref_type2, type1->base_type.rec_begin_type_idx,
type2->base_type.rec_begin_type_idx,
type1->base_type.rec_count, types, type_count))
return false;
j++;
@ -316,9 +363,11 @@ wasm_struct_type_equal(const WASMStructType *type1, const WASMStructType *type2,
ref_type1 = type1->ref_type_maps[j].ref_type;
ref_type2 = type2->ref_type_maps[j].ref_type;
if (!wasm_reftype_equal(ref_type1->ref_type, ref_type1,
ref_type2->ref_type, ref_type2, types,
type_count))
if (!rec_ref_type_equal(
ref_type1, ref_type2, type1->base_type.rec_begin_type_idx,
type2->base_type.rec_begin_type_idx,
type1->base_type.rec_count, types, type_count))
return false;
j++;
@ -338,21 +387,67 @@ wasm_array_type_equal(const WASMArrayType *type1, const WASMArrayType *type2,
if (type1->elem_flags != type2->elem_flags)
return false;
return wasm_reftype_equal(type1->elem_type, type1->elem_ref_type,
type2->elem_type, type2->elem_ref_type, types,
type_count);
if (type1->elem_type != type2->elem_type)
return false;
if (!wasm_is_type_multi_byte_type(type1->elem_type))
return true;
return rec_ref_type_equal(type1->elem_ref_type, type2->elem_ref_type,
type1->base_type.rec_begin_type_idx,
type2->base_type.rec_begin_type_idx,
type1->base_type.rec_count, types, type_count);
}
bool
wasm_type_equal(const WASMType *type1, const WASMType *type2,
const WASMTypePtr *types, uint32 type_count)
{
uint32 rec_begin_type_idx1 = type1->rec_begin_type_idx;
uint32 rec_begin_type_idx2 = type2->rec_begin_type_idx;
uint32 parent_type_idx1, parent_type_idx2, rec_count;
if (type1 == type2)
return true;
if (type1->type_flag != type2->type_flag)
if (!(type1->type_flag == type2->type_flag
&& type1->is_sub_final == type2->is_sub_final
&& type1->rec_count == type2->rec_count
&& type1->rec_idx == type2->rec_idx))
return false;
rec_count = type1->rec_count;
parent_type_idx1 = type1->parent_type_idx;
parent_type_idx2 = type2->parent_type_idx;
if (parent_type_idx1 >= rec_begin_type_idx1
&& parent_type_idx1 < rec_begin_type_idx1 + rec_count) {
/* The converted iso-recursive types should be the same */
if (!(parent_type_idx2 >= rec_begin_type_idx2
&& parent_type_idx2 < rec_begin_type_idx2 + rec_count
&& parent_type_idx1 - rec_begin_type_idx1
== parent_type_idx2 - rec_begin_type_idx2)) {
return false;
}
}
else if (parent_type_idx2 >= rec_begin_type_idx2
&& parent_type_idx2 < rec_begin_type_idx2 + rec_count) {
/* The converted iso-recursive types should be the same */
if (!(parent_type_idx1 >= rec_begin_type_idx1
&& parent_type_idx1 < rec_begin_type_idx1 + rec_count
&& parent_type_idx1 - rec_begin_type_idx1
== parent_type_idx2 - rec_begin_type_idx2)) {
return false;
}
}
else if (type1->parent_type != type2->parent_type) {
/* The parent types should be same since they have been
normalized and equivalence types with different type
indexes are referring to a same WASMType */
return false;
}
if (wasm_type_is_func_type(type1))
return wasm_func_type_equal((WASMFuncType *)type1,
(WASMFuncType *)type2, types, type_count);
@ -653,12 +748,6 @@ wasm_reftype_struct_size(const WASMRefType *ref_type)
return (uint32)sizeof(RefHeapType_Common);
}
static bool
type_idx_equal(uint32 type_idx1, uint32 type_idx2)
{
return (type_idx1 == type_idx2) ? true : false;
}
bool
wasm_refheaptype_equal(const RefHeapType_Common *ref_heap_type1,
const RefHeapType_Common *ref_heap_type2,
@ -673,8 +762,16 @@ wasm_refheaptype_equal(const RefHeapType_Common *ref_heap_type1,
if (ref_heap_type1->heap_type != ref_heap_type2->heap_type) {
if (wasm_is_refheaptype_typeidx(ref_heap_type1)
&& wasm_is_refheaptype_typeidx(ref_heap_type2)) {
return type_idx_equal(ref_heap_type1->heap_type,
ref_heap_type2->heap_type);
if (ref_heap_type1->heap_type == ref_heap_type2->heap_type)
return true;
else
/* the type_count may be 0 when called from reftype_equal */
return ((uint32)ref_heap_type1->heap_type < type_count
&& (uint32)ref_heap_type2->heap_type < type_count
&& types[ref_heap_type1->heap_type]
== types[ref_heap_type2->heap_type])
? true
: false;
}
return false;
}
@ -835,6 +932,13 @@ wasm_type_is_supers_of(const WASMType *type1, const WASMType *type2)
return false;
}
bool
wasm_func_type_is_super_of(const WASMFuncType *type1, const WASMFuncType *type2)
{
return wasm_type_is_supers_of((const WASMType *)type1,
(const WASMType *)type2);
}
bool
wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1,
uint8 type2, const WASMRefType *ref_type2,
@ -914,12 +1018,13 @@ wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1,
#endif
else if (type1 == REF_TYPE_HT_NULLABLE) {
if (wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)) {
bh_assert((uint32)ref_type1->ref_ht_typeidx.type_idx < type_count);
/* reftype1 is (ref null $t) */
if (type2 == REF_TYPE_HT_NULLABLE && ref_type2 != NULL
&& wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) {
return type_idx_equal(ref_type1->ref_ht_typeidx.type_idx,
ref_type2->ref_ht_typeidx.type_idx)
|| wasm_type_is_supers_of(
bh_assert((uint32)ref_type2->ref_ht_typeidx.type_idx
< type_count);
return wasm_type_is_supers_of(
types[ref_type2->ref_ht_typeidx.type_idx],
types[ref_type1->ref_ht_typeidx.type_idx]);
}
@ -963,14 +1068,15 @@ wasm_reftype_is_subtype_of(uint8 type1, const WASMRefType *ref_type1,
else if (type1 == REF_TYPE_HT_NON_NULLABLE) {
bh_assert(ref_type1);
if (wasm_is_refheaptype_typeidx(&ref_type1->ref_ht_common)) {
bh_assert((uint32)ref_type1->ref_ht_typeidx.type_idx < type_count);
/* reftype1 is (ref $t) */
if ((type2 == REF_TYPE_HT_NULLABLE
|| type2 == REF_TYPE_HT_NON_NULLABLE)
&& ref_type2 != NULL
&& wasm_is_refheaptype_typeidx(&ref_type2->ref_ht_common)) {
return type_idx_equal(ref_type1->ref_ht_typeidx.type_idx,
ref_type2->ref_ht_typeidx.type_idx)
|| wasm_type_is_supers_of(
bh_assert((uint32)ref_type2->ref_ht_typeidx.type_idx
< type_count);
return wasm_type_is_supers_of(
types[ref_type2->ref_ht_typeidx.type_idx],
types[ref_type1->ref_ht_typeidx.type_idx]);
}

View File

@ -47,6 +47,12 @@ wasm_func_type_is_subtype_of(const WASMFuncType *type1,
const WASMFuncType *type2,
const WASMTypePtr *types, uint32 type_count);
/* Whether func type1 is one of super types of func type2,
used for the func type check in call_indirect/call_ref opcodes */
bool
wasm_func_type_is_super_of(const WASMFuncType *type1,
const WASMFuncType *type2);
/* Whether func type1's result types are subtype of
func type2's result types */
bool

View File

@ -578,8 +578,7 @@ execute_func(WASMModuleInstanceCommon *module_inst, const char *name,
is_anyref = true;
}
if (wasm_is_type_multi_byte_type(
type->types[type->param_count + i])) {
if (wasm_is_type_multi_byte_type(type->types[i])) {
WASMRefType *ref_type = ref_type_map->ref_type;
if (wasm_is_refheaptype_common(
&ref_type->ref_ht_common)) {

View File

@ -29,16 +29,35 @@ static void *enlarge_memory_error_user_data;
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
static void *allocator_user_data = NULL;
static void *(*malloc_func)(void *user_data, unsigned int size) = NULL;
static void *(*realloc_func)(void *user_data, void *ptr,
unsigned int size) = NULL;
static void (*free_func)(void *user_data, void *ptr) = NULL;
#else
static void *(*malloc_func)(unsigned int size) = NULL;
static void *(*realloc_func)(void *ptr, unsigned int size) = NULL;
static void (*free_func)(void *ptr) = NULL;
#endif
static void *(*malloc_func)(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
mem_alloc_usage_t usage,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
void *user_data,
#endif
unsigned int size) = NULL;
static void *(*realloc_func)(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
mem_alloc_usage_t usage, bool full_size_mmaped,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
void *user_data,
#endif
void *ptr, unsigned int size) = NULL;
static void (*free_func)(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
mem_alloc_usage_t usage,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
void *user_data,
#endif
void *ptr) = NULL;
static unsigned int global_pool_size;
static uint64
@ -177,11 +196,14 @@ wasm_runtime_malloc_internal(unsigned int size)
return mem_allocator_malloc(pool_allocator, size);
}
else if (memory_mode == MEMORY_MODE_ALLOCATOR) {
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
return malloc_func(allocator_user_data, size);
#else
return malloc_func(size);
return malloc_func(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
Alloc_For_Runtime,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
allocator_user_data,
#endif
size);
}
else {
return os_malloc(size);
@ -201,11 +223,14 @@ wasm_runtime_realloc_internal(void *ptr, unsigned int size)
}
else if (memory_mode == MEMORY_MODE_ALLOCATOR) {
if (realloc_func)
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
return realloc_func(allocator_user_data, ptr, size);
#else
return realloc_func(ptr, size);
return realloc_func(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
Alloc_For_Runtime, false,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
allocator_user_data,
#endif
ptr, size);
else
return NULL;
}
@ -233,11 +258,14 @@ wasm_runtime_free_internal(void *ptr)
mem_allocator_free(pool_allocator, ptr);
}
else if (memory_mode == MEMORY_MODE_ALLOCATOR) {
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
free_func(allocator_user_data, ptr);
#else
free_func(ptr);
free_func(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
Alloc_For_Runtime,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
allocator_user_data,
#endif
ptr);
}
else {
os_free(ptr);
@ -765,6 +793,29 @@ wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count)
bh_assert(total_size_new
<= GET_MAX_LINEAR_MEMORY_SIZE(memory->is_memory64));
#if WASM_MEM_ALLOC_WITH_USAGE != 0
if (!(memory_data_new =
realloc_func(Alloc_For_LinearMemory, full_size_mmaped,
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
NULL,
#endif
memory_data_old, total_size_new))) {
ret = false;
goto return_func;
}
if (heap_size > 0) {
if (mem_allocator_migrate(memory->heap_handle,
(char *)heap_data_old
+ (memory_data_new - memory_data_old),
heap_size)
!= 0) {
ret = false;
}
}
memory->heap_data = memory_data_new + (heap_data_old - memory_data_old);
memory->heap_data_end = memory->heap_data + heap_size;
memory->memory_data = memory_data_new;
#else
if (full_size_mmaped) {
#ifdef BH_PLATFORM_WINDOWS
if (!os_mem_commit(memory->memory_data_end,
@ -823,6 +874,7 @@ wasm_enlarge_memory_internal(WASMModuleInstance *module, uint32 inc_page_count)
os_writegsbase(memory_data_new);
#endif
}
#endif /* end of WASM_MEM_ALLOC_WITH_USAGE */
memory->num_bytes_per_page = num_bytes_per_page;
memory->cur_page_count = total_page_count;
@ -903,8 +955,19 @@ wasm_deallocate_linear_memory(WASMMemoryInstance *memory_inst)
#else
map_size = 8 * (uint64)BH_GB;
#endif
#if WASM_MEM_ALLOC_WITH_USAGE != 0
(void)map_size;
free_func(Alloc_For_LinearMemory,
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
NULL,
#endif
memory_inst->memory_data);
#else
wasm_munmap_linear_memory(memory_inst->memory_data,
memory_inst->memory_data_size, map_size);
#endif
memory_inst->memory_data = NULL;
}
@ -954,9 +1017,20 @@ wasm_allocate_linear_memory(uint8 **data, bool is_shared_memory,
*memory_data_size = align_as_and_cast(*memory_data_size, page_size);
if (map_size > 0) {
#if WASM_MEM_ALLOC_WITH_USAGE != 0
(void)wasm_mmap_linear_memory;
if (!(*data = malloc_func(Alloc_For_LinearMemory,
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
NULL,
#endif
*memory_data_size))) {
return BHT_ERROR;
}
#else
if (!(*data = wasm_mmap_linear_memory(map_size, *memory_data_size))) {
return BHT_ERROR;
}
#endif
}
return BHT_OK;

View File

@ -84,9 +84,9 @@ compare_type_with_signautre(uint8 type, const char signature)
if ('r' == signature
#if WASM_ENABLE_GC != 0
#if WASM_ENABLE_STRINGREF != 0
&& (type >= REF_TYPE_STRINGVIEWITER && type <= REF_TYPE_FUNCREF)
&& (type >= REF_TYPE_STRINGVIEWITER && type <= REF_TYPE_NULLFUNCREF)
#else
&& (type >= REF_TYPE_NULLREF && type <= REF_TYPE_FUNCREF)
&& (type >= REF_TYPE_HT_NULLABLE && type <= REF_TYPE_NULLFUNCREF)
#endif
#else
&& type == VALUE_TYPE_EXTERNREF

View File

@ -3727,6 +3727,213 @@ static union {
#define is_little_endian() (__ue.b == 1) /* NOLINT */
int32
wasm_runtime_get_import_count(WASMModuleCommon *const module)
{
if (!module) {
bh_assert(0);
return -1;
}
#if WASM_ENABLE_AOT != 0
if (module->module_type == Wasm_Module_AoT) {
const AOTModule *aot_module = (const AOTModule *)module;
return (int32)(aot_module->import_func_count
+ aot_module->import_global_count
+ aot_module->import_table_count
+ aot_module->import_memory_count);
}
#endif
#if WASM_ENABLE_INTERP != 0
if (module->module_type == Wasm_Module_Bytecode) {
const WASMModule *wasm_module = (const WASMModule *)module;
return (int32)wasm_module->import_count;
}
#endif
return -1;
}
void
wasm_runtime_get_import_type(WASMModuleCommon *const module, int32 import_index,
wasm_import_type *import_type)
{
if (!import_type) {
bh_assert(0);
return;
}
memset(import_type, 0, sizeof(wasm_import_type));
if (!module) {
bh_assert(0);
return;
}
#if WASM_ENABLE_AOT != 0
if (module->module_type == Wasm_Module_AoT) {
const AOTModule *aot_module = (const AOTModule *)module;
uint32 func_index = (uint32)import_index;
if (func_index < aot_module->import_func_count) {
const AOTImportFunc *aot_import_func =
&aot_module->import_funcs[func_index];
import_type->module_name = aot_import_func->module_name;
import_type->name = aot_import_func->func_name;
import_type->kind = WASM_IMPORT_EXPORT_KIND_FUNC;
import_type->linked =
aot_import_func->func_ptr_linked ? true : false;
return;
}
uint32 global_index = func_index - aot_module->import_func_count;
if (global_index < aot_module->import_global_count) {
const AOTImportGlobal *aot_import_global =
&aot_module->import_globals[global_index];
import_type->module_name = aot_import_global->module_name;
import_type->name = aot_import_global->global_name;
import_type->kind = WASM_IMPORT_EXPORT_KIND_GLOBAL;
import_type->linked = aot_import_global->is_linked;
return;
}
uint32 table_index = global_index - aot_module->import_global_count;
if (table_index < aot_module->import_table_count) {
const AOTImportTable *aot_import_table =
&aot_module->import_tables[table_index];
import_type->module_name = aot_import_table->module_name;
import_type->name = aot_import_table->table_name;
import_type->kind = WASM_IMPORT_EXPORT_KIND_TABLE;
import_type->linked = false;
return;
}
uint32 memory_index = table_index - aot_module->import_table_count;
if (memory_index < aot_module->import_memory_count) {
const AOTImportMemory *aot_import_memory =
&aot_module->import_memories[memory_index];
import_type->module_name = aot_import_memory->module_name;
import_type->name = aot_import_memory->memory_name;
import_type->kind = WASM_IMPORT_EXPORT_KIND_MEMORY;
import_type->linked = false;
return;
}
bh_assert(0);
return;
}
#endif
#if WASM_ENABLE_INTERP != 0
if (module->module_type == Wasm_Module_Bytecode) {
const WASMModule *wasm_module = (const WASMModule *)module;
if ((uint32)import_index >= wasm_module->import_count) {
bh_assert(0);
return;
}
const WASMImport *wasm_import = &wasm_module->imports[import_index];
import_type->module_name = wasm_import->u.names.module_name;
import_type->name = wasm_import->u.names.field_name;
import_type->kind = wasm_import->kind;
switch (import_type->kind) {
case WASM_IMPORT_EXPORT_KIND_FUNC:
import_type->linked = wasm_import->u.function.func_ptr_linked;
break;
case WASM_IMPORT_EXPORT_KIND_GLOBAL:
import_type->linked = wasm_import->u.global.is_linked;
break;
case WASM_IMPORT_EXPORT_KIND_TABLE:
/* not supported */
import_type->linked = false;
break;
case WASM_IMPORT_EXPORT_KIND_MEMORY:
/* not supported */
import_type->linked = false;
break;
default:
bh_assert(0);
break;
}
return;
}
#endif
}
int32
wasm_runtime_get_export_count(WASMModuleCommon *const module)
{
if (!module) {
bh_assert(0);
return -1;
}
#if WASM_ENABLE_AOT != 0
if (module->module_type == Wasm_Module_AoT) {
const AOTModule *aot_module = (const AOTModule *)module;
return (int32)aot_module->export_count;
}
#endif
#if WASM_ENABLE_INTERP != 0
if (module->module_type == Wasm_Module_Bytecode) {
const WASMModule *wasm_module = (const WASMModule *)module;
return (int32)wasm_module->export_count;
}
#endif
return -1;
}
void
wasm_runtime_get_export_type(WASMModuleCommon *const module, int32 export_index,
wasm_export_type *export_type)
{
if (!export_type) {
bh_assert(0);
return;
}
memset(export_type, 0, sizeof(wasm_export_type));
if (!module) {
bh_assert(0);
return;
}
#if WASM_ENABLE_AOT != 0
if (module->module_type == Wasm_Module_AoT) {
const AOTModule *aot_module = (const AOTModule *)module;
if ((uint32)export_index >= aot_module->export_count) {
bh_assert(0);
return;
}
const AOTExport *aot_export = &aot_module->exports[export_index];
export_type->name = aot_export->name;
export_type->kind = aot_export->kind;
return;
}
#endif
#if WASM_ENABLE_INTERP != 0
if (module->module_type == Wasm_Module_Bytecode) {
const WASMModule *wasm_module = (const WASMModule *)module;
if ((uint32)export_index >= wasm_module->export_count) {
bh_assert(0);
return;
}
const WASMExport *wasm_export = &wasm_module->exports[export_index];
export_type->name = wasm_export->name;
export_type->kind = wasm_export->kind;
return;
}
#endif
}
bool
wasm_runtime_register_natives(const char *module_name,
NativeSymbol *native_symbols,

View File

@ -484,15 +484,15 @@ static uint32
get_func_type_size(AOTCompContext *comp_ctx, AOTFuncType *func_type)
{
#if WASM_ENABLE_GC != 0
/* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx + param
* count + result count
* + ref_type_map_count + types + context of ref_type_map */
/* type flag + equivalence type flag + is_sub_final + parent_type_idx
+ rec_count + rec_idx + param count + result count
+ ref_type_map_count + types + context of ref_type_map */
if (comp_ctx->enable_gc) {
uint32 size = 0;
/* type flag */
size += sizeof(func_type->base_type.type_flag);
/* is_sub_final */
/* equivalence type flag + is_sub_final */
size += sizeof(uint16);
/* parent_type_idx */
size += sizeof(func_type->base_type.parent_type_idx);
@ -529,12 +529,12 @@ static uint32
get_struct_type_size(AOTCompContext *comp_ctx, AOTStructType *struct_type)
{
uint32 size = 0;
/* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx + field
* count + fields */
/* type flag + equivalence type flag + is_sub_final + parent_type_idx
+ rec_count + rec_idx + field count + fields */
/* type flag */
size += sizeof(struct_type->base_type.type_flag);
/* is_sub_final */
/* equivalence type flag + is_sub_final */
size += sizeof(uint16);
/* parent_type_idx */
size += sizeof(struct_type->base_type.parent_type_idx);
@ -558,12 +558,12 @@ static uint32
get_array_type_size(AOTCompContext *comp_ctx, AOTArrayType *array_type)
{
uint32 size = 0;
/* type flag + is_sub_final + parent_type_idx + rec_count + rec_idx +
elem_flags + elem_type + elem_ref_type */
/* type flag + equivalence type flag + is_sub_final + parent_type_idx
+ rec_count + rec_idx + elem_flags + elem_type + elem_ref_type */
/* type flag */
size += sizeof(array_type->base_type.type_flag);
/* is_sub_final */
/* equivalence type flag + is_sub_final */
size += sizeof(uint16);
/* parent_type_idx (u32) */
size += sizeof(array_type->base_type.parent_type_idx);
@ -597,7 +597,22 @@ get_type_info_size(AOTCompContext *comp_ctx, AOTCompData *comp_data)
#if WASM_ENABLE_GC != 0
if (comp_ctx->enable_gc) {
for (i = 0; i < comp_data->type_count; i++) {
uint32 j;
size = align_uint(size, 4);
/* Emit simple info if there is an equivalence type */
for (j = 0; j < i; j++) {
if (comp_data->types[j] == comp_data->types[i]) {
/* type_flag (2 bytes) + equivalence type flag (1 byte)
+ padding (1 byte) + equivalence type index */
size += 8;
break;
}
}
if (j < i)
continue;
if (comp_data->types[i]->type_flag == WASM_TYPE_FUNC)
size += get_func_type_size(comp_ctx,
(AOTFuncType *)comp_data->types[i]);
@ -2093,13 +2108,32 @@ aot_emit_type_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
#if WASM_ENABLE_GC != 0
if (comp_ctx->enable_gc) {
int32 idx;
AOTType **types = comp_data->types;
int32 idx;
uint32 j;
for (i = 0; i < comp_data->type_count; i++) {
offset = align_uint(offset, 4);
/* Emit simple info if there is an equivalence type */
for (j = 0; j < i; j++) {
if (types[j] == types[i]) {
EMIT_U16(types[i]->type_flag);
EMIT_U16(types[i]->is_sub_final);
/* equivalence type flag is true */
EMIT_U8(1);
EMIT_U8(0);
/* equivalence type index */
EMIT_U32(j);
break;
}
}
if (j < i)
continue;
EMIT_U16(types[i]->type_flag);
/* equivalence type flag is false */
EMIT_U8(0);
EMIT_U8(types[i]->is_sub_final);
EMIT_U32(types[i]->parent_type_idx);
EMIT_U16(types[i]->rec_count);
@ -2593,7 +2627,7 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
if (comp_ctx->enable_gc) {
/* emit func_local_ref_flag arrays for both import and AOTed funcs */
AOTFuncType *func_type;
uint32 j, local_ref_flags_cell_num;
uint32 j, local_ref_flags_cell_num, paddings;
for (i = 0; i < comp_data->import_func_count; i++) {
func_type = comp_data->import_funcs[i].func_type;
@ -2603,6 +2637,8 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
local_ref_flags_cell_num += wasm_value_type_cell_num_internal(
func_type->types[j], comp_ctx->pointer_size);
}
paddings =
local_ref_flags_cell_num < 2 ? 2 - local_ref_flags_cell_num : 0;
local_ref_flags_cell_num =
local_ref_flags_cell_num > 2 ? local_ref_flags_cell_num : 2;
@ -2614,7 +2650,7 @@ aot_emit_func_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
func_type->types[j]))
return false;
}
for (; j < 2; j++)
for (j = 0; j < paddings; j++)
EMIT_U8(0);
}

View File

@ -1826,6 +1826,52 @@ fail:
return ret;
}
#if WASM_ENABLE_GC != 0
static LLVMValueRef
call_aot_func_type_is_super_of_func(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx,
LLVMValueRef type_idx1,
LLVMValueRef type_idx2)
{
LLVMValueRef param_values[3], ret_value, value, func;
LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type;
param_types[0] = comp_ctx->aot_inst_type;
param_types[1] = I32_TYPE;
param_types[2] = I32_TYPE;
ret_type = INT8_TYPE;
#if WASM_ENABLE_JIT != 0
if (comp_ctx->is_jit_mode)
GET_AOT_FUNCTION(llvm_jit_func_type_is_super_of, 3);
else
#endif
GET_AOT_FUNCTION(aot_func_type_is_super_of, 3);
param_values[0] = func_ctx->aot_inst;
param_values[1] = type_idx1;
param_values[2] = type_idx2;
if (!(ret_value =
LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
3, "call_aot_func_type_is_super_of"))) {
aot_set_last_error("llvm build call failed.");
return NULL;
}
if (!(ret_value = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, ret_value,
I8_ZERO, "check_fail"))) {
aot_set_last_error("llvm build icmp failed.");
return NULL;
}
return ret_value;
fail:
return NULL;
}
#endif
static bool
call_aot_call_indirect_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
AOTFuncType *aot_func_type,
@ -2018,15 +2064,23 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
return false;
}
if (!comp_ctx->enable_gc) {
/* Find the equivalent function type whose type index is the smallest:
the callee function's type index is also converted to the smallest
one in wasm loader, so we can just check whether the two type indexes
are equal (the type index of call_indirect opcode and callee func),
we don't need to check whether the whole function types are equal,
including param types and result types. */
type_idx =
wasm_get_smallest_type_idx((WASMTypePtr *)comp_ctx->comp_data->types,
type_idx = wasm_get_smallest_type_idx(
(WASMTypePtr *)comp_ctx->comp_data->types,
comp_ctx->comp_data->type_count, type_idx);
}
else {
/* Call aot_func_type_is_super_of to check whether the func type
provided in the bytecode is a super type of the func type of
the function to call */
}
ftype_idx_const = I32_CONST(type_idx);
CHECK_LLVM_CONST(ftype_idx_const);
@ -2254,12 +2308,24 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
goto fail;
}
#if WASM_ENABLE_GC != 0
if (comp_ctx->enable_gc) {
if (!(cmp_ftype_idx = call_aot_func_type_is_super_of_func(
comp_ctx, func_ctx, ftype_idx_const, ftype_idx))) {
goto fail;
}
}
else
#endif
{
/* Check if function type index not equal */
if (!(cmp_ftype_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, ftype_idx,
if (!(cmp_ftype_idx =
LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, ftype_idx,
ftype_idx_const, "cmp_ftype_idx"))) {
aot_set_last_error("llvm build icmp failed.");
goto fail;
}
}
/* Throw exception if ftype_idx != ftype_idx_const */
if (!(check_ftype_idx_succ = LLVMAppendBasicBlockInContext(

View File

@ -69,6 +69,25 @@ struct WASMModuleCommon;
typedef struct WASMModuleCommon *wasm_module_t;
#endif
typedef enum {
WASM_IMPORT_EXPORT_KIND_FUNC,
WASM_IMPORT_EXPORT_KIND_TABLE,
WASM_IMPORT_EXPORT_KIND_MEMORY,
WASM_IMPORT_EXPORT_KIND_GLOBAL
} wasm_import_export_kind_t;
typedef struct wasm_import_type {
const char *module_name;
const char *name;
wasm_import_export_kind_t kind;
bool linked;
} wasm_import_type;
typedef struct wasm_export_type {
const char *name;
wasm_import_export_kind_t kind;
} wasm_export_type;
/* Instantiated WASM module */
struct WASMModuleInstanceCommon;
typedef struct WASMModuleInstanceCommon *wasm_module_inst_t;
@ -113,6 +132,11 @@ typedef enum {
Alloc_With_System_Allocator,
} mem_alloc_type_t;
typedef enum {
Alloc_For_Runtime,
Alloc_For_LinearMemory
} mem_alloc_usage_t;
/* Memory allocator option */
typedef union MemAllocOption {
struct {
@ -120,6 +144,9 @@ typedef union MemAllocOption {
uint32_t heap_size;
} pool;
struct {
/* the function signature is varied when
WASM_MEM_ALLOC_WITH_USER_DATA and
WASM_MEM_ALLOC_WITH_USAGE are defined */
void *malloc_func;
void *realloc_func;
void *free_func;
@ -1197,6 +1224,48 @@ wasm_runtime_get_native_addr_range(wasm_module_inst_t module_inst,
uint8_t **p_native_start_addr,
uint8_t **p_native_end_addr);
/**
* Get the number of import items for a WASM module
*
* @param module the WASM module
*
* @return the number of imports (zero for none), or -1 for failure
*/
WASM_RUNTIME_API_EXTERN int32_t
wasm_runtime_get_import_count(const wasm_module_t module);
/**
* Get information about a specific WASM module import
*
* @param module the WASM module
* @param import_index the desired import index
* @param import_type the location to store information about the import
*/
WASM_RUNTIME_API_EXTERN void
wasm_runtime_get_import_type(const wasm_module_t module, int32_t import_index,
wasm_import_type *import_type);
/**
* Get the number of export items for a WASM module
*
* @param module the WASM module
*
* @return the number of exports (zero for none), or -1 for failure
*/
WASM_RUNTIME_API_EXTERN int32_t
wasm_runtime_get_export_count(const wasm_module_t module);
/**
* Get information about a specific WASM module export
*
* @param module the WASM module
* @param export_index the desired export index
* @param export_type the location to store information about the export
*/
WASM_RUNTIME_API_EXTERN void
wasm_runtime_get_export_type(const wasm_module_t module, int32_t export_index,
wasm_export_type *export_type);
/**
* Register native functions with same module name
*

View File

@ -274,7 +274,7 @@ typedef struct InitializerExpression {
*/
typedef struct RefHeapType_TypeIdx {
/* ref_type is REF_TYPE_HT_NULLABLE or
REF_TYPE_HT_NON_NULLABLE, (0x6C or 0x6B) */
REF_TYPE_HT_NON_NULLABLE, (0x63 or 0x64) */
uint8 ref_type;
/* true if ref_type is REF_TYPE_HT_NULLABLE */
bool nullable;
@ -288,7 +288,7 @@ typedef struct RefHeapType_TypeIdx {
*/
typedef struct RefHeapType_Common {
/* ref_type is REF_TYPE_HT_NULLABLE or
REF_TYPE_HT_NON_NULLABLE (0x6C or 0x6B) */
REF_TYPE_HT_NON_NULLABLE (0x63 or 0x64) */
uint8 ref_type;
/* true if ref_type is REF_TYPE_HT_NULLABLE */
bool nullable;
@ -338,18 +338,24 @@ typedef struct WASMType {
uint16 type_flag;
bool is_sub_final;
/* How many types are referring to this type */
uint16 ref_count;
/* The inheritance depth */
uint32 inherit_depth;
uint16 inherit_depth;
/* The root type */
struct WASMType *root_type;
/* The parent type */
struct WASMType *parent_type;
uint32 parent_type_idx;
/* number of internal types in the current rec group, if the type is not in
* a recursive group, rec_count = 0 */
/* The number of internal types in the current rec group, and if
the type is not in a recursive group, rec_count is 1 since a
single type definition is reinterpreted as a short-hand for a
recursive group containing just one type */
uint16 rec_count;
uint16 rec_idx;
/* The index of the begin type of this group */
uint32 rec_begin_type_idx;
} WASMType, *WASMTypePtr;
#endif /* end of WASM_ENABLE_GC */
@ -375,9 +381,6 @@ typedef struct WASMFuncType {
uint16 ref_type_map_count;
WASMRefTypeMap *ref_type_maps;
WASMRefTypeMap *result_ref_type_maps;
/* minimal type index of the type equal to this type,
used in type equal check in call_indirect opcode */
uint32 min_type_idx_normalized;
#else
uint16 ref_count;
#endif

View File

@ -2212,6 +2212,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
WASMFuncType *cur_type, *cur_func_type;
WASMTableInstance *tbl_inst;
uint32 tbl_idx;
#if WASM_ENABLE_TAIL_CALL != 0
opcode = *(frame_ip - 1);
#endif
@ -2282,8 +2283,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
goto got_exception;
}
#else
if (cur_type->min_type_idx_normalized
!= cur_func_type->min_type_idx_normalized) {
if (!wasm_func_type_is_super_of(cur_type, cur_func_type)) {
wasm_set_exception(module, "indirect call type mismatch");
goto got_exception;
}
@ -4202,7 +4202,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
else
#endif
{
*(uint32 *)global_addr = aux_stack_top;
*(uint32 *)global_addr = (uint32)aux_stack_top;
frame_sp--;
}
#if WASM_ENABLE_MEMORY_PROFILING != 0

View File

@ -1736,8 +1736,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
goto got_exception;
}
#else
if (cur_type->min_type_idx_normalized
!= cur_func_type->min_type_idx_normalized) {
if (!wasm_func_type_is_super_of(cur_type, cur_func_type)) {
wasm_set_exception(module, "indirect call type mismatch");
goto got_exception;
}

View File

@ -36,6 +36,21 @@
#define TEMPLATE_READ_VALUE(Type, p) \
(p += sizeof(Type), *(Type *)(p - sizeof(Type)))
#if WASM_ENABLE_MEMORY64 != 0
static bool
has_module_memory64(WASMModule *module)
{
/* TODO: multi-memories for now assuming the memory idx type is consistent
* across multi-memories */
if (module->import_memory_count > 0)
return !!(module->import_memories[0].u.memory.flags & MEMORY64_FLAG);
else if (module->memory_count > 0)
return !!(module->memories[0].flags & MEMORY64_FLAG);
return false;
}
#endif
static void
set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
{
@ -394,10 +409,10 @@ memory_realloc(void *mem_old, uint32 size_old, uint32 size_new, char *error_buf,
#if WASM_ENABLE_GC != 0
static bool
check_type_index(const WASMModule *module, uint32 type_index, char *error_buf,
uint32 error_buf_size)
check_type_index(const WASMModule *module, uint32 type_count, uint32 type_index,
char *error_buf, uint32 error_buf_size)
{
if (type_index >= module->type_count) {
if (type_index >= type_count) {
set_error_buf_v(error_buf, error_buf_size, "unknown type %d",
type_index);
return false;
@ -409,7 +424,8 @@ static bool
check_array_type(const WASMModule *module, uint32 type_index, char *error_buf,
uint32 error_buf_size)
{
if (!check_type_index(module, type_index, error_buf, error_buf_size)) {
if (!check_type_index(module, module->type_count, type_index, error_buf,
error_buf_size)) {
return false;
}
if (module->types[type_index]->type_flag != WASM_TYPE_ARRAY) {
@ -775,8 +791,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
if (!is_byte_a_type(type1)) {
p--;
read_leb_uint32(p, p_end, type_idx);
if (!check_type_index(module, type_idx, error_buf,
error_buf_size))
if (!check_type_index(module, module->type_count, type_idx,
error_buf, error_buf_size))
goto fail;
wasm_set_refheaptype_typeidx(&cur_ref_type.ref_ht_typeidx,
@ -902,7 +918,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
uint32 field_count;
read_leb_uint32(p, p_end, type_idx);
if (!check_type_index(module, type_idx, error_buf,
if (!check_type_index(module, module->type_count,
type_idx, error_buf,
error_buf_size)) {
goto fail;
}
@ -966,7 +983,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
read_leb_uint32(p, p_end, cur_value.type_index);
type_idx = cur_value.type_index;
if (!check_type_index(module, type_idx, error_buf,
if (!check_type_index(module, module->type_count,
type_idx, error_buf,
error_buf_size)) {
goto fail;
}
@ -1001,7 +1019,8 @@ load_init_expr(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
read_leb_uint32(p, p_end, cur_value.type_index);
type_idx = cur_value.type_index;
if (!check_type_index(module, type_idx, error_buf,
if (!check_type_index(module, module->type_count,
type_idx, error_buf,
error_buf_size)) {
goto fail;
}
@ -1275,6 +1294,13 @@ destroy_array_type(WASMArrayType *type)
static void
destroy_wasm_type(WASMType *type)
{
if (type->ref_count > 1) {
/* The type is referenced by other types
of current wasm module */
type->ref_count--;
return;
}
if (type->type_flag == WASM_TYPE_FUNC)
destroy_func_type((WASMFuncType *)type);
else if (type->type_flag == WASM_TYPE_STRUCT)
@ -1289,8 +1315,9 @@ destroy_wasm_type(WASMType *type)
/* Resolve (ref null ht) or (ref ht) */
static bool
resolve_reftype_htref(const uint8 **p_buf, const uint8 *buf_end,
WASMModule *module, bool nullable, WASMRefType *ref_type,
char *error_buf, uint32 error_buf_size)
WASMModule *module, uint32 type_count, bool nullable,
WASMRefType *ref_type, char *error_buf,
uint32 error_buf_size)
{
const uint8 *p = *p_buf, *p_end = buf_end;
@ -1301,8 +1328,9 @@ resolve_reftype_htref(const uint8 **p_buf, const uint8 *buf_end,
if (wasm_is_refheaptype_typeidx(&ref_type->ref_ht_common)) {
/* heap type is (type i), i : typeidx, >= 0 */
if (!check_type_index(module, ref_type->ref_ht_typeidx.type_idx,
error_buf, error_buf_size)) {
if (!check_type_index(module, type_count,
ref_type->ref_ht_typeidx.type_idx, error_buf,
error_buf_size)) {
return false;
}
}
@ -1320,9 +1348,10 @@ fail:
static bool
resolve_value_type(const uint8 **p_buf, const uint8 *buf_end,
WASMModule *module, bool *p_need_ref_type_map,
WASMRefType *ref_type, bool allow_packed_type,
char *error_buf, uint32 error_buf_size)
WASMModule *module, uint32 type_count,
bool *p_need_ref_type_map, WASMRefType *ref_type,
bool allow_packed_type, char *error_buf,
uint32 error_buf_size)
{
const uint8 *p = *p_buf, *p_end = buf_end;
uint8 type;
@ -1334,8 +1363,8 @@ resolve_value_type(const uint8 **p_buf, const uint8 *buf_end,
if (wasm_is_reftype_htref_nullable(type)) {
/* (ref null ht) */
if (!resolve_reftype_htref(&p, p_end, module, true, ref_type, error_buf,
error_buf_size))
if (!resolve_reftype_htref(&p, p_end, module, type_count, true,
ref_type, error_buf, error_buf_size))
return false;
if (!wasm_is_refheaptype_common(&ref_type->ref_ht_common))
*p_need_ref_type_map = true;
@ -1351,8 +1380,8 @@ resolve_value_type(const uint8 **p_buf, const uint8 *buf_end,
}
else if (wasm_is_reftype_htref_non_nullable(type)) {
/* (ref ht) */
if (!resolve_reftype_htref(&p, p_end, module, false, ref_type,
error_buf, error_buf_size))
if (!resolve_reftype_htref(&p, p_end, module, type_count, false,
ref_type, error_buf, error_buf_size))
return false;
*p_need_ref_type_map = true;
#if WASM_ENABLE_STRINGREF != 0
@ -1401,7 +1430,8 @@ reftype_set_insert(HashMap *ref_type_set, const WASMRefType *ref_type,
static bool
resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
uint32 type_idx, char *error_buf, uint32 error_buf_size)
uint32 type_count, uint32 type_idx, char *error_buf,
uint32 error_buf_size)
{
const uint8 *p = *p_buf, *p_end = buf_end, *p_org;
uint32 param_count, result_count, i, j = 0;
@ -1417,8 +1447,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
read_leb_uint32(p, p_end, param_count);
p_org = p;
for (i = 0; i < param_count; i++) {
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
&ref_type, false, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, type_count,
&need_ref_type_map, &ref_type, false, error_buf,
error_buf_size)) {
return false;
}
if (need_ref_type_map)
@ -1427,8 +1458,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
read_leb_uint32(p, p_end, result_count);
for (i = 0; i < result_count; i++) {
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
&ref_type, false, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, type_count,
&need_ref_type_map, &ref_type, false, error_buf,
error_buf_size)) {
return false;
}
if (need_ref_type_map) {
@ -1468,8 +1500,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
}
for (i = 0; i < param_count; i++) {
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
&ref_type, false, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, type_count,
&need_ref_type_map, &ref_type, false, error_buf,
error_buf_size)) {
goto fail;
}
type->types[i] = ref_type.ref_type;
@ -1485,8 +1518,9 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
read_leb_uint32(p, p_end, result_count);
for (i = 0; i < result_count; i++) {
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
&ref_type, false, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, type_count,
&need_ref_type_map, &ref_type, false, error_buf,
error_buf_size)) {
goto fail;
}
type->types[param_count + i] = ref_type.ref_type;
@ -1527,18 +1561,6 @@ resolve_func_type(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
}
#endif
/* Calculate the minimal type index of the type equal to this type */
type->min_type_idx_normalized = type_idx;
for (i = 0; i < type_idx; i++) {
WASMFuncType *func_type = (WASMFuncType *)module->types[i];
if (func_type->base_type.type_flag == WASM_TYPE_FUNC
&& wasm_func_type_equal(type, func_type, module->types,
type_idx + 1)) {
type->min_type_idx_normalized = i;
break;
}
}
*p_buf = p;
module->types[type_idx] = (WASMType *)type;
@ -1552,8 +1574,8 @@ fail:
static bool
resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end,
WASMModule *module, uint32 type_idx, char *error_buf,
uint32 error_buf_size)
WASMModule *module, uint32 type_count, uint32 type_idx,
char *error_buf, uint32 error_buf_size)
{
const uint8 *p = *p_buf, *p_end = buf_end, *p_org;
uint32 field_count, ref_type_map_count = 0, ref_field_count = 0;
@ -1569,8 +1591,9 @@ resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end,
read_leb_uint32(p, p_end, field_count);
p_org = p;
for (i = 0; i < field_count; i++) {
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
&ref_type, true, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, type_count,
&need_ref_type_map, &ref_type, true, error_buf,
error_buf_size)) {
return false;
}
if (need_ref_type_map)
@ -1617,8 +1640,9 @@ resolve_struct_type(const uint8 **p_buf, const uint8 *buf_end,
offset = (uint32)offsetof(WASMStructObject, field_data);
for (i = 0; i < field_count; i++) {
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
&ref_type, true, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, type_count,
&need_ref_type_map, &ref_type, true, error_buf,
error_buf_size)) {
goto fail;
}
type->fields[i].field_type = ref_type.ref_type;
@ -1671,8 +1695,8 @@ fail:
static bool
resolve_array_type(const uint8 **p_buf, const uint8 *buf_end,
WASMModule *module, uint32 type_idx, char *error_buf,
uint32 error_buf_size)
WASMModule *module, uint32 type_count, uint32 type_idx,
char *error_buf, uint32 error_buf_size)
{
const uint8 *p = *p_buf, *p_end = buf_end;
uint8 mutable;
@ -1680,8 +1704,8 @@ resolve_array_type(const uint8 **p_buf, const uint8 *buf_end,
WASMRefType ref_type;
WASMArrayType *type = NULL;
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, &ref_type,
true, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, type_count, &need_ref_type_map,
&ref_type, true, error_buf, error_buf_size)) {
return false;
}
@ -1730,7 +1754,8 @@ init_ref_type(WASMModule *module, WASMRefType *ref_type, bool nullable,
int32 heap_type, char *error_buf, uint32 error_buf_size)
{
if (heap_type >= 0) {
if (!check_type_index(module, heap_type, error_buf, error_buf_size)) {
if (!check_type_index(module, module->type_count, heap_type, error_buf,
error_buf_size)) {
return false;
}
wasm_set_refheaptype_typeidx(&ref_type->ref_ht_typeidx, nullable,
@ -2010,6 +2035,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
if (flag == DEFINED_TYPE_FUNC) {
if (!resolve_func_type(&p, buf_end, module,
processed_type_count + rec_count,
processed_type_count + j, error_buf,
error_buf_size)) {
return false;
@ -2017,6 +2043,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
}
else if (flag == DEFINED_TYPE_STRUCT) {
if (!resolve_struct_type(&p, buf_end, module,
processed_type_count + rec_count,
processed_type_count + j,
error_buf, error_buf_size)) {
return false;
@ -2024,6 +2051,7 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
}
else if (flag == DEFINED_TYPE_ARRAY) {
if (!resolve_array_type(&p, buf_end, module,
processed_type_count + rec_count,
processed_type_count + j, error_buf,
error_buf_size)) {
return false;
@ -2037,13 +2065,13 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
cur_type = module->types[processed_type_count + j];
cur_type->ref_count = 1;
cur_type->parent_type_idx = parent_type_idx;
cur_type->is_sub_final = is_sub_final;
if (rec_count > 1) {
cur_type->rec_count = rec_count;
cur_type->rec_idx = j;
}
cur_type->rec_begin_type_idx = processed_type_count;
}
/* resolve subtyping relationship in current rec group */
@ -2055,6 +2083,11 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
module->types[cur_type->parent_type_idx];
cur_type->parent_type = parent_type;
cur_type->root_type = parent_type->root_type;
if (parent_type->inherit_depth == UINT16_MAX) {
set_error_buf(error_buf, error_buf_size,
"parent type's inherit depth too large");
return false;
}
cur_type->inherit_depth = parent_type->inherit_depth + 1;
}
else {
@ -2080,6 +2113,49 @@ load_type_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
}
}
/* If there is already an equivalence type or a group of equivalence
recursive types created, use it or them instead */
for (j = 0; j < processed_type_count;) {
WASMType *src_type = module->types[j];
WASMType *cur_type = module->types[processed_type_count];
uint32 k, src_rec_count;
src_rec_count = src_type->rec_count;
if (src_rec_count != rec_count) {
/* no type equivalence */
j += src_rec_count;
continue;
}
for (k = 0; k < rec_count; k++) {
src_type = module->types[j + k];
cur_type = module->types[processed_type_count + k];
if (!wasm_type_equal(src_type, cur_type, module->types,
module->type_count)) {
break;
}
}
if (k < rec_count) {
/* no type equivalence */
j += src_rec_count;
continue;
}
/* type equivalence */
for (k = 0; k < rec_count; k++) {
if (module->types[j + k]->ref_count == UINT16_MAX) {
set_error_buf(error_buf, error_buf_size,
"wasm type's ref count too large");
return false;
}
destroy_wasm_type(module->types[processed_type_count + k]);
module->types[processed_type_count + k] =
module->types[j + k];
module->types[j + k]->ref_count++;
}
break;
}
if (rec_count > 1) {
LOG_VERBOSE("Finished processing rec group [%d-%d]",
processed_type_count,
@ -2511,8 +2587,9 @@ load_table_import(const uint8 **p_buf, const uint8 *buf_end,
return false;
}
#else /* else of WASM_ENABLE_GC == 0 */
if (!resolve_value_type(&p, p_end, parent_module, &need_ref_type_map,
&ref_type, false, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, parent_module, parent_module->type_count,
&need_ref_type_map, &ref_type, false, error_buf,
error_buf_size)) {
return false;
}
if (wasm_is_reftype_htref_non_nullable(ref_type.ref_type)) {
@ -2947,8 +3024,9 @@ load_global_import(const uint8 **p_buf, const uint8 *buf_end,
declare_type = read_uint8(p);
declare_mutable = read_uint8(p);
#else
if (!resolve_value_type(&p, p_end, parent_module, &need_ref_type_map,
&ref_type, false, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, parent_module, parent_module->type_count,
&need_ref_type_map, &ref_type, false, error_buf,
error_buf_size)) {
return false;
}
declare_type = ref_type.ref_type;
@ -3050,8 +3128,9 @@ load_table(const uint8 **p_buf, const uint8 *buf_end, WASMModule *module,
return false;
}
#else /* else of WASM_ENABLE_GC == 0 */
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map, &ref_type,
false, error_buf, error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, module->type_count,
&need_ref_type_map, &ref_type, false, error_buf,
error_buf_size)) {
return false;
}
table->elem_type = ref_type.ref_type;
@ -3536,7 +3615,8 @@ load_function_section(const uint8 *buf, const uint8 *buf_end,
type_index_org = type_index;
#endif
#if (WASM_ENABLE_WAMR_COMPILER != 0) || (WASM_ENABLE_JIT != 0)
#if (WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0) \
&& WASM_ENABLE_GC == 0
type_index = wasm_get_smallest_type_idx(
module->types, module->type_count, type_index);
#endif
@ -3577,8 +3657,9 @@ load_function_section(const uint8 *buf, const uint8 *buf_end,
#endif
#else
if (!resolve_value_type(&p_code, buf_code_end, module,
&need_ref_type_map, &ref_type, false,
error_buf, error_buf_size)) {
module->type_count, &need_ref_type_map,
&ref_type, false, error_buf,
error_buf_size)) {
return false;
}
local_count += sub_local_count;
@ -3664,8 +3745,9 @@ load_function_section(const uint8 *buf, const uint8 *buf_end,
}
#else
if (!resolve_value_type(&p_code, buf_code_end, module,
&need_ref_type_map, &ref_type, false,
error_buf, error_buf_size)) {
module->type_count, &need_ref_type_map,
&ref_type, false, error_buf,
error_buf_size)) {
return false;
}
if (need_ref_type_map) {
@ -3923,9 +4005,9 @@ load_global_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
global->type = read_uint8(p);
mutable = read_uint8(p);
#else
if (!resolve_value_type(&p, p_end, module, &need_ref_type_map,
&ref_type, false, error_buf,
error_buf_size)) {
if (!resolve_value_type(&p, p_end, module, module->type_count,
&need_ref_type_map, &ref_type, false,
error_buf, error_buf_size)) {
return false;
}
global->type = ref_type.ref_type;
@ -4231,8 +4313,8 @@ load_elem_type(WASMModule *module, const uint8 **p_buf, const uint8 *buf_end,
#else
p--;
if (!resolve_value_type((const uint8 **)&p, p_end, module,
&need_ref_type_map, &elem_ref_type, false,
error_buf, error_buf_size)) {
module->type_count, &need_ref_type_map,
&elem_ref_type, false, error_buf, error_buf_size)) {
return false;
}
if (!wasm_is_type_reftype(elem_ref_type.ref_type)) {
@ -5592,6 +5674,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
uint32 aux_data_end_global_index = (uint32)-1;
uint32 aux_heap_base_global_index = (uint32)-1;
WASMFuncType *func_type;
uint8 malloc_free_io_type = VALUE_TYPE_I32;
/* Find code and function sections if have */
while (section) {
@ -5820,6 +5903,10 @@ load_from_sections(WASMModule *module, WASMSection *sections,
module->retain_function = (uint32)-1;
/* Resolve malloc/free function exported by wasm module */
#if WASM_ENABLE_MEMORY64 != 0
if (has_module_memory64(module))
malloc_free_io_type = VALUE_TYPE_I64;
#endif
export = module->exports;
for (i = 0; i < module->export_count; i++, export ++) {
if (export->kind == EXPORT_KIND_FUNC) {
@ -5828,8 +5915,8 @@ load_from_sections(WASMModule *module, WASMSection *sections,
func_index = export->index - module->import_function_count;
func_type = module->functions[func_index]->func_type;
if (func_type->param_count == 1 && func_type->result_count == 1
&& func_type->types[0] == VALUE_TYPE_I32
&& func_type->types[1] == VALUE_TYPE_I32) {
&& func_type->types[0] == malloc_free_io_type
&& func_type->types[1] == malloc_free_io_type) {
bh_assert(module->malloc_function == (uint32)-1);
module->malloc_function = export->index;
LOG_VERBOSE("Found malloc function, name: %s, index: %u",
@ -5842,9 +5929,9 @@ load_from_sections(WASMModule *module, WASMSection *sections,
func_index = export->index - module->import_function_count;
func_type = module->functions[func_index]->func_type;
if (func_type->param_count == 2 && func_type->result_count == 1
&& func_type->types[0] == VALUE_TYPE_I32
&& func_type->types[0] == malloc_free_io_type
&& func_type->types[1] == VALUE_TYPE_I32
&& func_type->types[2] == VALUE_TYPE_I32) {
&& func_type->types[2] == malloc_free_io_type) {
uint32 j;
WASMExport *export_tmp;
@ -5868,8 +5955,8 @@ load_from_sections(WASMModule *module, WASMSection *sections,
module->functions[func_index]->func_type;
if (func_type->param_count == 1
&& func_type->result_count == 1
&& func_type->types[0] == VALUE_TYPE_I32
&& func_type->types[1] == VALUE_TYPE_I32) {
&& func_type->types[0] == malloc_free_io_type
&& func_type->types[1] == malloc_free_io_type) {
bh_assert(module->retain_function
== (uint32)-1);
module->retain_function = export_tmp->index;
@ -5895,7 +5982,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
func_index = export->index - module->import_function_count;
func_type = module->functions[func_index]->func_type;
if (func_type->param_count == 1 && func_type->result_count == 0
&& func_type->types[0] == VALUE_TYPE_I32) {
&& func_type->types[0] == malloc_free_io_type) {
bh_assert(module->free_function == (uint32)-1);
module->free_function = export->index;
LOG_VERBOSE("Found free function, name: %s, index: %u",
@ -10670,14 +10757,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func,
func->param_cell_num, func->local_cell_num, func->ret_cell_num);
#endif
#if WASM_ENABLE_MEMORY64 != 0
bool is_memory64 = false;
/* TODO: multi-memories for now assuming the memory idx type is consistent
* across multi-memories */
if (module->import_memory_count > 0)
is_memory64 = module->import_memories[0].u.memory.flags & MEMORY64_FLAG;
else if (module->memory_count > 0)
is_memory64 = module->memories[0].flags & MEMORY64_FLAG;
bool is_memory64 = has_module_memory64(module);
mem_offset_type = is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32;
#else
mem_offset_type = VALUE_TYPE_I32;
@ -10827,7 +10907,8 @@ re_scan:
p_org = p;
p--;
if (!resolve_value_type((const uint8 **)&p, p_end,
module, &need_ref_type_map,
module, module->type_count,
&need_ref_type_map,
&wasm_ref_type, false,
error_buf, error_buf_size)) {
goto fail;
@ -11303,8 +11384,8 @@ re_scan:
bh_memcpy_s(loader_ctx->frame_offset, size,
block->param_frame_offsets, size);
loader_ctx->frame_offset += (size / sizeof(int16));
loader_ctx->dynamic_offset = block->start_dynamic_offset;
}
loader_ctx->dynamic_offset = block->start_dynamic_offset;
#endif
break;
@ -11598,6 +11679,15 @@ re_scan:
if (opcode == WASM_OP_CALL_REF
|| opcode == WASM_OP_RETURN_CALL_REF) {
read_leb_uint32(p, p_end, type_idx1);
if (!check_type_index(module, module->type_count, type_idx1,
error_buf, error_buf_size)) {
goto fail;
}
if (module->types[type_idx1]->type_flag != WASM_TYPE_FUNC) {
set_error_buf(error_buf, error_buf_size,
"unkown function type");
goto fail;
}
if (!wasm_loader_pop_nullable_typeidx(loader_ctx, &type,
&type_idx, error_buf,
error_buf_size)) {
@ -11606,8 +11696,8 @@ re_scan:
if (type == VALUE_TYPE_ANY) {
type_idx = type_idx1;
}
if (!check_type_index(module, type_idx, error_buf,
error_buf_size)) {
if (!check_type_index(module, module->type_count, type_idx,
error_buf, error_buf_size)) {
goto fail;
}
if (module->types[type_idx]->type_flag != WASM_TYPE_FUNC) {
@ -11615,7 +11705,9 @@ re_scan:
"unkown function type");
goto fail;
}
if (type_idx != type_idx1) {
if (!wasm_func_type_is_super_of(
(WASMFuncType *)module->types[type_idx1],
(WASMFuncType *)module->types[type_idx])) {
set_error_buf(error_buf, error_buf_size,
"function type mismatch");
goto fail;
@ -12055,8 +12147,9 @@ re_scan:
#else
p_org = p + 1;
if (!resolve_value_type((const uint8 **)&p, p_end, module,
&need_ref_type_map, &wasm_ref_type,
false, error_buf, error_buf_size)) {
module->type_count, &need_ref_type_map,
&wasm_ref_type, false, error_buf,
error_buf_size)) {
goto fail;
}
type = wasm_ref_type.ref_type;
@ -12223,8 +12316,8 @@ re_scan:
#else
read_leb_int32(p, p_end, heap_type);
if (heap_type >= 0) {
if (!check_type_index(module, heap_type, error_buf,
error_buf_size)) {
if (!check_type_index(module, module->type_count, heap_type,
error_buf, error_buf_size)) {
goto fail;
}
wasm_set_refheaptype_typeidx(&wasm_ref_type.ref_ht_typeidx,
@ -13288,7 +13381,8 @@ re_scan:
#if WASM_ENABLE_FAST_INTERP != 0
emit_uint32(loader_ctx, type_idx);
#endif
if (!check_type_index(module, type_idx, error_buf,
if (!check_type_index(module, module->type_count,
type_idx, error_buf,
error_buf_size)) {
goto fail;
}
@ -13374,7 +13468,8 @@ re_scan:
#if WASM_ENABLE_FAST_INTERP != 0
emit_uint32(loader_ctx, type_idx);
#endif
if (!check_type_index(module, type_idx, error_buf,
if (!check_type_index(module, module->type_count,
type_idx, error_buf,
error_buf_size)) {
goto fail;
}
@ -13787,7 +13882,8 @@ re_scan:
emit_uint32(loader_ctx, (uint32)heap_type);
#endif
if (heap_type >= 0) {
if (!check_type_index(module, heap_type, error_buf,
if (!check_type_index(module, module->type_count,
heap_type, error_buf,
error_buf_size)) {
goto fail;
}

View File

@ -25,6 +25,21 @@
#define TEMPLATE_READ_VALUE(Type, p) \
(p += sizeof(Type), *(Type *)(p - sizeof(Type)))
#if WASM_ENABLE_MEMORY64 != 0
static bool
has_module_memory64(WASMModule *module)
{
/* TODO: multi-memories for now assuming the memory idx type is consistent
* across multi-memories */
if (module->import_memory_count > 0)
return !!(module->import_memories[0].u.memory.flags & MEMORY64_FLAG);
else if (module->memory_count > 0)
return !!(module->memories[0].flags & MEMORY64_FLAG);
return false;
}
#endif
static void
set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
{
@ -2573,6 +2588,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
uint32 aux_data_end_global_index = (uint32)-1;
uint32 aux_heap_base_global_index = (uint32)-1;
WASMFuncType *func_type;
uint8 malloc_free_io_type = VALUE_TYPE_I32;
/* Find code and function sections if have */
while (section) {
@ -2781,6 +2797,10 @@ load_from_sections(WASMModule *module, WASMSection *sections,
module->retain_function = (uint32)-1;
/* Resolve malloc/free function exported by wasm module */
#if WASM_ENABLE_MEMORY64 != 0
if (has_module_memory64(module))
malloc_free_io_type = VALUE_TYPE_I64;
#endif
export = module->exports;
for (i = 0; i < module->export_count; i++, export ++) {
if (export->kind == EXPORT_KIND_FUNC) {
@ -2789,8 +2809,8 @@ load_from_sections(WASMModule *module, WASMSection *sections,
func_index = export->index - module->import_function_count;
func_type = module->functions[func_index]->func_type;
if (func_type->param_count == 1 && func_type->result_count == 1
&& func_type->types[0] == VALUE_TYPE_I32
&& func_type->types[1] == VALUE_TYPE_I32) {
&& func_type->types[0] == malloc_free_io_type
&& func_type->types[1] == malloc_free_io_type) {
bh_assert(module->malloc_function == (uint32)-1);
module->malloc_function = export->index;
LOG_VERBOSE("Found malloc function, name: %s, index: %u",
@ -2803,9 +2823,9 @@ load_from_sections(WASMModule *module, WASMSection *sections,
func_index = export->index - module->import_function_count;
func_type = module->functions[func_index]->func_type;
if (func_type->param_count == 2 && func_type->result_count == 1
&& func_type->types[0] == VALUE_TYPE_I32
&& func_type->types[0] == malloc_free_io_type
&& func_type->types[1] == VALUE_TYPE_I32
&& func_type->types[2] == VALUE_TYPE_I32) {
&& func_type->types[2] == malloc_free_io_type) {
uint32 j;
WASMExport *export_tmp;
@ -2829,8 +2849,8 @@ load_from_sections(WASMModule *module, WASMSection *sections,
module->functions[func_index]->func_type;
if (func_type->param_count == 1
&& func_type->result_count == 1
&& func_type->types[0] == VALUE_TYPE_I32
&& func_type->types[1] == VALUE_TYPE_I32) {
&& func_type->types[0] == malloc_free_io_type
&& func_type->types[1] == malloc_free_io_type) {
bh_assert(module->retain_function
== (uint32)-1);
module->retain_function = export_tmp->index;
@ -2856,7 +2876,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
func_index = export->index - module->import_function_count;
func_type = module->functions[func_index]->func_type;
if (func_type->param_count == 1 && func_type->result_count == 0
&& func_type->types[0] == VALUE_TYPE_I32) {
&& func_type->types[0] == malloc_free_io_type) {
bh_assert(module->free_function == (uint32)-1);
module->free_function = export->index;
LOG_VERBOSE("Found free function, name: %s, index: %u",
@ -5906,14 +5926,7 @@ wasm_loader_prepare_bytecode(WASMModule *module, WASMFunction *func,
func->param_cell_num, func->local_cell_num, func->ret_cell_num);
#endif
#if WASM_ENABLE_MEMORY64 != 0
bool is_memory64 = false;
/* TODO: multi-memories for now assuming the memory idx type is consistent
* across multi-memories */
if (module->import_memory_count > 0)
is_memory64 = module->import_memories[0].u.memory.flags & MEMORY64_FLAG;
else if (module->memory_count > 0)
is_memory64 = module->memories[0].flags & MEMORY64_FLAG;
bool is_memory64 = has_module_memory64(module);
mem_offset_type = is_memory64 ? VALUE_TYPE_I64 : VALUE_TYPE_I32;
#else
mem_offset_type = VALUE_TYPE_I32;
@ -6226,8 +6239,8 @@ re_scan:
bh_memcpy_s(loader_ctx->frame_offset, size,
block->param_frame_offsets, size);
loader_ctx->frame_offset += (size / sizeof(int16));
loader_ctx->dynamic_offset = block->start_dynamic_offset;
}
loader_ctx->dynamic_offset = block->start_dynamic_offset;
#endif
break;

View File

@ -1400,30 +1400,39 @@ fail:
static bool
execute_malloc_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
WASMFunctionInstance *malloc_func,
WASMFunctionInstance *retain_func, uint32 size,
uint32 *p_result)
WASMFunctionInstance *retain_func, uint64 size,
uint64 *p_result)
{
#ifdef OS_ENABLE_HW_BOUND_CHECK
WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls();
#endif
WASMExecEnv *exec_env_created = NULL;
WASMModuleInstanceCommon *module_inst_old = NULL;
uint32 argv[2], argc;
uint32 argv[3], argc;
bool ret;
argv[0] = size;
#if WASM_ENABLE_MEMORY64 != 0
bool is_memory64 = module_inst->memories[0]->is_memory64;
if (is_memory64) {
argc = 2;
PUT_I64_TO_ADDR(&argv[0], size);
}
else
#endif
{
argc = 1;
argv[0] = size;
}
/* if __retain is exported, then this module is compiled by
assemblyscript, the memory should be managed by as's runtime,
in this case we need to call the retain function after malloc
the memory */
if (retain_func) {
/* the malloc functino from assemblyscript is:
/* the malloc function from assemblyscript is:
function __new(size: usize, id: u32)
id = 0 means this is an ArrayBuffer object */
argv[1] = 0;
argc = 2;
argv[argc] = 0;
argc++;
}
if (exec_env) {
@ -1475,24 +1484,42 @@ execute_malloc_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
if (exec_env_created)
wasm_exec_env_destroy(exec_env_created);
if (ret)
if (ret) {
#if WASM_ENABLE_MEMORY64 != 0
if (is_memory64)
*p_result = GET_I64_FROM_ADDR(&argv[0]);
else
#endif
{
*p_result = argv[0];
}
}
return ret;
}
static bool
execute_free_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
WASMFunctionInstance *free_func, uint32 offset)
WASMFunctionInstance *free_func, uint64 offset)
{
#ifdef OS_ENABLE_HW_BOUND_CHECK
WASMExecEnv *exec_env_tls = wasm_runtime_get_exec_env_tls();
#endif
WASMExecEnv *exec_env_created = NULL;
WASMModuleInstanceCommon *module_inst_old = NULL;
uint32 argv[2];
uint32 argv[2], argc;
bool ret;
argv[0] = offset;
#if WASM_ENABLE_MEMORY64 != 0
if (module_inst->memories[0]->is_memory64) {
PUT_I64_TO_ADDR(&argv[0], offset);
argc = 2;
}
else
#endif
{
argv[0] = (uint32)offset;
argc = 1;
}
if (exec_env) {
#ifdef OS_ENABLE_HW_BOUND_CHECK
@ -1531,7 +1558,7 @@ execute_free_function(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
}
}
ret = wasm_call_function(exec_env, free_func, 1, argv);
ret = wasm_call_function(exec_env, free_func, argc, argv);
if (module_inst_old)
/* Restore the existing exec_env's module inst */
@ -3336,7 +3363,7 @@ wasm_module_malloc_internal(WASMModuleInstance *module_inst,
{
WASMMemoryInstance *memory = wasm_get_default_memory(module_inst);
uint8 *addr = NULL;
uint32 offset = 0;
uint64 offset = 0;
/* TODO: Memory64 size check based on memory idx type */
bh_assert(size <= UINT32_MAX);
@ -3352,7 +3379,7 @@ wasm_module_malloc_internal(WASMModuleInstance *module_inst,
else if (module_inst->e->malloc_function && module_inst->e->free_function) {
if (!execute_malloc_function(
module_inst, exec_env, module_inst->e->malloc_function,
module_inst->e->retain_function, (uint32)size, &offset)) {
module_inst->e->retain_function, size, &offset)) {
return 0;
}
/* If we use app's malloc function,
@ -3452,7 +3479,7 @@ wasm_module_free_internal(WASMModuleInstance *module_inst,
&& module_inst->e->free_function && memory->memory_data <= addr
&& addr < memory_data_end) {
execute_free_function(module_inst, exec_env,
module_inst->e->free_function, (uint32)ptr);
module_inst->e->free_function, ptr);
}
}
}
@ -4404,6 +4431,22 @@ llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst,
return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count);
}
bool
llvm_jit_func_type_is_super_of(WASMModuleInstance *module_inst,
uint32 type_idx1, uint32 type_idx2)
{
WASMModule *module = module_inst->module;
WASMType **types = module->types;
if (type_idx1 == type_idx2)
return true;
bh_assert(types[type_idx1]->type_flag == WASM_TYPE_FUNC);
bh_assert(types[type_idx2]->type_flag == WASM_TYPE_FUNC);
return wasm_func_type_is_super_of((WASMFuncType *)types[type_idx1],
(WASMFuncType *)types[type_idx2]);
}
WASMRttTypeRef
llvm_jit_rtt_type_new(WASMModuleInstance *module_inst, uint32 type_index)
{

View File

@ -811,6 +811,11 @@ bool
llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst,
WASMObjectRef gc_obj, uint32 type_index);
/* Whether func type1 is one of super types of func type2 */
bool
llvm_jit_func_type_is_super_of(WASMModuleInstance *module_inst,
uint32 type_idx1, uint32 type_idx2);
WASMRttTypeRef
llvm_jit_rtt_type_new(WASMModuleInstance *module_inst, uint32 type_index);

View File

@ -254,6 +254,10 @@ Currently we only profile the memory consumption of module, module_instance and
> See [Enable segue optimization for wamrc when generating the aot file](./perf_tune.md#3-enable-segue-optimization-for-wamrc-when-generating-the-aot-file) for more details.
#### **User defined linear memory allocator**
- **WAMR_BUILD_ALLOC_WITH_USAGE**=1/0, default to disable if not set
> Notes: by default, the linear memory is allocated by system. when it's set to 1 and Alloc_With_Allocator is selected, it will be allocated by customer.
#### **Enable running PGO(Profile-Guided Optimization) instrumented AOT file**
- **WAMR_BUILD_STATIC_PGO**=1/0, default to disable if not set
> Note: See [Use the AOT static PGO method](./perf_tune.md#5-use-the-aot-static-pgo-method) for more details.

View File

@ -129,6 +129,10 @@ if(CONFIG_INTERPRETERS_WAMR_GLOBAL_HEAP_POOL)
set(WAMR_BUILD_GLOBAL_HEAP_SIZE ${_HEAP_SIZE_})
endif()
if (CONFIG_INTERPRETERS_WAMR_MEM_ALLOC_WITH_USAGE)
set(WAMR_BUILD_MEM_ALLOC_WITH_USAGE 1)
endif()
if(CONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST)
set(WAMR_BUILD_SPEC_TEST 1)
endif()

View File

@ -373,6 +373,11 @@ CFLAGS += -DWASM_ENABLE_GLOBAL_HEAP_POOL=1
CFLAGS += -DWASM_GLOBAL_HEAP_SIZE="$(CONFIG_INTERPRETERS_WAMR_GLOBAL_HEAP_POOL_SIZE) * 1024"
else
CFLAGS += -DWASM_ENABLE_GLOBAL_HEAP_POOL=0
ifeq ($(CONFIG_INTERPRETERS_WAMR_MEM_ALLOC_WITH_USAGE),y)
CFLAGS += -DWASM_MEM_ALLOC_WITH_USAGE=1
else
CFLAGS += -DWASM_MEM_ALLOC_WITH_USAGE=0
endif
endif
ifeq ($(CONFIG_INTERPRETERS_WAMR_ENABLE_SPEC_TEST),y)

View File

@ -445,6 +445,9 @@ static char global_heap_buf[WASM_GLOBAL_HEAP_SIZE] = { 0 };
#else
static void *
malloc_func(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
mem_alloc_usage_t usage,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
void *user_data,
#endif
@ -455,6 +458,9 @@ malloc_func(
static void *
realloc_func(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
mem_alloc_usage_t usage, bool full_size_mmaped,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
void *user_data,
#endif
@ -465,6 +471,9 @@ realloc_func(
static void
free_func(
#if WASM_MEM_ALLOC_WITH_USAGE != 0
mem_alloc_usage_t usage,
#endif
#if WASM_MEM_ALLOC_WITH_USER_DATA != 0
void *user_data,
#endif

View File

@ -0,0 +1 @@
/out/

View File

@ -0,0 +1,87 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
cmake_minimum_required (VERSION 3.14)
include(CheckPIESupported)
if (NOT WAMR_BUILD_PLATFORM STREQUAL "windows")
project (native-stack-overflow)
else()
project (native-stack-overflow C ASM)
endif()
################ runtime settings ################
string (TOLOWER ${CMAKE_HOST_SYSTEM_NAME} WAMR_BUILD_PLATFORM)
if (APPLE)
add_definitions(-DBH_PLATFORM_DARWIN)
endif ()
# Reset default linker flags
set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
set (CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
# WAMR features switch
# Set WAMR_BUILD_TARGET, currently values supported:
# "X86_64", "AMD_64", "X86_32", "AARCH64[sub]", "ARM[sub]", "THUMB[sub]",
# "MIPS", "XTENSA", "RISCV64[sub]", "RISCV32[sub]"
if (NOT DEFINED WAMR_BUILD_TARGET)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)")
set (WAMR_BUILD_TARGET "AARCH64")
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64")
set (WAMR_BUILD_TARGET "RISCV64")
elseif (CMAKE_SIZEOF_VOID_P EQUAL 8)
# Build as X86_64 by default in 64-bit platform
set (WAMR_BUILD_TARGET "X86_64")
elseif (CMAKE_SIZEOF_VOID_P EQUAL 4)
# Build as X86_32 by default in 32-bit platform
set (WAMR_BUILD_TARGET "X86_32")
else ()
message(SEND_ERROR "Unsupported build target platform!")
endif ()
endif ()
if (NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE Debug)
endif ()
set (WAMR_BUILD_INTERP 1)
set (WAMR_BUILD_AOT 1)
set (WAMR_BUILD_JIT 0)
set (WAMR_BUILD_LIBC_BUILTIN 0)
set (WAMR_BUILD_LIBC_WASI 1)
if (NOT MSVC)
# linker flags
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}/../..)
include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake)
add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE})
################ application related ################
include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
include (${SHARED_DIR}/utils/uncommon/shared_uncommon.cmake)
add_executable (native-stack-overflow src/main.c src/native_impl.c ${UNCOMMON_SHARED_SOURCE})
check_pie_supported()
set_target_properties (native-stack-overflow PROPERTIES POSITION_INDEPENDENT_CODE ON)
if (APPLE)
target_link_libraries (native-stack-overflow vmlib -lm -ldl -lpthread)
else ()
target_link_libraries (native-stack-overflow vmlib -lm -ldl -lpthread -lrt)
endif ()

View File

@ -0,0 +1,4 @@
The "native-stack-overflow" sample project
==========================================
This sample examines native stack overflow detection mechanisms.

View File

@ -0,0 +1,75 @@
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
#!/bin/bash
CURR_DIR=$PWD
WAMR_DIR=${PWD}/../..
OUT_DIR=${PWD}/out
WASM_APPS=${PWD}/wasm-apps
rm -rf ${OUT_DIR}
mkdir ${OUT_DIR}
mkdir ${OUT_DIR}/wasm-apps
echo "##################### build (default)"
cd ${CURR_DIR}
mkdir -p cmake_build
cd cmake_build
cmake ..
make -j 4
if [ $? != 0 ];then
echo "BUILD_FAIL native-stack-overflow exit as $?\n"
exit 2
fi
cp -a native-stack-overflow ${OUT_DIR}
echo "##################### build (WAMR_DISABLE_HW_BOUND_CHECK=1)"
cd ${CURR_DIR}
mkdir -p cmake_build_disable_hw_bound
cd cmake_build_disable_hw_bound
cmake -D WAMR_DISABLE_HW_BOUND_CHECK=1 ..
make -j 4
if [ $? != 0 ];then
echo "BUILD_FAIL native-stack-overflow exit as $?\n"
exit 2
fi
cp -a native-stack-overflow ${OUT_DIR}/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK
echo
echo "##################### build wasm apps"
cd ${WASM_APPS}
for i in `ls *.c`
do
APP_SRC="$i"
OUT_FILE=${i%.*}.wasm
# use WAMR SDK to build out the .wasm binary
/opt/wasi-sdk/bin/clang \
-mexec-model=reactor \
-Os -z stack-size=4096 -Wl,--initial-memory=65536 \
-Wl,--allow-undefined \
-o ${OUT_DIR}/wasm-apps/${OUT_FILE} ${APP_SRC}
if [ -f ${OUT_DIR}/wasm-apps/${OUT_FILE} ]; then
echo "build ${OUT_FILE} success"
else
echo "build ${OUT_FILE} fail"
fi
done
echo "#################### build wasm apps done"
echo "#################### aot-compile"
WAMRC=${WAMR_DIR}/wamr-compiler/build/wamrc
${WAMRC} -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot ${OUT_DIR}/wasm-apps/${OUT_FILE}
echo "#################### aot-compile (--bounds-checks=1)"
${WAMRC} -o ${OUT_DIR}/wasm-apps/${OUT_FILE}.aot.bounds-checks --bounds-checks=1 ${OUT_DIR}/wasm-apps/${OUT_FILE}

View File

@ -0,0 +1 @@
rm -r cmake_build cmake_build_disable_hw_bound out

View File

@ -0,0 +1,12 @@
#!/bin/bash
echo "====== Interpreter"
out/native-stack-overflow out/wasm-apps/testapp.wasm
echo
echo "====== AOT"
out/native-stack-overflow out/wasm-apps/testapp.wasm.aot
echo
echo "====== AOT WAMR_DISABLE_HW_BOUND_CHECK=1"
out/native-stack-overflow.WAMR_DISABLE_HW_BOUND_CHECK out/wasm-apps/testapp.wasm.aot.bounds-checks

View File

@ -0,0 +1,178 @@
/*
* Copyright (C) 2024 Midokura Japan KK. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "wasm_export.h"
#include "bh_read_file.h"
uint32_t
host_consume_stack_and_call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx,
uint32_t x, uint32_t stack);
uint32_t
host_consume_stack(wasm_exec_env_t exec_env, uint32_t stack);
extern unsigned int nest;
static NativeSymbol native_symbols[] = {
{ "host_consume_stack_and_call_indirect",
host_consume_stack_and_call_indirect, "(iii)i", NULL },
{ "host_consume_stack", host_consume_stack, "(i)i", NULL },
};
struct record {
bool failed;
bool leaked;
char exception[128]; /* EXCEPTION_BUF_LEN */
};
void
print_record(unsigned int start, unsigned int end, const struct record *rec)
{
printf("%5u - %5u | %6s | %6s | %s\n", start, end,
rec->failed ? "failed" : "ok", rec->leaked ? "leaked" : "ok",
rec->exception);
}
int
main(int argc, char **argv)
{
char *buffer;
char error_buf[128];
if (argc != 2) {
return 2;
}
char *module_path = argv[1];
wasm_module_t module = NULL;
uint32 buf_size;
uint32 stack_size = 4096;
/*
* disable app heap.
* - we use wasi
* - https://github.com/bytecodealliance/wasm-micro-runtime/issues/2275
*/
uint32 heap_size = 0;
RuntimeInitArgs init_args;
memset(&init_args, 0, sizeof(RuntimeInitArgs));
init_args.mem_alloc_type = Alloc_With_System_Allocator;
init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol);
init_args.native_module_name = "env";
init_args.native_symbols = native_symbols;
if (!wasm_runtime_full_init(&init_args)) {
printf("wasm_runtime_full_init failed.\n");
return -1;
}
buffer = bh_read_file_to_buffer(module_path, &buf_size);
if (!buffer) {
printf("bh_read_file_to_buffer failed\n");
goto fail;
}
module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf,
sizeof(error_buf));
if (!module) {
printf("wasm_runtime_load failed: %s\n", error_buf);
goto fail;
}
/* header */
printf(" stack size | fail? | leak? | exception\n");
printf("-------------------------------------------------------------------"
"--------\n");
unsigned int stack;
unsigned int prevstack;
unsigned int stack_range_start = 0;
unsigned int stack_range_end = 4096 * 6;
unsigned int step = 16;
struct record rec0;
struct record rec1;
struct record *rec = &rec0;
struct record *prevrec = &rec1;
bool have_prevrec = false;
for (stack = stack_range_start; stack < stack_range_end; stack += step) {
wasm_module_inst_t module_inst = NULL;
wasm_exec_env_t exec_env = NULL;
bool failed = true;
const char *exception = NULL;
nest = 0;
module_inst = wasm_runtime_instantiate(module, stack_size, heap_size,
error_buf, sizeof(error_buf));
if (!module_inst) {
printf("wasm_runtime_instantiate failed: %s\n", error_buf);
goto fail2;
}
exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
if (!exec_env) {
printf("wasm_runtime_create_exec_env failed\n");
goto fail2;
}
const char *funcname = "test";
wasm_function_inst_t func =
wasm_runtime_lookup_function(module_inst, funcname);
if (!func) {
printf("wasm_runtime_lookup_function failed for %s\n", funcname);
goto fail2;
}
/* note: the function type is (ii)i */
uint32_t wasm_argv[] = {
stack,
30,
};
uint32_t wasm_argc = 2;
if (!wasm_runtime_call_wasm(exec_env, func, wasm_argc, wasm_argv)) {
exception = wasm_runtime_get_exception(module_inst);
goto fail2;
}
failed = false;
fail2:
/*
* note: non-zero "nest" here demonstrates resource leak on longjmp
* from signal handler.
* cf.
* https://github.com/bytecodealliance/wasm-micro-runtime/issues/3320
*/
memset(rec, 0, sizeof(*rec));
rec->failed = failed;
rec->leaked = nest != 0;
strncpy(rec->exception, exception ? exception : "",
sizeof(rec->exception));
if (have_prevrec && memcmp(prevrec, rec, sizeof(*rec))) {
print_record(prevstack, stack, prevrec);
have_prevrec = false;
}
if (!have_prevrec) {
prevstack = stack;
struct record *tmp = prevrec;
prevrec = rec;
rec = tmp;
have_prevrec = true;
}
if (exec_env) {
wasm_runtime_destroy_exec_env(exec_env);
}
if (module_inst) {
wasm_runtime_deinstantiate(module_inst);
}
}
if (have_prevrec) {
print_record(prevstack, stack, prevrec);
}
fail:
if (module) {
wasm_runtime_unload(module);
}
if (buffer) {
BH_FREE(buffer);
}
wasm_runtime_destroy();
}

View File

@ -0,0 +1,71 @@
/*
* Copyright (C) 2024 Midokura Japan KK. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include <inttypes.h>
#include <stdio.h>
#include "wasm_export.h"
#include "bh_platform.h"
/*
* this "nest" var has two purposes:
* - prevent tail-call optimization
* - detect possible resource leak
*/
unsigned int nest = 0;
ptrdiff_t prev_diff = 0;
uint32_t
call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx, uint32_t x)
{
uint32_t argv[1] = {
x,
};
uint32_t argc = 1;
if (!wasm_runtime_call_indirect(exec_env, funcidx, argc, argv)) {
/* failed */
return 0;
}
return argv[0];
}
uint32_t
host_consume_stack_and_call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx,
uint32_t x, uint32_t stack)
{
void *boundary = os_thread_get_stack_boundary();
void *fp = __builtin_frame_address(0);
ptrdiff_t diff = fp - boundary;
if (diff > stack) {
prev_diff = diff;
nest++;
uint32_t ret =
host_consume_stack_and_call_indirect(exec_env, funcidx, x, stack);
nest--;
return ret;
}
return call_indirect(exec_env, funcidx, x);
}
static uint32_t
consume_stack1(wasm_exec_env_t exec_env, void *base, uint32_t stack)
{
void *fp = __builtin_frame_address(0);
ptrdiff_t diff = (unsigned char *)base - (unsigned char *)fp;
assert(diff > 0);
char buf[16];
memset_s(buf, sizeof(buf), 0, sizeof(buf));
if (diff > stack) {
return diff;
}
return consume_stack1(exec_env, base, stack);
}
uint32_t
host_consume_stack(wasm_exec_env_t exec_env, uint32_t stack)
{
void *base = __builtin_frame_address(0);
return consume_stack1(exec_env, base, stack);
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2024 Midokura Japan KK. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include <stdio.h>
#include <stdint.h>
uint32_t
host_consume_stack_and_call_indirect(int (*)(int), uint32_t, uint32_t);
uint32_t host_consume_stack(uint32_t);
int
cb(int x)
{
return x * x;
}
int
consume_stack_cb(int x)
{
/*
* intentions:
*
* - consume native stack by making recursive calls
*
* - avoid tail-call optimization (either by the C compiler or
* aot-compiler)
*/
if (x == 0) {
return 0;
}
return consume_stack_cb(x - 1) + 1;
}
int
host_consume_stack_cb(int x)
{
return host_consume_stack(x);
}
__attribute__((export_name("test"))) uint32_t
test(uint32_t native_stack, uint32_t recurse_count)
{
uint32_t ret;
ret = host_consume_stack_and_call_indirect(cb, 321, native_stack);
ret = host_consume_stack_and_call_indirect(consume_stack_cb, recurse_count,
native_stack);
#if 0 /* notyet */
ret = host_consume_stack_and_call_indirect(host_consume_stack_cb, 1000000,
native_stack);
#endif
return 42;
}

View File

@ -95,7 +95,7 @@ def ignore_the_case(
return True
if gc_flag:
if case_name in ["type-equivalence", "type-rec", "array_init_elem", "array_init_data"]:
if case_name in ["array_init_elem", "array_init_data"]:
return True
if sgx_flag:

View File

@ -9,78 +9,6 @@ index 335496f0..5b975028 100644
- "integer representation too long"
+ "invalid type flag" ;; In GC extension, the first byte in rectype define is just one byte, not LEB128 encoded.
)
diff --git a/test/core/binary.wast b/test/core/binary.wast
index 1661a1c6..84c716b9 100644
--- a/test/core/binary.wast
+++ b/test/core/binary.wast
@@ -1082,7 +1082,7 @@
)
;; 1 br_table target declared, 2 given
-(assert_malformed
+(;assert_malformed
(module binary
"\00asm" "\01\00\00\00"
"\01\04\01" ;; type section
@@ -1132,3 +1132,4 @@
)
"unexpected content after last section"
)
+;)
diff --git a/test/core/data.wast b/test/core/data.wast
index a5c87fbb..6f948bae 100644
--- a/test/core/data.wast
+++ b/test/core/data.wast
@@ -306,9 +306,10 @@
"\02\01\41\00\0b" ;; active data segment 0 for memory 1
"\00" ;; empty vec(byte)
)
- "unknown memory 1"
+ "unknown memory"
)
+(; not supported by wat2wasm
;; Data segment with memory index 0 (no memory section)
(assert_invalid
(module binary
@@ -317,7 +318,7 @@
"\00\41\00\0b" ;; active data segment 0 for memory 0
"\00" ;; empty vec(byte)
)
- "unknown memory 0"
+ "unknown memory"
)
;; Data segment with memory index 1 (no memory section)
@@ -328,7 +329,7 @@
"\02\01\41\00\0b" ;; active data segment 0 for memory 1
"\00" ;; empty vec(byte)
)
- "unknown memory 1"
+ "unknown memory"
)
;; Data segment with memory index 1 and vec(byte) as above,
@@ -348,7 +349,7 @@
"\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f"
"\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d"
)
- "unknown memory 1"
+ "unknown memory"
)
;; Data segment with memory index 1 and specially crafted vec(byte) after.
@@ -368,8 +369,9 @@
"\20\21\22\23\24\25\26\27\28\29\2a\2b\2c\2d\2e\2f"
"\30\31\32\33\34\35\36\37\38\39\3a\3b\3c\3d"
)
- "unknown memory 1"
+ "unknown memory"
)
+;)
;; Invalid offsets
diff --git a/test/core/elem.wast b/test/core/elem.wast
index df1610f6..32c1d8b3 100644
--- a/test/core/elem.wast
@ -268,196 +196,11 @@ index 00000000..32650644
+ )
+ "unsupported initializer expression for table"
+)
diff --git a/test/core/gc/ref_test.wast b/test/core/gc/ref_test.wast
index 590b81b8..e0aa49ed 100644
--- a/test/core/gc/ref_test.wast
+++ b/test/core/gc/ref_test.wast
@@ -310,15 +310,16 @@
(br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 11)))))
(br_if $l (i32.eqz (ref.test (ref $t0) (table.get (i32.const 12)))))
- (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1)))))
- (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2)))))
+ ;; Must have explicit sub relationship
+ ;; (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 1)))))
+ ;; (br_if $l (i32.eqz (ref.test (ref $t1') (table.get (i32.const 2)))))
- (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11)))))
- (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12)))))
+ ;; (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 11)))))
+ ;; (br_if $l (i32.eqz (ref.test (ref $t1) (table.get (i32.const 12)))))
- (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2)))))
+ ;; (br_if $l (i32.eqz (ref.test (ref $t2') (table.get (i32.const 2)))))
- (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12)))))
+ ;; (br_if $l (i32.eqz (ref.test (ref $t2) (table.get (i32.const 12)))))
(return)
)
diff --git a/test/core/gc/type-subtyping.wast b/test/core/gc/type-subtyping.wast
index a9022fc3..4e22e91b 100644
index a9022fc3..4aa36e2a 100644
--- a/test/core/gc/type-subtyping.wast
+++ b/test/core/gc/type-subtyping.wast
@@ -112,6 +112,8 @@
)
)
+;; don't support recursive type equality and subtype check
+(;
(module
(rec (type $f1 (sub (func))) (type (struct (field (ref $f1)))))
(rec (type $f2 (sub (func))) (type (struct (field (ref $f2)))))
@@ -135,6 +137,7 @@
(func $g (type $g2))
(global (ref $g1) (ref.func $g))
)
+;)
(assert_invalid
(module
@@ -156,6 +159,8 @@
(global (ref $f1) (ref.func $g))
)
+;; don't support recursive type equality and subtype check
+(;
(module
(rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
(rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
@@ -201,6 +206,7 @@
(global (ref $g12) (ref.func $g12))
(global (ref $g22) (ref.func $g12))
)
+;)
(assert_invalid
(module
@@ -226,6 +232,8 @@
;; Runtime types
+;; don't support recursive type equality and subtype check
+(;
(module
(type $t0 (sub (func (result (ref null func)))))
(rec (type $t1 (sub $t0 (func (result (ref null $t1))))))
@@ -286,6 +294,7 @@
(assert_trap (invoke "fail4") "cast")
(assert_trap (invoke "fail5") "cast")
(assert_trap (invoke "fail6") "cast")
+;)
(module
(type $t1 (sub (func)))
@@ -316,7 +325,8 @@
(assert_trap (invoke "fail3") "cast")
(assert_trap (invoke "fail4") "cast")
-
+;; don't support recursive type equality and subtype check
+(;
(module
(rec (type $f1 (sub (func))) (type (struct (field (ref $f1)))))
(rec (type $f2 (sub (func))) (type (struct (field (ref $f2)))))
@@ -346,6 +356,7 @@
)
)
(assert_return (invoke "run") (i32.const 1))
+;)
(module
(rec (type $f1 (sub (func))) (type (struct (field (ref $f1)))))
@@ -370,6 +381,8 @@
)
(assert_return (invoke "run") (i32.const 1))
+;; don't support recursive type equality and subtype check
+(;
(module
(rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
(rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
@@ -390,7 +403,6 @@
)
(assert_return (invoke "run") (i32.const 1) (i32.const 1))
-
(module
(rec (type $f11 (sub (func (result (ref func))))) (type $f12 (sub $f11 (func (result (ref $f11))))))
(rec (type $f21 (sub (func (result (ref func))))) (type $f22 (sub $f21 (func (result (ref $f21))))))
@@ -429,7 +441,9 @@
(i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1)
(i32.const 1) (i32.const 1) (i32.const 1) (i32.const 1)
)
+;)
+(; we use normalized function type index
(module
(rec (type $f11 (sub (func))) (type $f12 (sub $f11 (func))))
(rec (type $f21 (sub (func))) (type $f22 (sub $f11 (func))))
@@ -439,6 +453,7 @@
)
)
(assert_return (invoke "run") (i32.const 0))
+;)
(module
(rec (type $f01 (sub (func))) (type $f02 (sub $f01 (func))))
@@ -547,15 +562,15 @@
(func (import "M3" "g") (type $g1))
)
-(module
- (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
- (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
- (rec
- (type $g2 (sub $f2 (func)))
- (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2)))))
- )
- (func (export "g") (type $g2))
-)
+;; (module
+;; (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
+;; (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
+;; (rec
+;; (type $g2 (sub $f2 (func)))
+;; (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2)))))
+;; )
+;; (func (export "g") (type $g2))
+;; )
(register "M4")
(module
(rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
@@ -597,17 +612,17 @@
(func (import "M6" "g") (type $f1))
)
-(module
- (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
- (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
- (rec
- (type $g2 (sub $f2 (func)))
- (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2)))))
- )
- (rec (type $h (sub $g2 (func))) (type (struct)))
- (func (export "h") (type $h))
-)
-(register "M7")
+;; (module
+;; (rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
+;; (rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
+;; (rec
+;; (type $g2 (sub $f2 (func)))
+;; (type (sub $s2 (struct (field (ref $f1) (ref $f2) (ref $f1) (ref $f2) (ref $g2)))))
+;; )
+;; (rec (type $h (sub $g2 (func))) (type (struct)))
+;; (func (export "h") (type $h))
+;; )
+;; (register "M7")
(module
(rec (type $f1 (sub (func))) (type $s1 (sub (struct (field (ref $f1))))))
(rec (type $f2 (sub (func))) (type $s2 (sub (struct (field (ref $f2))))))
@@ -740,7 +755,7 @@
@@ -740,7 +740,7 @@
"sub type"
)
@ -466,7 +209,7 @@ index a9022fc3..4e22e91b 100644
(module
(type $f0 (sub (func (param i32) (result i32))))
(type $s0 (sub $f0 (struct)))
@@ -764,7 +779,7 @@
@@ -764,7 +764,7 @@
"sub type"
)
@ -475,7 +218,7 @@ index a9022fc3..4e22e91b 100644
(module
(type $s0 (sub (struct)))
(type $f0 (sub $s0 (func (param i32) (result i32))))
@@ -772,7 +787,7 @@
@@ -772,7 +772,7 @@
"sub type"
)
@ -1262,29 +1005,3 @@ index 0b2d26f7..bdab6a01 100644
(table $t0 30 30 funcref)
(table $t1 30 30 funcref)
(elem (table $t1) (i32.const 2) func 3 1 4 1)
diff --git a/test/core/unreached-valid.wast b/test/core/unreached-valid.wast
index f3feb0f3..d8ef8743 100644
--- a/test/core/unreached-valid.wast
+++ b/test/core/unreached-valid.wast
@@ -60,7 +60,7 @@
;; Validation after unreachable
-(module
+(;module
(func (export "meet-bottom")
(block (result f64)
(block (result f32)
@@ -76,7 +76,6 @@
(assert_trap (invoke "meet-bottom") "unreachable")
-
;; Bottom heap type
(module
@@ -106,3 +105,4 @@
(unreachable)
)
)
+;)