Implement memory access bound check with hardware trap for 64-bit platforms (#293)
Also implement native stack overflow check with hardware trap for 64-bit platforms Refine classic interpreter and fast interpreter to improve performance Update document
This commit is contained in:
@ -398,7 +398,8 @@ aot_compile_op_call(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
aot_func = func_ctxes[func_idx - import_func_count]->aot_func;
|
||||
callee_cell_num = aot_func->param_cell_num + aot_func->local_cell_num + 1;
|
||||
|
||||
if (!check_stack_boundary(comp_ctx, func_ctx, callee_cell_num))
|
||||
if (comp_ctx->enable_bound_check
|
||||
&& !check_stack_boundary(comp_ctx, func_ctx, callee_cell_num))
|
||||
goto fail;
|
||||
|
||||
/* Call the function */
|
||||
|
||||
@ -75,18 +75,13 @@ check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
uint32 offset, uint32 bytes)
|
||||
{
|
||||
LLVMValueRef offset_const = I32_CONST(offset);
|
||||
LLVMValueRef bytes_const = I32_CONST(bytes);
|
||||
LLVMValueRef bytes64_const = I64_CONST(bytes);
|
||||
LLVMValueRef heap_base_offset = func_ctx->heap_base_offset;
|
||||
LLVMValueRef addr, maddr, offset1, offset2, cmp;
|
||||
LLVMValueRef mem_base_addr, mem_check_bound, total_mem_size;
|
||||
LLVMValueRef addr, maddr, offset1, cmp, cmp1, cmp2;
|
||||
LLVMValueRef mem_base_addr, mem_check_bound;
|
||||
LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder);
|
||||
LLVMBasicBlockRef check_succ, check_mem_space;
|
||||
LLVMBasicBlockRef check_succ;
|
||||
AOTValue *aot_value;
|
||||
|
||||
CHECK_LLVM_CONST(offset_const);
|
||||
CHECK_LLVM_CONST(bytes_const);
|
||||
CHECK_LLVM_CONST(bytes64_const);
|
||||
|
||||
/* Get memory base address and memory data size */
|
||||
if (func_ctx->mem_space_unchanged) {
|
||||
@ -104,21 +99,20 @@ check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
aot_value = func_ctx->block_stack.block_list_end->value_stack.value_list_end;
|
||||
|
||||
POP_I32(addr);
|
||||
/* offset1 = offset + addr; */
|
||||
BUILD_OP(Add, offset_const, addr, offset1, "offset1");
|
||||
|
||||
/* return addres directly if constant offset and inside memory space */
|
||||
if (LLVMIsConstant(offset1)) {
|
||||
uint32 mem_offset = (uint32)LLVMConstIntGetZExtValue(offset1);
|
||||
if (LLVMIsConstant(addr)) {
|
||||
int64 mem_offset = (int64)LLVMConstIntGetSExtValue(addr) + (int64)offset;
|
||||
uint32 num_bytes_per_page = comp_ctx->comp_data->num_bytes_per_page;
|
||||
uint32 init_page_count = comp_ctx->comp_data->mem_init_page_count;
|
||||
uint32 mem_data_size = num_bytes_per_page * init_page_count;
|
||||
int64 mem_data_size = num_bytes_per_page * init_page_count;
|
||||
if (mem_data_size > 0
|
||||
&& mem_offset >= 0
|
||||
&& mem_offset <= mem_data_size - bytes) {
|
||||
/* inside memory space */
|
||||
/* maddr = mem_base_addr + moffset */
|
||||
if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder,
|
||||
mem_base_addr,
|
||||
offset1 = I32_CONST((uint32)mem_offset);
|
||||
CHECK_LLVM_CONST(offset_const);
|
||||
if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr,
|
||||
&offset1, 1, "maddr"))) {
|
||||
aot_set_last_error("llvm build add failed.");
|
||||
goto fail;
|
||||
@ -127,51 +121,35 @@ check_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
if (comp_ctx->comp_data->mem_init_page_count == 0) {
|
||||
/* Get total memory size */
|
||||
if (func_ctx->mem_space_unchanged) {
|
||||
total_mem_size = func_ctx->total_mem_size;
|
||||
}
|
||||
else {
|
||||
if (!(total_mem_size = LLVMBuildLoad(comp_ctx->builder,
|
||||
func_ctx->total_mem_size,
|
||||
"total_mem_size"))) {
|
||||
aot_set_last_error("llvm build load failed.");
|
||||
if (!(offset_const = LLVMBuildZExt(comp_ctx->builder, offset_const,
|
||||
I64_TYPE, "offset_i64"))
|
||||
|| !(addr = LLVMBuildSExt(comp_ctx->builder, addr,
|
||||
I64_TYPE, "addr_i64"))) {
|
||||
aot_set_last_error("llvm build extend i32 to i64 failed.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
ADD_BASIC_BLOCK(check_mem_space, "check_mem_space");
|
||||
LLVMMoveBasicBlockAfter(check_mem_space, block_curr);
|
||||
|
||||
/* if total_mem_size is zero, boundary check fail */
|
||||
BUILD_ICMP(LLVMIntEQ, total_mem_size, I32_ZERO, cmp,
|
||||
"cmp_total_mem_size");
|
||||
if (!aot_emit_exception(comp_ctx, func_ctx,
|
||||
EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS,
|
||||
true, cmp, check_mem_space)) {
|
||||
goto fail;
|
||||
}
|
||||
SET_BUILD_POS(check_mem_space);
|
||||
}
|
||||
|
||||
if (!(aot_value->is_local
|
||||
&& aot_checked_addr_list_find(func_ctx, aot_value->local_idx,
|
||||
offset, bytes))) {
|
||||
/* offset2 = offset1 - heap_base_offset; */
|
||||
BUILD_OP(Sub, offset1, heap_base_offset, offset2, "offset2");
|
||||
/* offset1 = offset + addr; */
|
||||
BUILD_OP(Add, offset_const, addr, offset1, "offset1");
|
||||
|
||||
if (comp_ctx->enable_bound_check
|
||||
&& !(aot_value->is_local
|
||||
&& aot_checked_addr_list_find(func_ctx, aot_value->local_idx,
|
||||
offset, bytes))) {
|
||||
if (!(mem_check_bound =
|
||||
get_memory_check_bound(comp_ctx, func_ctx, bytes))) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BUILD_ICMP(LLVMIntSGT, func_ctx->mem_bound_check_heap_base, offset1,
|
||||
cmp1, "cmp1");
|
||||
BUILD_ICMP(LLVMIntSGT, offset1, mem_check_bound, cmp2, "cmp2");
|
||||
BUILD_OP(Or, cmp1, cmp2, cmp, "cmp");
|
||||
|
||||
/* Add basic blocks */
|
||||
ADD_BASIC_BLOCK(check_succ, "check_succ");
|
||||
LLVMMoveBasicBlockAfter(check_succ, block_curr);
|
||||
|
||||
/* offset2 > bound ? */
|
||||
BUILD_ICMP(LLVMIntUGT, offset2, mem_check_bound, cmp, "cmp");
|
||||
if (!aot_emit_exception(comp_ctx, func_ctx,
|
||||
EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS,
|
||||
true, cmp, check_succ)) {
|
||||
|
||||
@ -182,29 +182,6 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
/* Load total memory size */
|
||||
offset = I32_CONST(offsetof(AOTModuleInstance, total_mem_size));
|
||||
if (!(func_ctx->total_mem_size =
|
||||
LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst,
|
||||
&offset, 1, "bound_check_1byte_offset"))) {
|
||||
aot_set_last_error("llvm build in bounds gep failed");
|
||||
return false;
|
||||
}
|
||||
if (!(func_ctx->total_mem_size =
|
||||
LLVMBuildBitCast(comp_ctx->builder, func_ctx->total_mem_size,
|
||||
INT32_PTR_TYPE, "bound_check_1byte_ptr"))) {
|
||||
aot_set_last_error("llvm build bit cast failed");
|
||||
return false;
|
||||
}
|
||||
if (mem_space_unchanged) {
|
||||
if (!(func_ctx->total_mem_size =
|
||||
LLVMBuildLoad(comp_ctx->builder, func_ctx->total_mem_size,
|
||||
"bound_check_1byte"))) {
|
||||
aot_set_last_error("llvm build load failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Load memory bound check constants */
|
||||
offset = I32_CONST(offsetof(AOTModuleInstance, mem_bound_check_1byte));
|
||||
if (!(func_ctx->mem_bound_check_1byte =
|
||||
@ -215,7 +192,7 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
}
|
||||
if (!(func_ctx->mem_bound_check_1byte =
|
||||
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_1byte,
|
||||
INT32_PTR_TYPE, "bound_check_1byte_ptr"))) {
|
||||
INT64_PTR_TYPE, "bound_check_1byte_ptr"))) {
|
||||
aot_set_last_error("llvm build bit cast failed");
|
||||
return false;
|
||||
}
|
||||
@ -237,7 +214,7 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
}
|
||||
if (!(func_ctx->mem_bound_check_2bytes =
|
||||
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_2bytes,
|
||||
INT32_PTR_TYPE, "bound_check_2bytes_ptr"))) {
|
||||
INT64_PTR_TYPE, "bound_check_2bytes_ptr"))) {
|
||||
aot_set_last_error("llvm build bit cast failed");
|
||||
return false;
|
||||
}
|
||||
@ -259,7 +236,7 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
}
|
||||
if (!(func_ctx->mem_bound_check_4bytes =
|
||||
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_4bytes,
|
||||
INT32_PTR_TYPE, "bound_check_4bytes_ptr"))) {
|
||||
INT64_PTR_TYPE, "bound_check_4bytes_ptr"))) {
|
||||
aot_set_last_error("llvm build bit cast failed");
|
||||
return false;
|
||||
}
|
||||
@ -281,7 +258,7 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
}
|
||||
if (!(func_ctx->mem_bound_check_8bytes =
|
||||
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_8bytes,
|
||||
INT32_PTR_TYPE, "bound_check_8bytes_ptr"))) {
|
||||
INT64_PTR_TYPE, "bound_check_8bytes_ptr"))) {
|
||||
aot_set_last_error("llvm build bit cast failed");
|
||||
return false;
|
||||
}
|
||||
@ -294,23 +271,23 @@ create_memory_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
/* Load heap base offset */
|
||||
offset = I32_CONST(offsetof(AOTModuleInstance, heap_base_offset));
|
||||
if (!(func_ctx->heap_base_offset =
|
||||
/* Load bound_check_heap_base */
|
||||
offset = I32_CONST(offsetof(AOTModuleInstance, mem_bound_check_heap_base));
|
||||
if (!(func_ctx->mem_bound_check_heap_base =
|
||||
LLVMBuildInBoundsGEP(comp_ctx->builder, func_ctx->aot_inst,
|
||||
&offset, 1, "heap_base_offset_offset"))) {
|
||||
&offset, 1, "bound_check_heap_base_offset"))) {
|
||||
aot_set_last_error("llvm build in bounds gep failed");
|
||||
return false;
|
||||
}
|
||||
if (!(func_ctx->heap_base_offset =
|
||||
LLVMBuildBitCast(comp_ctx->builder, func_ctx->heap_base_offset,
|
||||
INT32_PTR_TYPE, "heap_base_offset_tmp"))) {
|
||||
if (!(func_ctx->mem_bound_check_heap_base =
|
||||
LLVMBuildBitCast(comp_ctx->builder, func_ctx->mem_bound_check_heap_base,
|
||||
INT64_PTR_TYPE, "bound_check_heap_base_tmp"))) {
|
||||
aot_set_last_error("llvm build bit cast failed");
|
||||
return false;
|
||||
}
|
||||
if (!(func_ctx->heap_base_offset =
|
||||
LLVMBuildLoad(comp_ctx->builder, func_ctx->heap_base_offset,
|
||||
"heap_base_offset"))) {
|
||||
if (!(func_ctx->mem_bound_check_heap_base =
|
||||
LLVMBuildLoad(comp_ctx->builder, func_ctx->mem_bound_check_heap_base,
|
||||
"bound_check_heap_base"))) {
|
||||
aot_set_last_error("llvm build load failed");
|
||||
return false;
|
||||
}
|
||||
@ -936,6 +913,11 @@ aot_create_comp_context(AOTCompData *comp_data,
|
||||
comp_ctx->is_jit_mode = true;
|
||||
comp_ctx->target_machine =
|
||||
LLVMGetExecutionEngineTargetMachine(comp_ctx->exec_engine);
|
||||
#ifndef OS_ENABLE_HW_BOUND_CHECK
|
||||
comp_ctx->enable_bound_check = true;
|
||||
#else
|
||||
comp_ctx->enable_bound_check = false;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
/* Create LLVM target machine */
|
||||
@ -1049,6 +1031,21 @@ aot_create_comp_context(AOTCompData *comp_data,
|
||||
get_target_arch_from_triple(triple_norm, comp_ctx->target_arch,
|
||||
sizeof(comp_ctx->target_arch));
|
||||
|
||||
if (option->bounds_checks == 1 || option->bounds_checks == 0) {
|
||||
/* Set by user */
|
||||
comp_ctx->enable_bound_check =
|
||||
(option->bounds_checks == 1) ? true : false;
|
||||
}
|
||||
else {
|
||||
/* Unset by user, use default value */
|
||||
if (strstr(comp_ctx->target_arch, "64") && !option->is_sgx_platform) {
|
||||
comp_ctx->enable_bound_check = false;
|
||||
}
|
||||
else {
|
||||
comp_ctx->enable_bound_check = true;
|
||||
}
|
||||
}
|
||||
|
||||
os_printf("Create AoT compiler with:\n");
|
||||
os_printf(" target: %s\n", comp_ctx->target_arch);
|
||||
os_printf(" target cpu: %s\n", cpu);
|
||||
@ -1114,14 +1111,11 @@ aot_create_comp_context(AOTCompData *comp_data,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
LLVMAddBasicAliasAnalysisPass(comp_ctx->pass_mgr);
|
||||
LLVMAddPromoteMemoryToRegisterPass(comp_ctx->pass_mgr);
|
||||
LLVMAddInstructionCombiningPass(comp_ctx->pass_mgr);
|
||||
LLVMAddCFGSimplificationPass(comp_ctx->pass_mgr);
|
||||
LLVMAddJumpThreadingPass(comp_ctx->pass_mgr);
|
||||
LLVMAddConstantPropagationPass(comp_ctx->pass_mgr);
|
||||
LLVMAddReassociatePass(comp_ctx->pass_mgr);
|
||||
LLVMAddGVNPass(comp_ctx->pass_mgr);
|
||||
LLVMAddCFGSimplificationPass(comp_ctx->pass_mgr);
|
||||
|
||||
/* Create metadata for llvm float experimental constrained intrinsics */
|
||||
if (!(comp_ctx->fp_rounding_mode =
|
||||
|
||||
@ -105,9 +105,8 @@ typedef struct AOTFuncContext {
|
||||
LLVMValueRef native_stack_bound;
|
||||
LLVMValueRef last_alloca;
|
||||
|
||||
LLVMValueRef heap_base_offset;
|
||||
LLVMValueRef mem_base_addr;
|
||||
LLVMValueRef total_mem_size;
|
||||
LLVMValueRef mem_bound_check_heap_base;
|
||||
LLVMValueRef mem_bound_check_1byte;
|
||||
LLVMValueRef mem_bound_check_2bytes;
|
||||
LLVMValueRef mem_bound_check_4bytes;
|
||||
@ -188,6 +187,9 @@ typedef struct AOTCompContext {
|
||||
/* Bulk memory feature */
|
||||
bool enable_bulk_memory;
|
||||
|
||||
/* Bounday Check */
|
||||
bool enable_bound_check;
|
||||
|
||||
/* Whether optimize the JITed code */
|
||||
bool optimize;
|
||||
|
||||
@ -227,9 +229,11 @@ typedef struct AOTCompOption{
|
||||
char *target_cpu;
|
||||
char *cpu_features;
|
||||
bool enable_bulk_memory;
|
||||
bool is_sgx_platform;
|
||||
uint32 opt_level;
|
||||
uint32 size_level;
|
||||
uint32 output_format;
|
||||
uint32 bounds_checks;
|
||||
} AOTCompOption, *aot_comp_option_t;
|
||||
|
||||
AOTCompContext *
|
||||
|
||||
Reference in New Issue
Block a user