Enhance GC subtyping checks (#3317)
Enhance the GC subtyping checks: - Fix issues in the type equivalence check - Enable the recursive type subtyping check - Add a equivalence type flag in defined types of aot file, if there is an equivalence type before, just set it true and re-use the previous type - Normalize the defined types for interpreter and AOT - Enable spec test case type-equivalence.wast and type-subtyping.wast, and enable some commented cases - Enable set WAMR_BUILD_SANITIZER from cmake variable
This commit is contained in:
@ -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);
|
||||
/* 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);
|
||||
EMIT_U16(types[i]->is_sub_final);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
comp_ctx->comp_data->type_count, type_idx);
|
||||
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,
|
||||
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,11 +2308,23 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check if function type index not equal */
|
||||
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;
|
||||
#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,
|
||||
ftype_idx_const, "cmp_ftype_idx"))) {
|
||||
aot_set_last_error("llvm build icmp failed.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Throw exception if ftype_idx != ftype_idx_const */
|
||||
|
||||
Reference in New Issue
Block a user