wasm-micro-runtime/core/iwasm/compilation/aot_emit_gc.c
2024-05-08 09:30:29 +08:00

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 */