Implement multi-module feature and bulk-memory feature (#271)

Refine wasm loader and aot loader
Fix potential issue of os_mmap/os_munmap
Update document
This commit is contained in:
wenyongh
2020-06-02 14:53:06 +08:00
committed by GitHub
parent e81f72d41f
commit 752826a667
57 changed files with 4902 additions and 818 deletions

View File

@ -60,6 +60,10 @@ aot_create_mem_init_data_list(const WASMModule *module)
goto fail;
}
#if WASM_ENABLE_BULK_MEMORY != 0
data_list[i]->is_passive = module->data_segments[i]->is_passive;
data_list[i]->memory_index = module->data_segments[i]->memory_index;
#endif
data_list[i]->offset = module->data_segments[i]->base_offset;
data_list[i]->byte_count = module->data_segments[i]->data_length;
memcpy(data_list[i]->bytes, module->data_segments[i]->data,

View File

@ -24,6 +24,12 @@ typedef WASMType AOTFuncType;
* A segment of memory init data
*/
typedef struct AOTMemInitData {
#if WASM_ENABLE_BULK_MEMORY != 0
/* Passive flag */
bool is_passive;
/* memory index */
uint32 memory_index;
#endif
/* Start address of init data */
AOTInitExpr offset;
/* Byte count */

View File

@ -741,6 +741,39 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index)
if (!aot_compile_op_i64_trunc_f64(comp_ctx, func_ctx, sign, true))
return false;
break;
#if WASM_ENABLE_BULK_MEMORY != 0
case WASM_OP_MEMORY_INIT:
{
uint32 seg_index;
read_leb_uint32(frame_ip, frame_ip_end, seg_index);
frame_ip ++;
if (!aot_compile_op_memory_init(comp_ctx, func_ctx, seg_index))
return false;
break;
}
case WASM_OP_DATA_DROP:
{
uint32 seg_index;
read_leb_uint32(frame_ip, frame_ip_end, seg_index);
if (!aot_compile_op_data_drop(comp_ctx, func_ctx, seg_index))
return false;
break;
}
case WASM_OP_MEMORY_COPY:
{
frame_ip += 2;
if (!aot_compile_op_memory_copy(comp_ctx, func_ctx))
return false;
break;
}
case WASM_OP_MEMORY_FILL:
{
frame_ip ++;
if (!aot_compile_op_memory_fill(comp_ctx, func_ctx))
return false;
break;
}
#endif /* WASM_ENABLE_BULK_MEMORY */
default:
break;
}

View File

@ -124,8 +124,18 @@ get_mem_init_data_size(AOTMemInitData *mem_init_data)
{
/* init expr type (4 bytes) + init expr value (8 bytes)
+ byte count (4 bytes) + bytes */
return (uint32)(sizeof(uint32) + sizeof(uint64)
+ sizeof(uint32) + mem_init_data->byte_count);
uint32 total_size =
(uint32)(sizeof(uint32) + sizeof(uint64)
+ sizeof(uint32) + mem_init_data->byte_count);
/* bulk_memory enabled:
is_passive (4 bytes) + memory_index (4 bytes)
bulk memory disabled:
placeholder (4 bytes) + placeholder (4 bytes)
*/
total_size += (sizeof(uint32) + sizeof(uint32));
return total_size;
}
static uint32
@ -682,7 +692,8 @@ get_relocation_section_size(AOTObjectData *obj_data)
}
static uint32
get_aot_file_size(AOTCompData *comp_data, AOTObjectData *obj_data)
get_aot_file_size(AOTCompContext *comp_ctx, AOTCompData *comp_data,
AOTObjectData *obj_data)
{
uint32 size = 0;
@ -868,7 +879,8 @@ aot_emit_target_info_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
static bool
aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
AOTCompData *comp_data, AOTObjectData *obj_data)
AOTCompContext *comp_ctx, AOTCompData *comp_data,
AOTObjectData *obj_data)
{
uint32 offset = *p_offset, i;
AOTMemInitData **init_datas = comp_data->mem_init_data_list;
@ -882,6 +894,18 @@ aot_emit_mem_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
for (i = 0; i < comp_data->mem_init_data_count; i++) {
offset = align_uint(offset, 4);
#if WASM_ENABLE_BULK_MEMORY != 0
if (comp_ctx->enable_bulk_memory) {
EMIT_U32(init_datas[i]->is_passive);
EMIT_U32(init_datas[i]->memory_index);
}
else
#endif
{
/* emit two placeholder to keep the same size */
EMIT_U32(0);
EMIT_U32(0);
}
EMIT_U32(init_datas[i]->offset.init_expr_type);
EMIT_U64(init_datas[i]->offset.u.i64);
EMIT_U32(init_datas[i]->byte_count);
@ -1077,7 +1101,8 @@ aot_emit_object_data_section_info(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
static bool
aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
AOTCompData *comp_data, AOTObjectData *obj_data)
AOTCompContext *comp_ctx, AOTCompData *comp_data,
AOTObjectData *obj_data)
{
uint32 section_size = get_init_data_section_size(comp_data, obj_data);
uint32 offset = *p_offset;
@ -1087,7 +1112,7 @@ aot_emit_init_data_section(uint8 *buf, uint8 *buf_end, uint32 *p_offset,
EMIT_U32(AOT_SECTION_TYPE_INIT_DATA);
EMIT_U32(section_size);
if (!aot_emit_mem_info(buf, buf_end, &offset, comp_data, obj_data)
if (!aot_emit_mem_info(buf, buf_end, &offset, comp_ctx, comp_data, obj_data)
|| !aot_emit_table_info(buf, buf_end, &offset, comp_data, obj_data)
|| !aot_emit_func_type_info(buf, buf_end, &offset, comp_data, obj_data)
|| !aot_emit_import_global_info(buf, buf_end, &offset, comp_data, obj_data)
@ -1405,7 +1430,8 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data)
obj_data->target_info.bin_type = bin_type - LLVMBinaryTypeELF32L;
if (bin_type == LLVMBinaryTypeELF32L || bin_type == LLVMBinaryTypeELF32B) {
if (bin_type == LLVMBinaryTypeELF32L
|| bin_type == LLVMBinaryTypeELF32B) {
struct elf32_ehdr *elf_header;
bool is_little_bin = bin_type == LLVMBinaryTypeELF32L;
@ -1420,7 +1446,8 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data)
SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin);
SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin);
}
else {
else if (bin_type == LLVMBinaryTypeELF64L
|| bin_type == LLVMBinaryTypeELF64B) {
struct elf64_ehdr *elf_header;
bool is_little_bin = bin_type == LLVMBinaryTypeELF64L;
@ -1435,6 +1462,19 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data)
SET_TARGET_INFO(e_version, e_version, uint32, is_little_bin);
SET_TARGET_INFO(e_flags, e_flags, uint32, is_little_bin);
}
else if (bin_type == LLVMBinaryTypeMachO32L
|| bin_type == LLVMBinaryTypeMachO32B) {
/* TODO: parse file type of Mach-O 32 */
aot_set_last_error("invaid llvm binary bin_type.");
return false;
}
else if (bin_type == LLVMBinaryTypeMachO64L
|| bin_type == LLVMBinaryTypeMachO64B) {
/* TODO: parse file type of Mach-O 64 */
aot_set_last_error("invaid llvm binary bin_type.");
return false;
}
strncpy(obj_data->target_info.arch, comp_ctx->target_arch,
sizeof(obj_data->target_info.arch));
@ -1941,7 +1981,7 @@ aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data,
bh_print_time("Begin to emit AOT file");
aot_file_size = get_aot_file_size(comp_data, obj_data);
aot_file_size = get_aot_file_size(comp_ctx, comp_data, obj_data);
if (!(buf = aot_file_buf = wasm_runtime_malloc(aot_file_size))) {
aot_set_last_error("allocate memory failed.");
@ -1953,7 +1993,7 @@ aot_emit_aot_file(AOTCompContext *comp_ctx, AOTCompData *comp_data,
if (!aot_emit_file_header(buf, buf_end, &offset, comp_data, obj_data)
|| !aot_emit_target_info_section(buf, buf_end, &offset, comp_data, obj_data)
|| !aot_emit_init_data_section(buf, buf_end, &offset, comp_data, obj_data)
|| !aot_emit_init_data_section(buf, buf_end, &offset, comp_ctx, comp_data, obj_data)
|| !aot_emit_text_section(buf, buf_end, &offset, comp_data, obj_data)
|| !aot_emit_func_section(buf, buf_end, &offset, comp_data, obj_data)
|| !aot_emit_export_section(buf, buf_end, &offset, comp_data, obj_data)

View File

@ -626,3 +626,290 @@ fail:
return false;
}
#if WASM_ENABLE_BULK_MEMORY != 0
static LLVMValueRef
check_bulk_memory_overflow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
LLVMValueRef offset, LLVMValueRef bytes)
{
LLVMValueRef maddr, max_addr, cmp;
LLVMValueRef mem_base_addr;
LLVMBasicBlockRef block_curr = LLVMGetInsertBlock(comp_ctx->builder);
LLVMBasicBlockRef check_succ;
uint32 off = offsetof(AOTModuleInstance, memory_data_size);
LLVMValueRef mem_size_offset, mem_size_ptr, mem_size;
/* Get memory base address and memory data size */
if (func_ctx->mem_space_unchanged) {
mem_base_addr = func_ctx->mem_base_addr;
}
else {
if (!(mem_base_addr = LLVMBuildLoad(comp_ctx->builder,
func_ctx->mem_base_addr,
"mem_base"))) {
aot_set_last_error("llvm build load failed.");
goto fail;
}
}
/* return addres directly if constant offset and inside memory space */
if (LLVMIsConstant(offset) && LLVMIsConstant(bytes)) {
uint64 mem_offset = (uint64)LLVMConstIntGetZExtValue(offset);
uint64 mem_len = (uint64)LLVMConstIntGetZExtValue(bytes);
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;
if (mem_data_size > 0
&& mem_offset + mem_len <= mem_data_size) {
/* inside memory space */
/* maddr = mem_base_addr + moffset */
if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder,
mem_base_addr,
&offset, 1, "maddr"))) {
aot_set_last_error("llvm build add failed.");
goto fail;
}
return maddr;
}
}
/* mem_size_offset = aot_inst + off */
mem_size_offset = I32_CONST(off);
if (!(mem_size_ptr = LLVMBuildInBoundsGEP(comp_ctx->builder,
func_ctx->aot_inst,
&mem_size_offset, 1,
"mem_size_ptr_tmp"))) {
aot_set_last_error("llvm build inbounds gep failed.");
return NULL;
}
/* cast to int32* */
if (!(mem_size_ptr = LLVMBuildBitCast(comp_ctx->builder, mem_size_ptr,
INT32_PTR_TYPE, "mem_size_ptr"))) {
aot_set_last_error("llvm build bitcast failed.");
return NULL;
}
/* load memory size */
if (!(mem_size = LLVMBuildLoad(comp_ctx->builder,
mem_size_ptr, "mem_size"))) {
aot_set_last_error("llvm build load failed.");
return NULL;
}
ADD_BASIC_BLOCK(check_succ, "check_succ");
LLVMMoveBasicBlockAfter(check_succ, block_curr);
offset = LLVMBuildZExt(comp_ctx->builder, offset, I64_TYPE, "extend_offset");
bytes = LLVMBuildZExt(comp_ctx->builder, bytes, I64_TYPE, "extend_len");
mem_size = LLVMBuildZExt(comp_ctx->builder, mem_size, I64_TYPE, "extend_size");
BUILD_OP(Add, offset, bytes, max_addr, "max_addr");
BUILD_ICMP(LLVMIntUGT, max_addr, mem_size, cmp,
"cmp_max_mem_addr");
if (!aot_emit_exception(comp_ctx, func_ctx,
EXCE_OUT_OF_BOUNDS_MEMORY_ACCESS,
true, cmp, check_succ)) {
goto fail;
}
/* maddr = mem_base_addr + offset */
if (!(maddr = LLVMBuildInBoundsGEP(comp_ctx->builder, mem_base_addr,
&offset, 1, "maddr"))) {
aot_set_last_error("llvm build add failed.");
goto fail;
}
return maddr;
fail:
return NULL;
}
#define GET_AOT_FUNCTION(name, argc) do { \
if (!(func_type = LLVMFunctionType(ret_type, param_types, \
argc, false))) { \
aot_set_last_error("llvm add function type failed."); \
return false; \
} \
if (comp_ctx->is_jit_mode) { \
/* JIT mode, call the function directly */ \
if (!(func_ptr_type = LLVMPointerType(func_type, 0))) { \
aot_set_last_error("llvm add pointer type failed."); \
return false; \
} \
if (!(value = I64_CONST((uint64)(uintptr_t)name)) \
|| !(func = LLVMConstIntToPtr(value, func_ptr_type))) { \
aot_set_last_error("create LLVM value failed."); \
return false; \
} \
} \
else { \
char *func_name = #name; \
/* AOT mode, delcare the function */ \
if (!(func = LLVMGetNamedFunction(comp_ctx->module, func_name)) \
&& !(func = LLVMAddFunction(comp_ctx->module, \
func_name, func_type))) { \
aot_set_last_error("llvm add function failed."); \
return false; \
} \
} \
} while (0)
bool
aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 seg_index)
{
LLVMValueRef seg, offset, dst, len, param_values[5], ret_value, 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 mem_init_fail, init_success;
seg = I32_CONST(seg_index);
POP_I32(len);
POP_I32(offset);
POP_I32(dst);
param_types[0] = INT8_PTR_TYPE;
param_types[1] = I32_TYPE;
param_types[2] = I32_TYPE;
param_types[3] = I32_TYPE;
param_types[4] = I32_TYPE;
ret_type = INT8_TYPE;
GET_AOT_FUNCTION(aot_memory_init, 5);
/* Call function aot_memory_init() */
param_values[0] = func_ctx->aot_inst;
param_values[1] = seg;
param_values[2] = offset;
param_values[3] = len;
param_values[4] = dst;
if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func,
param_values, 5, "call"))) {
aot_set_last_error("llvm build call failed.");
return false;
}
BUILD_ICMP(LLVMIntUGT, ret_value, I8_ZERO, ret_value, "mem_init_ret");
ADD_BASIC_BLOCK(mem_init_fail, "mem_init_fail");
ADD_BASIC_BLOCK(init_success, "init_success");
LLVMMoveBasicBlockAfter(mem_init_fail, block_curr);
LLVMMoveBasicBlockAfter(init_success, block_curr);
if (!LLVMBuildCondBr(comp_ctx->builder, ret_value,
init_success, mem_init_fail)) {
aot_set_last_error("llvm build cond br failed.");
goto fail;
}
/* If memory.init failed, return this function
so the runtime can catch the exception */
LLVMPositionBuilderAtEnd(comp_ctx->builder, mem_init_fail);
if (aot_func_type->result_count) {
switch (aot_func_type->types[aot_func_type->param_count]) {
case VALUE_TYPE_I32:
LLVMBuildRet(comp_ctx->builder, I32_ZERO);
break;
case VALUE_TYPE_I64:
LLVMBuildRet(comp_ctx->builder, I64_ZERO);
break;
case VALUE_TYPE_F32:
LLVMBuildRet(comp_ctx->builder, F32_ZERO);
break;
case VALUE_TYPE_F64:
LLVMBuildRet(comp_ctx->builder, F64_ZERO);
break;
}
}
else {
LLVMBuildRetVoid(comp_ctx->builder);
}
LLVMPositionBuilderAtEnd(comp_ctx->builder, init_success);
return true;
fail:
return false;
}
bool
aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 seg_index)
{
LLVMValueRef seg, param_values[2], ret_value, func, value;
LLVMTypeRef param_types[2], ret_type, func_type, func_ptr_type;
seg = I32_CONST(seg_index);
param_types[0] = INT8_PTR_TYPE;
param_types[1] = I32_TYPE;
ret_type = INT8_TYPE;
GET_AOT_FUNCTION(aot_data_drop, 2);
/* Call function aot_data_drop() */
param_values[0] = func_ctx->aot_inst;
param_values[1] = seg;
if (!(ret_value = LLVMBuildCall(comp_ctx->builder, func,
param_values, 2, "call"))) {
aot_set_last_error("llvm build call failed.");
return false;
}
return true;
}
bool
aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
{
LLVMValueRef src, dst, src_addr, dst_addr, len, res;
POP_I32(len);
POP_I32(src);
POP_I32(dst);
if (!(src_addr =
check_bulk_memory_overflow(comp_ctx, func_ctx, src, len)))
return false;
if (!(dst_addr =
check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len)))
return false;
if (!(res = LLVMBuildMemMove(comp_ctx->builder, dst_addr, 1,
src_addr, 1, len))) {
aot_set_last_error("llvm build memmove failed.");
return false;
}
return true;
fail:
return false;
}
bool
aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
{
LLVMValueRef val, dst, dst_addr, len, res;
POP_I32(len);
POP_I32(val);
POP_I32(dst);
if (!(dst_addr =
check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len)))
return false;
val = LLVMBuildIntCast2(comp_ctx->builder, val, INT8_TYPE, true, "mem_set_value");
if (!(res = LLVMBuildMemSet(comp_ctx->builder, dst_addr,
val, len, 1))) {
aot_set_last_error("llvm build memset failed.");
return false;
}
return true;
fail:
return false;
}
#endif /* WASM_ENABLE_BULK_MEMORY */

View File

@ -50,6 +50,22 @@ aot_compile_op_memory_size(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
bool
aot_compile_op_memory_grow(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
#if WASM_ENABLE_BULK_MEMORY != 0
bool
aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 seg_index);
bool
aot_compile_op_data_drop(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
uint32 seg_index);
bool
aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
bool
aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx);
#endif
#ifdef __cplusplus
} /* end of extern "C" */
#endif

View File

@ -914,6 +914,9 @@ aot_create_comp_context(AOTCompData *comp_data,
goto fail;
}
if (option->enable_bulk_memory)
comp_ctx->enable_bulk_memory = true;
if (option->is_jit_mode) {
/* Create LLVM execution engine */
LLVMInitializeMCJITCompilerOptions(&jit_options, sizeof(jit_options));

View File

@ -185,6 +185,9 @@ typedef struct AOTCompContext {
LLVMExecutionEngineRef exec_engine;
bool is_jit_mode;
/* Bulk memory feature */
bool enable_bulk_memory;
/* Whether optimize the JITed code */
bool optimize;
@ -223,6 +226,7 @@ typedef struct AOTCompOption{
char *target_abi;
char *target_cpu;
char *cpu_features;
bool enable_bulk_memory;
uint32 opt_level;
uint32 size_level;
uint32 output_format;