Kaynağa Gözat

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.
Wenyong Huang 2 yıl önce
ebeveyn
işleme
943274e3b9

+ 74 - 1
core/iwasm/compilation/aot.c

@@ -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;

+ 2 - 1
core/iwasm/compilation/aot.h

@@ -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);

+ 51 - 10
core/iwasm/compilation/aot_emit_gc.c

@@ -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,

+ 2 - 1
core/iwasm/include/aot_export.h

@@ -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);

+ 13 - 0
core/iwasm/interpreter/wasm.h

@@ -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 {

+ 3 - 5
core/iwasm/interpreter/wasm_loader.c

@@ -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);

+ 1 - 1
core/iwasm/interpreter/wasm_mini_loader.c

@@ -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);

+ 2 - 1
wamr-compiler/main.c

@@ -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;
     }