Implement GC (Garbage Collection) feature for interpreter, AOT and LLVM-JIT (#3125)

Implement the GC (Garbage Collection) feature for interpreter mode,
AOT mode and LLVM-JIT mode, and support most features of the latest
spec proposal, and also enable the stringref feature.

Use `cmake -DWAMR_BUILD_GC=1/0` to enable/disable the feature,
and `wamrc --enable-gc` to generate the AOT file with GC supported.

And update the AOT file version from 2 to 3 since there are many AOT
ABI breaks, including the changes of AOT file format, the changes of
AOT module/memory instance layouts, the AOT runtime APIs for the
AOT code to invoke and so on.
This commit is contained in:
Wenyong Huang
2024-02-06 20:47:11 +08:00
committed by GitHub
parent 5931aaacbe
commit 16a4d71b34
98 changed files with 33469 additions and 3159 deletions

View File

@ -10,6 +10,7 @@
#include "aot_emit_table.h"
#include "../aot/aot_runtime.h"
#include "../aot/aot_intrinsic.h"
#include "../interpreter/wasm_runtime.h"
#if WASM_ENABLE_DEBUG_AOT != 0
#include "debug/dwarf_extractor.h"
@ -25,13 +26,20 @@ create_native_stack_top_min(const AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx);
LLVMTypeRef
wasm_type_to_llvm_type(const AOTLLVMTypes *llvm_types, uint8 wasm_type)
wasm_type_to_llvm_type(const AOTCompContext *comp_ctx,
const AOTLLVMTypes *llvm_types, uint8 wasm_type)
{
switch (wasm_type) {
case VALUE_TYPE_I32:
return llvm_types->int32_type;
case VALUE_TYPE_FUNCREF:
case VALUE_TYPE_EXTERNREF:
return llvm_types->int32_type;
if (comp_ctx->enable_ref_types)
return llvm_types->int32_type;
else {
bh_assert(comp_ctx->enable_gc);
return llvm_types->gc_ref_type;
}
case VALUE_TYPE_I64:
return llvm_types->int64_type;
case VALUE_TYPE_F32:
@ -42,9 +50,31 @@ wasm_type_to_llvm_type(const AOTLLVMTypes *llvm_types, uint8 wasm_type)
return llvm_types->i64x2_vec_type;
case VALUE_TYPE_VOID:
return llvm_types->void_type;
case REF_TYPE_NULLFUNCREF:
case REF_TYPE_NULLEXTERNREF:
case REF_TYPE_NULLREF:
/* case REF_TYPE_FUNCREF: */
/* case REF_TYPE_EXTERNREF: */
case REF_TYPE_ANYREF:
case REF_TYPE_EQREF:
case REF_TYPE_HT_NULLABLE:
case REF_TYPE_HT_NON_NULLABLE:
case REF_TYPE_I31REF:
case REF_TYPE_STRUCTREF:
case REF_TYPE_ARRAYREF:
#if WASM_ENABLE_STRINGREF != 0
case REF_TYPE_STRINGREF:
case REF_TYPE_STRINGVIEWWTF8:
case REF_TYPE_STRINGVIEWWTF16:
case REF_TYPE_STRINGVIEWITER:
#endif
case VALUE_TYPE_GC_REF:
bh_assert(comp_ctx->enable_gc);
return llvm_types->gc_ref_type;
default:
break;
}
bh_assert(0);
return NULL;
}
@ -245,18 +275,6 @@ aot_estimate_stack_usage_for_function_call(const AOTCompContext *comp_ctx,
return size;
}
static uint32
get_inst_extra_offset(AOTCompContext *comp_ctx)
{
const AOTCompData *comp_data = comp_ctx->comp_data;
uint32 table_count = comp_data->import_table_count + comp_data->table_count;
uint64 offset = get_tbl_inst_offset(comp_ctx, NULL, table_count);
uint32 offset_32 = (uint32)offset;
bh_assert(offset <= UINT32_MAX);
offset_32 = align_uint((uint32)offset_32, 8);
return offset_32;
}
/*
* a "precheck" function performs a few things before calling wrapped_func.
*
@ -353,7 +371,7 @@ aot_build_precheck_function(AOTCompContext *comp_ctx, LLVMModuleRef module,
LLVMValueRef offset;
LLVMValueRef stack_sizes_p;
offset_u32 = get_inst_extra_offset(comp_ctx);
offset_u32 = get_module_inst_extra_offset(comp_ctx);
offset_u32 += offsetof(AOTModuleInstanceExtra, stack_sizes);
offset = I32_CONST(offset_u32);
if (!offset) {
@ -552,6 +570,44 @@ fail:
return false;
}
static bool
check_wasm_type(AOTCompContext *comp_ctx, uint8 type)
{
if (type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF) {
if (!comp_ctx->enable_ref_types && !comp_ctx->enable_gc) {
aot_set_last_error("funcref or externref type was found, "
"try removing --disable-ref-types option "
"or adding --enable-gc option.");
return false;
}
else
return true;
}
else if (aot_is_type_gc_reftype(type)) {
if (!comp_ctx->enable_gc) {
aot_set_last_error("GC reference type was found, "
"try adding --enable-gc option.");
return false;
}
else
return true;
}
else if (type == VALUE_TYPE_V128) {
if (!comp_ctx->enable_simd) {
aot_set_last_error("SIMD type was found, try removing "
" --disable-simd option.");
return false;
}
return true;
}
else if (type != VALUE_TYPE_I32 && type != VALUE_TYPE_I64
&& type != VALUE_TYPE_F32 && type != VALUE_TYPE_F64) {
bh_assert(0);
}
return true;
}
/**
* Add LLVM function
*/
@ -560,6 +616,8 @@ aot_add_llvm_func(AOTCompContext *comp_ctx, LLVMModuleRef module,
const AOTFuncType *aot_func_type, uint32 func_index,
LLVMTypeRef *p_func_type, LLVMValueRef *p_precheck_func)
{
WASMFunction *aot_func =
comp_ctx->comp_data->wasm_module->functions[func_index];
LLVMValueRef func = NULL;
LLVMTypeRef *param_types, ret_type, func_type;
LLVMTypeRef func_type_wrapper;
@ -570,6 +628,18 @@ aot_add_llvm_func(AOTCompContext *comp_ctx, LLVMModuleRef module,
uint32 i, j = 0, param_count = (uint64)aot_func_type->param_count;
uint32 backend_thread_num, compile_thread_num;
/* Check function parameter types and result types */
for (i = 0; i < aot_func_type->param_count + aot_func_type->result_count;
i++) {
if (!check_wasm_type(comp_ctx, aot_func_type->types[i]))
return NULL;
}
/* Check function local types */
for (i = 0; i < aot_func->local_count; i++) {
if (!check_wasm_type(comp_ctx, aot_func->local_types[i]))
return NULL;
}
/* exec env as first parameter */
param_count++;
@ -921,6 +991,49 @@ create_aux_stack_info(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
return true;
}
static bool
create_aux_stack_frame(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
{
LLVMValueRef wasm_stack_top_bound_ptr, offset;
offset = I32_ONE;
if (!(func_ctx->cur_frame_ptr = LLVMBuildInBoundsGEP2(
comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1,
"cur_frame_ptr"))) {
aot_set_last_error("llvm build in bounds gep failed");
return false;
}
if (!(func_ctx->cur_frame =
LLVMBuildLoad2(comp_ctx->builder, OPQ_PTR_TYPE,
func_ctx->cur_frame_ptr, "cur_frame"))) {
aot_set_last_error("llvm build load failed");
return false;
}
/* Get exec_env->wasm_stack.top_boundary and its address */
offset = I32_TEN;
if (!(wasm_stack_top_bound_ptr = LLVMBuildInBoundsGEP2(
comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1,
"wasm_stack_top_bound_ptr"))
|| !(func_ctx->wasm_stack_top_bound = LLVMBuildLoad2(
comp_ctx->builder, INT8_PTR_TYPE, wasm_stack_top_bound_ptr,
"wasm_stack_top_bound"))) {
aot_set_last_error("load wasm_stack.top_boundary failed");
return false;
}
offset = I32_ELEVEN;
if (!(func_ctx->wasm_stack_top_ptr = LLVMBuildInBoundsGEP2(
comp_ctx->builder, OPQ_PTR_TYPE, func_ctx->exec_env, &offset, 1,
"wasm_stack_top_ptr"))) {
aot_set_last_error("llvm build inbounds gep failed");
return false;
}
return true;
}
static bool
create_native_symbol(const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
{
@ -955,7 +1068,8 @@ create_local_variables(const AOTCompData *comp_data,
const AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
const AOTFunc *func)
{
AOTFuncType *aot_func_type = comp_data->func_types[func->func_type_index];
AOTFuncType *aot_func_type =
(AOTFuncType *)comp_data->types[func->func_type_index];
char local_name[32];
uint32 i, j = 1;
@ -980,14 +1094,14 @@ create_local_variables(const AOTCompData *comp_data,
LLVMValueRef local_value = NULL;
snprintf(local_name, sizeof(local_name), "l%d",
aot_func_type->param_count + i);
local_type = TO_LLVM_TYPE(func->local_types[i]);
local_type = TO_LLVM_TYPE(func->local_types_wp[i]);
func_ctx->locals[aot_func_type->param_count + i] =
LLVMBuildAlloca(comp_ctx->builder, local_type, local_name);
if (!func_ctx->locals[aot_func_type->param_count + i]) {
aot_set_last_error("llvm build alloca failed.");
return false;
}
switch (func->local_types[i]) {
switch (func->local_types_wp[i]) {
case VALUE_TYPE_I32:
local_value = I32_ZERO;
break;
@ -1005,8 +1119,33 @@ create_local_variables(const AOTCompData *comp_data,
break;
case VALUE_TYPE_FUNCREF:
case VALUE_TYPE_EXTERNREF:
local_value = REF_NULL;
if (!comp_ctx->enable_gc)
local_value = REF_NULL;
else
local_value = GC_REF_NULL;
break;
#if WASM_ENABLE_GC != 0
case REF_TYPE_NULLFUNCREF:
case REF_TYPE_NULLEXTERNREF:
case REF_TYPE_NULLREF:
/* case REF_TYPE_FUNCREF: */
/* case REF_TYPE_EXTERNREF: */
case REF_TYPE_ANYREF:
case REF_TYPE_EQREF:
case REF_TYPE_HT_NULLABLE:
case REF_TYPE_HT_NON_NULLABLE:
case REF_TYPE_I31REF:
case REF_TYPE_STRUCTREF:
case REF_TYPE_ARRAYREF:
#if WASM_ENABLE_STRINGREF != 0
case REF_TYPE_STRINGREF:
case REF_TYPE_STRINGVIEWWTF8:
case REF_TYPE_STRINGVIEWWTF16:
case REF_TYPE_STRINGVIEWITER:
#endif
local_value = GC_REF_NULL;
break;
#endif
default:
bh_assert(0);
break;
@ -1539,7 +1678,8 @@ aot_create_func_context(const AOTCompData *comp_data, AOTCompContext *comp_ctx,
AOTFunc *func, uint32 func_index)
{
AOTFuncContext *func_ctx;
AOTFuncType *aot_func_type = comp_data->func_types[func->func_type_index];
AOTFuncType *aot_func_type =
(AOTFuncType *)comp_data->types[func->func_type_index];
WASMModule *module = comp_ctx->comp_data->wasm_module;
WASMFunction *wasm_func = module->functions[func_index];
AOTBlock *aot_block;
@ -1597,6 +1737,11 @@ aot_create_func_context(const AOTCompData *comp_data, AOTCompContext *comp_ctx,
goto fail;
}
if (comp_ctx->enable_aux_stack_frame
&& !create_aux_stack_frame(comp_ctx, func_ctx)) {
goto fail;
}
/* Create local variables */
if (!create_local_variables(comp_data, comp_ctx, func_ctx, func)) {
goto fail;
@ -1634,13 +1779,14 @@ aot_create_func_context(const AOTCompData *comp_data, AOTCompContext *comp_ctx,
fail:
if (func_ctx->mem_info)
wasm_runtime_free(func_ctx->mem_info);
aot_block_stack_destroy(&func_ctx->block_stack);
aot_block_stack_destroy(comp_ctx, &func_ctx->block_stack);
wasm_runtime_free(func_ctx);
return NULL;
}
static void
aot_destroy_func_contexts(AOTFuncContext **func_ctxes, uint32 count)
aot_destroy_func_contexts(AOTCompContext *comp_ctx, AOTFuncContext **func_ctxes,
uint32 count)
{
uint32 i;
@ -1648,7 +1794,7 @@ aot_destroy_func_contexts(AOTFuncContext **func_ctxes, uint32 count)
if (func_ctxes[i]) {
if (func_ctxes[i]->mem_info)
wasm_runtime_free(func_ctxes[i]->mem_info);
aot_block_stack_destroy(&func_ctxes[i]->block_stack);
aot_block_stack_destroy(comp_ctx, &func_ctxes[i]->block_stack);
aot_checked_addr_list_destroy(func_ctxes[i]);
wasm_runtime_free(func_ctxes[i]);
}
@ -1685,7 +1831,8 @@ aot_create_func_contexts(const AOTCompData *comp_data, AOTCompContext *comp_ctx)
AOTFunc *func = comp_data->funcs[i];
if (!(func_ctxes[i] =
aot_create_func_context(comp_data, comp_ctx, func, i))) {
aot_destroy_func_contexts(func_ctxes, comp_data->func_count);
aot_destroy_func_contexts(comp_ctx, func_ctxes,
comp_data->func_count);
return NULL;
}
}
@ -1694,7 +1841,8 @@ aot_create_func_contexts(const AOTCompData *comp_data, AOTCompContext *comp_ctx)
}
static bool
aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context)
aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context,
int pointer_size)
{
basic_types->int1_type = LLVMInt1TypeInContext(context);
basic_types->int8_type = LLVMInt8TypeInContext(context);
@ -1759,15 +1907,29 @@ aot_set_llvm_basic_types(AOTLLVMTypes *basic_types, LLVMContextRef context)
basic_types->funcref_type = LLVMInt32TypeInContext(context);
basic_types->externref_type = LLVMInt32TypeInContext(context);
if (pointer_size == 4) {
basic_types->intptr_t_type = basic_types->int32_type;
basic_types->intptr_t_ptr_type = basic_types->int32_ptr_type;
}
else {
basic_types->intptr_t_type = basic_types->int64_type;
basic_types->intptr_t_ptr_type = basic_types->int64_ptr_type;
}
basic_types->gc_ref_type = LLVMPointerType(basic_types->void_type, 0);
basic_types->gc_ref_ptr_type = LLVMPointerType(basic_types->gc_ref_type, 0);
return (basic_types->int8_ptr_type && basic_types->int8_pptr_type
&& basic_types->int16_ptr_type && basic_types->int32_ptr_type
&& basic_types->int64_ptr_type && basic_types->float32_ptr_type
&& basic_types->int64_ptr_type && basic_types->intptr_t_type
&& basic_types->intptr_t_ptr_type && basic_types->float32_ptr_type
&& basic_types->float64_ptr_type && basic_types->i8x16_vec_type
&& basic_types->i16x8_vec_type && basic_types->i32x4_vec_type
&& basic_types->i64x2_vec_type && basic_types->f32x4_vec_type
&& basic_types->f64x2_vec_type && basic_types->i1x2_vec_type
&& basic_types->meta_data_type && basic_types->funcref_type
&& basic_types->externref_type)
&& basic_types->externref_type && basic_types->gc_ref_type
&& basic_types->gc_ref_ptr_type)
? true
: false;
}
@ -1787,6 +1949,9 @@ aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx)
if (!(consts->i8_zero = I8_CONST(0)))
return false;
if (!(consts->i8_one = I8_CONST(1)))
return false;
if (!(consts->f32_zero = F32_CONST(0)))
return false;
@ -1857,6 +2022,13 @@ aot_create_llvm_consts(AOTLLVMConsts *consts, AOTCompContext *comp_ctx)
CREATE_VEC_ZERO_MASK(2)
#undef CREATE_VEC_ZERO_MASK
if (!(consts->gc_ref_null =
LLVMConstNull(comp_ctx->basic_types.gc_ref_type)))
return false;
if (!(consts->i8_ptr_null =
LLVMConstNull(comp_ctx->basic_types.int8_ptr_type)))
return false;
return true;
}
@ -2369,6 +2541,12 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
if (option->enable_aux_stack_frame)
comp_ctx->enable_aux_stack_frame = true;
if (option->enable_perf_profiling)
comp_ctx->enable_perf_profiling = true;
if (option->enable_memory_profiling)
comp_ctx->enable_memory_profiling = true;
if (option->enable_aux_stack_check)
comp_ctx->enable_aux_stack_check = true;
@ -2399,6 +2577,9 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
if (option->builtin_intrinsics)
comp_ctx->builtin_intrinsics = option->builtin_intrinsics;
if (option->enable_gc)
comp_ctx->enable_gc = true;
comp_ctx->opt_level = option->opt_level;
comp_ctx->size_level = option->size_level;
@ -2885,6 +3066,29 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
}
LLVMDisposeMessage(triple);
#if WASM_ENABLE_WAMR_COMPILER != 0
WASMModule *wasm_module = (WASMModule *)comp_data->wasm_module;
/* Return error if SIMD is disabled by command line but SIMD instructions
* are used */
if (!option->enable_simd && wasm_module->is_simd_used) {
aot_set_last_error("SIMD is disabled by --disable-simd but SIMD "
"instructions are used in this module");
goto fail;
}
/* Disable features when they are not actually used */
if (!wasm_module->is_simd_used) {
option->enable_simd = comp_ctx->enable_simd = false;
}
if (!wasm_module->is_ref_types_used) {
option->enable_ref_types = comp_ctx->enable_ref_types = false;
}
if (!wasm_module->is_bulk_memory_used) {
option->enable_bulk_memory = comp_ctx->enable_bulk_memory = false;
}
#endif
if (option->enable_simd && strcmp(comp_ctx->target_arch, "x86_64") != 0
&& strncmp(comp_ctx->target_arch, "aarch64", 7) != 0) {
/* Disable simd if it isn't supported by target arch */
@ -2935,7 +3139,8 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
goto fail;
}
if (!aot_set_llvm_basic_types(&comp_ctx->basic_types, comp_ctx->context)) {
if (!aot_set_llvm_basic_types(&comp_ctx->basic_types, comp_ctx->context,
comp_ctx->pointer_size)) {
aot_set_last_error("create LLVM basic types failed.");
goto fail;
}
@ -3013,7 +3218,7 @@ aot_destroy_comp_context(AOTCompContext *comp_ctx)
LLVMOrcDisposeLLLazyJIT(comp_ctx->orc_jit);
if (comp_ctx->func_ctxes)
aot_destroy_func_contexts(comp_ctx->func_ctxes,
aot_destroy_func_contexts(comp_ctx, comp_ctx->func_ctxes,
comp_ctx->func_ctx_count);
if (bh_list_length(&comp_ctx->native_symbols) > 0) {
@ -3030,6 +3235,10 @@ aot_destroy_comp_context(AOTCompContext *comp_ctx)
wasm_runtime_free(comp_ctx->target_cpu);
}
if (comp_ctx->aot_frame) {
wasm_runtime_free(comp_ctx->aot_frame);
}
wasm_runtime_free(comp_ctx);
}
@ -3108,7 +3317,8 @@ aot_get_native_symbol_index(AOTCompContext *comp_ctx, const char *symbol)
}
void
aot_value_stack_push(AOTValueStack *stack, AOTValue *value)
aot_value_stack_push(const AOTCompContext *comp_ctx, AOTValueStack *stack,
AOTValue *value)
{
if (!stack->value_list_head)
stack->value_list_head = stack->value_list_end = value;
@ -3117,10 +3327,44 @@ aot_value_stack_push(AOTValueStack *stack, AOTValue *value)
value->prev = stack->value_list_end;
stack->value_list_end = value;
}
if (comp_ctx->aot_frame) {
switch (value->type) {
case VALUE_TYPE_I32:
case VALUE_TYPE_I1:
push_i32(comp_ctx->aot_frame, value);
break;
case VALUE_TYPE_I64:
push_i64(comp_ctx->aot_frame, value);
break;
case VALUE_TYPE_F32:
push_f32(comp_ctx->aot_frame, value);
break;
case VALUE_TYPE_F64:
push_f64(comp_ctx->aot_frame, value);
break;
case VALUE_TYPE_V128:
push_v128(comp_ctx->aot_frame, value);
break;
case VALUE_TYPE_FUNCREF:
case VALUE_TYPE_EXTERNREF:
push_ref(comp_ctx->aot_frame, value);
break;
#if WASM_ENABLE_GC != 0
case VALUE_TYPE_GC_REF:
bh_assert(comp_ctx->enable_gc);
push_gc_ref(comp_ctx->aot_frame, value);
break;
#endif
default:
bh_assert(0);
break;
}
}
}
AOTValue *
aot_value_stack_pop(AOTValueStack *stack)
aot_value_stack_pop(const AOTCompContext *comp_ctx, AOTValueStack *stack)
{
AOTValue *value = stack->value_list_end;
@ -3134,11 +3378,49 @@ aot_value_stack_pop(AOTValueStack *stack)
value->prev = NULL;
}
if (comp_ctx->aot_frame) {
bh_assert(value);
bh_assert(value->value == (comp_ctx->aot_frame->sp - 1)->value);
bh_assert(value->type == (comp_ctx->aot_frame->sp - 1)->type);
switch (value->type) {
case VALUE_TYPE_I32:
case VALUE_TYPE_I1:
pop_i32(comp_ctx->aot_frame);
break;
case VALUE_TYPE_I64:
pop_i64(comp_ctx->aot_frame);
break;
case VALUE_TYPE_F32:
pop_f32(comp_ctx->aot_frame);
break;
case VALUE_TYPE_F64:
pop_f64(comp_ctx->aot_frame);
break;
case VALUE_TYPE_V128:
pop_v128(comp_ctx->aot_frame);
break;
case VALUE_TYPE_FUNCREF:
case VALUE_TYPE_EXTERNREF:
pop_ref(comp_ctx->aot_frame);
break;
#if WASM_ENABLE_GC != 0
case VALUE_TYPE_GC_REF:
bh_assert(comp_ctx->enable_gc);
pop_gc_ref(comp_ctx->aot_frame);
break;
#endif
default:
bh_assert(0);
break;
}
}
return value;
}
void
aot_value_stack_destroy(AOTValueStack *stack)
aot_value_stack_destroy(AOTCompContext *comp_ctx, AOTValueStack *stack)
{
AOTValue *value = stack->value_list_head, *p;
@ -3183,14 +3465,14 @@ aot_block_stack_pop(AOTBlockStack *stack)
}
void
aot_block_stack_destroy(AOTBlockStack *stack)
aot_block_stack_destroy(AOTCompContext *comp_ctx, AOTBlockStack *stack)
{
AOTBlock *block = stack->block_list_head, *p;
while (block) {
p = block->next;
aot_value_stack_destroy(&block->value_stack);
aot_block_destroy(block);
aot_value_stack_destroy(comp_ctx, &block->value_stack);
aot_block_destroy(comp_ctx, block);
block = p;
}
@ -3199,9 +3481,9 @@ aot_block_stack_destroy(AOTBlockStack *stack)
}
void
aot_block_destroy(AOTBlock *block)
aot_block_destroy(AOTCompContext *comp_ctx, AOTBlock *block)
{
aot_value_stack_destroy(&block->value_stack);
aot_value_stack_destroy(comp_ctx, &block->value_stack);
if (block->param_types)
wasm_runtime_free(block->param_types);
if (block->param_phis)
@ -3316,8 +3598,38 @@ aot_build_zero_function_ret(const AOTCompContext *comp_ctx,
break;
case VALUE_TYPE_FUNCREF:
case VALUE_TYPE_EXTERNREF:
ret = LLVMBuildRet(comp_ctx->builder, REF_NULL);
if (comp_ctx->enable_ref_types)
ret = LLVMBuildRet(comp_ctx->builder, REF_NULL);
#if WASM_ENABLE_GC != 0
else if (comp_ctx->enable_gc)
ret = LLVMBuildRet(comp_ctx->builder, GC_REF_NULL);
#endif
else
bh_assert(0);
break;
#if WASM_ENABLE_GC != 0
case REF_TYPE_NULLFUNCREF:
case REF_TYPE_NULLEXTERNREF:
case REF_TYPE_NULLREF:
/* case REF_TYPE_FUNCREF: */
/* case REF_TYPE_EXTERNREF: */
case REF_TYPE_ANYREF:
case REF_TYPE_EQREF:
case REF_TYPE_HT_NULLABLE:
case REF_TYPE_HT_NON_NULLABLE:
case REF_TYPE_I31REF:
case REF_TYPE_STRUCTREF:
case REF_TYPE_ARRAYREF:
#if WASM_ENABLE_STRINGREF != 0
case REF_TYPE_STRINGREF:
case REF_TYPE_STRINGVIEWWTF8:
case REF_TYPE_STRINGVIEWWTF16:
case REF_TYPE_STRINGVIEWITER:
#endif
bh_assert(comp_ctx->enable_gc);
ret = LLVMBuildRet(comp_ctx->builder, GC_REF_NULL);
break;
#endif
default:
bh_assert(0);
}