Jelajahi Sumber

AOT compiler: Implement part of GC opcode compilation (#2486)

Implement the compilation to LLVM IRs for the GC opcodes below:
- WASM_OP_REF_EQ, WASM_OP_CALL_REF, WASM_OP_RETURN_CALL_REF
- WASM_OP_REF_AS_NON_NULL, WASM_OP_BR_ON_NULL, WASM_OP_BR_ON_NON_NULL
- WASM_OP_I31_NEW, WASM_OP_I31_GET_S, WASM_OP_I31_GET_U
- WASM_OP_REF_TEST, WASM_OP_REF_CAST
- WASM_OP_REF_TEST_NULLABLE, WASM_OP_REF_CAST_NULLABLE
- WASM_OP_BR_ON_CAST, WASM_OP_BR_ON_CAST_FAIL
- WASM_OP_BR_ON_CAST_NULLABLE, WASM_OP_BR_ON_CAST_FAIL_NULLABLE
- WASM_OP_EXTERN_INTERNALIZE, WASM_OP_EXTERN_EXTERNALIZE
TianlongLiang 2 tahun lalu
induk
melakukan
a9cac2ec0a

+ 11 - 0
core/iwasm/aot/aot_runtime.c

@@ -3592,4 +3592,15 @@ aot_create_func_obj(AOTModuleInstance *module_inst, uint32 func_idx,
     return func_obj;
 }
 
+bool
+aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj,
+                       uint32 type_index)
+{
+    AOTModule *aot_module = (AOTModule *)module_inst->module;
+    AOTType **types = aot_module->types;
+    uint32 type_count = aot_module->type_count;
+
+    return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count);
+}
+
 #endif /* end of WASM_ENABLE_GC != 0 */

+ 4 - 0
core/iwasm/aot/aot_runtime.h

@@ -662,6 +662,10 @@ void *
 aot_create_func_obj(AOTModuleInstance *module_inst, uint32 func_idx,
                     bool throw_exce, char *error_buf, uint32 error_buf_size);
 
+bool
+aot_obj_is_instance_of(AOTModuleInstance *module_inst, WASMObjectRef gc_obj,
+                       uint32 type_index);
+
 #endif /* end of WASM_ENABLE_GC != 0 */
 
 #ifdef __cplusplus

+ 161 - 1
core/iwasm/compilation/aot_compiler.c

@@ -15,6 +15,7 @@
 #include "aot_emit_function.h"
 #include "aot_emit_parametric.h"
 #include "aot_emit_table.h"
+#include "aot_emit_gc.h"
 #include "simd/simd_access_lanes.h"
 #include "simd/simd_bitmask_extracts.h"
 #include "simd/simd_bit_shifts.h"
@@ -525,7 +526,159 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index)
                     return false;
                 break;
             }
-#endif
+#endif /* end of WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0 */
+
+#if WASM_ENABLE_GC != 0
+
+            case WASM_OP_CALL_REF:
+                if (!comp_ctx->enable_gc) {
+                    goto unsupport_gc;
+                }
+
+                read_leb_uint32(frame_ip, frame_ip_end, type_idx);
+                if (!aot_compile_op_call_ref(comp_ctx, func_ctx, type_idx,
+                                             false))
+                    return false;
+                break;
+
+            case WASM_OP_RETURN_CALL_REF:
+                if (!comp_ctx->enable_gc) {
+                    goto unsupport_gc;
+                }
+
+                read_leb_uint32(frame_ip, frame_ip_end, type_idx);
+                if (!aot_compile_op_call_ref(comp_ctx, func_ctx, type_idx,
+                                             true))
+                    return false;
+                if (!aot_compile_op_return(comp_ctx, func_ctx, &frame_ip))
+                    return false;
+                break;
+
+            case WASM_OP_REF_EQ:
+                if (!comp_ctx->enable_gc) {
+                    goto unsupport_gc;
+                }
+
+                if (!aot_compile_op_ref_eq(comp_ctx, func_ctx))
+                    return false;
+                break;
+
+            case WASM_OP_REF_AS_NON_NULL:
+                if (!comp_ctx->enable_gc) {
+                    goto unsupport_gc;
+                }
+
+                if (!aot_compile_op_ref_as_non_null(comp_ctx, func_ctx))
+                    return false;
+                break;
+
+            case WASM_OP_BR_ON_NULL:
+                if (!comp_ctx->enable_gc) {
+                    goto unsupport_gc;
+                }
+
+                read_leb_uint32(frame_ip, frame_ip_end, br_depth);
+                if (!aot_compile_op_br_on_null(comp_ctx, func_ctx, br_depth,
+                                               &frame_ip))
+                    return false;
+                break;
+
+            case WASM_OP_BR_ON_NON_NULL:
+                if (!comp_ctx->enable_gc) {
+                    goto unsupport_gc;
+                }
+
+                read_leb_uint32(frame_ip, frame_ip_end, br_depth);
+                if (!aot_compile_op_br_on_non_null(comp_ctx, func_ctx, br_depth,
+                                                   &frame_ip))
+                    return false;
+                break;
+
+            case WASM_OP_GC_PREFIX:
+            {
+                if (!comp_ctx->enable_gc) {
+                    goto unsupport_gc;
+                }
+
+                uint32 opcode1;
+
+                read_leb_uint32(frame_ip, frame_ip_end, opcode1);
+                opcode = (uint8)opcode1;
+
+                switch (opcode) {
+                    case WASM_OP_I31_NEW:
+                        if (!aot_compile_op_i31_new(comp_ctx, func_ctx))
+                            return false;
+                        break;
+
+                    case WASM_OP_I31_GET_S:
+                    case WASM_OP_I31_GET_U:
+                        if (!aot_compile_op_i31_get(
+                                comp_ctx, func_ctx,
+                                opcode == WASM_OP_I31_GET_S ? true : false))
+                            return false;
+                        break;
+
+                    case WASM_OP_REF_TEST:
+                    case WASM_OP_REF_TEST_NULLABLE:
+                    case WASM_OP_REF_CAST:
+                    case WASM_OP_REF_CAST_NULLABLE:
+                    {
+                        int32 heap_type;
+
+                        read_leb_int32(frame_ip, frame_ip_end, heap_type);
+                        if (!aot_compile_op_ref_test(
+                                comp_ctx, func_ctx, heap_type,
+                                opcode == WASM_OP_REF_TEST_NULLABLE
+                                    || opcode == WASM_OP_REF_CAST_NULLABLE,
+                                opcode == WASM_OP_REF_CAST
+                                    || opcode == WASM_OP_REF_CAST_NULLABLE))
+                            return false;
+                        break;
+                    }
+
+                    case WASM_OP_BR_ON_CAST:
+                    case WASM_OP_BR_ON_CAST_FAIL:
+                    case WASM_OP_BR_ON_CAST_NULLABLE:
+                    case WASM_OP_BR_ON_CAST_FAIL_NULLABLE:
+                    {
+                        int32 heap_type;
+
+                        read_leb_uint32(frame_ip, frame_ip_end, br_depth);
+                        read_leb_int32(frame_ip, frame_ip_end, heap_type);
+
+                        if (!aot_compile_op_br_on_cast(
+                                comp_ctx, func_ctx, heap_type,
+                                opcode == WASM_OP_BR_ON_CAST_NULLABLE
+                                    || opcode
+                                           == WASM_OP_BR_ON_CAST_FAIL_NULLABLE,
+                                opcode == WASM_OP_BR_ON_CAST_FAIL
+                                    || opcode
+                                           == WASM_OP_BR_ON_CAST_FAIL_NULLABLE,
+                                br_depth, &frame_ip))
+                            return false;
+                        break;
+                    }
+
+                    case WASM_OP_EXTERN_INTERNALIZE:
+                        if (!aot_compile_op_extern_internalize(comp_ctx,
+                                                               func_ctx))
+                            return false;
+                        break;
+
+                    case WASM_OP_EXTERN_EXTERNALIZE:
+                        if (!aot_compile_op_extern_externalize(comp_ctx,
+                                                               func_ctx))
+                            return false;
+                        break;
+
+                    default:
+                        aot_set_last_error("unsupported opcode");
+                        return false;
+                }
+            }
+
+#endif /* end of WASM_ENABLE_GC != 0 */
 
             case WASM_OP_GET_LOCAL:
                 read_leb_uint32(frame_ip, frame_ip_end, local_idx);
@@ -2590,6 +2743,13 @@ unsupport_ref_types:
     return false;
 #endif
 
+#if WASM_ENABLE_GC != 0
+unsupport_gc:
+    aot_set_last_error("garbage collection instruction was found, "
+                       "try adding --enable-gc");
+    return false;
+#endif
+
 #if WASM_ENABLE_REF_TYPES != 0 || WASM_ENABLE_GC != 0
 unsupport_gc_and_ref_types:
     aot_set_last_error(

+ 17 - 0
core/iwasm/compilation/aot_compiler.h

@@ -119,6 +119,23 @@ check_type_compatible(uint8 src_type, uint8 dst_type)
         }                                                      \
     } while (0)
 
+#if WASM_ENABLE_GC != 0
+
+#define GET_REF_FROM_STACK(llvm_value)                                        \
+    do {                                                                      \
+        AOTValue *aot_value;                                                  \
+        CHECK_STACK();                                                        \
+        aot_value =                                                           \
+            func_ctx->block_stack.block_list_end->value_stack.value_list_end; \
+        if (aot_value->type != VALUE_TYPE_GC_REF) {                           \
+            aot_set_last_error("WASM stack data type is not reference");      \
+            goto fail;                                                        \
+        }                                                                     \
+        llvm_value = aot_value->value;                                        \
+    } while (0)
+
+#endif
+
 #define POP(llvm_value, value_type)                                          \
     do {                                                                     \
         AOTValue *aot_value;                                                 \

+ 24 - 0
core/iwasm/compilation/aot_emit_compare.c

@@ -230,3 +230,27 @@ aot_compile_op_f64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 fail:
     return false;
 }
+
+bool
+aot_compile_op_ref_eq(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
+{
+    LLVMValueRef gc_obj1, gc_obj2, res;
+
+    POP_REF(gc_obj1);
+    POP_REF(gc_obj2);
+
+    /* LLVM pointer values pointers are compared using LLVMBuildICmp */
+    res = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, gc_obj1, gc_obj2,
+                        "cmp_gc_obj_eq");
+
+    if (!res) {
+        aot_set_last_error("llvm build compare failed.");
+        return false;
+    }
+
+    PUSH_COND(res);
+
+    return true;
+fail:
+    return false;
+}

+ 7 - 0
core/iwasm/compilation/aot_emit_compare.h

@@ -28,6 +28,13 @@ bool
 aot_compile_op_f64_compare(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
                            FloatCond cond);
 
+#if WASM_ENABLE_GC != 0
+
+bool
+aot_compile_op_ref_eq(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
+
+#endif
+
 #ifdef __cplusplus
 } /* end of extern "C" */
 #endif

+ 184 - 14
core/iwasm/compilation/aot_emit_control.c

@@ -5,6 +5,9 @@
 
 #include "aot_emit_control.h"
 #include "aot_emit_exception.h"
+#if WASM_ENABLE_GC != 0
+#include "aot_emit_gc.h"
+#endif
 #include "../aot/aot_runtime.h"
 #include "../interpreter/wasm_loader.h"
 
@@ -800,27 +803,18 @@ fail:
     return false;
 }
 
-bool
-aot_compile_op_br_if(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
-                     uint32 br_depth, uint8 **p_frame_ip)
+static bool
+aot_compile_conditional_br(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                           uint32 br_depth, uint8 **p_frame_ip,
+                           LLVMValueRef value_cmp)
 {
     AOTBlock *block_dst;
-    LLVMValueRef value_cmp, value, *values = NULL;
+    LLVMValueRef value, *values = NULL;
     LLVMBasicBlockRef llvm_else_block, next_llvm_end_block;
     char name[32];
     uint32 i, param_index, result_index;
     uint64 size;
 
-#if WASM_ENABLE_THREAD_MGR != 0
-    /* Insert suspend check point */
-    if (comp_ctx->enable_thread_mgr) {
-        if (!check_suspend_flags(comp_ctx, func_ctx))
-            return false;
-    }
-#endif
-
-    POP_COND(value_cmp);
-
     if (LLVMIsUndef(value_cmp)
 #if LLVM_VERSION_NUMBER >= 12
         || LLVMIsPoison(value_cmp)
@@ -934,6 +928,28 @@ fail:
     return false;
 }
 
+bool
+aot_compile_op_br_if(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                     uint32 br_depth, uint8 **p_frame_ip)
+{
+    LLVMValueRef value_cmp;
+
+#if WASM_ENABLE_THREAD_MGR != 0
+    /* Insert suspend check point */
+    if (comp_ctx->enable_thread_mgr) {
+        if (!check_suspend_flags(comp_ctx, func_ctx))
+            return false;
+    }
+#endif
+
+    POP_COND(value_cmp);
+
+    return aot_compile_conditional_br(comp_ctx, func_ctx, br_depth, p_frame_ip,
+                                      value_cmp);
+fail:
+    return false;
+}
+
 bool
 aot_compile_op_br_table(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
                         uint32 *br_depths, uint32 br_count, uint8 **p_frame_ip)
@@ -1153,3 +1169,157 @@ aot_handle_next_reachable_block(AOTCompContext *comp_ctx,
 {
     return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip);
 }
+
+#if WASM_ENABLE_GC != 0
+bool
+aot_compile_op_br_on_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                          uint32 br_depth, uint8 **p_frame_ip)
+{
+    LLVMValueRef gc_obj, value_cmp;
+
+#if WASM_ENABLE_THREAD_MGR != 0
+    /* Insert suspend check point */
+    if (comp_ctx->enable_thread_mgr) {
+        if (!check_suspend_flags(comp_ctx, func_ctx))
+            return false;
+    }
+#endif
+
+    GET_REF_FROM_STACK(gc_obj);
+
+    if (!(value_cmp =
+              LLVMBuildIsNull(comp_ctx->builder, gc_obj, "cmp gc obj"))) {
+        aot_set_last_error("llvm build isnull failed.");
+        goto fail;
+    }
+
+    return aot_compile_conditional_br(comp_ctx, func_ctx, br_depth, p_frame_ip,
+                                      value_cmp);
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_br_on_non_null(AOTCompContext *comp_ctx,
+                              AOTFuncContext *func_ctx, uint32 br_depth,
+                              uint8 **p_frame_ip)
+{
+    LLVMValueRef gc_obj, value_cmp;
+
+#if WASM_ENABLE_THREAD_MGR != 0
+    /* Insert suspend check point */
+    if (comp_ctx->enable_thread_mgr) {
+        if (!check_suspend_flags(comp_ctx, func_ctx))
+            return false;
+    }
+#endif
+
+    GET_REF_FROM_STACK(gc_obj);
+
+    if (!(value_cmp =
+              LLVMBuildIsNotNull(comp_ctx->builder, gc_obj, "cmp gc obj"))) {
+        aot_set_last_error("llvm build isnotnull failed.");
+        goto fail;
+    }
+
+    return aot_compile_conditional_br(comp_ctx, func_ctx, br_depth, p_frame_ip,
+                                      value_cmp);
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_br_on_cast(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                          int32 heap_type, bool nullable, bool br_on_fail,
+                          uint32 br_depth, uint8 **p_frame_ip)
+{
+    LLVMValueRef gc_obj, cmp, castable, is_branching_phi, phi_values[2];
+    LLVMBasicBlockRef gc_obj_null, gc_obj_non_null, conditional_branching,
+        phi_blocks[2];
+
+#if WASM_ENABLE_THREAD_MGR != 0
+    /* Insert suspend check point */
+    if (comp_ctx->enable_thread_mgr) {
+        if (!check_suspend_flags(comp_ctx, func_ctx))
+            return false;
+    }
+#endif
+
+    GET_REF_FROM_STACK(gc_obj);
+
+    /* Create if block */
+    CREATE_BLOCK(gc_obj_null, "gc_obj_null");
+    MOVE_BLOCK_AFTER_CURR(gc_obj_null);
+
+    /* Create else block */
+    CREATE_BLOCK(gc_obj_non_null, "gc_obj_non_null");
+    MOVE_BLOCK_AFTER_CURR(gc_obj_non_null);
+
+    /* Create branch_on block */
+    CREATE_BLOCK(conditional_branching, "conditional branching");
+    MOVE_BLOCK_AFTER_CURR(conditional_branching);
+
+    if (!(cmp = LLVMBuildIsNull(comp_ctx->builder, gc_obj, "cmp gc obj"))) {
+        aot_set_last_error("llvm build isnull failed.");
+        goto fail;
+    }
+    BUILD_COND_BR(cmp, gc_obj_null, gc_obj_non_null);
+
+    /* Move builder to gc_obj NULL block */
+    SET_BUILDER_POS(gc_obj_null);
+    if ((!br_on_fail && nullable) || (br_on_fail && !nullable)) {
+        /* WASM_OP_BR_ON_CAST_NULLABLE or WASM_OP_BR_ON_CAST_FAIL, branching */
+        phi_values[0] = I8_CONST(1);
+    }
+    else {
+        phi_values[1] = I8_ZERO;
+    }
+    BUILD_BR(conditional_branching);
+
+    /* Move builder to gc_obj non NULL block */
+    SET_BUILDER_POS(gc_obj_non_null);
+    if (heap_type >= 0) {
+        if (!aot_call_aot_obj_is_instance_of(comp_ctx, func_ctx, gc_obj,
+                                             I32_CONST(heap_type), &castable))
+            goto fail;
+    }
+    else {
+        if (!aot_call_wasm_obj_is_type_of(comp_ctx, func_ctx, gc_obj,
+                                          I32_CONST(heap_type), &castable))
+            goto fail;
+    }
+
+    if (!br_on_fail) {
+        /* WASM_OP_BR_ON_CAST || WASM_OP_BR_ON_CAST_NULLABLE */
+        BUILD_ICMP(LLVMIntNE, castable, I8_ZERO, phi_values[1], "castable");
+    }
+    else {
+        /* WASM_OP_BR_ON_CAST_FAIL || WASM_OP_BR_ON_CAST_FAIL_NULLABLE */
+        BUILD_ICMP(LLVMIntEQ, castable, I8_ZERO, phi_values[1], "castable");
+    }
+    BUILD_BR(conditional_branching);
+
+    /* Move builder to branch_on block */
+    SET_BUILDER_POS(conditional_branching);
+    /* create phi for branching condition, since it can come from both if and
+     * else block */
+    if (!(is_branching_phi =
+              LLVMBuildPhi(comp_ctx->builder, INT8_TYPE, "is_branching_phi"))) {
+        aot_set_last_error("llvm build phi failed.");
+        goto fail;
+    }
+
+    phi_blocks[0] = gc_obj_null;
+    phi_blocks[1] = gc_obj_non_null;
+    LLVMAddIncoming(is_branching_phi, phi_values, phi_blocks, 2);
+
+    if (!aot_compile_conditional_br(comp_ctx, func_ctx, br_depth, p_frame_ip,
+                                    is_branching_phi))
+        goto fail;
+
+    return true;
+fail:
+    return false;
+}
+
+#endif /* End of WASM_ENABLE_GC != 0 */

+ 16 - 0
core/iwasm/compilation/aot_emit_control.h

@@ -55,6 +55,22 @@ bool
 check_suspend_flags(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
 #endif
 
+#if WASM_ENABLE_GC != 0
+bool
+aot_compile_op_br_on_null(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                          uint32 br_depth, uint8 **p_frame_ip);
+
+bool
+aot_compile_op_br_on_non_null(AOTCompContext *comp_ctx,
+                              AOTFuncContext *func_ctx, uint32 br_depth,
+                              uint8 **p_frame_ip);
+
+bool
+aot_compile_op_br_on_cast(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                          int32 heap_type, bool nullable, bool br_on_fail,
+                          uint32 br_depth, uint8 **p_frame_ip);
+#endif
+
 #ifdef __cplusplus
 } /* end of extern "C" */
 #endif

+ 402 - 0
core/iwasm/compilation/aot_emit_function.c

@@ -1723,3 +1723,405 @@ aot_compile_op_ref_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 fail:
     return false;
 }
+
+#if WASM_ENABLE_GC != 0
+
+bool
+aot_compile_op_call_ref(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                        uint32 type_idx, bool tail_call)
+{
+    AOTFuncType *func_type;
+    LLVMValueRef func_obj, func_idx;
+    LLVMValueRef cmp_func_obj, cmp_func_idx;
+    LLVMValueRef func, func_ptr;
+    LLVMValueRef ext_ret_offset, ext_ret_ptr, ext_ret, res;
+    LLVMValueRef *param_values = NULL;
+    LLVMValueRef *result_phis = NULL, value_ret, import_func_count;
+    LLVMTypeRef *param_types = NULL, ret_type;
+    LLVMTypeRef llvm_func_type, llvm_func_ptr_type;
+    LLVMTypeRef ext_ret_ptr_type;
+    LLVMBasicBlockRef check_func_obj_succ, block_return, block_curr;
+    LLVMBasicBlockRef block_call_import, block_call_non_import;
+    LLVMValueRef offset;
+    uint32 total_param_count, func_param_count, func_result_count;
+    uint32 ext_cell_num, param_cell_num, i, j;
+    uint8 wasm_ret_type;
+    uint64 total_size;
+    char buf[32];
+    bool ret = false;
+
+    /* Check function type index */
+    bh_assert(type_idx >= comp_ctx->comp_data->type_count);
+
+    func_type = (AOTFuncType *)comp_ctx->comp_data->types[type_idx];
+    aot_estimate_and_record_stack_usage_for_function_call(comp_ctx, func_ctx,
+                                                          func_type);
+    func_param_count = func_type->param_count;
+    func_result_count = func_type->result_count;
+
+    POP_REF(func_obj);
+
+    /* Check if func object is NULL */
+    if (!(cmp_func_obj =
+              LLVMBuildIsNull(comp_ctx->builder, func_obj, "cmp_func_obj"))) {
+        aot_set_last_error("llvm build isnull failed.");
+        goto fail;
+    }
+
+    /* Throw exception if func object is NULL */
+    if (!(check_func_obj_succ = LLVMAppendBasicBlockInContext(
+              comp_ctx->context, func_ctx->func, "check_func_obj_succ"))) {
+        aot_set_last_error("llvm add basic block failed.");
+        goto fail;
+    }
+
+    LLVMMoveBasicBlockAfter(check_func_obj_succ,
+                            LLVMGetInsertBlock(comp_ctx->builder));
+
+    if (!(aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_GC_REF, true,
+                             cmp_func_obj, check_func_obj_succ)))
+        goto fail;
+
+    /* Get the func idx bound of the WASMFuncObject, the offset may be
+     * different in 32-bit runtime and 64-bit runtime since WASMObjectHeader
+     * is uintptr_t. Use comp_ctx->pointer_size as the
+     * offsetof(WASMFuncObject, func_idx_bound) */
+    if (!(offset = I32_CONST(comp_ctx->pointer_size))) {
+        HANDLE_FAILURE("LLVMConstInt");
+        goto fail;
+    }
+
+    if (!(func_idx =
+              LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, func_obj,
+                                    &offset, 1, "func_idx_bound_i8p"))) {
+        HANDLE_FAILURE("LLVMBuildGEP");
+        goto fail;
+    }
+
+    if (!(func_idx = LLVMBuildBitCast(comp_ctx->builder, func_idx,
+                                      INT32_PTR_TYPE, "func_idx_bound_i32p"))) {
+        HANDLE_FAILURE("LLVMBuildBitCast");
+        goto fail;
+    }
+
+    if (!(func_idx = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, func_idx,
+                                    "func_idx_bound"))) {
+        HANDLE_FAILURE("LLVMBuildLoad");
+        goto fail;
+    }
+
+    /* Initialize parameter types of the LLVM function */
+    total_param_count = 1 + func_param_count;
+
+    /* Extra function results' addresses (except the first one) are
+       appended to aot function parameters. */
+    if (func_result_count > 1)
+        total_param_count += func_result_count - 1;
+
+    total_size = sizeof(LLVMTypeRef) * (uint64)total_param_count;
+    if (total_size >= UINT32_MAX
+        || !(param_types = wasm_runtime_malloc((uint32)total_size))) {
+        aot_set_last_error("allocate memory failed.");
+        goto fail;
+    }
+
+    /* Prepare param types */
+    j = 0;
+    param_types[j++] = comp_ctx->exec_env_type;
+    for (i = 0; i < func_param_count; i++)
+        param_types[j++] = TO_LLVM_TYPE(func_type->types[i]);
+
+    for (i = 1; i < func_result_count; i++, j++) {
+        param_types[j] = TO_LLVM_TYPE(func_type->types[func_param_count + i]);
+        if (!(param_types[j] = LLVMPointerType(param_types[j], 0))) {
+            aot_set_last_error("llvm get pointer type failed.");
+            goto fail;
+        }
+    }
+
+    /* Resolve return type of the LLVM function */
+    if (func_result_count) {
+        wasm_ret_type = func_type->types[func_param_count];
+        ret_type = TO_LLVM_TYPE(wasm_ret_type);
+    }
+    else {
+        wasm_ret_type = VALUE_TYPE_VOID;
+        ret_type = VOID_TYPE;
+    }
+
+    /* Allocate memory for parameters */
+    total_size = sizeof(LLVMValueRef) * (uint64)total_param_count;
+    if (total_size >= UINT32_MAX
+        || !(param_values = wasm_runtime_malloc((uint32)total_size))) {
+        aot_set_last_error("allocate memory failed.");
+        goto fail;
+    }
+
+    /* First parameter is exec env */
+    j = 0;
+    param_values[j++] = func_ctx->exec_env;
+
+    /* Pop parameters from stack */
+    for (i = func_param_count - 1; (int32)i >= 0; i--)
+        POP(param_values[i + j], func_type->types[i]);
+
+    /* Prepare extra parameters */
+    ext_cell_num = 0;
+    for (i = 1; i < func_result_count; i++) {
+        ext_ret_offset = I32_CONST(ext_cell_num);
+        CHECK_LLVM_CONST(ext_ret_offset);
+
+        snprintf(buf, sizeof(buf), "ext_ret%d_ptr", i - 1);
+        if (!(ext_ret_ptr = LLVMBuildInBoundsGEP2(comp_ctx->builder, I32_TYPE,
+                                                  func_ctx->argv_buf,
+                                                  &ext_ret_offset, 1, buf))) {
+            aot_set_last_error("llvm build GEP failed.");
+            goto fail;
+        }
+
+        ext_ret_ptr_type = param_types[func_param_count + i];
+        snprintf(buf, sizeof(buf), "ext_ret%d_ptr_cast", i - 1);
+        if (!(ext_ret_ptr = LLVMBuildBitCast(comp_ctx->builder, ext_ret_ptr,
+                                             ext_ret_ptr_type, buf))) {
+            aot_set_last_error("llvm build bit cast failed.");
+            goto fail;
+        }
+
+        param_values[func_param_count + i] = ext_ret_ptr;
+        ext_cell_num +=
+            wasm_value_type_cell_num(func_type->types[func_param_count + i]);
+    }
+
+    if (ext_cell_num > 64) {
+        aot_set_last_error("prepare call-indirect arguments failed: "
+                           "maximum 64 extra cell number supported.");
+        goto fail;
+    }
+
+#if WASM_ENABLE_THREAD_MGR != 0
+    /* Insert suspend check point */
+    if (comp_ctx->enable_thread_mgr) {
+        if (!check_suspend_flags(comp_ctx, func_ctx))
+            goto fail;
+    }
+#endif
+
+#if (WASM_ENABLE_DUMP_CALL_STACK != 0) || (WASM_ENABLE_PERF_PROFILING != 0)
+    if (comp_ctx->enable_aux_stack_frame) {
+        if (!call_aot_alloc_frame_func(comp_ctx, func_ctx, func_idx))
+            goto fail;
+    }
+#endif
+
+    /* Add basic blocks */
+    block_call_import = LLVMAppendBasicBlockInContext(
+        comp_ctx->context, func_ctx->func, "call_import");
+    block_call_non_import = LLVMAppendBasicBlockInContext(
+        comp_ctx->context, func_ctx->func, "call_non_import");
+    block_return = LLVMAppendBasicBlockInContext(comp_ctx->context,
+                                                 func_ctx->func, "func_return");
+    if (!block_call_import || !block_call_non_import || !block_return) {
+        aot_set_last_error("llvm add basic block failed.");
+        goto fail;
+    }
+
+    LLVMMoveBasicBlockAfter(block_call_import,
+                            LLVMGetInsertBlock(comp_ctx->builder));
+    LLVMMoveBasicBlockAfter(block_call_non_import, block_call_import);
+    LLVMMoveBasicBlockAfter(block_return, block_call_non_import);
+
+    import_func_count = I32_CONST(comp_ctx->comp_data->import_func_count);
+    CHECK_LLVM_CONST(import_func_count);
+
+    /* Check if func_idx < import_func_count */
+    if (!(cmp_func_idx = LLVMBuildICmp(comp_ctx->builder, LLVMIntULT, func_idx,
+                                       import_func_count, "cmp_func_idx"))) {
+        aot_set_last_error("llvm build icmp failed.");
+        goto fail;
+    }
+
+    /* If func_idx < import_func_count, jump to call import block,
+       else jump to call non-import block */
+    if (!LLVMBuildCondBr(comp_ctx->builder, cmp_func_idx, block_call_import,
+                         block_call_non_import)) {
+        aot_set_last_error("llvm build cond br failed.");
+        goto fail;
+    }
+
+    /* Add result phis for return block */
+    LLVMPositionBuilderAtEnd(comp_ctx->builder, block_return);
+
+    if (func_result_count > 0) {
+        total_size = sizeof(LLVMValueRef) * (uint64)func_result_count;
+        if (total_size >= UINT32_MAX
+            || !(result_phis = wasm_runtime_malloc((uint32)total_size))) {
+            aot_set_last_error("allocate memory failed.");
+            goto fail;
+        }
+        memset(result_phis, 0, (uint32)total_size);
+        for (i = 0; i < func_result_count; i++) {
+            LLVMTypeRef tmp_type =
+                TO_LLVM_TYPE(func_type->types[func_param_count + i]);
+            if (!(result_phis[i] =
+                      LLVMBuildPhi(comp_ctx->builder, tmp_type, "phi"))) {
+                aot_set_last_error("llvm build phi failed.");
+                goto fail;
+            }
+        }
+    }
+
+    /* Translate call import block */
+    LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_import);
+
+    param_cell_num = func_type->param_cell_num;
+
+    /* Similar to opcode call_indirect, but for opcode ref.func needs to call
+     * aot_invoke_native_func instead */
+    if (!call_aot_invoke_native_func(comp_ctx, func_ctx, func_idx, func_type,
+                                     param_types + 1, param_values + 1,
+                                     func_param_count, param_cell_num, ret_type,
+                                     wasm_ret_type, &value_ret, &res))
+        goto fail;
+
+    /* Check whether exception was thrown when executing the function */
+    if (comp_ctx->enable_bound_check
+        && !check_call_return(comp_ctx, func_ctx, res))
+        goto fail;
+
+    block_curr = LLVMGetInsertBlock(comp_ctx->builder);
+
+    /* Get function return values, for aot_invoke_native_func, the extra ret
+     * values are put into param's array */
+    if (func_result_count > 0) {
+        /* Push the first result to stack */
+        LLVMAddIncoming(result_phis[0], &value_ret, &block_curr, 1);
+
+        /* Load extra result from its address and push to stack */
+        for (i = 1; i < func_result_count; i++) {
+            ret_type = TO_LLVM_TYPE(func_type->types[func_param_count + i]);
+            snprintf(buf, sizeof(buf), "ext_ret%d", i - 1);
+            if (!(ext_ret = LLVMBuildLoad2(comp_ctx->builder, ret_type,
+                                           param_values[func_param_count + i],
+                                           buf))) {
+                aot_set_last_error("llvm build load failed.");
+                goto fail;
+            }
+            LLVMAddIncoming(result_phis[i], &ext_ret, &block_curr, 1);
+        }
+    }
+
+    if (!LLVMBuildBr(comp_ctx->builder, block_return)) {
+        aot_set_last_error("llvm build br failed.");
+        goto fail;
+    }
+
+    /* Translate call non-import block */
+    LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_non_import);
+
+    /* Load function pointer */
+    if (!(func_ptr = LLVMBuildInBoundsGEP2(comp_ctx->builder, OPQ_PTR_TYPE,
+                                           func_ctx->func_ptrs, &func_idx, 1,
+                                           "func_ptr_tmp"))) {
+        aot_set_last_error("llvm build inbounds gep failed.");
+        goto fail;
+    }
+
+    if (!(func_ptr = LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE, func_ptr,
+                                    "func_ptr"))) {
+        aot_set_last_error("llvm build load failed.");
+        goto fail;
+    }
+
+    if (!(llvm_func_type =
+              LLVMFunctionType(ret_type, param_types, total_param_count, false))
+        || !(llvm_func_ptr_type = LLVMPointerType(llvm_func_type, 0))) {
+        aot_set_last_error("llvm add function type failed.");
+        goto fail;
+    }
+
+    if (!(func = LLVMBuildBitCast(comp_ctx->builder, func_ptr,
+                                  llvm_func_ptr_type, "indirect_func"))) {
+        aot_set_last_error("llvm build bit cast failed.");
+        goto fail;
+    }
+
+    if (!(value_ret = LLVMBuildCall2(comp_ctx->builder, llvm_func_type, func,
+                                     param_values, total_param_count,
+                                     func_result_count > 0 ? "ret" : ""))) {
+        aot_set_last_error("llvm build call failed.");
+        goto fail;
+    }
+
+    /* Set calling convention for the call with the func's calling
+       convention */
+    LLVMSetInstructionCallConv(value_ret, LLVMGetFunctionCallConv(func));
+
+    if (tail_call)
+        LLVMSetTailCall(value_ret, true);
+
+    /* Check whether exception was thrown when executing the function */
+    if (!tail_call
+        && (comp_ctx->enable_bound_check || is_win_platform(comp_ctx))
+        && !check_exception_thrown(comp_ctx, func_ctx))
+        goto fail;
+
+    if (func_result_count > 0) {
+        block_curr = LLVMGetInsertBlock(comp_ctx->builder);
+
+        /* Push the first result to stack */
+        LLVMAddIncoming(result_phis[0], &value_ret, &block_curr, 1);
+
+        /* Load extra result from its address and push to stack */
+        for (i = 1; i < func_result_count; i++) {
+            ret_type = TO_LLVM_TYPE(func_type->types[func_param_count + i]);
+            snprintf(buf, sizeof(buf), "ext_ret%d", i - 1);
+            if (!(ext_ret = LLVMBuildLoad2(comp_ctx->builder, ret_type,
+                                           param_values[func_param_count + i],
+                                           buf))) {
+                aot_set_last_error("llvm build load failed.");
+                goto fail;
+            }
+            LLVMAddIncoming(result_phis[i], &ext_ret, &block_curr, 1);
+        }
+    }
+
+    if (!LLVMBuildBr(comp_ctx->builder, block_return)) {
+        aot_set_last_error("llvm build br failed.");
+        goto fail;
+    }
+
+    /* Translate function return block */
+    LLVMPositionBuilderAtEnd(comp_ctx->builder, block_return);
+
+    for (i = 0; i < func_result_count; i++) {
+        PUSH(result_phis[i], func_type->types[func_param_count + i]);
+    }
+
+#if (WASM_ENABLE_DUMP_CALL_STACK != 0) || (WASM_ENABLE_PERF_PROFILING != 0)
+    if (comp_ctx->enable_aux_stack_frame) {
+        if (!call_aot_free_frame_func(comp_ctx, func_ctx))
+            goto fail;
+    }
+#endif
+
+#if WASM_ENABLE_THREAD_MGR != 0
+    /* Insert suspend check point */
+    if (comp_ctx->enable_thread_mgr) {
+        if (!check_suspend_flags(comp_ctx, func_ctx))
+            goto fail;
+    }
+#endif
+
+    ret = true;
+
+fail:
+    if (param_values)
+        wasm_runtime_free(param_values);
+    if (param_types)
+        wasm_runtime_free(param_types);
+    if (result_phis)
+        wasm_runtime_free(result_phis);
+    return ret;
+}
+
+#endif /* end of WASM_ENABLE_GC != 0 */

+ 6 - 0
core/iwasm/compilation/aot_emit_function.h

@@ -30,6 +30,12 @@ bool
 aot_compile_op_ref_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
                         uint32 func_idx);
 
+#if WASM_ENABLE_GC != 0
+bool
+aot_compile_op_call_ref(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                        uint32 type_idx, bool tail_call);
+#endif
+
 #ifdef __cplusplus
 } /* end of extern "C" */
 #endif

+ 475 - 8
core/iwasm/compilation/aot_emit_gc.c

@@ -4,6 +4,7 @@
  */
 
 #include "aot_emit_gc.h"
+#include "aot_emit_exception.h"
 
 #if WASM_ENABLE_GC != 0
 
@@ -23,20 +24,51 @@
         }                                                                \
     } while (0)
 
-#define BUILD_ICMP(op, left, right, res, name)                                \
+#define ADD_BASIC_BLOCK(block, name)                                          \
     do {                                                                      \
-        if (!(res =                                                           \
-                  LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \
-            aot_set_last_error("llvm build icmp failed.");                    \
+        if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context,        \
+                                                    func_ctx->func, name))) { \
+            aot_set_last_error("llvm add basic block failed.");               \
             goto fail;                                                        \
         }                                                                     \
     } while (0)
 
-#define ADD_BASIC_BLOCK(block, name)                                          \
+#define CURR_BLOCK() LLVMGetInsertBlock(comp_ctx->builder)
+
+#define MOVE_BLOCK_AFTER(llvm_block, llvm_block_after) \
+    LLVMMoveBasicBlockAfter(llvm_block, llvm_block_after)
+
+#define MOVE_BLOCK_AFTER_CURR(llvm_block) \
+    LLVMMoveBasicBlockAfter(llvm_block, CURR_BLOCK())
+
+#define MOVE_BLOCK_BEFORE(llvm_block, llvm_block_before) \
+    LLVMMoveBasicBlockBefore(llvm_block, llvm_block_before)
+
+#define BUILD_COND_BR(value_if, block_then, block_else)               \
+    do {                                                              \
+        if (!LLVMBuildCondBr(comp_ctx->builder, value_if, block_then, \
+                             block_else)) {                           \
+            aot_set_last_error("llvm build cond br failed.");         \
+            goto fail;                                                \
+        }                                                             \
+    } while (0)
+
+#define SET_BUILDER_POS(llvm_block) \
+    LLVMPositionBuilderAtEnd(comp_ctx->builder, llvm_block)
+
+#define BUILD_BR(llvm_block)                               \
+    do {                                                   \
+        if (!LLVMBuildBr(comp_ctx->builder, llvm_block)) { \
+            aot_set_last_error("llvm build br failed.");   \
+            goto fail;                                     \
+        }                                                  \
+    } while (0)
+
+#define BUILD_ICMP(op, left, right, res, name)                                \
     do {                                                                      \
-        if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context,        \
-                                                    func_ctx->func, name))) { \
-            aot_set_last_error("llvm add basic block failed.");               \
+        if (!(res =                                                           \
+                  LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \
+            aot_set_last_error("llvm build icmp failed.");                    \
             goto fail;                                                        \
         }                                                                     \
     } while (0)
@@ -104,4 +136,439 @@ fail:
     return false;
 }
 
+bool
+aot_call_aot_obj_is_instance_of(AOTCompContext *comp_ctx,
+                                AOTFuncContext *func_ctx, LLVMValueRef gc_obj,
+                                LLVMValueRef heap_type, LLVMValueRef *castable)
+{
+    LLVMValueRef param_values[3], func, value, res;
+    LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type;
+
+    param_types[0] = INT8_PTR_TYPE;
+    param_types[1] = GC_REF_TYPE;
+    param_types[2] = I32_TYPE;
+    ret_type = INT8_TYPE;
+
+    if (comp_ctx->is_jit_mode)
+        GET_AOT_FUNCTION(llvm_jit_obj_is_instance_of, 3);
+    else
+        GET_AOT_FUNCTION(aot_obj_is_instance_of, 3);
+
+    /* Call function aot_obj_is_instance_of() or llvm_jit_obj_is_instance_of()
+     */
+    param_values[0] = func_ctx->aot_inst;
+    param_values[1] = gc_obj;
+    param_values[2] = heap_type;
+
+    if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
+                               3, "call"))) {
+        aot_set_last_error("llvm build call failed.");
+        goto fail;
+    }
+
+    *castable = res;
+
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_call_wasm_obj_is_type_of(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                             LLVMValueRef gc_obj, LLVMValueRef heap_type,
+                             LLVMValueRef *castable)
+{
+    LLVMValueRef param_values[2], func, value, res;
+    LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type;
+
+    param_types[0] = GC_REF_TYPE;
+    param_types[1] = I32_TYPE;
+    ret_type = INT8_TYPE;
+
+    GET_AOT_FUNCTION(wasm_obj_is_type_of, 2);
+
+    /* Call function wasm_obj_is_type_of() */
+    param_values[0] = gc_obj;
+    param_values[1] = heap_type;
+    if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
+                               2, "call"))) {
+        aot_set_last_error("llvm build call failed.");
+        goto fail;
+    }
+
+    *castable = res;
+
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_ref_as_non_null(AOTCompContext *comp_ctx,
+                               AOTFuncContext *func_ctx)
+{
+    LLVMValueRef gc_obj, cmp_gc_obj;
+    LLVMBasicBlockRef check_gc_obj_succ;
+
+    GET_REF_FROM_STACK(gc_obj);
+
+    /* Check if gc object is NULL */
+    BUILD_ISNULL(gc_obj, cmp_gc_obj, "cmp_gc_obj");
+
+    ADD_BASIC_BLOCK(check_gc_obj_succ, "check_gc_obj_succ");
+    MOVE_BLOCK_AFTER_CURR(check_gc_obj_succ);
+
+    /*  Throw exception if it is NULL */
+    if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_GC_REF, true,
+                            cmp_gc_obj, check_gc_obj_succ))
+        goto fail;
+
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_i31_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
+{
+    LLVMValueRef i31_val, i31_obj;
+
+    POP_I32(i31_val);
+
+    /* Equivalent to wasm_i31_obj_new: ((i31_value << 1) | 1) */
+    if (!(i31_obj = LLVMBuildShl(comp_ctx->builder, i31_val, I32_ONE,
+                                 "i31_value << 1"))) {
+        aot_set_last_error("llvm build shl failed.");
+        goto fail;
+    }
+
+    if (!(i31_obj = LLVMBuildOr(comp_ctx->builder, i31_obj, I32_ONE,
+                                "((i31_value << 1) | 1)"))) {
+        aot_set_last_error("llvm build or failed.");
+        goto fail;
+    }
+
+    /* if uintptr_t is 64 bits, extend i32 to i64, equivalent to:
+     * (WASMI31ObjectRef)((i31_value << 1) | 1)  */
+    if (comp_ctx->pointer_size == 8) {
+        if (!(i31_obj = LLVMBuildZExt(comp_ctx->builder, i31_obj, I64_TYPE,
+                                      "extend i32 to uintptr_t"))) {
+            aot_set_last_error("llvm build zext failed.");
+            goto fail;
+        }
+    }
+
+    PUSH_REF(i31_obj);
+
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_i31_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                       bool sign)
+{
+    LLVMValueRef i31_obj, i31_val, cmp_i31_obj, bit_30, bit_30_is_set,
+        i31_sign_val;
+    LLVMBasicBlockRef check_i31_obj_succ;
+
+    POP_REF(i31_obj);
+
+    ADD_BASIC_BLOCK(check_i31_obj_succ, "check_i31_obj_succ");
+    MOVE_BLOCK_AFTER_CURR(check_i31_obj_succ);
+
+    /* Check if i31 object is NULL, throw exception if it is */
+    BUILD_ISNULL(i31_obj, cmp_i31_obj, "cmp_i31_obj");
+    if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_GC_REF, true,
+                            cmp_i31_obj, check_i31_obj_succ))
+        goto fail;
+
+    /* if uintptr_t is 64 bits, trunc i64 to i32 */
+    if (comp_ctx->pointer_size == 8) {
+        if (!(i31_val = LLVMBuildTrunc(comp_ctx->builder, i31_obj, I32_TYPE,
+                                       "trunc uintptr_t to i32"))) {
+            aot_set_last_error("llvm build trunc failed.");
+            goto fail;
+        }
+    }
+
+    /* i31_obj >> 1 */
+    if (!(i31_val = LLVMBuildLShr(comp_ctx->builder, i31_val, I32_ONE,
+                                  "i31_value"))) {
+        aot_set_last_error("llvm build lshr failed.");
+        goto fail;
+    }
+
+    if (sign) {
+        /* i31_val = i31_val & 0x40000000? i31_val |= 0x80000000 : i31_val */
+        if (!(bit_30 = LLVMBuildAnd(comp_ctx->builder, i31_val,
+                                    I32_CONST(0x40000000), "bit30"))) {
+            aot_set_last_error("llvm build and failed.");
+            goto fail;
+        }
+        BUILD_ICMP(LLVMIntNE, bit_30, I32_ZERO, bit_30_is_set, "bit30_is_set");
+        if (!(i31_sign_val = LLVMBuildOr(comp_ctx->builder, i31_val,
+                                         I32_CONST(0x80000000), "or"))) {
+            aot_set_last_error("llvm build or failed.");
+            goto fail;
+        }
+        if (!(i31_val =
+                  LLVMBuildSelect(comp_ctx->builder, bit_30_is_set,
+                                  i31_sign_val, i31_val, "final_value"))) {
+            aot_set_last_error("llvm build select failed.");
+            goto fail;
+        }
+    }
+
+    PUSH_I32(i31_val);
+
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_ref_test(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                        int32 heap_type, bool nullable, bool cast)
+{
+    LLVMValueRef gc_obj, cmp, castable;
+    LLVMBasicBlockRef gc_obj_null, gc_obj_non_null, end_block;
+
+    GET_REF_FROM_STACK(gc_obj);
+
+    /* Create if block */
+    ADD_BASIC_BLOCK(gc_obj_null, "gc_obj_null");
+    MOVE_BLOCK_AFTER_CURR(gc_obj_null);
+
+    /* Create else block */
+    ADD_BASIC_BLOCK(gc_obj_non_null, "gc_obj_non_null");
+    MOVE_BLOCK_AFTER_CURR(gc_obj_non_null);
+
+    /* Create end block */
+    ADD_BASIC_BLOCK(end_block, "end_block");
+    MOVE_BLOCK_AFTER_CURR(end_block);
+
+    /* Check if gc object is NULL */
+    BUILD_ISNULL(gc_obj, cmp, "cmp_gc_obj");
+    BUILD_COND_BR(cmp, gc_obj_null, gc_obj_non_null);
+
+    /* Move builder to gc_obj NULL block */
+    SET_BUILDER_POS(gc_obj_null);
+    if (!cast) {
+        /* For WASM_OP_REF_TEST and WASM_OP_REF_TEST_NULLABLE */
+        if (nullable)
+            PUSH_I32(I32_ONE);
+        else
+            PUSH_I32(I32_ZERO);
+    }
+    else {
+        /* For WASM_OP_REF_CAST, exception */
+        if (!nullable
+            && !aot_emit_exception(comp_ctx, func_ctx, EXCE_TYPE_NONCASTABLE,
+                                   false, NULL, NULL))
+            goto fail;
+        /* For WASM_OP_REF_CAST_NULLABLE, do nothing */
+    }
+
+    BUILD_BR(end_block);
+
+    /* Move builder to gc_obj not NULL block */
+    SET_BUILDER_POS(gc_obj_non_null);
+    if (heap_type >= 0) {
+        if (!aot_call_aot_obj_is_instance_of(comp_ctx, func_ctx, gc_obj,
+                                             I32_CONST(heap_type), &castable))
+            goto fail;
+    }
+    else {
+        if (!aot_call_wasm_obj_is_type_of(comp_ctx, func_ctx, gc_obj,
+                                          I32_CONST(heap_type), &castable))
+            goto fail;
+    }
+
+    if (!(castable = LLVMBuildZExt(comp_ctx->builder, castable, I32_TYPE,
+                                   "castable_i32"))) {
+        aot_set_last_error("llvm build zext failed.");
+        goto fail;
+    }
+
+    if (!cast) {
+        /* For WASM_OP_REF_TEST and WASM_OP_REF_TEST_NULLABLE */
+        PUSH_I32(castable);
+        BUILD_BR(end_block);
+    }
+    else {
+        /* For WASM_OP_REF_CAST and WASM_OP_REF_CAST_NULLABLE */
+        BUILD_ICMP(LLVMIntEQ, castable, I32_ZERO, cmp, "cmp_castable");
+        if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_TYPE_NONCASTABLE, true,
+                                cmp, end_block))
+            goto fail;
+    }
+
+    /* Move builder to end block */
+    SET_BUILDER_POS(end_block);
+
+    return true;
+fail:
+    return false;
+}
+
+static bool
+aot_call_wasm_externref_obj_to_internal_obj(AOTCompContext *comp_ctx,
+                                            AOTFuncContext *func_ctx,
+                                            LLVMValueRef externref_obj,
+                                            LLVMValueRef *gc_obj)
+{
+    LLVMValueRef param_values[1], func, value, res;
+    LLVMTypeRef param_types[1], ret_type, func_type, func_ptr_type;
+
+    param_types[0] = GC_REF_TYPE;
+    ret_type = GC_REF_TYPE;
+
+    GET_AOT_FUNCTION(wasm_externref_obj_to_internal_obj, 1);
+
+    /* Call function wasm_externref_obj_to_internal_obj */
+    param_values[0] = externref_obj;
+    if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
+                               1, "call"))) {
+        aot_set_last_error("llvm build call failed.");
+        goto fail;
+    }
+
+    *gc_obj = res;
+
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_extern_internalize(AOTCompContext *comp_ctx,
+                                  AOTFuncContext *func_ctx)
+{
+    LLVMValueRef externref_obj, gc_obj, cmp;
+    LLVMBasicBlockRef obj_null, obj_non_null, end_block;
+
+    POP_REF(externref_obj);
+
+    /* Create if block */
+    ADD_BASIC_BLOCK(obj_null, "obj_null");
+    MOVE_BLOCK_AFTER_CURR(obj_null);
+
+    /* Create else block */
+    ADD_BASIC_BLOCK(obj_non_null, "obj_non_null");
+    MOVE_BLOCK_AFTER_CURR(obj_non_null);
+
+    /* Create end block */
+    ADD_BASIC_BLOCK(end_block, "end_block");
+    MOVE_BLOCK_AFTER_CURR(end_block);
+
+    /* Check if externref object is NULL */
+    BUILD_ISNULL(externref_obj, cmp, "cmp_externref_obj");
+    BUILD_COND_BR(cmp, obj_null, obj_non_null);
+
+    /* Move builder to obj NULL block */
+    SET_BUILDER_POS(obj_null);
+    PUSH_REF(GC_REF_NULL);
+    BUILD_BR(end_block);
+
+    /* Move builder to obj not NULL block */
+    SET_BUILDER_POS(obj_non_null);
+    if (!aot_call_wasm_externref_obj_to_internal_obj(comp_ctx, func_ctx,
+                                                     externref_obj, &gc_obj))
+        goto fail;
+    PUSH_REF(gc_obj);
+    BUILD_BR(end_block);
+
+    /* Move builder to end block */
+    SET_BUILDER_POS(end_block);
+
+    return true;
+fail:
+    return false;
+}
+
+static bool
+aot_call_wasm_internal_obj_to_external_obj(AOTCompContext *comp_ctx,
+                                           AOTFuncContext *func_ctx,
+                                           LLVMValueRef gc_obj,
+                                           LLVMValueRef *externref_obj)
+{
+    LLVMValueRef param_values[2], func, value, res;
+    LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type;
+
+    param_types[0] = INT8_PTR_TYPE;
+    param_types[1] = GC_REF_TYPE;
+    ret_type = GC_REF_TYPE;
+
+    GET_AOT_FUNCTION(wasm_internal_obj_to_externref_obj, 1);
+
+    /* Call function wasm_internal_obj_to_externref_obj() */
+    param_values[0] = func_ctx->exec_env;
+    param_values[1] = gc_obj;
+    if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
+                               2, "call"))) {
+        aot_set_last_error("llvm build call failed.");
+        goto fail;
+    }
+
+    *externref_obj = res;
+
+    return true;
+fail:
+    return false;
+}
+
+bool
+aot_compile_op_extern_externalize(AOTCompContext *comp_ctx,
+                                  AOTFuncContext *func_ctx)
+{
+    LLVMValueRef gc_obj, externref_obj, cmp;
+    LLVMBasicBlockRef obj_null, obj_non_null, end_block, externalize_succ;
+
+    POP_REF(gc_obj);
+
+    /* Create if block */
+    ADD_BASIC_BLOCK(obj_null, "obj_null");
+    MOVE_BLOCK_AFTER_CURR(obj_null);
+
+    /* Create else block */
+    ADD_BASIC_BLOCK(obj_non_null, "obj_non_null");
+    MOVE_BLOCK_AFTER_CURR(obj_non_null);
+    ADD_BASIC_BLOCK(externalize_succ, "externalized_succ");
+    MOVE_BLOCK_AFTER(externalize_succ, obj_non_null);
+
+    /* Create end block */
+    ADD_BASIC_BLOCK(end_block, "end_block");
+    MOVE_BLOCK_AFTER_CURR(end_block);
+
+    /* Check if gc object is NULL */
+    BUILD_ISNULL(gc_obj, cmp, "cmp_gc_obj");
+    BUILD_COND_BR(cmp, obj_null, obj_non_null);
+
+    /* Move builder to obj NULL block */
+    SET_BUILDER_POS(obj_null);
+    PUSH_REF(GC_REF_NULL);
+    BUILD_BR(end_block);
+
+    /* Move builder to obj not NULL block */
+    SET_BUILDER_POS(obj_non_null);
+    if (!aot_call_wasm_internal_obj_to_external_obj(comp_ctx, func_ctx, gc_obj,
+                                                    &externref_obj))
+        goto fail;
+    BUILD_ISNULL(externref_obj, cmp, "cmp_externref_obj");
+    if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_GC_REF, true, cmp,
+                            externalize_succ))
+        goto fail;
+    PUSH_REF(externref_obj);
+    BUILD_BR(end_block);
+
+    /* Move builder to end block */
+    SET_BUILDER_POS(end_block);
+
+    return true;
+fail:
+    return false;
+}
+
 #endif /* end of WASM_ENABLE_GC != 0 */

+ 33 - 0
core/iwasm/compilation/aot_emit_gc.h

@@ -19,6 +19,39 @@ bool
 aot_call_aot_create_func_obj(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
                              LLVMValueRef func_idx, LLVMValueRef *p_gc_obj);
 
+bool
+aot_call_aot_obj_is_instance_of(AOTCompContext *comp_ctx,
+                                AOTFuncContext *func_ctx, LLVMValueRef gc_obj,
+                                LLVMValueRef heap_type, LLVMValueRef *castable);
+
+bool
+aot_call_wasm_obj_is_type_of(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                             LLVMValueRef gc_obj, LLVMValueRef heap_type,
+                             LLVMValueRef *castable);
+
+bool
+aot_compile_op_ref_as_non_null(AOTCompContext *comp_ctx,
+                               AOTFuncContext *func_ctx);
+
+bool
+aot_compile_op_i31_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
+
+bool
+aot_compile_op_i31_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                       bool sign);
+
+bool
+aot_compile_op_ref_test(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
+                        int32 heap_type, bool nullable, bool cast);
+
+bool
+aot_compile_op_extern_internalize(AOTCompContext *comp_ctx,
+                                  AOTFuncContext *func_ctx);
+
+bool
+aot_compile_op_extern_externalize(AOTCompContext *comp_ctx,
+                                  AOTFuncContext *func_ctx);
+
 #endif
 
 #ifdef __cplusplus

+ 13 - 1
core/iwasm/interpreter/wasm_runtime.c

@@ -3742,7 +3742,19 @@ llvm_jit_create_func_obj(WASMModuleInstance *module_inst, uint32 func_idx,
     return wasm_create_func_obj(module_inst, func_idx, throw_exce, error_buf,
                                 error_buf_size);
 }
-#endif /* end of WASM_ENABLE_GC != 0 */
+
+bool
+llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst,
+                            WASMObjectRef gc_obj, uint32 type_index)
+{
+    WASMModule *module = module_inst->module;
+    WASMType **types = module->types;
+    uint32 type_count = module->type_count;
+
+    return wasm_obj_is_instance_of(gc_obj, type_index, types, type_count);
+}
+
+#endif /* end of WASM_ENABLE_GC != 0  */
 
 #endif /* end of WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 */
 

+ 5 - 0
core/iwasm/interpreter/wasm_runtime.h

@@ -69,6 +69,7 @@ typedef enum WASMExceptionID {
     EXCE_FAILED_TO_COMPILE_FAST_JIT_FUNC,
     EXCE_ALREADY_THROWN,
     EXCE_NULL_GC_REF,
+    EXCE_TYPE_NONCASTABLE,
     EXCE_NUM,
 } WASMExceptionID;
 
@@ -706,6 +707,10 @@ void *
 llvm_jit_create_func_obj(WASMModuleInstance *module_inst, uint32 func_idx,
                          bool throw_exce, char *error_buf,
                          uint32 error_buf_size);
+
+bool
+llvm_jit_obj_is_instance_of(WASMModuleInstance *module_inst,
+                            WASMObjectRef gc_obj, uint32 type_index);
 #endif
 #endif /* end of WASM_ENABLE_JIT != 0 || WASM_ENABLE_WAMR_COMPILER != 0 */