wasm-micro-runtime/core/iwasm/compilation/aot_emit_variable.c
Benbuck Nason c85bada2a9
Add wasm module global type information APIs (#3406)
Support getting global type from `wasm_runtime_get_import_type` and
`wasm_runtime_get_export_type`, and add two APIs:

```C
wasm_valkind_t
wasm_global_type_get_valkind(const wasm_global_type_t global_type);

bool
wasm_global_type_get_mutable(const wasm_global_type_t global_type);
```
2024-05-10 09:15:58 +08:00

333 lines
11 KiB
C

/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "aot_emit_variable.h"
#include "aot_emit_exception.h"
#include "../aot/aot_runtime.h"
#define CHECK_LOCAL(idx) \
do { \
if (idx >= func_ctx->aot_func->func_type->param_count \
+ func_ctx->aot_func->local_count) { \
aot_set_last_error("local index out of range"); \
return false; \
} \
} while (0)
static uint8
get_local_type(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 local_idx)
{
AOTFunc *aot_func = func_ctx->aot_func;
uint32 param_count = aot_func->func_type->param_count;
uint8 local_type;
local_type = local_idx < param_count
? aot_func->func_type->types[local_idx]
: aot_func->local_types_wp[local_idx - param_count];
if (comp_ctx->enable_gc && aot_is_type_gc_reftype(local_type))
local_type = VALUE_TYPE_GC_REF;
return local_type;
}
bool
aot_compile_op_get_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 local_idx)
{
char name[32];
LLVMValueRef value;
AOTValue *aot_value_top;
uint8 local_type;
CHECK_LOCAL(local_idx);
local_type = get_local_type(comp_ctx, func_ctx, local_idx);
snprintf(name, sizeof(name), "%s%d%s", "local", local_idx, "#");
if (!(value = LLVMBuildLoad2(comp_ctx->builder, TO_LLVM_TYPE(local_type),
func_ctx->locals[local_idx], name))) {
aot_set_last_error("llvm build load fail");
return false;
}
PUSH(value, local_type);
aot_value_top =
func_ctx->block_stack.block_list_end->value_stack.value_list_end;
aot_value_top->is_local = true;
aot_value_top->local_idx = local_idx;
return true;
fail:
return false;
}
static bool
aot_compile_op_set_or_tee_local(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx, uint32 local_idx,
bool is_tee_local)
{
LLVMValueRef value;
uint8 local_type;
uint32 n;
CHECK_LOCAL(local_idx);
local_type = get_local_type(comp_ctx, func_ctx, local_idx);
POP(value, local_type);
if (comp_ctx->aot_frame) {
/* Get the slot index */
n = func_ctx->aot_func->local_offsets[local_idx];
bh_assert(comp_ctx->aot_frame->lp[n].type == local_type);
switch (local_type) {
case VALUE_TYPE_I32:
set_local_i32(comp_ctx->aot_frame, n, value);
break;
case VALUE_TYPE_I64:
set_local_i64(comp_ctx->aot_frame, n, value);
break;
case VALUE_TYPE_F32:
set_local_f32(comp_ctx->aot_frame, n, value);
break;
case VALUE_TYPE_F64:
set_local_f64(comp_ctx->aot_frame, n, value);
break;
case VALUE_TYPE_V128:
set_local_v128(comp_ctx->aot_frame, n, value);
break;
case VALUE_TYPE_FUNCREF:
case VALUE_TYPE_EXTERNREF:
set_local_ref(comp_ctx->aot_frame, n, value, local_type);
break;
#if WASM_ENABLE_GC != 0
case VALUE_TYPE_GC_REF:
set_local_gc_ref(comp_ctx->aot_frame, n, value, local_type);
break;
#endif
default:
bh_assert(0);
break;
}
}
if (!LLVMBuildStore(comp_ctx->builder, value,
func_ctx->locals[local_idx])) {
aot_set_last_error("llvm build store fail");
return false;
}
if (is_tee_local) {
PUSH(value, local_type);
}
aot_checked_addr_list_del(func_ctx, local_idx);
return true;
fail:
return false;
}
bool
aot_compile_op_set_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 local_idx)
{
return aot_compile_op_set_or_tee_local(comp_ctx, func_ctx, local_idx,
false);
}
bool
aot_compile_op_tee_local(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 local_idx)
{
return aot_compile_op_set_or_tee_local(comp_ctx, func_ctx, local_idx, true);
}
static bool
compile_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 global_idx, bool is_set, bool is_aux_stack)
{
const AOTCompData *comp_data = comp_ctx->comp_data;
uint32 import_global_count = comp_data->import_global_count;
uint32 global_base_offset;
uint32 global_offset;
uint8 global_type;
LLVMValueRef offset, global_ptr, global, res;
LLVMTypeRef ptr_type = NULL;
global_base_offset =
offsetof(AOTModuleInstance, global_table_data.bytes)
+ sizeof(AOTMemoryInstance) * comp_ctx->comp_data->memory_count;
bh_assert(global_idx < import_global_count + comp_data->global_count);
if (global_idx < import_global_count) {
global_offset =
global_base_offset
/* Get global data offset according to target info */
+ (comp_ctx->pointer_size == sizeof(uint64)
? comp_data->import_globals[global_idx].data_offset_64bit
: comp_data->import_globals[global_idx].data_offset_32bit);
global_type = comp_data->import_globals[global_idx].type.val_type;
}
else {
global_offset =
global_base_offset
/* Get global data offset according to target info */
+ (comp_ctx->pointer_size == sizeof(uint64)
? comp_data->globals[global_idx - import_global_count]
.data_offset_64bit
: comp_data->globals[global_idx - import_global_count]
.data_offset_32bit);
global_type =
comp_data->globals[global_idx - import_global_count].type.val_type;
}
if (comp_ctx->enable_gc && aot_is_type_gc_reftype(global_type))
global_type = VALUE_TYPE_GC_REF;
offset = I32_CONST(global_offset);
if (!(global_ptr = LLVMBuildInBoundsGEP2(comp_ctx->builder, INT8_TYPE,
func_ctx->aot_inst, &offset, 1,
"global_ptr_tmp"))) {
aot_set_last_error("llvm build in bounds gep failed.");
return false;
}
switch (global_type) {
case VALUE_TYPE_I32:
case VALUE_TYPE_EXTERNREF:
case VALUE_TYPE_FUNCREF:
ptr_type = INT32_PTR_TYPE;
break;
case VALUE_TYPE_I64:
ptr_type = INT64_PTR_TYPE;
break;
case VALUE_TYPE_F32:
ptr_type = F32_PTR_TYPE;
break;
case VALUE_TYPE_F64:
ptr_type = F64_PTR_TYPE;
break;
case VALUE_TYPE_V128:
ptr_type = V128_PTR_TYPE;
break;
#if WASM_ENABLE_GC != 0
case VALUE_TYPE_GC_REF:
ptr_type = GC_REF_PTR_TYPE;
break;
#endif
default:
bh_assert("unknown type");
break;
}
if (!(global_ptr = LLVMBuildBitCast(comp_ctx->builder, global_ptr, ptr_type,
"global_ptr"))) {
aot_set_last_error("llvm build bit cast failed.");
return false;
}
if (!is_set) {
if (!(global =
LLVMBuildLoad2(comp_ctx->builder, TO_LLVM_TYPE(global_type),
global_ptr, "global"))) {
aot_set_last_error("llvm build load failed.");
return false;
}
/* All globals' data is 4-byte aligned */
LLVMSetAlignment(global, 4);
PUSH(global, global_type);
}
else {
POP(global, global_type);
if (is_aux_stack && comp_ctx->enable_aux_stack_check) {
LLVMBasicBlockRef block_curr =
LLVMGetInsertBlock(comp_ctx->builder);
LLVMBasicBlockRef check_overflow_succ, check_underflow_succ;
LLVMValueRef cmp, global_i64;
/* Add basic blocks */
if (!(check_overflow_succ = LLVMAppendBasicBlockInContext(
comp_ctx->context, func_ctx->func,
"check_overflow_succ"))) {
aot_set_last_error("llvm add basic block failed.");
return false;
}
LLVMMoveBasicBlockAfter(check_overflow_succ, block_curr);
if (!(check_underflow_succ = LLVMAppendBasicBlockInContext(
comp_ctx->context, func_ctx->func,
"check_underflow_succ"))) {
aot_set_last_error("llvm add basic block failed.");
return false;
}
LLVMMoveBasicBlockAfter(check_underflow_succ, check_overflow_succ);
if (!(global_i64 = LLVMBuildZExt(comp_ctx->builder, global,
I64_TYPE, "global_i64"))) {
aot_set_last_error("llvm build zext failed.");
return false;
}
/* Check aux stack overflow */
if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntULE, global_i64,
func_ctx->aux_stack_bound, "cmp"))) {
aot_set_last_error("llvm build icmp failed.");
return false;
}
if (!aot_emit_exception(comp_ctx, func_ctx, EXCE_AUX_STACK_OVERFLOW,
true, cmp, check_overflow_succ)) {
return false;
}
/* Check aux stack underflow */
LLVMPositionBuilderAtEnd(comp_ctx->builder, check_overflow_succ);
if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGT, global_i64,
func_ctx->aux_stack_bottom, "cmp"))) {
aot_set_last_error("llvm build icmp failed.");
return false;
}
if (!aot_emit_exception(comp_ctx, func_ctx,
EXCE_AUX_STACK_UNDERFLOW, true, cmp,
check_underflow_succ)) {
return false;
}
LLVMPositionBuilderAtEnd(comp_ctx->builder, check_underflow_succ);
}
if (!(res = LLVMBuildStore(comp_ctx->builder, global, global_ptr))) {
aot_set_last_error("llvm build store failed.");
return false;
}
/* All globals' data is 4-byte aligned */
LLVMSetAlignment(res, 4);
}
return true;
fail:
return false;
}
bool
aot_compile_op_get_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 global_idx)
{
return compile_global(comp_ctx, func_ctx, global_idx, false, false);
}
bool
aot_compile_op_set_global(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 global_idx, bool is_aux_stack)
{
return compile_global(comp_ctx, func_ctx, global_idx, true, is_aux_stack);
}