mirror of
				https://github.com/bytecodealliance/wasm-micro-runtime.git
				synced 2025-10-31 21:27:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2145 lines
		
	
	
		
			69 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2145 lines
		
	
	
		
			69 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2019 Intel Corporation. All rights reserved.
 | |
|  * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
|  */
 | |
| 
 | |
| #include "aot_emit_gc.h"
 | |
| #include "aot_compiler.h"
 | |
| #include "aot_emit_exception.h"
 | |
| 
 | |
| #if WASM_ENABLE_GC != 0
 | |
| 
 | |
| #define BUILD_ISNULL(ptr, res, name)                                  \
 | |
|     do {                                                              \
 | |
|         if (!(res = LLVMBuildIsNull(comp_ctx->builder, ptr, name))) { \
 | |
|             aot_set_last_error("llvm build isnull failed.");          \
 | |
|             goto fail;                                                \
 | |
|         }                                                             \
 | |
|     } while (0)
 | |
| 
 | |
| #define BUILD_ISNOTNULL(ptr, res, name)                                  \
 | |
|     do {                                                                 \
 | |
|         if (!(res = LLVMBuildIsNotNull(comp_ctx->builder, ptr, name))) { \
 | |
|             aot_set_last_error("llvm build isnotnull failed.");          \
 | |
|             goto fail;                                                   \
 | |
|         }                                                                \
 | |
|     } while (0)
 | |
| 
 | |
| #define ADD_BASIC_BLOCK(block, name)                                          \
 | |
|     do {                                                                      \
 | |
|         if (!(block = LLVMAppendBasicBlockInContext(comp_ctx->context,        \
 | |
|                                                     func_ctx->func, name))) { \
 | |
|             aot_set_last_error("llvm add basic block failed.");               \
 | |
|             goto fail;                                                        \
 | |
|         }                                                                     \
 | |
|     } while (0)
 | |
| 
 | |
| #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 (!(res =                                                           \
 | |
|                   LLVMBuildICmp(comp_ctx->builder, op, left, right, name))) { \
 | |
|             aot_set_last_error("llvm build icmp failed.");                    \
 | |
|             goto fail;                                                        \
 | |
|         }                                                                     \
 | |
|     } 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)
 | |
| {
 | |
|     LLVMValueRef gc_obj, cmp_gc_obj, param_values[5], func, value;
 | |
|     LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type;
 | |
|     AOTFuncType *aot_func_type = func_ctx->aot_func->func_type;
 | |
|     LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder);
 | |
|     LLVMBasicBlockRef init_gc_obj_fail, init_gc_obj_succ;
 | |
| 
 | |
|     param_types[0] = INT8_PTR_TYPE;
 | |
|     param_types[1] = I32_TYPE;
 | |
|     param_types[2] = INT8_TYPE;
 | |
|     param_types[3] = INT8_PTR_TYPE;
 | |
|     param_types[4] = I32_TYPE;
 | |
|     ret_type = GC_REF_TYPE;
 | |
| 
 | |
|     if (comp_ctx->is_jit_mode)
 | |
|         GET_AOT_FUNCTION(llvm_jit_create_func_obj, 5);
 | |
|     else
 | |
|         GET_AOT_FUNCTION(aot_create_func_obj, 5);
 | |
| 
 | |
|     /* Call function llvm_jit/aot_create_func_obj()  */
 | |
|     param_values[0] = func_ctx->aot_inst;
 | |
|     param_values[1] = func_idx;
 | |
|     param_values[2] = I8_CONST(1);
 | |
|     param_values[3] = I8_PTR_NULL;
 | |
|     param_values[4] = I32_ZERO;
 | |
|     if (!(gc_obj = LLVMBuildCall2(comp_ctx->builder, func_type, func,
 | |
|                                   param_values, 5, "call"))) {
 | |
|         aot_set_last_error("llvm build call failed.");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     BUILD_ISNOTNULL(gc_obj, cmp_gc_obj, "gc_obj_not_null");
 | |
| 
 | |
|     ADD_BASIC_BLOCK(init_gc_obj_fail, "init_gc_obj_fail");
 | |
|     ADD_BASIC_BLOCK(init_gc_obj_succ, "init_gc_obj_success");
 | |
| 
 | |
|     LLVMMoveBasicBlockAfter(init_gc_obj_fail, block_curr);
 | |
|     LLVMMoveBasicBlockAfter(init_gc_obj_succ, block_curr);
 | |
| 
 | |
|     if (!LLVMBuildCondBr(comp_ctx->builder, cmp_gc_obj, init_gc_obj_succ,
 | |
|                          init_gc_obj_fail)) {
 | |
|         aot_set_last_error("llvm build cond br failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     /* If init gc_obj failed, return this function
 | |
|        so the runtime can catch the exception */
 | |
|     LLVMPositionBuilderAtEnd(comp_ctx->builder, init_gc_obj_fail);
 | |
|     if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) {
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     LLVMPositionBuilderAtEnd(comp_ctx->builder, init_gc_obj_succ);
 | |
|     *p_gc_obj = gc_obj;
 | |
| 
 | |
|     return true;
 | |
| 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_call_aot_rtt_type_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                           LLVMValueRef type_index, LLVMValueRef *rtt_type)
 | |
| {
 | |
|     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] = I32_TYPE;
 | |
|     ret_type = GC_REF_TYPE;
 | |
| 
 | |
|     if (comp_ctx->is_jit_mode)
 | |
|         GET_AOT_FUNCTION(llvm_jit_rtt_type_new, 2);
 | |
|     else
 | |
|         GET_AOT_FUNCTION(aot_rtt_type_new, 2);
 | |
| 
 | |
|     /* Call function llvm_jit/aot_rtt_type_new() */
 | |
|     param_values[0] = func_ctx->aot_inst;
 | |
|     param_values[1] = type_index;
 | |
|     if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
 | |
|                                2, "call"))) {
 | |
|         aot_set_last_error("llvm build call failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     *rtt_type = 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_GC_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_REFERENCE, true,
 | |
|                             cmp_gc_obj, check_gc_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| aot_call_wasm_struct_obj_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                              LLVMValueRef rtt_type, LLVMValueRef *struct_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] = INT8_PTR_TYPE;
 | |
|     ret_type = GC_REF_TYPE;
 | |
| 
 | |
|     GET_AOT_FUNCTION(wasm_struct_obj_new, 2);
 | |
| 
 | |
|     /* Call function wasm_struct_obj_new() */
 | |
|     param_values[0] = func_ctx->exec_env;
 | |
|     param_values[1] = rtt_type;
 | |
|     if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
 | |
|                                2, "call"))) {
 | |
|         aot_set_last_error("llvm build call failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     *struct_obj = res;
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static void
 | |
| get_struct_field_data_types(const AOTCompContext *comp_ctx, uint8 field_type,
 | |
|                             LLVMTypeRef *p_field_data_type,
 | |
|                             LLVMTypeRef *p_field_data_ptr_type,
 | |
|                             bool *p_trunc_or_extend)
 | |
| {
 | |
|     LLVMTypeRef field_data_type = NULL, field_data_ptr_type = NULL;
 | |
|     bool trunc_or_extend = false;
 | |
| 
 | |
|     if (wasm_is_type_reftype(field_type)) {
 | |
|         field_data_type = GC_REF_TYPE;
 | |
|         field_data_ptr_type = GC_REF_PTR_TYPE;
 | |
|     }
 | |
|     else {
 | |
|         switch (field_type) {
 | |
|             case VALUE_TYPE_I32:
 | |
|                 field_data_type = I32_TYPE;
 | |
|                 field_data_ptr_type = INT32_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_I64:
 | |
|                 field_data_type = I64_TYPE;
 | |
|                 field_data_ptr_type = INT64_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_F32:
 | |
|                 field_data_type = F32_TYPE;
 | |
|                 field_data_ptr_type = F32_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_F64:
 | |
|                 field_data_type = F64_TYPE;
 | |
|                 field_data_ptr_type = F64_PTR_TYPE;
 | |
|                 break;
 | |
|             case PACKED_TYPE_I8:
 | |
|                 field_data_type = INT8_TYPE;
 | |
|                 field_data_ptr_type = INT8_PTR_TYPE;
 | |
|                 trunc_or_extend = true;
 | |
|                 break;
 | |
|             case PACKED_TYPE_I16:
 | |
|                 field_data_type = INT16_TYPE;
 | |
|                 field_data_ptr_type = INT16_PTR_TYPE;
 | |
|                 trunc_or_extend = true;
 | |
|                 break;
 | |
|             default:
 | |
|                 bh_assert(0);
 | |
|                 break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     *p_field_data_type = field_data_type;
 | |
|     *p_field_data_ptr_type = field_data_ptr_type;
 | |
|     *p_trunc_or_extend = trunc_or_extend;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| aot_struct_obj_set_field(AOTCompContext *comp_ctx, LLVMValueRef struct_obj,
 | |
|                          LLVMValueRef field_offset, LLVMValueRef field_value,
 | |
|                          uint8 field_type)
 | |
| {
 | |
|     bool trunc = false;
 | |
|     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,
 | |
|                                 &field_data_ptr_type, &trunc);
 | |
| 
 | |
|     /* Truncate field_value if necessary */
 | |
|     if (trunc) {
 | |
|         if (!(field_value =
 | |
|                   LLVMBuildTrunc(comp_ctx->builder, field_value,
 | |
|                                  field_data_type, "field_value_trunc"))) {
 | |
|             aot_set_last_error("llvm build trunc failed.");
 | |
|             goto fail;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!(struct_obj = LLVMBuildBitCast(comp_ctx->builder, struct_obj,
 | |
|                                         INT8_PTR_TYPE, "struct_obj_i8p"))) {
 | |
|         aot_set_last_error("llvm build bitcast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     /* Build field data ptr and store the value */
 | |
|     if (!(field_data_ptr =
 | |
|               LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, struct_obj,
 | |
|                                     &field_offset, 1, "field_data_i8p"))) {
 | |
|         aot_set_last_error("llvm build gep failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     /* Cast to the field data type ptr */
 | |
|     if (!(field_data_ptr =
 | |
|               LLVMBuildBitCast(comp_ctx->builder, field_data_ptr,
 | |
|                                field_data_ptr_type, "field_value_ptr"))) {
 | |
|         aot_set_last_error("llvm build bitcast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     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;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| aot_struct_obj_get_field(AOTCompContext *comp_ctx, LLVMValueRef struct_obj,
 | |
|                          LLVMValueRef field_offset, LLVMValueRef *p_field_value,
 | |
|                          uint8 field_type, bool sign_extend)
 | |
| {
 | |
|     bool extend = false;
 | |
|     LLVMValueRef field_value, field_data_ptr;
 | |
|     LLVMTypeRef field_data_type = NULL, field_data_ptr_type = NULL;
 | |
| 
 | |
|     get_struct_field_data_types(comp_ctx, field_type, &field_data_type,
 | |
|                                 &field_data_ptr_type, &extend);
 | |
| 
 | |
|     if (!(struct_obj = LLVMBuildBitCast(comp_ctx->builder, struct_obj,
 | |
|                                         INT8_PTR_TYPE, "struct_obj_i8p"))) {
 | |
|         aot_set_last_error("llvm build bitcast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(field_data_ptr =
 | |
|               LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, struct_obj,
 | |
|                                     &field_offset, 1, "field_data_i8p"))) {
 | |
|         aot_set_last_error("llvm build gep failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(field_data_ptr =
 | |
|               LLVMBuildBitCast(comp_ctx->builder, field_data_ptr,
 | |
|                                field_data_ptr_type, "field_value_ptr"))) {
 | |
|         aot_set_last_error("llvm build bitcast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(field_value = LLVMBuildLoad2(comp_ctx->builder, field_data_type,
 | |
|                                        field_data_ptr, "field_value"))) {
 | |
|         aot_set_last_error("llvm build load 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(field_value, 4);
 | |
|     }
 | |
| 
 | |
|     if (extend) {
 | |
|         if (sign_extend) {
 | |
|             if (!(field_value = LLVMBuildSExt(comp_ctx->builder, field_value,
 | |
|                                               I32_TYPE, "field_value_sext"))) {
 | |
|                 aot_set_last_error("llvm build signed ext failed.");
 | |
|                 goto fail;
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             if (!(field_value = LLVMBuildZExt(comp_ctx->builder, field_value,
 | |
|                                               I32_TYPE, "field_value_zext"))) {
 | |
|                 aot_set_last_error("llvm build unsigned ext failed.");
 | |
|                 goto fail;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     *p_field_value = field_value;
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| struct_new_canon_init_fields(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                              uint32 type_index, LLVMValueRef struct_obj)
 | |
| {
 | |
|     LLVMValueRef field_value = NULL;
 | |
|     /* Used in compile time, to distinguish what type of AOTValue POP,
 | |
|      * field_data offset, size  */
 | |
|     WASMStructType *compile_time_struct_type =
 | |
|         (WASMStructType *)comp_ctx->comp_data->types[type_index];
 | |
|     WASMStructFieldType *fields = compile_time_struct_type->fields;
 | |
|     int32 field_count = (int32)compile_time_struct_type->field_count;
 | |
|     int32 field_idx;
 | |
|     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 = 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);
 | |
|         }
 | |
|         else if (field_type == VALUE_TYPE_I32 || field_type == PACKED_TYPE_I8
 | |
|                  || field_type == PACKED_TYPE_I16) {
 | |
|             POP_I32(field_value);
 | |
|         }
 | |
|         else if (field_type == VALUE_TYPE_I64) {
 | |
|             POP_I64(field_value);
 | |
|         }
 | |
|         else if (field_type == VALUE_TYPE_F32) {
 | |
|             POP_F32(field_value);
 | |
|         }
 | |
|         else if (field_type == VALUE_TYPE_F64) {
 | |
|             POP_F64(field_value);
 | |
|         }
 | |
|         else {
 | |
|             bh_assert(0);
 | |
|         }
 | |
| 
 | |
|         if (!aot_struct_obj_set_field(comp_ctx, struct_obj,
 | |
|                                       I32_CONST(field_offset), field_value,
 | |
|                                       field_type))
 | |
|             goto fail;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_struct_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                           uint32 type_index, bool init_with_default)
 | |
| {
 | |
|     LLVMValueRef rtt_type, struct_obj, cmp;
 | |
|     LLVMBasicBlockRef check_rtt_type_succ, check_struct_obj_succ;
 | |
| 
 | |
|     if (!aot_gen_commit_values(comp_ctx->aot_frame))
 | |
|         return false;
 | |
| 
 | |
|     if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true))
 | |
|         return false;
 | |
| 
 | |
|     /* Generate call wasm_rtt_type_new and check for exception */
 | |
|     if (!aot_call_aot_rtt_type_new(comp_ctx, func_ctx, I32_CONST(type_index),
 | |
|                                    &rtt_type))
 | |
|         goto fail;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_rtt_type_succ, "check rtt type succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_rtt_type_succ);
 | |
| 
 | |
|     BUILD_ISNULL(rtt_type, cmp, "cmp_rtt_type");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_RTT_TYPE,
 | |
|                             true, cmp, check_rtt_type_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     /* Generate call wasm_struct_obj_new and check for exception */
 | |
|     if (!aot_call_wasm_struct_obj_new(comp_ctx, func_ctx, rtt_type,
 | |
|                                       &struct_obj))
 | |
|         goto fail;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_struct_obj_succ, "check struct obj succ");
 | |
|     MOVE_BLOCK_AFTER(check_struct_obj_succ, check_rtt_type_succ);
 | |
| 
 | |
|     BUILD_ISNULL(struct_obj, cmp, "cmp_struct_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx,
 | |
|                             EXCE_FAILED_TO_CREATE_STRUCT_OBJ, true, cmp,
 | |
|                             check_struct_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     SET_BUILDER_POS(check_struct_obj_succ);
 | |
| 
 | |
|     /* For WASM_OP_STRUCT_NEW, init field with poped value */
 | |
|     if (!init_with_default
 | |
|         && !struct_new_canon_init_fields(comp_ctx, func_ctx, type_index,
 | |
|                                          struct_obj)) {
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     PUSH_GC_REF(struct_obj);
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_struct_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                           uint32 type_index, uint32 field_idx, bool sign)
 | |
| {
 | |
|     LLVMValueRef struct_obj, cmp, field_value;
 | |
|     LLVMBasicBlockRef check_struct_obj_succ;
 | |
| 
 | |
|     /* Used in compile time, to distinguish what type of AOTValue PUSH,
 | |
|      * field_data offset, size  */
 | |
|     WASMStructType *compile_time_struct_type =
 | |
|         (WASMStructType *)comp_ctx->comp_data->types[type_index];
 | |
|     WASMStructFieldType *field;
 | |
|     uint32 field_offset;
 | |
|     uint8 field_type;
 | |
| 
 | |
|     field = compile_time_struct_type->fields + field_idx;
 | |
|     field_type = field->field_type;
 | |
|     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");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     POP_GC_REF(struct_obj);
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_struct_obj_succ, "check struct obj succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_struct_obj_succ);
 | |
| 
 | |
|     BUILD_ISNULL(struct_obj, cmp, "cmp_struct_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_STRUCT_OBJ, true, cmp,
 | |
|                             check_struct_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     if (!aot_struct_obj_get_field(comp_ctx, struct_obj, I32_CONST(field_offset),
 | |
|                                   &field_value, field_type, sign))
 | |
|         goto fail;
 | |
| 
 | |
|     if (wasm_is_type_reftype(field_type)) {
 | |
|         PUSH_GC_REF(field_value);
 | |
|     }
 | |
|     else if (field_type == VALUE_TYPE_I32 || field_type == PACKED_TYPE_I8
 | |
|              || field_type == PACKED_TYPE_I16) {
 | |
|         PUSH_I32(field_value);
 | |
|     }
 | |
|     else if (field_type == VALUE_TYPE_I64) {
 | |
|         PUSH_I64(field_value);
 | |
|     }
 | |
|     else if (field_type == VALUE_TYPE_F32) {
 | |
|         PUSH_F32(field_value);
 | |
|     }
 | |
|     else if (field_type == VALUE_TYPE_F64) {
 | |
|         PUSH_F64(field_value);
 | |
|     }
 | |
|     else {
 | |
|         bh_assert(0);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_struct_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                           uint32 type_index, uint32 field_idx)
 | |
| {
 | |
|     LLVMValueRef struct_obj, cmp, field_value = NULL;
 | |
|     LLVMBasicBlockRef check_struct_obj_succ;
 | |
|     /* Used in compile time, to distinguish what type of AOTValue POP,
 | |
|      * field_data offset, size  */
 | |
|     WASMStructType *compile_time_struct_type =
 | |
|         (WASMStructType *)comp_ctx->comp_data->types[type_index];
 | |
|     WASMStructFieldType *field;
 | |
|     uint32 field_offset;
 | |
|     uint8 field_type;
 | |
| 
 | |
|     field = compile_time_struct_type->fields + field_idx;
 | |
|     field_type = field->field_type;
 | |
|     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");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (wasm_is_type_reftype(field_type)) {
 | |
|         POP_GC_REF(field_value);
 | |
|     }
 | |
|     else if (field_type == VALUE_TYPE_I32 || field_type == PACKED_TYPE_I8
 | |
|              || field_type == PACKED_TYPE_I16) {
 | |
|         POP_I32(field_value);
 | |
|     }
 | |
|     else if (field_type == VALUE_TYPE_I64) {
 | |
|         POP_I64(field_value);
 | |
|     }
 | |
|     else if (field_type == VALUE_TYPE_F32) {
 | |
|         POP_F32(field_value);
 | |
|     }
 | |
|     else if (field_type == VALUE_TYPE_F64) {
 | |
|         POP_F64(field_value);
 | |
|     }
 | |
|     else {
 | |
|         bh_assert(0);
 | |
|     }
 | |
| 
 | |
|     POP_GC_REF(struct_obj);
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_struct_obj_succ, "check struct obj succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_struct_obj_succ);
 | |
| 
 | |
|     BUILD_ISNULL(struct_obj, cmp, "cmp_struct_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_STRUCT_OBJ, true, cmp,
 | |
|                             check_struct_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     if (!aot_struct_obj_set_field(comp_ctx, struct_obj, I32_CONST(field_offset),
 | |
|                                   field_value, field_type))
 | |
|         goto fail;
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| aot_call_wasm_array_obj_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                             LLVMValueRef rtt_type, LLVMValueRef array_len,
 | |
|                             LLVMValueRef array_elem, LLVMValueRef *array_obj)
 | |
| {
 | |
|     LLVMValueRef param_values[4], func, value, res, array_elem_ptr;
 | |
|     LLVMTypeRef param_types[4], ret_type, func_type, func_ptr_type;
 | |
| 
 | |
|     if (!(array_elem_ptr = LLVMBuildAlloca(
 | |
|               comp_ctx->builder, LLVMTypeOf(array_elem), "array_elem_ptr"))) {
 | |
|         aot_set_last_error("llvm build alloca failed.");
 | |
|         goto fail;
 | |
|     }
 | |
|     if (!LLVMBuildStore(comp_ctx->builder, array_elem, array_elem_ptr)) {
 | |
|         aot_set_last_error("llvm build store failed.");
 | |
|         goto fail;
 | |
|     }
 | |
|     if (!(array_elem_ptr = LLVMBuildBitCast(comp_ctx->builder, array_elem_ptr,
 | |
|                                             INT8_PTR_TYPE, "array_elem_ptr"))) {
 | |
|         aot_set_last_error("llvm build bitcast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     param_types[0] = INT8_PTR_TYPE;
 | |
|     param_types[1] = INT8_PTR_TYPE;
 | |
|     param_types[2] = I32_TYPE;
 | |
|     param_types[3] = INT8_PTR_TYPE;
 | |
|     ret_type = GC_REF_TYPE;
 | |
| 
 | |
|     GET_AOT_FUNCTION(wasm_array_obj_new, 4);
 | |
| 
 | |
|     /* Call function wasm_array_obj_new() */
 | |
|     param_values[0] = func_ctx->exec_env;
 | |
|     param_values[1] = rtt_type;
 | |
|     param_values[2] = array_len;
 | |
|     param_values[3] = array_elem_ptr;
 | |
|     if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
 | |
|                                4, "call"))) {
 | |
|         aot_set_last_error("llvm build call failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     *array_obj = res;
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static uint32
 | |
| aot_array_obj_elem_size_log(AOTCompContext *comp_ctx, uint8 array_elem_type)
 | |
| {
 | |
|     uint32 elem_size_log = 0;
 | |
| 
 | |
|     if (wasm_is_type_reftype(array_elem_type)) {
 | |
|         elem_size_log = comp_ctx->pointer_size == sizeof(uint32) ? 2 : 3;
 | |
|     }
 | |
|     else if (array_elem_type == PACKED_TYPE_I8) {
 | |
|         elem_size_log = 0;
 | |
|     }
 | |
|     else if (array_elem_type == PACKED_TYPE_I16) {
 | |
|         elem_size_log = 1;
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_I32
 | |
|              || array_elem_type == VALUE_TYPE_F32) {
 | |
|         elem_size_log = 2;
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_I64
 | |
|              || array_elem_type == VALUE_TYPE_F64) {
 | |
|         elem_size_log = 3;
 | |
|     }
 | |
|     else {
 | |
|         bh_assert(0);
 | |
|     }
 | |
| 
 | |
|     return elem_size_log;
 | |
| }
 | |
| 
 | |
| /* array_obj->elem_data + (elem_idx << elem_size_log) */
 | |
| bool
 | |
| aot_array_obj_elem_addr(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                         LLVMValueRef array_obj, LLVMValueRef elem_idx,
 | |
|                         LLVMValueRef *p_elem_data, uint8 array_elem_type)
 | |
| {
 | |
|     uint32 elem_size_log = 0;
 | |
|     LLVMValueRef start_offset, elem_offset, elem_data;
 | |
| 
 | |
|     elem_size_log = aot_array_obj_elem_size_log(comp_ctx, array_elem_type);
 | |
| 
 | |
|     /* Get the elem data start offset of the WASMArrayObject, the offset may be
 | |
|      * different in 32-bit runtime and 64-bit runtime since WASMObjectHeader
 | |
|      * is uintptr_t. Use comp_ctx->pointer_size + 4(uint32 for length) as the
 | |
|      * offsetof(WASMArrayObject, length)*/
 | |
|     if (!(start_offset = I32_CONST(comp_ctx->pointer_size + sizeof(uint32)))) {
 | |
|         aot_set_last_error("llvm build const failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(elem_offset =
 | |
|               LLVMBuildShl(comp_ctx->builder, elem_idx,
 | |
|                            I32_CONST(elem_size_log), "elem_offset"))) {
 | |
|         aot_set_last_error("llvm build shl failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(elem_offset = LLVMBuildAdd(comp_ctx->builder, start_offset,
 | |
|                                      elem_offset, "total_offset"))) {
 | |
|         aot_set_last_error("llvm build add failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(elem_data = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE,
 | |
|                                             array_obj, &elem_offset, 1,
 | |
|                                             "array_obj_elem_data_i8p"))) {
 | |
|         aot_set_last_error("llvm build gep failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     *p_elem_data = elem_data;
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| aot_array_obj_set_elem(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                        LLVMValueRef array_obj, LLVMValueRef elem_idx,
 | |
|                        LLVMValueRef array_elem, uint8 array_elem_type)
 | |
| {
 | |
|     bool trunc = false;
 | |
|     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,
 | |
|                                  &elem_data_ptr, array_elem_type))
 | |
|         goto fail;
 | |
| 
 | |
|     if (wasm_is_type_reftype(array_elem_type)) {
 | |
|         elem_data_type = GC_REF_TYPE;
 | |
|         elem_data_ptr_type = GC_REF_PTR_TYPE;
 | |
|     }
 | |
|     else
 | |
|         switch (array_elem_type) {
 | |
|             case PACKED_TYPE_I8:
 | |
|                 elem_data_type = INT8_TYPE;
 | |
|                 elem_data_ptr_type = INT8_PTR_TYPE;
 | |
|                 trunc = true;
 | |
|                 break;
 | |
|             case PACKED_TYPE_I16:
 | |
|                 elem_data_type = INT16_TYPE;
 | |
|                 elem_data_ptr_type = INT16_PTR_TYPE;
 | |
|                 trunc = true;
 | |
|                 break;
 | |
|             case VALUE_TYPE_I32:
 | |
|                 elem_data_type = I32_TYPE;
 | |
|                 elem_data_ptr_type = INT32_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_I64:
 | |
|                 elem_data_type = I64_TYPE;
 | |
|                 elem_data_ptr_type = INT64_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_F32:
 | |
|                 elem_data_type = F32_TYPE;
 | |
|                 elem_data_ptr_type = F32_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_F64:
 | |
|                 elem_data_type = F64_TYPE;
 | |
|                 elem_data_ptr_type = F64_PTR_TYPE;
 | |
|                 break;
 | |
|             default:
 | |
|                 bh_assert(0);
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|     /* Based on elem_size, trunc array_elem if necessary */
 | |
|     if (trunc) {
 | |
|         if (!(array_elem =
 | |
|                   LLVMBuildTrunc(comp_ctx->builder, array_elem, elem_data_type,
 | |
|                                  "array_elem_trunc"))) {
 | |
|             aot_set_last_error("llvm build trunc failed.");
 | |
|             goto fail;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Cast to the field data type ptr */
 | |
|     if (!(elem_data_ptr =
 | |
|               LLVMBuildBitCast(comp_ctx->builder, elem_data_ptr,
 | |
|                                elem_data_ptr_type, "elem_data_ptr"))) {
 | |
|         aot_set_last_error("llvm build bitcast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     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;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| aot_call_aot_array_init_with_data(
 | |
|     AOTCompContext *comp_ctx, AOTFuncContext *func_ctx, LLVMValueRef seg_index,
 | |
|     LLVMValueRef data_seg_offset, LLVMValueRef array_obj,
 | |
|     LLVMValueRef elem_size, LLVMValueRef array_len)
 | |
| {
 | |
|     LLVMValueRef param_values[6], func, value, res, cmp;
 | |
|     LLVMTypeRef param_types[6], ret_type, func_type, func_ptr_type;
 | |
|     LLVMBasicBlockRef init_success;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(init_success, "init success");
 | |
|     MOVE_BLOCK_AFTER_CURR(init_success);
 | |
| 
 | |
|     param_types[0] = INT8_PTR_TYPE;
 | |
|     param_types[1] = I32_TYPE;
 | |
|     param_types[2] = I32_TYPE;
 | |
|     param_types[3] = INT8_PTR_TYPE;
 | |
|     param_types[4] = I32_TYPE;
 | |
|     param_types[5] = I32_TYPE;
 | |
|     ret_type = INT8_TYPE;
 | |
| 
 | |
|     if (comp_ctx->is_jit_mode)
 | |
|         GET_AOT_FUNCTION(llvm_array_init_with_data, 6);
 | |
|     else
 | |
|         GET_AOT_FUNCTION(aot_array_init_with_data, 6);
 | |
| 
 | |
|     /* Call function aot_array_init_with_data() */
 | |
|     param_values[0] = func_ctx->aot_inst;
 | |
|     param_values[1] = seg_index;
 | |
|     param_values[2] = data_seg_offset;
 | |
|     param_values[3] = array_obj;
 | |
|     param_values[4] = elem_size;
 | |
|     param_values[5] = array_len;
 | |
|     if (!(res = LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values,
 | |
|                                6, "call"))) {
 | |
|         aot_set_last_error("llvm build call failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     BUILD_ICMP(LLVMIntEQ, res, I8_ZERO, cmp, "array_init_ret");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, cmp,
 | |
|                             init_success))
 | |
|         goto fail;
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| aot_call_wasm_array_get_elem(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                              LLVMValueRef array_obj, LLVMValueRef elem_idx,
 | |
|                              LLVMValueRef *p_array_elem, uint8 array_elem_type,
 | |
|                              bool sign)
 | |
| {
 | |
|     bool extend = false;
 | |
|     LLVMValueRef elem_data_ptr, array_elem;
 | |
|     LLVMTypeRef elem_data_type = NULL, elem_data_ptr_type = NULL;
 | |
| 
 | |
|     if (!aot_array_obj_elem_addr(comp_ctx, func_ctx, array_obj, elem_idx,
 | |
|                                  &elem_data_ptr, array_elem_type))
 | |
|         goto fail;
 | |
| 
 | |
|     if (wasm_is_type_reftype(array_elem_type)) {
 | |
|         elem_data_type = GC_REF_TYPE;
 | |
|         elem_data_ptr_type = GC_REF_PTR_TYPE;
 | |
|     }
 | |
|     else
 | |
|         switch (array_elem_type) {
 | |
|             case PACKED_TYPE_I8:
 | |
|                 elem_data_type = INT8_TYPE;
 | |
|                 elem_data_ptr_type = INT8_PTR_TYPE;
 | |
|                 extend = true;
 | |
|                 break;
 | |
|             case PACKED_TYPE_I16:
 | |
|                 elem_data_type = INT16_TYPE;
 | |
|                 elem_data_ptr_type = INT16_PTR_TYPE;
 | |
|                 extend = true;
 | |
|                 break;
 | |
|             case VALUE_TYPE_I32:
 | |
|                 elem_data_type = I32_TYPE;
 | |
|                 elem_data_ptr_type = INT32_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_I64:
 | |
|                 elem_data_type = I64_TYPE;
 | |
|                 elem_data_ptr_type = INT64_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_F32:
 | |
|                 elem_data_type = F32_TYPE;
 | |
|                 elem_data_ptr_type = F32_PTR_TYPE;
 | |
|                 break;
 | |
|             case VALUE_TYPE_F64:
 | |
|                 elem_data_type = F64_TYPE;
 | |
|                 elem_data_ptr_type = F64_PTR_TYPE;
 | |
|                 break;
 | |
|             default:
 | |
|                 bh_assert(0);
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|     /* Based on elem_size, trunc array_elem if necessary */
 | |
|     if (!(elem_data_ptr =
 | |
|               LLVMBuildBitCast(comp_ctx->builder, elem_data_ptr,
 | |
|                                elem_data_ptr_type, "elem_data_ptr"))) {
 | |
|         aot_set_last_error("llvm build bitcast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(array_elem = LLVMBuildLoad2(comp_ctx->builder, elem_data_type,
 | |
|                                       elem_data_ptr, "array_elem"))) {
 | |
|         aot_set_last_error("llvm build load 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(array_elem, 4);
 | |
|     }
 | |
| 
 | |
|     if (extend) {
 | |
|         if (sign) {
 | |
|             if (!(array_elem = LLVMBuildSExt(comp_ctx->builder, array_elem,
 | |
|                                              I32_TYPE, "array_elem_sext"))) {
 | |
|                 aot_set_last_error("llvm build signed ext failed.");
 | |
|                 goto fail;
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             if (!(array_elem = LLVMBuildZExt(comp_ctx->builder, array_elem,
 | |
|                                              I32_TYPE, "array_elem_zext"))) {
 | |
|                 aot_set_last_error("llvm build unsigned ext failed.");
 | |
|                 goto fail;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     *p_array_elem = array_elem;
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| /* array_obj->length >> WASM_ARRAY_LENGTH_SHIFT */
 | |
| bool
 | |
| aot_array_obj_length(AOTCompContext *comp_ctx, LLVMValueRef array_obj,
 | |
|                      LLVMValueRef *p_array_len)
 | |
| {
 | |
|     LLVMValueRef offset, array_len;
 | |
| 
 | |
|     /* Get the length of the WASMArrayObject, 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(WASMArrayObject, length)*/
 | |
|     if (!(offset = I32_CONST(comp_ctx->pointer_size))) {
 | |
|         aot_set_last_error("llvm build const failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(array_len =
 | |
|               LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE, array_obj,
 | |
|                                     &offset, 1, "array_obj_length_i8p"))) {
 | |
|         aot_set_last_error("llvm build gep failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(array_len =
 | |
|               LLVMBuildBitCast(comp_ctx->builder, array_len, INT32_PTR_TYPE,
 | |
|                                "array_obj_length_i32ptr"))) {
 | |
|         aot_set_last_error("llvm build bitcast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(array_len = LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, array_len,
 | |
|                                      "array_obj_length"))) {
 | |
|         aot_set_last_error("llvm build load failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(array_len = LLVMBuildLShr(comp_ctx->builder, array_len,
 | |
|                                     I32_CONST(WASM_ARRAY_LENGTH_SHIFT),
 | |
|                                     "array_obj_length_shr"))) {
 | |
|         aot_set_last_error("llvm build lshr failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     *p_array_len = array_len;
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_array_new(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                          uint32 type_index, bool init_with_default,
 | |
|                          bool fixed_size, uint32 array_len)
 | |
| {
 | |
|     LLVMValueRef array_length, array_elem = NULL, array_obj;
 | |
|     LLVMValueRef rtt_type, cmp, elem_idx;
 | |
|     LLVMBasicBlockRef check_rtt_type_succ, check_array_obj_succ;
 | |
|     /* Use for distinguish what type of AOTValue POP */
 | |
|     WASMArrayType *compile_time_array_type =
 | |
|         (WASMArrayType *)comp_ctx->comp_data->types[type_index];
 | |
|     uint8 array_elem_type = compile_time_array_type->elem_type;
 | |
|     uint32 i;
 | |
| 
 | |
|     if (!aot_gen_commit_values(comp_ctx->aot_frame))
 | |
|         return false;
 | |
| 
 | |
|     if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true))
 | |
|         return false;
 | |
| 
 | |
|     /* Generate call aot_rtt_type_new and check for exception */
 | |
|     if (!aot_call_aot_rtt_type_new(comp_ctx, func_ctx, I32_CONST(type_index),
 | |
|                                    &rtt_type))
 | |
|         goto fail;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_rtt_type_succ, "check rtt type succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_rtt_type_succ);
 | |
| 
 | |
|     BUILD_ISNULL(rtt_type, cmp, "cmp_rtt_type");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_RTT_TYPE,
 | |
|                             true, cmp, check_rtt_type_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     if (!fixed_size)
 | |
|         POP_I32(array_length);
 | |
|     else
 | |
|         array_length = I32_CONST(array_len);
 | |
| 
 | |
|     /* For WASM_OP_ARRAY_NEW */
 | |
|     if (!fixed_size && !init_with_default) {
 | |
|         if (wasm_is_type_reftype(array_elem_type)) {
 | |
|             POP_GC_REF(array_elem);
 | |
|         }
 | |
|         else if (array_elem_type == VALUE_TYPE_I32
 | |
|                  || array_elem_type == PACKED_TYPE_I8
 | |
|                  || array_elem_type == PACKED_TYPE_I16) {
 | |
|             POP_I32(array_elem);
 | |
|         }
 | |
|         else if (array_elem_type == VALUE_TYPE_I64) {
 | |
|             POP_I64(array_elem);
 | |
|         }
 | |
|         else if (array_elem_type == VALUE_TYPE_F32) {
 | |
|             POP_F32(array_elem);
 | |
|         }
 | |
|         else if (array_elem_type == VALUE_TYPE_F64) {
 | |
|             POP_F64(array_elem);
 | |
|         }
 | |
|         else {
 | |
|             bh_assert(0);
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         /* I64 will alloca large enough space for all union access includes
 | |
|          * array_elem.gc_ob, i32, i64 to be interpreted as 0*/
 | |
|         array_elem = I64_ZERO;
 | |
|     }
 | |
| 
 | |
|     /* Generate call wasm_array_obj_new and check for exception */
 | |
|     if (!aot_call_wasm_array_obj_new(comp_ctx, func_ctx, rtt_type, array_length,
 | |
|                                      array_elem, &array_obj))
 | |
|         goto fail;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ");
 | |
|     MOVE_BLOCK_AFTER(check_array_obj_succ, check_rtt_type_succ);
 | |
| 
 | |
|     BUILD_ISNULL(array_obj, cmp, "cmp_array_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_ARRAY_OBJ,
 | |
|                             true, cmp, check_array_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     if (fixed_size) {
 | |
|         for (i = 0; i < array_len; i++) {
 | |
|             if (wasm_is_type_reftype(array_elem_type)) {
 | |
|                 POP_GC_REF(array_elem);
 | |
|             }
 | |
|             else if (array_elem_type == VALUE_TYPE_I32
 | |
|                      || array_elem_type == PACKED_TYPE_I8
 | |
|                      || array_elem_type == PACKED_TYPE_I16) {
 | |
|                 POP_I32(array_elem);
 | |
|             }
 | |
|             else if (array_elem_type == VALUE_TYPE_I64) {
 | |
|                 POP_I64(array_elem);
 | |
|             }
 | |
|             else if (array_elem_type == VALUE_TYPE_F32) {
 | |
|                 POP_F32(array_elem);
 | |
|             }
 | |
|             else if (array_elem_type == VALUE_TYPE_F64) {
 | |
|                 POP_F64(array_elem);
 | |
|             }
 | |
|             else {
 | |
|                 bh_assert(0);
 | |
|             }
 | |
| 
 | |
|             /* array_len - 1 - i */
 | |
|             if (!(elem_idx = LLVMBuildSub(comp_ctx->builder, array_length,
 | |
|                                           I32_CONST(i + 1), "elem_idx"))) {
 | |
|                 aot_set_last_error("llvm build sub failed.");
 | |
|                 goto fail;
 | |
|             }
 | |
| 
 | |
|             if (!aot_array_obj_set_elem(comp_ctx, func_ctx, array_obj, elem_idx,
 | |
|                                         array_elem, array_elem_type))
 | |
|                 goto fail;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     PUSH_GC_REF(array_obj);
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_array_new_data(AOTCompContext *comp_ctx,
 | |
|                               AOTFuncContext *func_ctx, uint32 type_index,
 | |
|                               uint32 data_seg_index)
 | |
| {
 | |
|     LLVMValueRef array_length, data_seg_offset, rtt_type,
 | |
|         elem_size = NULL, array_elem, array_obj, cmp;
 | |
|     LLVMBasicBlockRef check_rtt_type_succ, check_array_obj_succ;
 | |
|     /* Use for distinguish what type of element in array */
 | |
|     WASMArrayType *compile_time_array_type =
 | |
|         (WASMArrayType *)comp_ctx->comp_data->types[type_index];
 | |
|     uint8 array_elem_type = compile_time_array_type->elem_type;
 | |
| 
 | |
|     if (!aot_gen_commit_values(comp_ctx->aot_frame))
 | |
|         return false;
 | |
| 
 | |
|     if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true))
 | |
|         return false;
 | |
| 
 | |
|     /* Generate call aot_rtt_type_new and check for exception */
 | |
|     if (!aot_call_aot_rtt_type_new(comp_ctx, func_ctx, I32_CONST(type_index),
 | |
|                                    &rtt_type))
 | |
|         goto fail;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_rtt_type_succ, "check rtt type succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_rtt_type_succ);
 | |
| 
 | |
|     BUILD_ISNULL(rtt_type, cmp, "cmp_rtt_type");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_RTT_TYPE,
 | |
|                             true, cmp, check_rtt_type_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     POP_I32(array_length);
 | |
|     POP_I32(data_seg_offset);
 | |
| 
 | |
|     switch (array_elem_type) {
 | |
|         case PACKED_TYPE_I8:
 | |
|             elem_size = I32_ONE;
 | |
|             break;
 | |
|         case PACKED_TYPE_I16:
 | |
|             elem_size = I32_TWO;
 | |
|             break;
 | |
|         case VALUE_TYPE_I32:
 | |
|         case VALUE_TYPE_F32:
 | |
|             elem_size = I32_FOUR;
 | |
|             break;
 | |
|         case VALUE_TYPE_I64:
 | |
|         case VALUE_TYPE_F64:
 | |
|             elem_size = I32_EIGHT;
 | |
|             break;
 | |
|         default:
 | |
|             bh_assert(0);
 | |
|     }
 | |
| 
 | |
|     if (elem_size == I32_EIGHT)
 | |
|         array_elem = I64_ZERO;
 | |
|     else
 | |
|         array_elem = I32_ZERO;
 | |
| 
 | |
|     /* Generate call wasm_array_obj_new and check for exception */
 | |
|     if (!aot_call_wasm_array_obj_new(comp_ctx, func_ctx, rtt_type, array_length,
 | |
|                                      array_elem, &array_obj))
 | |
|         goto fail;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ");
 | |
|     MOVE_BLOCK_AFTER(check_array_obj_succ, check_rtt_type_succ);
 | |
| 
 | |
|     BUILD_ISNULL(array_obj, cmp, "cmp_array_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_FAILED_TO_CREATE_ARRAY_OBJ,
 | |
|                             true, cmp, check_array_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     if (!aot_call_aot_array_init_with_data(
 | |
|             comp_ctx, func_ctx, I32_CONST(data_seg_index), data_seg_offset,
 | |
|             array_obj, elem_size, array_length))
 | |
|         goto fail;
 | |
| 
 | |
|     PUSH_GC_REF(array_obj);
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_array_get(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                          uint32 type_index, bool sign)
 | |
| {
 | |
|     LLVMValueRef elem_idx, array_obj, cmp, array_len, array_elem;
 | |
|     LLVMBasicBlockRef check_array_obj_succ, check_boundary_succ;
 | |
|     /* Use for distinguish what type of AOTValue PUSH */
 | |
|     WASMArrayType *compile_time_array_type =
 | |
|         (WASMArrayType *)comp_ctx->comp_data->types[type_index];
 | |
|     uint8 array_elem_type = compile_time_array_type->elem_type;
 | |
| 
 | |
|     POP_I32(elem_idx);
 | |
|     POP_GC_REF(array_obj);
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_array_obj_succ);
 | |
| 
 | |
|     BUILD_ISNULL(array_obj, cmp, "cmp_array_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true, cmp,
 | |
|                             check_array_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     SET_BUILDER_POS(check_array_obj_succ);
 | |
|     if (!aot_array_obj_length(comp_ctx, array_obj, &array_len))
 | |
|         goto fail;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_boundary_succ, "check boundary succ");
 | |
|     MOVE_BLOCK_AFTER(check_boundary_succ, check_array_obj_succ);
 | |
| 
 | |
|     BUILD_ICMP(LLVMIntUGE, elem_idx, array_len, cmp, "cmp_array_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, cmp,
 | |
|                             check_boundary_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     SET_BUILDER_POS(check_boundary_succ);
 | |
|     if (!aot_call_wasm_array_get_elem(comp_ctx, func_ctx, array_obj, elem_idx,
 | |
|                                       &array_elem, array_elem_type, sign))
 | |
|         goto fail;
 | |
| 
 | |
|     if (wasm_is_type_reftype(array_elem_type)) {
 | |
|         PUSH_GC_REF(array_elem);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_I32
 | |
|              || array_elem_type == PACKED_TYPE_I8
 | |
|              || array_elem_type == PACKED_TYPE_I16) {
 | |
|         PUSH_I32(array_elem);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_I64) {
 | |
|         PUSH_I64(array_elem);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_F32) {
 | |
|         PUSH_F32(array_elem);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_F64) {
 | |
|         PUSH_F64(array_elem);
 | |
|     }
 | |
|     else {
 | |
|         bh_assert(0);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_array_set(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                          uint32 type_index)
 | |
| {
 | |
|     LLVMValueRef elem_idx, array_obj, cmp, array_len, array_elem = NULL;
 | |
|     LLVMBasicBlockRef check_array_obj_succ, check_boundary_succ;
 | |
|     /* Use for distinguish what type of AOTValue POP */
 | |
|     WASMArrayType *compile_time_array_type =
 | |
|         (WASMArrayType *)comp_ctx->comp_data->types[type_index];
 | |
|     uint8 array_elem_type = compile_time_array_type->elem_type;
 | |
| 
 | |
|     /* Get LLVM type based on array_elem_type */
 | |
|     if (wasm_is_type_reftype(array_elem_type)) {
 | |
|         POP_GC_REF(array_elem);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_I32
 | |
|              || array_elem_type == PACKED_TYPE_I8
 | |
|              || array_elem_type == PACKED_TYPE_I16) {
 | |
|         POP_I32(array_elem);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_I64) {
 | |
|         POP_I64(array_elem);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_F32) {
 | |
|         POP_F32(array_elem);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_F64) {
 | |
|         POP_F64(array_elem);
 | |
|     }
 | |
|     else {
 | |
|         bh_assert(0);
 | |
|     }
 | |
| 
 | |
|     POP_I32(elem_idx);
 | |
|     POP_GC_REF(array_obj);
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_array_obj_succ);
 | |
| 
 | |
|     BUILD_ISNULL(array_obj, cmp, "cmp_array_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true, cmp,
 | |
|                             check_array_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     SET_BUILDER_POS(check_array_obj_succ);
 | |
|     if (!aot_array_obj_length(comp_ctx, array_obj, &array_len))
 | |
|         goto fail;
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_boundary_succ, "check boundary succ");
 | |
|     MOVE_BLOCK_AFTER(check_boundary_succ, check_array_obj_succ);
 | |
| 
 | |
|     BUILD_ICMP(LLVMIntUGE, elem_idx, array_len, cmp, "cmp_array_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true, cmp,
 | |
|                             check_boundary_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     SET_BUILDER_POS(check_boundary_succ);
 | |
|     if (!aot_array_obj_set_elem(comp_ctx, func_ctx, array_obj, elem_idx,
 | |
|                                 array_elem, array_elem_type)) {
 | |
|         aot_set_last_error("llvm build alloca failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_array_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                           uint32 type_index)
 | |
| {
 | |
|     LLVMValueRef len, array_obj, fill_value = NULL, offset, array_len, cmp[2],
 | |
|                                  boundary, loop_counter_addr, loop_counter_val;
 | |
|     LLVMBasicBlockRef check_obj_succ, len_gt_zero, len_le_zero, inner_else;
 | |
|     LLVMBasicBlockRef fill_loop_header, fill_loop_body;
 | |
|     WASMArrayType *compile_time_array_type =
 | |
|         (WASMArrayType *)comp_ctx->comp_data->types[type_index];
 | |
|     uint8 array_elem_type = compile_time_array_type->elem_type;
 | |
| 
 | |
|     POP_I32(len);
 | |
|     /* Get LLVM type based on array_elem_type */
 | |
|     if (wasm_is_type_reftype(array_elem_type)) {
 | |
|         POP_GC_REF(fill_value);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_I32
 | |
|              || array_elem_type == PACKED_TYPE_I8
 | |
|              || array_elem_type == PACKED_TYPE_I16) {
 | |
|         POP_I32(fill_value);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_I64) {
 | |
|         POP_I64(fill_value);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_F32) {
 | |
|         POP_F32(fill_value);
 | |
|     }
 | |
|     else if (array_elem_type == VALUE_TYPE_F64) {
 | |
|         POP_F64(fill_value);
 | |
|     }
 | |
|     else {
 | |
|         bh_assert(0);
 | |
|     }
 | |
| 
 | |
|     POP_I32(offset);
 | |
|     POP_GC_REF(array_obj);
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_obj_succ, "check array objs succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_obj_succ);
 | |
| 
 | |
|     BUILD_ISNULL(array_obj, cmp[0], "cmp_obj");
 | |
| 
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true,
 | |
|                             cmp[0], check_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     /* Create if block */
 | |
|     ADD_BASIC_BLOCK(len_gt_zero, "len_gt_zero");
 | |
|     MOVE_BLOCK_AFTER_CURR(len_gt_zero);
 | |
| 
 | |
|     /* Create inner else block */
 | |
|     ADD_BASIC_BLOCK(inner_else, "inner_else");
 | |
|     MOVE_BLOCK_AFTER(inner_else, len_gt_zero);
 | |
| 
 | |
|     /* Create fill_loop_header block */
 | |
|     ADD_BASIC_BLOCK(fill_loop_header, "fill_loop_header");
 | |
|     MOVE_BLOCK_AFTER(fill_loop_header, len_gt_zero);
 | |
| 
 | |
|     /* Create fill_loop_body block */
 | |
|     ADD_BASIC_BLOCK(fill_loop_body, "fill_loop_body");
 | |
|     MOVE_BLOCK_AFTER(fill_loop_body, len_gt_zero);
 | |
| 
 | |
|     /* Create else(end) block */
 | |
|     ADD_BASIC_BLOCK(len_le_zero, "len_le_zero");
 | |
|     MOVE_BLOCK_AFTER(len_le_zero, len_gt_zero);
 | |
| 
 | |
|     BUILD_ICMP(LLVMIntSGT, len, I32_ZERO, cmp[0], "cmp_len");
 | |
|     BUILD_COND_BR(cmp[0], len_gt_zero, len_le_zero);
 | |
| 
 | |
|     /* Move builder to len > 0 block */
 | |
|     SET_BUILDER_POS(len_gt_zero);
 | |
|     /* dst_offset > UINT32_MAX - len */
 | |
|     if (!(boundary = LLVMBuildAdd(comp_ctx->builder, offset, len, ""))) {
 | |
|         aot_set_last_error("llvm build failed.");
 | |
|         goto fail;
 | |
|     }
 | |
|     BUILD_ICMP(LLVMIntUGT, boundary, I32_CONST(UINT32_MAX), cmp[0],
 | |
|                "boundary_check1");
 | |
|     /* dst_offset + len > wasm_array_obj_length(dst_obj) */
 | |
|     if (!aot_array_obj_length(comp_ctx, array_obj, &array_len))
 | |
|         goto fail;
 | |
|     BUILD_ICMP(LLVMIntUGT, boundary, array_len, cmp[1], "boundary_check2");
 | |
| 
 | |
|     if (!(cmp[0] = LLVMBuildOr(comp_ctx->builder, cmp[0], cmp[1], ""))) {
 | |
|         aot_set_last_error("llvm build failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true,
 | |
|                             cmp[0], inner_else))
 | |
|         goto fail;
 | |
| 
 | |
|     if (!(loop_counter_addr = LLVMBuildAlloca(comp_ctx->builder, I32_TYPE,
 | |
|                                               "fill_loop_counter"))) {
 | |
|         aot_set_last_error("llvm build alloc failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!is_target_x86(comp_ctx)) {
 | |
|         LLVMSetAlignment(loop_counter_addr, 4);
 | |
|     }
 | |
| 
 | |
|     if (!LLVMBuildStore(comp_ctx->builder, offset, loop_counter_addr)) {
 | |
|         aot_set_last_error("llvm build store failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     BUILD_BR(fill_loop_header);
 | |
|     SET_BUILDER_POS(fill_loop_header);
 | |
| 
 | |
|     if (!(loop_counter_val =
 | |
|               LLVMBuildLoad2(comp_ctx->builder, I32_TYPE, loop_counter_addr,
 | |
|                              "fill_loop_counter"))) {
 | |
|         aot_set_last_error("llvm build load failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     BUILD_ICMP(LLVMIntULT, loop_counter_val, boundary, cmp[0],
 | |
|                "cmp_loop_counter");
 | |
|     BUILD_COND_BR(cmp[0], fill_loop_body, len_le_zero);
 | |
| 
 | |
|     SET_BUILDER_POS(fill_loop_body);
 | |
| 
 | |
|     if (!aot_array_obj_set_elem(comp_ctx, func_ctx, array_obj, loop_counter_val,
 | |
|                                 fill_value, array_elem_type))
 | |
|         goto fail;
 | |
| 
 | |
|     if (!(loop_counter_val = LLVMBuildAdd(comp_ctx->builder, loop_counter_val,
 | |
|                                           I32_ONE, "fill_loop_counter"))) {
 | |
|         aot_set_last_error("llvm build add failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!LLVMBuildStore(comp_ctx->builder, loop_counter_val,
 | |
|                         loop_counter_addr)) {
 | |
|         aot_set_last_error("llvm build store failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     BUILD_BR(fill_loop_header);
 | |
| 
 | |
|     SET_BUILDER_POS(len_le_zero);
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| aot_call_wasm_array_obj_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                              LLVMValueRef dst_obj, LLVMValueRef dst_offset,
 | |
|                              LLVMValueRef src_obj, LLVMValueRef src_offset,
 | |
|                              LLVMValueRef len)
 | |
| {
 | |
|     LLVMValueRef param_values[5], func, value;
 | |
|     LLVMTypeRef param_types[5], ret_type, func_type, func_ptr_type;
 | |
| 
 | |
|     param_types[0] = GC_REF_TYPE;
 | |
|     param_types[1] = I32_TYPE;
 | |
|     param_types[2] = GC_REF_TYPE;
 | |
|     param_types[3] = I32_TYPE;
 | |
|     param_types[4] = I32_TYPE;
 | |
|     ret_type = VOID_TYPE;
 | |
| 
 | |
|     GET_AOT_FUNCTION(wasm_array_obj_copy, 5);
 | |
| 
 | |
|     /* Call function wasm_array_obj_copy() */
 | |
|     param_values[0] = dst_obj;
 | |
|     param_values[1] = dst_offset;
 | |
|     param_values[2] = src_obj;
 | |
|     param_values[3] = src_offset;
 | |
|     param_values[4] = len;
 | |
|     if (!LLVMBuildCall2(comp_ctx->builder, func_type, func, param_values, 5,
 | |
|                         "")) {
 | |
|         aot_set_last_error("llvm build call failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_array_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                           uint32 type_index, uint32 src_type_index)
 | |
| {
 | |
|     LLVMValueRef len, src_offset, src_obj, dst_offset, dst_obj, array_len,
 | |
|         cmp[4], boundary;
 | |
|     LLVMBasicBlockRef check_objs_succ, len_gt_zero, len_le_zero, inner_else;
 | |
|     int i;
 | |
| 
 | |
|     POP_I32(len);
 | |
|     POP_I32(src_offset);
 | |
|     POP_GC_REF(src_obj);
 | |
|     POP_I32(dst_offset);
 | |
|     POP_GC_REF(dst_obj);
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_objs_succ, "check array objs succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_objs_succ);
 | |
| 
 | |
|     BUILD_ISNULL(src_obj, cmp[0], "cmp_src_obj");
 | |
|     BUILD_ISNULL(dst_obj, cmp[1], "cmp_dst_obj");
 | |
| 
 | |
|     /* src_obj is null or dst_obj is null, throw exception */
 | |
|     if (!(cmp[0] = LLVMBuildOr(comp_ctx->builder, cmp[0], cmp[1], ""))) {
 | |
|         aot_set_last_error("llvm build or failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true,
 | |
|                             cmp[0], check_objs_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     /* Create if block */
 | |
|     ADD_BASIC_BLOCK(len_gt_zero, "len_gt_zero");
 | |
|     MOVE_BLOCK_AFTER_CURR(len_gt_zero);
 | |
| 
 | |
|     /* Create else(end) block */
 | |
|     ADD_BASIC_BLOCK(len_le_zero, "len_le_zero");
 | |
|     MOVE_BLOCK_AFTER(len_le_zero, len_gt_zero);
 | |
| 
 | |
|     /* Create inner else block */
 | |
|     ADD_BASIC_BLOCK(inner_else, "inner_else");
 | |
|     MOVE_BLOCK_AFTER(inner_else, len_gt_zero);
 | |
| 
 | |
|     BUILD_ICMP(LLVMIntSGT, len, I32_ZERO, cmp[0], "cmp_len");
 | |
|     BUILD_COND_BR(cmp[0], len_gt_zero, len_le_zero);
 | |
| 
 | |
|     /* Move builder to len > 0 block */
 | |
|     SET_BUILDER_POS(len_gt_zero);
 | |
|     /* dst_offset > UINT32_MAX - len */
 | |
|     if (!(boundary = LLVMBuildAdd(comp_ctx->builder, dst_offset, len, ""))) {
 | |
|         aot_set_last_error("llvm build failed.");
 | |
|         goto fail;
 | |
|     }
 | |
|     BUILD_ICMP(LLVMIntUGT, boundary, I32_CONST(UINT32_MAX), cmp[0],
 | |
|                "boundary_check1");
 | |
|     /* dst_offset + len > wasm_array_obj_length(dst_obj) */
 | |
|     if (!aot_array_obj_length(comp_ctx, dst_obj, &array_len))
 | |
|         goto fail;
 | |
|     BUILD_ICMP(LLVMIntUGT, boundary, array_len, cmp[1], "boundary_check2");
 | |
|     /* src_offset > UINT32_MAX - len */
 | |
|     if (!(boundary = LLVMBuildAdd(comp_ctx->builder, src_offset, len, ""))) {
 | |
|         aot_set_last_error("llvm build failed.");
 | |
|         goto fail;
 | |
|     }
 | |
|     BUILD_ICMP(LLVMIntUGT, boundary, I32_CONST(UINT32_MAX), cmp[2],
 | |
|                "boundary_check3");
 | |
|     /* src_offset + len > wasm_array_obj_length(src_obj) */
 | |
|     if (!aot_array_obj_length(comp_ctx, src_obj, &array_len))
 | |
|         goto fail;
 | |
|     BUILD_ICMP(LLVMIntUGT, boundary, array_len, cmp[3], "boundary_check4");
 | |
| 
 | |
|     /* logical or above 4 boundary checks */
 | |
|     for (i = 1; i < 4; ++i) {
 | |
|         if (!(cmp[0] = LLVMBuildOr(comp_ctx->builder, cmp[0], cmp[i], ""))) {
 | |
|             aot_set_last_error("llvm build failed.");
 | |
|             goto fail;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_ARRAY_IDX_OOB, true,
 | |
|                             cmp[0], inner_else))
 | |
|         goto fail;
 | |
| 
 | |
|     if (!aot_call_wasm_array_obj_copy(comp_ctx, func_ctx, dst_obj, dst_offset,
 | |
|                                       src_obj, src_offset, len))
 | |
|         goto fail;
 | |
| 
 | |
|     BUILD_BR(len_le_zero);
 | |
|     SET_BUILDER_POS(len_le_zero);
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_array_len(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
 | |
| {
 | |
|     LLVMValueRef array_obj, cmp, array_len;
 | |
|     LLVMBasicBlockRef check_array_obj_succ;
 | |
| 
 | |
|     POP_GC_REF(array_obj);
 | |
| 
 | |
|     ADD_BASIC_BLOCK(check_array_obj_succ, "check array obj succ");
 | |
|     MOVE_BLOCK_AFTER_CURR(check_array_obj_succ);
 | |
| 
 | |
|     BUILD_ISNULL(array_obj, cmp, "cmp_array_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_NULL_ARRAY_OBJ, true, cmp,
 | |
|                             check_array_obj_succ))
 | |
|         goto fail;
 | |
| 
 | |
|     if (!aot_array_obj_length(comp_ctx, array_obj, &array_len))
 | |
|         goto fail;
 | |
| 
 | |
|     PUSH_I32(array_len);
 | |
| 
 | |
|     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);
 | |
| 
 | |
|     /* i31_val <<= 1 */
 | |
|     if (!(i31_val = LLVMBuildShl(comp_ctx->builder, i31_val, I32_ONE,
 | |
|                                  "i31_val_shl"))) {
 | |
|         aot_set_last_error("llvm build shl failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     /* i31_val |= 1 */
 | |
|     if (!(i31_val =
 | |
|               LLVMBuildOr(comp_ctx->builder, i31_val, I32_ONE, "i31_val_or"))) {
 | |
|         aot_set_last_error("llvm build or failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(i31_obj = LLVMBuildIntToPtr(comp_ctx->builder, i31_val, GC_REF_TYPE,
 | |
|                                       "i31_obj"))) {
 | |
|         aot_set_last_error("llvm build bit cast failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     PUSH_GC_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;
 | |
|     LLVMBasicBlockRef check_i31_obj_succ;
 | |
| 
 | |
|     POP_GC_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_I31_OBJ, true,
 | |
|                             cmp_i31_obj, check_i31_obj_succ)) {
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!(i31_val = LLVMBuildPtrToInt(comp_ctx->builder, i31_obj, I32_TYPE,
 | |
|                                       "i31_val"))) {
 | |
|         aot_set_last_error("llvm build ptr to init failed.");
 | |
|         goto fail;
 | |
|     }
 | |
| 
 | |
|     if (!sign) {
 | |
|         if (!(i31_val = LLVMBuildLShr(comp_ctx->builder, i31_val, I32_ONE,
 | |
|                                       "i31_value"))) {
 | |
|             aot_set_last_error("llvm build lshr failed.");
 | |
|             goto fail;
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         if (!(i31_val = LLVMBuildAShr(comp_ctx->builder, i31_val, I32_ONE,
 | |
|                                       "i31_value"))) {
 | |
|             aot_set_last_error("llvm build ashr 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)
 | |
| {
 | |
|     LLVMValueRef gc_obj, ref_test_phi, cmp, castable;
 | |
|     LLVMBasicBlockRef block_curr, block_obj_non_null, block_end;
 | |
| 
 | |
|     POP_GC_REF(gc_obj);
 | |
| 
 | |
|     block_curr = CURR_BLOCK();
 | |
| 
 | |
|     /* Create non-null object block */
 | |
|     ADD_BASIC_BLOCK(block_obj_non_null, "non_null_obj");
 | |
|     MOVE_BLOCK_AFTER_CURR(block_obj_non_null);
 | |
| 
 | |
|     /* Create end block */
 | |
|     ADD_BASIC_BLOCK(block_end, "ref_test_end");
 | |
|     MOVE_BLOCK_AFTER(block_end, block_obj_non_null);
 | |
| 
 | |
|     /* Create ref test result phi */
 | |
|     SET_BUILDER_POS(block_end);
 | |
|     if (!(ref_test_phi =
 | |
|               LLVMBuildPhi(comp_ctx->builder, INT1_TYPE, "ref_test_res"))) {
 | |
|         aot_set_last_error("llvm build phi failed");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /* Check if gc object is NULL */
 | |
|     SET_BUILDER_POS(block_curr);
 | |
|     BUILD_ISNULL(gc_obj, cmp, "cmp_gc_obj");
 | |
|     BUILD_COND_BR(cmp, block_end, block_obj_non_null);
 | |
| 
 | |
|     if (nullable)
 | |
|         LLVMAddIncoming(ref_test_phi, &I1_ONE, &block_curr, 1);
 | |
|     else
 | |
|         LLVMAddIncoming(ref_test_phi, &I1_ZERO, &block_curr, 1);
 | |
| 
 | |
|     /* Move builder to non-null object block */
 | |
|     SET_BUILDER_POS(block_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))
 | |
|             return false;
 | |
|     }
 | |
|     else {
 | |
|         if (!aot_call_wasm_obj_is_type_of(comp_ctx, func_ctx, gc_obj,
 | |
|                                           I32_CONST(heap_type), &castable))
 | |
|             return false;
 | |
|     }
 | |
| 
 | |
|     if (!(castable = LLVMBuildICmp(comp_ctx->builder, LLVMIntNE, castable,
 | |
|                                    I8_ZERO, "castable"))) {
 | |
|         aot_set_last_error("llvm build icmp failed.");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     BUILD_BR(block_end);
 | |
|     LLVMAddIncoming(ref_test_phi, &castable, &block_obj_non_null, 1);
 | |
| 
 | |
|     SET_BUILDER_POS(block_end);
 | |
|     PUSH_COND(ref_test_phi);
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| aot_compile_op_ref_cast(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 | |
|                         int32 heap_type, bool nullable)
 | |
| {
 | |
|     LLVMValueRef gc_obj, cmp, castable;
 | |
|     LLVMBasicBlockRef block_obj_non_null, block_end;
 | |
| 
 | |
|     GET_GC_REF_FROM_STACK(gc_obj);
 | |
| 
 | |
|     /* Create non null block */
 | |
|     ADD_BASIC_BLOCK(block_obj_non_null, "non_null_obj");
 | |
|     MOVE_BLOCK_AFTER_CURR(block_obj_non_null);
 | |
| 
 | |
|     /* Create end block */
 | |
|     ADD_BASIC_BLOCK(block_end, "ref_cast_end");
 | |
|     MOVE_BLOCK_AFTER(block_end, block_obj_non_null);
 | |
| 
 | |
|     BUILD_ISNULL(gc_obj, cmp, "obj_is_null");
 | |
|     if (nullable) {
 | |
|         BUILD_COND_BR(cmp, block_end, block_obj_non_null);
 | |
|     }
 | |
|     else {
 | |
|         if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_CAST_FAILURE, true,
 | |
|                                 cmp, block_obj_non_null)) {
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     SET_BUILDER_POS(block_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))
 | |
|             return false;
 | |
|     }
 | |
|     else {
 | |
|         if (!aot_call_wasm_obj_is_type_of(comp_ctx, func_ctx, gc_obj,
 | |
|                                           I32_CONST(heap_type), &castable))
 | |
|             return false;
 | |
|     }
 | |
| 
 | |
|     if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntEQ, castable, I8_ZERO,
 | |
|                               "is_uncastable"))) {
 | |
|         aot_set_last_error("llvm build not failed");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_CAST_FAILURE, true, cmp,
 | |
|                             block_end)) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     SET_BUILDER_POS(block_end);
 | |
| 
 | |
|     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, internal_obj_phi;
 | |
|     LLVMBasicBlockRef block_curr, block_obj_non_null, block_end;
 | |
| 
 | |
|     POP_GC_REF(externref_obj);
 | |
| 
 | |
|     block_curr = CURR_BLOCK();
 | |
| 
 | |
|     /* Create non-null object block */
 | |
|     ADD_BASIC_BLOCK(block_obj_non_null, "non_null_obj");
 | |
|     MOVE_BLOCK_AFTER_CURR(block_obj_non_null);
 | |
| 
 | |
|     /* Create end block */
 | |
|     ADD_BASIC_BLOCK(block_end, "internalize_end");
 | |
|     MOVE_BLOCK_AFTER(block_end, block_obj_non_null);
 | |
| 
 | |
|     /* Create internalized object phi */
 | |
|     SET_BUILDER_POS(block_end);
 | |
|     if (!(internal_obj_phi =
 | |
|               LLVMBuildPhi(comp_ctx->builder, GC_REF_TYPE, "internal_obj"))) {
 | |
|         aot_set_last_error("llvm build phi failed");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /* Check if externref object is NULL */
 | |
|     SET_BUILDER_POS(block_curr);
 | |
|     BUILD_ISNULL(externref_obj, cmp, "cmp_externref_obj");
 | |
|     BUILD_COND_BR(cmp, block_end, block_obj_non_null);
 | |
|     LLVMAddIncoming(internal_obj_phi, &GC_REF_NULL, &block_curr, 1);
 | |
| 
 | |
|     /* Move builder to non-null object block */
 | |
|     SET_BUILDER_POS(block_obj_non_null);
 | |
|     if (!aot_call_wasm_externref_obj_to_internal_obj(comp_ctx, func_ctx,
 | |
|                                                      externref_obj, &gc_obj)) {
 | |
|         return false;
 | |
|     }
 | |
|     BUILD_BR(block_end);
 | |
|     LLVMAddIncoming(internal_obj_phi, &gc_obj, &block_obj_non_null, 1);
 | |
| 
 | |
|     /* Move builder to end block */
 | |
|     SET_BUILDER_POS(block_end);
 | |
|     PUSH_GC_REF(internal_obj_phi);
 | |
| 
 | |
|     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, 2);
 | |
| 
 | |
|     /* 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, cmp, external_obj_phi, externref_obj;
 | |
|     LLVMBasicBlockRef block_curr, block_obj_non_null, block_end;
 | |
| 
 | |
|     if (!aot_gen_commit_values(comp_ctx->aot_frame))
 | |
|         return false;
 | |
| 
 | |
|     if (!aot_gen_commit_sp_ip(comp_ctx->aot_frame, true, true))
 | |
|         return false;
 | |
| 
 | |
|     POP_GC_REF(gc_obj);
 | |
| 
 | |
|     block_curr = CURR_BLOCK();
 | |
| 
 | |
|     /* Create non-null object block */
 | |
|     ADD_BASIC_BLOCK(block_obj_non_null, "non_null_obj");
 | |
|     MOVE_BLOCK_AFTER_CURR(block_obj_non_null);
 | |
| 
 | |
|     /* Create end block */
 | |
|     ADD_BASIC_BLOCK(block_end, "externalize_end");
 | |
|     MOVE_BLOCK_AFTER(block_end, block_obj_non_null);
 | |
| 
 | |
|     /* Create externalized object phi */
 | |
|     SET_BUILDER_POS(block_end);
 | |
|     if (!(external_obj_phi =
 | |
|               LLVMBuildPhi(comp_ctx->builder, GC_REF_TYPE, "external_obj"))) {
 | |
|         aot_set_last_error("llvm build phi failed");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /* Check if gc object is NULL */
 | |
|     SET_BUILDER_POS(block_curr);
 | |
|     BUILD_ISNULL(gc_obj, cmp, "cmp_gc_obj");
 | |
|     BUILD_COND_BR(cmp, block_end, block_obj_non_null);
 | |
|     LLVMAddIncoming(external_obj_phi, &GC_REF_NULL, &block_curr, 1);
 | |
| 
 | |
|     /* Move builder to non-null object block */
 | |
|     SET_BUILDER_POS(block_obj_non_null);
 | |
| 
 | |
|     if (!aot_call_wasm_internal_obj_to_external_obj(comp_ctx, func_ctx, gc_obj,
 | |
|                                                     &externref_obj)) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /* Check whether failed to externalize */
 | |
|     BUILD_ISNULL(externref_obj, cmp, "cmp_externref_obj");
 | |
|     if (!aot_emit_exception(comp_ctx, func_ctx,
 | |
|                             EXCE_FAILED_TO_CREATE_EXTERNREF_OBJ, true, cmp,
 | |
|                             block_end)) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     LLVMAddIncoming(external_obj_phi, &externref_obj, &block_obj_non_null, 1);
 | |
| 
 | |
|     /* Move builder to end block */
 | |
|     SET_BUILDER_POS(block_end);
 | |
|     PUSH_GC_REF(external_obj_phi);
 | |
| 
 | |
|     return true;
 | |
| fail:
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| #endif /* end of WASM_ENABLE_GC != 0 */
 | 
