Fix GC struct field offset calculation in AOT compiler (#2746)

The struct field size and field offset of a wasm struct may vary
in 32-bit target and 64-bit target, the aot compiler should not
use the offset calculated in wasm loader. It re-calculates them
according to the target info and whether GC is enabled.

And set the alignment of sruct.get/set when field size is 2, 4, or 8.
This commit is contained in:
Wenyong Huang 2023-11-13 15:22:10 +08:00 committed by GitHub
parent f92630108c
commit 943274e3b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 148 additions and 20 deletions

View File

@ -384,13 +384,81 @@ fail:
return NULL;
}
#if WASM_ENABLE_GC != 0
static void
calculate_struct_field_sizes_offsets(AOTCompData *comp_data, bool is_target_x86,
bool gc_enabled)
{
uint32 i;
for (i = 0; i < comp_data->type_count; i++) {
if (comp_data->types[i]->type_flag == WASM_TYPE_STRUCT) {
WASMStructType *struct_type = (WASMStructType *)comp_data->types[i];
WASMStructFieldType *fields = struct_type->fields;
uint32 field_offset_64bit, field_offset_32bit;
uint32 field_size_64bit, field_size_32bit, j;
/* offsetof(WASMStructObject, field_data) in 64-bit */
field_offset_64bit = sizeof(uint64);
/* offsetof(WASMStructObject, field_data) in 32-bit */
field_offset_32bit = sizeof(uint32);
for (j = 0; j < struct_type->field_count; j++) {
get_value_type_size(fields[j].field_type, gc_enabled,
&field_size_64bit, &field_size_32bit);
fields[j].field_size_64bit = field_size_64bit;
fields[j].field_size_32bit = field_size_32bit;
if (!is_target_x86) {
if (field_size_64bit == 2)
field_offset_64bit = align_uint(field_offset_64bit, 2);
else if (field_size_64bit >= 4)
field_offset_64bit = align_uint(field_offset_64bit, 4);
if (field_size_32bit == 2)
field_offset_32bit = align_uint(field_offset_32bit, 2);
else if (field_size_32bit >= 4)
field_offset_32bit = align_uint(field_offset_32bit, 4);
}
fields[j].field_offset_64bit = field_offset_64bit;
fields[j].field_offset_32bit = field_offset_32bit;
field_offset_64bit += field_size_64bit;
field_offset_32bit += field_size_32bit;
}
}
}
}
#endif
AOTCompData *
aot_create_comp_data(WASMModule *module, bool gc_enabled)
aot_create_comp_data(WASMModule *module, const char *target_arch,
bool gc_enabled)
{
AOTCompData *comp_data;
uint32 import_global_data_size_64bit = 0, global_data_size_64bit = 0, i, j;
uint32 import_global_data_size_32bit = 0, global_data_size_32bit = 0;
uint64 size;
#if WASM_ENABLE_GC != 0
bool is_target_x86 = false;
#endif
#if WASM_ENABLE_GC != 0
if (!target_arch) {
#if defined(BUILD_TARGET_X86_64) || defined(BUILD_TARGET_AMD_64) \
|| defined(BUILD_TARGET_X86_32)
is_target_x86 = true;
#endif
}
else {
if (!strncmp(target_arch, "x86_64", 6)
|| !strncmp(target_arch, "i386", 4))
is_target_x86 = true;
}
#endif
/* Allocate memory */
if (!(comp_data = wasm_runtime_malloc(sizeof(AOTCompData)))) {
@ -534,6 +602,11 @@ aot_create_comp_data(WASMModule *module, bool gc_enabled)
/* Create types, they are checked by wasm loader */
comp_data->type_count = module->type_count;
comp_data->types = module->types;
#if WASM_ENABLE_GC != 0
/* Calculate the field sizes and field offsets for 64-bit and 32-bit
targets since they may vary in 32-bit target and 64-bit target */
calculate_struct_field_sizes_offsets(comp_data, is_target_x86, gc_enabled);
#endif
/* Create import functions */
comp_data->import_func_count = module->import_function_count;

View File

@ -332,7 +332,8 @@ typedef struct AOTNativeSymbol {
} AOTNativeSymbol;
AOTCompData *
aot_create_comp_data(WASMModule *module, bool gc_enabled);
aot_create_comp_data(WASMModule *module, const char *target_arch,
bool gc_enabled);
void
aot_destroy_comp_data(AOTCompData *comp_data);

View File

@ -73,6 +73,13 @@
} \
} while (0)
static bool
is_target_x86(AOTCompContext *comp_ctx)
{
return !strncmp(comp_ctx->target_arch, "x86_64", 6)
|| !strncmp(comp_ctx->target_arch, "i386", 4);
}
bool
aot_call_aot_create_func_obj(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
LLVMValueRef func_idx, LLVMValueRef *p_gc_obj)
@ -345,7 +352,7 @@ aot_struct_obj_set_field(AOTCompContext *comp_ctx, LLVMValueRef struct_obj,
uint8 field_type)
{
bool trunc = false;
LLVMValueRef field_data_ptr;
LLVMValueRef field_data_ptr, res;
LLVMTypeRef field_data_type = NULL, field_data_ptr_type = NULL;
get_struct_field_data_types(comp_ctx, field_type, &field_data_type,
@ -383,11 +390,18 @@ aot_struct_obj_set_field(AOTCompContext *comp_ctx, LLVMValueRef struct_obj,
goto fail;
}
if (!LLVMBuildStore(comp_ctx->builder, field_value, field_data_ptr)) {
if (!(res =
LLVMBuildStore(comp_ctx->builder, field_value, field_data_ptr))) {
aot_set_last_error("llvm build store failed.");
goto fail;
}
if (!is_target_x86(comp_ctx)
&& (field_data_type == I64_TYPE || field_data_type == F64_TYPE
|| field_data_type == GC_REF_TYPE)) {
LLVMSetAlignment(res, 4);
}
return true;
fail:
return false;
@ -431,6 +445,12 @@ aot_struct_obj_get_field(AOTCompContext *comp_ctx, LLVMValueRef struct_obj,
goto fail;
}
if (!is_target_x86(comp_ctx)
&& (field_data_type == I64_TYPE || field_data_type == F64_TYPE
|| field_data_type == GC_REF_TYPE)) {
LLVMSetAlignment(field_value, 4);
}
if (extend) {
if (sign_extend) {
if (!(field_value = LLVMBuildSExt(comp_ctx->builder, field_value,
@ -466,11 +486,14 @@ struct_new_canon_init_fields(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
WASMStructFieldType *fields = compile_time_struct_type->fields;
int32 field_count = (int32)compile_time_struct_type->field_count;
int32 field_idx;
uint8 field_type, field_offset;
uint32 field_offset;
uint8 field_type;
for (field_idx = field_count - 1; field_idx >= 0; field_idx--) {
field_type = fields[field_idx].field_type;
field_offset = fields[field_idx].field_offset;
field_offset = comp_ctx->pointer_size == sizeof(uint64)
? fields[field_idx].field_offset_64bit
: fields[field_idx].field_offset_32bit;
if (wasm_is_type_reftype(field_type)) {
POP_GC_REF(field_value);
@ -565,11 +588,14 @@ aot_compile_op_struct_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
WASMStructType *compile_time_struct_type =
(WASMStructType *)comp_ctx->comp_data->types[type_index];
WASMStructFieldType *field;
uint8 field_type, field_offset;
uint32 field_offset;
uint8 field_type;
field = compile_time_struct_type->fields + field_idx;
field_type = field->field_type;
field_offset = field->field_offset;
field_offset = comp_ctx->pointer_size == sizeof(uint64)
? field->field_offset_64bit
: field->field_offset_32bit;
if (field_idx >= compile_time_struct_type->field_count) {
aot_set_last_error("struct field index out of bounds");
@ -626,11 +652,14 @@ aot_compile_op_struct_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
WASMStructType *compile_time_struct_type =
(WASMStructType *)comp_ctx->comp_data->types[type_index];
WASMStructFieldType *field;
uint8 field_type, field_offset;
uint32 field_offset;
uint8 field_type;
field = compile_time_struct_type->fields + field_idx;
field_type = field->field_type;
field_offset = field->field_offset;
field_offset = comp_ctx->pointer_size == sizeof(uint64)
? field->field_offset_64bit
: field->field_offset_32bit;
if (field_idx >= compile_time_struct_type->field_count) {
aot_set_last_error("struct field index out of bounds");
@ -805,7 +834,7 @@ aot_array_obj_set_elem(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
LLVMValueRef array_elem, uint8 array_elem_type)
{
bool trunc = false;
LLVMValueRef elem_data_ptr;
LLVMValueRef elem_data_ptr, res;
LLVMTypeRef elem_data_type = NULL, elem_data_ptr_type = NULL;
if (!aot_array_obj_elem_addr(comp_ctx, func_ctx, array_obj, elem_idx,
@ -867,11 +896,17 @@ aot_array_obj_set_elem(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
goto fail;
}
if (!LLVMBuildStore(comp_ctx->builder, array_elem, elem_data_ptr)) {
if (!(res = LLVMBuildStore(comp_ctx->builder, array_elem, elem_data_ptr))) {
aot_set_last_error("llvm build store failed.");
goto fail;
}
if (!is_target_x86(comp_ctx)
&& (elem_data_type == I64_TYPE || elem_data_type == F64_TYPE
|| elem_data_type == GC_REF_TYPE)) {
LLVMSetAlignment(res, 4);
}
return true;
fail:
return false;
@ -991,6 +1026,12 @@ aot_call_wasm_array_get_elem(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
goto fail;
}
if (!is_target_x86(comp_ctx)
&& (elem_data_type == I64_TYPE || elem_data_type == F64_TYPE
|| elem_data_type == GC_REF_TYPE)) {
LLVMSetAlignment(array_elem, 4);
}
if (extend) {
if (sign) {
if (!(array_elem = LLVMBuildSExt(comp_ctx->builder, array_elem,

View File

@ -22,7 +22,8 @@ struct AOTCompContext;
typedef struct AOTCompContext *aot_comp_context_t;
aot_comp_data_t
aot_create_comp_data(void *wasm_module, bool gc_enabled);
aot_create_comp_data(void *wasm_module, const char *target_arch,
bool gc_enabled);
void
aot_destroy_comp_data(aot_comp_data_t comp_data);

View File

@ -339,6 +339,19 @@ typedef struct WASMStructFieldType {
uint8 field_type;
uint8 field_size;
uint32 field_offset;
#if WASM_ENABLE_WAMR_COMPILER != 0 || WASM_ENABLE_JIT != 0
/*
* The field size and field offset of a wasm struct may vary
* in 32-bit target and 64-bit target, e.g., the size of a
* GC reference is 4 bytes in the former and 8 bytes in the
* latter, the AOT compiler needs to use the correct field
* offset according to the target info.
*/
uint8 field_size_64bit;
uint8 field_size_32bit;
uint32 field_offset_64bit;
uint32 field_offset_32bit;
#endif
} WASMStructFieldType;
typedef struct WASMStructType {

View File

@ -4019,13 +4019,11 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf,
AOTCompOption option = { 0 };
char *aot_last_error;
uint64 size;
bool gc_enabled =
#if WASM_ENABLE_GC != 0
true
bool gc_enabled = true;
#else
false
bool gc_enabled = false;
#endif
;
if (module->function_count == 0)
return true;
@ -4052,7 +4050,7 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf,
(bool *)((uint8 *)module->func_ptrs
+ sizeof(void *) * module->function_count);
module->comp_data = aot_create_comp_data(module, gc_enabled);
module->comp_data = aot_create_comp_data(module, NULL, gc_enabled);
if (!module->comp_data) {
aot_last_error = aot_get_last_error();
bh_assert(aot_last_error != NULL);

View File

@ -1867,7 +1867,7 @@ init_llvm_jit_functions_stage1(WASMModule *module, char *error_buf,
(bool *)((uint8 *)module->func_ptrs
+ sizeof(void *) * module->function_count);
module->comp_data = aot_create_comp_data(module, gc_enabled);
module->comp_data = aot_create_comp_data(module, NULL, gc_enabled);
if (!module->comp_data) {
aot_last_error = aot_get_last_error();
bh_assert(aot_last_error != NULL);

View File

@ -630,7 +630,8 @@ main(int argc, char *argv[])
goto fail2;
}
if (!(comp_data = aot_create_comp_data(wasm_module, option.enable_gc))) {
if (!(comp_data = aot_create_comp_data(wasm_module, option.target_arch,
option.enable_gc))) {
printf("%s\n", aot_get_last_error());
goto fail3;
}