AOT call stack optimizations (#3773)

- Implement TINY / STANDARD frame modes - tiny mode is only able to keep track on the IP
  and func idx, STANDARD mode provides more capabilities (parameters, stack pointer etc.).
- Implement FRAME_PER_FUNCTION / FRAME_PER_CALL modes - frame per function adds
  code at the beginning and at the end of each function for allocating / deallocating stack frame,
  whereas in per-call mode the frame is allocated before each call. The exception is call to
  the imported function, where frame-per-function mode also allocates the stack before the
  `call` instruction (as it can't instrument the imported function).

At the moment TINY + FRAME_PER_FUNCTION is automatically enabled in case GC and perf
profiling are disabled and `values` call stack feature is not requested. In all the other cases
STANDARD + FRAME_PER_CALL is used.

STANDARD + FRAME_PER_FUNCTION and TINY + FRAME_PER_CALL are currently not
implemented but possible, and might be enabled in the future.

ps. https://github.com/bytecodealliance/wasm-micro-runtime/issues/3758
This commit is contained in:
Marcin Kolny
2024-09-10 02:05:23 +01:00
committed by GitHub
parent 0599351262
commit cbc2078898
17 changed files with 590 additions and 85 deletions

View File

@ -16,6 +16,7 @@
#include "aot_emit_parametric.h"
#include "aot_emit_table.h"
#include "aot_emit_gc.h"
#include "aot_stack_frame_comp.h"
#include "simd/simd_access_lanes.h"
#include "simd/simd_bitmask_extracts.h"
#include "simd/simd_bit_shifts.h"
@ -253,6 +254,13 @@ store_value(AOTCompContext *comp_ctx, LLVMValueRef value, uint8 value_type,
return true;
}
void
aot_call_stack_features_init_default(AOTCallStackFeatures *features)
{
memset(features, 1, sizeof(AOTCallStackFeatures));
features->frame_per_function = false;
}
bool
aot_frame_store_value(AOTCompContext *comp_ctx, LLVMValueRef value,
uint8 value_type, LLVMValueRef cur_frame, uint32 offset)
@ -573,9 +581,10 @@ aot_gen_commit_values(AOTCompFrame *frame)
return true;
}
bool
aot_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
LLVMValueRef ip_value, bool is_64bit)
static bool
aot_standard_frame_gen_commit_ip(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx,
LLVMValueRef ip_value, bool is_64bit)
{
LLVMValueRef cur_frame = func_ctx->cur_frame;
LLVMValueRef value_offset, value_addr, value_ptr;
@ -613,6 +622,23 @@ aot_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
return true;
}
bool
aot_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
LLVMValueRef ip_value, bool is_64bit)
{
switch (comp_ctx->aux_stack_frame_type) {
case AOT_STACK_FRAME_TYPE_STANDARD:
return aot_standard_frame_gen_commit_ip(comp_ctx, func_ctx,
ip_value, is_64bit);
case AOT_STACK_FRAME_TYPE_TINY:
return aot_tiny_frame_gen_commit_ip(comp_ctx, func_ctx, ip_value);
default:
aot_set_last_error(
"unsupported mode when generating commit_ip code");
return false;
}
}
bool
aot_gen_commit_sp_ip(AOTCompFrame *frame, bool commit_sp, bool commit_ip)
{
@ -962,6 +988,7 @@ static bool
aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index)
{
AOTFuncContext *func_ctx = comp_ctx->func_ctxes[func_index];
LLVMValueRef func_index_ref;
uint8 *frame_ip = func_ctx->aot_func->code, opcode, *p_f32, *p_f64;
uint8 *frame_ip_end = frame_ip + func_ctx->aot_func->code_size;
uint8 *param_types = NULL;
@ -984,16 +1011,27 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index)
LLVMMetadataRef location;
#endif
if (comp_ctx->enable_aux_stack_frame) {
/* Start to translate the opcodes */
LLVMPositionBuilderAtEnd(
comp_ctx->builder,
func_ctx->block_stack.block_list_head->llvm_entry_block);
if (comp_ctx->aux_stack_frame_type
&& comp_ctx->call_stack_features.frame_per_function) {
INT_CONST(func_index_ref,
func_index + comp_ctx->comp_data->import_func_count, I32_TYPE,
true);
if (!aot_alloc_frame_per_function_frame_for_aot_func(comp_ctx, func_ctx,
func_index_ref)) {
return false;
}
}
if (comp_ctx->aux_stack_frame_type) {
if (!init_comp_frame(comp_ctx, func_ctx, func_index)) {
return false;
}
}
/* Start to translate the opcodes */
LLVMPositionBuilderAtEnd(
comp_ctx->builder,
func_ctx->block_stack.block_list_head->llvm_entry_block);
while (frame_ip < frame_ip_end) {
opcode = *frame_ip++;

View File

@ -661,6 +661,15 @@ set_local_gc_ref(AOTCompFrame *frame, int n, LLVMValueRef value, uint8 ref_type)
#define F64_CONST(v) LLVMConstReal(F64_TYPE, v)
#define I8_CONST(v) LLVMConstInt(INT8_TYPE, v, true)
#define INT_CONST(variable, value, type, is_signed) \
do { \
variable = LLVMConstInt(type, value, is_signed); \
if (!variable) { \
aot_set_last_error("llvm build const failed"); \
return false; \
} \
} while (0)
#define LLVM_CONST(name) (comp_ctx->llvm_consts.name)
#define I1_ZERO LLVM_CONST(i1_zero)
#define I1_ONE LLVM_CONST(i1_one)

View File

@ -4433,6 +4433,12 @@ aot_obj_data_create(AOTCompContext *comp_ctx)
if (comp_ctx->enable_gc) {
obj_data->target_info.feature_flags |= WASM_FEATURE_GARBAGE_COLLECTION;
}
if (comp_ctx->aux_stack_frame_type == AOT_STACK_FRAME_TYPE_TINY) {
obj_data->target_info.feature_flags |= WASM_FEATURE_TINY_STACK_FRAME;
}
if (comp_ctx->call_stack_features.frame_per_function) {
obj_data->target_info.feature_flags |= WASM_FEATURE_FRAME_PER_FUNCTION;
}
bh_print_time("Begin to resolve object file info");

View File

@ -6,6 +6,7 @@
#include "aot_emit_control.h"
#include "aot_compiler.h"
#include "aot_emit_exception.h"
#include "aot_stack_frame_comp.h"
#if WASM_ENABLE_GC != 0
#include "aot_emit_gc.h"
#endif
@ -38,13 +39,24 @@ format_block_name(char *name, uint32 name_size, uint32 block_index,
snprintf(name, name_size, "%s", "func_end");
}
#define CREATE_BLOCK(new_llvm_block, name) \
do { \
if (!(new_llvm_block = LLVMAppendBasicBlockInContext( \
comp_ctx->context, func_ctx->func, name))) { \
aot_set_last_error("add LLVM basic block failed."); \
goto fail; \
} \
#define CREATE_BLOCK(new_llvm_block, name) \
do { \
if (!(new_llvm_block = LLVMAppendBasicBlockInContext( \
comp_ctx->context, func_ctx->func, name))) { \
aot_set_last_error("add LLVM basic block failed."); \
goto fail; \
} \
if (!strcmp(name, "func_end") && comp_ctx->aux_stack_frame_type \
&& comp_ctx->call_stack_features.frame_per_function) { \
LLVMBasicBlockRef cur_block = \
LLVMGetInsertBlock(comp_ctx->builder); \
SET_BUILDER_POS(new_llvm_block); \
if (!aot_free_frame_per_function_frame_for_aot_func(comp_ctx, \
func_ctx)) { \
goto fail; \
} \
SET_BUILDER_POS(cur_block); \
} \
} while (0)
#define CURR_BLOCK() LLVMGetInsertBlock(comp_ctx->builder)
@ -93,6 +105,11 @@ format_block_name(char *name, uint32 name_size, uint32 block_index,
goto fail; \
} \
SET_BUILDER_POS(block->llvm_end_block); \
LLVMValueRef first_instr = \
get_first_non_phi(block->llvm_end_block); \
if (first_instr) { \
LLVMPositionBuilderBefore(comp_ctx->builder, first_instr); \
} \
for (_i = 0; _i < block->result_count; _i++) { \
if (!(block->result_phis[_i] = LLVMBuildPhi( \
comp_ctx->builder, \
@ -158,6 +175,18 @@ get_target_block(AOTFuncContext *func_ctx, uint32 br_depth)
return block;
}
LLVMValueRef
get_first_non_phi(LLVMBasicBlockRef block)
{
LLVMValueRef instr = LLVMGetFirstInstruction(block);
while (instr && LLVMIsAPHINode(instr)) {
instr = LLVMGetNextInstruction(instr);
}
return instr;
}
static void
clear_frame_locals(AOTCompFrame *aot_frame)
{
@ -1361,6 +1390,13 @@ aot_compile_op_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
(*p_frame_ip - 1) - comp_ctx->comp_data->wasm_module->buf_code);
#endif
if (comp_ctx->aux_stack_frame_type
&& comp_ctx->call_stack_features.frame_per_function
&& !aot_free_frame_per_function_frame_for_aot_func(comp_ctx,
func_ctx)) {
return false;
}
if (block_func->result_count) {
/* Store extra result values to function parameters */
for (i = 0; i < block_func->result_count - 1; i++) {

View File

@ -7,6 +7,7 @@
#include "aot_emit_exception.h"
#include "aot_emit_control.h"
#include "aot_emit_table.h"
#include "aot_stack_frame_comp.h"
#include "../aot/aot_runtime.h"
#if WASM_ENABLE_GC != 0
#include "aot_emit_gc.h"
@ -1403,6 +1404,7 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
LLVMValueRef *param_values = NULL, value_ret = NULL, func;
LLVMValueRef import_func_idx, res;
LLVMValueRef ext_ret, ext_ret_ptr, ext_ret_idx;
LLVMValueRef func_idx_ref;
int32 i, j = 0, param_count, result_count, ext_ret_count;
uint64 total_size;
uint8 wasm_ret_type;
@ -1447,12 +1449,28 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
return false;
}
if (comp_ctx->enable_aux_stack_frame) {
#if WASM_ENABLE_AOT_STACK_FRAME != 0
if (!alloc_frame_for_aot_func(comp_ctx, func_ctx, func_idx))
return false;
#endif
if (comp_ctx->aux_stack_frame_type) {
if (func_idx < import_func_count
&& comp_ctx->call_stack_features.frame_per_function) {
INT_CONST(func_idx_ref, func_idx, I32_TYPE, true);
if (!aot_alloc_frame_per_function_frame_for_aot_func(
comp_ctx, func_ctx, func_idx_ref)) {
return false;
}
}
else if (!comp_ctx->call_stack_features.frame_per_function) {
if (comp_ctx->aux_stack_frame_type
!= AOT_STACK_FRAME_TYPE_STANDARD) {
aot_set_last_error("unsupported mode");
return false;
}
if (!alloc_frame_for_aot_func(comp_ctx, func_ctx, func_idx)) {
return false;
}
}
}
#endif
/* Get param cell number */
param_cell_num = func_type->param_cell_num;
@ -1522,7 +1540,7 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
if (func_idx < import_func_count) {
if (comp_ctx->enable_aux_stack_frame
if (comp_ctx->aux_stack_frame_type == AOT_STACK_FRAME_TYPE_STANDARD
&& !commit_params_to_frame_of_import_func(
comp_ctx, func_ctx, func_type, param_values + 1)) {
goto fail;
@ -1813,12 +1831,26 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
}
}
if (comp_ctx->enable_aux_stack_frame) {
#if WASM_ENABLE_AOT_STACK_FRAME != 0
if (!free_frame_for_aot_func(comp_ctx, func_ctx))
goto fail;
#endif
if (comp_ctx->aux_stack_frame_type) {
if (func_idx < import_func_count
&& comp_ctx->call_stack_features.frame_per_function) {
if (!aot_free_frame_per_function_frame_for_aot_func(comp_ctx,
func_ctx)) {
goto fail;
}
}
else if (!comp_ctx->call_stack_features.frame_per_function) {
if (comp_ctx->aux_stack_frame_type
!= AOT_STACK_FRAME_TYPE_STANDARD) {
aot_set_last_error("unsupported mode");
}
if (!free_frame_for_aot_func(comp_ctx, func_ctx)) {
goto fail;
}
}
}
#endif
/* Insert suspend check point */
if (comp_ctx->enable_thread_mgr) {
@ -2439,7 +2471,8 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
goto fail;
}
if (comp_ctx->enable_aux_stack_frame) {
if (comp_ctx->aux_stack_frame_type
&& !comp_ctx->call_stack_features.frame_per_function) {
#if WASM_ENABLE_AOT_STACK_FRAME != 0
/* TODO: use current frame instead of allocating new frame
for WASM_OP_RETURN_CALL_INDIRECT */
@ -2508,7 +2541,13 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
/* Translate call import block */
LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_import);
if (comp_ctx->enable_aux_stack_frame
if (comp_ctx->aot_frame && comp_ctx->call_stack_features.frame_per_function
&& !aot_alloc_frame_per_function_frame_for_aot_func(comp_ctx, func_ctx,
func_idx)) {
goto fail;
}
if (comp_ctx->aux_stack_frame_type == AOT_STACK_FRAME_TYPE_STANDARD
&& !commit_params_to_frame_of_import_func(comp_ctx, func_ctx, func_type,
param_values + 1)) {
goto fail;
@ -2545,6 +2584,12 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
&& !check_call_return(comp_ctx, func_ctx, res))
goto fail;
if (comp_ctx->aot_frame && comp_ctx->call_stack_features.frame_per_function
&& !aot_free_frame_per_function_frame_for_aot_func(comp_ctx,
func_ctx)) {
goto fail;
}
block_curr = LLVMGetInsertBlock(comp_ctx->builder);
for (i = 0; i < func_result_count; i++) {
LLVMAddIncoming(result_phis[i], &value_rets[i], &block_curr, 1);
@ -2629,7 +2674,8 @@ aot_compile_op_call_indirect(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
PUSH(result_phis[i], func_type->types[func_param_count + i]);
}
if (comp_ctx->enable_aux_stack_frame) {
if (comp_ctx->aux_stack_frame_type
&& !comp_ctx->call_stack_features.frame_per_function) {
#if WASM_ENABLE_AOT_STACK_FRAME != 0
if (!free_frame_for_aot_func(comp_ctx, func_ctx))
goto fail;
@ -2936,7 +2982,8 @@ aot_compile_op_call_ref(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
goto fail;
}
if (comp_ctx->enable_aux_stack_frame) {
if (comp_ctx->aux_stack_frame_type
&& !comp_ctx->call_stack_features.frame_per_function) {
#if WASM_ENABLE_AOT_STACK_FRAME != 0
/* TODO: use current frame instead of allocating new frame
for WASM_OP_RETURN_CALL_REF */
@ -3005,7 +3052,7 @@ aot_compile_op_call_ref(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
/* Translate call import block */
LLVMPositionBuilderAtEnd(comp_ctx->builder, block_call_import);
if (comp_ctx->enable_aux_stack_frame
if (comp_ctx->aux_stack_frame_type == AOT_STACK_FRAME_TYPE_STANDARD
&& !commit_params_to_frame_of_import_func(comp_ctx, func_ctx, func_type,
param_values + 1)) {
goto fail;
@ -3133,7 +3180,8 @@ aot_compile_op_call_ref(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
PUSH(result_phis[i], func_type->types[func_param_count + i]);
}
if (comp_ctx->enable_aux_stack_frame) {
if (comp_ctx->aux_stack_frame_type
&& !comp_ctx->call_stack_features.frame_per_function) {
#if WASM_ENABLE_AOT_STACK_FRAME != 0
if (!free_frame_for_aot_func(comp_ctx, func_ctx))
goto fail;

View File

@ -1771,7 +1771,7 @@ aot_create_func_context(const AOTCompData *comp_data, AOTCompContext *comp_ctx,
goto fail;
}
if (comp_ctx->enable_aux_stack_frame
if (comp_ctx->aux_stack_frame_type
&& !create_aux_stack_frame(comp_ctx, func_ctx)) {
goto fail;
}
@ -2577,9 +2577,7 @@ aot_create_comp_context(const AOTCompData *comp_data, aot_comp_option_t option)
if (option->enable_ref_types)
comp_ctx->enable_ref_types = true;
if (option->enable_aux_stack_frame)
comp_ctx->enable_aux_stack_frame = true;
comp_ctx->aux_stack_frame_type = option->aux_stack_frame_type;
comp_ctx->call_stack_features = option->call_stack_features;
if (option->enable_perf_profiling)

View File

@ -410,7 +410,7 @@ typedef struct AOTCompContext {
bool enable_aux_stack_check;
/* Generate auxiliary stack frame */
bool enable_aux_stack_frame;
AOTStackFrameType aux_stack_frame_type;
/* Auxiliary call stack features */
AOTCallStackFeatures call_stack_features;

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2024 Amazon Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef _AOT_STACK_FRAME_H_
#define _AOT_STACK_FRAME_H_
#include "platform_common.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
/* The non-imported function index of current function */
uint32 func_index;
/* Instruction pointer: offset to the bytecode array */
uint32 ip_offset;
} AOTTinyFrame;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2024 Amazon Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include "aot_stack_frame_comp.h"
#include "aot_emit_exception.h"
#define ADD_IN_BOUNDS_GEP(variable, type, pointer, indices, num_indices) \
do { \
if (!(variable = \
LLVMBuildInBoundsGEP2(comp_ctx->builder, type, pointer, \
indices, num_indices, #variable))) { \
aot_set_last_error("llvm build in bounds gep failed"); \
return false; \
} \
} while (0)
#define ADD_STORE(value, pointer) \
do { \
if (!LLVMBuildStore(comp_ctx->builder, value, pointer)) { \
aot_set_last_error("llvm build store failed"); \
return false; \
} \
} while (0)
#define ADD_LOAD(value, type, pointer) \
do { \
if (!(value = \
LLVMBuildLoad2(comp_ctx->builder, type, pointer, #value))) { \
aot_set_last_error("llvm build load failed"); \
return false; \
} \
} while (0)
static bool
aot_alloc_tiny_frame_for_aot_func(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx,
LLVMValueRef func_index)
{
LLVMValueRef wasm_stack_top_ptr = func_ctx->wasm_stack_top_ptr,
wasm_stack_top_bound = func_ctx->wasm_stack_top_bound,
wasm_stack_top, cmp;
LLVMBasicBlockRef check_wasm_stack_succ;
LLVMValueRef offset;
ADD_LOAD(wasm_stack_top, INT8_PTR_TYPE, wasm_stack_top_ptr);
if (comp_ctx->call_stack_features.bounds_checks) {
if (!(check_wasm_stack_succ = LLVMAppendBasicBlockInContext(
comp_ctx->context, func_ctx->func,
"check_wasm_stack_succ"))) {
aot_set_last_error("llvm add basic block failed.");
return false;
}
LLVMMoveBasicBlockAfter(check_wasm_stack_succ,
LLVMGetInsertBlock(comp_ctx->builder));
if (!(cmp = LLVMBuildICmp(comp_ctx->builder, LLVMIntUGE, wasm_stack_top,
wasm_stack_top_bound, "cmp"))) {
aot_set_last_error("llvm build icmp failed");
return false;
}
if (!(aot_emit_exception(comp_ctx, func_ctx,
EXCE_OPERAND_STACK_OVERFLOW, true, cmp,
check_wasm_stack_succ))) {
return false;
}
}
/* Save the func_idx on the top of the stack */
ADD_STORE(func_index, wasm_stack_top);
/* increment the stack pointer */
INT_CONST(offset, sizeof(AOTTinyFrame), I32_TYPE, true);
ADD_IN_BOUNDS_GEP(wasm_stack_top, INT8_TYPE, wasm_stack_top, &offset, 1);
ADD_STORE(wasm_stack_top, wasm_stack_top_ptr);
return true;
}
static bool
aot_free_tiny_frame_for_aot_func(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx)
{
LLVMValueRef wasm_stack_top_ptr = func_ctx->wasm_stack_top_ptr,
wasm_stack_top;
LLVMValueRef offset;
ADD_LOAD(wasm_stack_top, INT8_PTR_TYPE, wasm_stack_top_ptr);
INT_CONST(offset, -sizeof(AOTTinyFrame),
comp_ctx->pointer_size == 8 ? I64_TYPE : I32_TYPE, true);
ADD_IN_BOUNDS_GEP(wasm_stack_top, INT8_TYPE, wasm_stack_top, &offset, 1);
ADD_STORE(wasm_stack_top, wasm_stack_top_ptr);
return true;
}
bool
aot_tiny_frame_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
LLVMValueRef ip_value)
{
LLVMValueRef wasm_stack_top_ptr = func_ctx->wasm_stack_top_ptr,
wasm_stack_top;
LLVMValueRef offset, ip_addr;
bh_assert(ip_value);
ADD_LOAD(wasm_stack_top, INT8_PTR_TYPE, wasm_stack_top_ptr);
INT_CONST(offset, -4, comp_ctx->pointer_size == 8 ? I64_TYPE : I32_TYPE,
true);
ADD_IN_BOUNDS_GEP(ip_addr, INT8_TYPE, wasm_stack_top, &offset, 1);
ADD_STORE(ip_value, ip_addr);
return true;
}
bool
aot_alloc_frame_per_function_frame_for_aot_func(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx,
LLVMValueRef func_index)
{
switch (comp_ctx->aux_stack_frame_type) {
case AOT_STACK_FRAME_TYPE_TINY:
return aot_alloc_tiny_frame_for_aot_func(comp_ctx, func_ctx,
func_index);
default:
aot_set_last_error("unsupported mode");
return false;
}
}
bool
aot_free_frame_per_function_frame_for_aot_func(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx)
{
switch (comp_ctx->aux_stack_frame_type) {
case AOT_STACK_FRAME_TYPE_TINY:
return aot_free_tiny_frame_for_aot_func(comp_ctx, func_ctx);
default:
aot_set_last_error("unsupported mode");
return false;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2024 Amazon Inc. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#ifndef _AOT_STACK_FRAME_COMP_H_
#define _AOT_STACK_FRAME_COMP_H_
#include "aot_stack_frame.h"
#include "aot_compiler.h"
#ifdef __cplusplus
extern "C" {
#endif
bool
aot_alloc_frame_per_function_frame_for_aot_func(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx,
LLVMValueRef func_index);
bool
aot_free_frame_per_function_frame_for_aot_func(AOTCompContext *comp_ctx,
AOTFuncContext *func_ctx);
bool
aot_tiny_frame_gen_commit_ip(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
LLVMValueRef ip_value);
#ifdef __cplusplus
}
#endif
#endif