mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-09-09 19:29:43 +00:00

Refactor the layout of interpreter and AOT module instance: - Unify the interp/AOT module instance, use the same WASMModuleInstance/ WASMMemoryInstance/WASMTableInstance data structures for both interpreter and AOT - Make the offset of most fields the same in module instance for both interpreter and AOT, append memory instance structure, global data and table instances to the end of module instance for interpreter mode (like AOT mode) - For extra fields in WASM module instance, use WASMModuleInstanceExtra to create a field `e` for interpreter - Change the LLVM JIT module instance creating process, LLVM JIT uses the WASM module and module instance same as interpreter/Fast-JIT mode. So that Fast JIT and LLVM JIT can access the same data structures, and make it possible to implement the Multi-tier JIT (tier-up from Fast JIT to LLVM JIT) in the future - Unify some APIs: merge some APIs for module instance and memory instance's related operations (only implement one copy) Note that the AOT ABI is same, the AOT file format, AOT relocation types, how AOT code accesses the AOT module instance and so on are kept unchanged. Refer to: https://github.com/bytecodealliance/wasm-micro-runtime/issues/1384
519 lines
12 KiB
C
519 lines
12 KiB
C
/*
|
|
* Copyright (C) 2019 Intel Corporation. All rights reserved.
|
|
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
*/
|
|
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "wasm_c_api.h"
|
|
|
|
#define WORKER_NUMBER 10
|
|
|
|
/******************************* VM *******************************/
|
|
/* Use wasm_vm_t and vm_xxx to simulate a minimal Wasm VM in Envoy */
|
|
|
|
typedef struct _vm {
|
|
wasm_engine_t *engine;
|
|
wasm_store_t *store;
|
|
wasm_module_t *module;
|
|
wasm_instance_t *instance;
|
|
wasm_func_t **function_list;
|
|
wasm_memory_t *memory;
|
|
wasm_table_t *table;
|
|
wasm_extern_vec_t *exports;
|
|
} wasm_vm_t;
|
|
|
|
typedef enum _clone_level {
|
|
not_cloneable = 0,
|
|
compiled_bytecode,
|
|
instantiated_module
|
|
} clone_level;
|
|
|
|
typedef struct _thread_arg_t {
|
|
char name[32];
|
|
bool *ready_go_flag;
|
|
pthread_mutex_t *ready_go_lock;
|
|
pthread_cond_t *ready_go_cond;
|
|
const wasm_vm_t *base_vm;
|
|
} thread_arg_t;
|
|
|
|
wasm_vm_t *
|
|
vm_new()
|
|
{
|
|
wasm_vm_t *vm = NULL;
|
|
|
|
vm = malloc(sizeof(struct _vm));
|
|
if (!vm)
|
|
goto fail;
|
|
|
|
memset(vm, 0, sizeof(wasm_vm_t));
|
|
|
|
vm->engine = wasm_engine_new();
|
|
if (!vm->engine)
|
|
goto fail;
|
|
|
|
vm->store = wasm_store_new(vm->engine);
|
|
if (!vm->store)
|
|
goto fail;
|
|
|
|
return vm;
|
|
|
|
fail:
|
|
if (vm) {
|
|
if (vm->engine)
|
|
wasm_engine_delete(vm->engine);
|
|
|
|
free(vm);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool
|
|
vm_load(wasm_vm_t *vm, const wasm_byte_vec_t *binary)
|
|
{
|
|
vm->module = wasm_module_new(vm->store, binary);
|
|
return vm->module != NULL;
|
|
}
|
|
|
|
bool
|
|
vm_link(wasm_vm_t *vm, wasm_extern_vec_t *imports)
|
|
{
|
|
vm->instance = wasm_instance_new(vm->store, vm->module, imports, NULL);
|
|
if (!vm->instance)
|
|
goto fail;
|
|
|
|
vm->exports = malloc(sizeof(wasm_extern_vec_t));
|
|
if (!vm->exports)
|
|
goto fail;
|
|
|
|
memset(vm->exports, 0, sizeof(wasm_extern_vec_t));
|
|
wasm_instance_exports(vm->instance, vm->exports);
|
|
/* an exported memory, and two exported functions */
|
|
assert(vm->exports->size == 3);
|
|
|
|
/* bind memory */
|
|
assert(wasm_extern_kind(vm->exports->data[0]) == WASM_EXTERN_MEMORY);
|
|
vm->memory = wasm_extern_as_memory(vm->exports->data[0]);
|
|
|
|
vm->function_list = malloc(2 * sizeof(wasm_func_t *));
|
|
if (!vm->function_list)
|
|
goto fail;
|
|
|
|
memset(vm->function_list, 0, sizeof(2 * sizeof(wasm_func_t *)));
|
|
|
|
/* bind wasm_set_byte(...) */
|
|
assert(wasm_extern_kind(vm->exports->data[1]) == WASM_EXTERN_FUNC);
|
|
vm->function_list[0] = wasm_extern_as_func(vm->exports->data[1]);
|
|
|
|
/* bind wasm_get_byte(...) */
|
|
assert(wasm_extern_kind(vm->exports->data[2]) == WASM_EXTERN_FUNC);
|
|
vm->function_list[1] = wasm_extern_as_func(vm->exports->data[2]);
|
|
|
|
return true;
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
wasm_vm_t *
|
|
vm_clone_from_module(const wasm_vm_t *base)
|
|
{
|
|
printf("Initializing...\n");
|
|
wasm_vm_t *secondary = NULL;
|
|
|
|
secondary = vm_new();
|
|
if (secondary) {
|
|
printf("Reuse module and bypass vm_load()...");
|
|
secondary->module = base->module;
|
|
}
|
|
|
|
return secondary;
|
|
}
|
|
|
|
wasm_vm_t *
|
|
vm_clone_from_instance(const wasm_vm_t *base)
|
|
{
|
|
/**
|
|
* if do a clone of the level instantiated_module, need to malloc and
|
|
* initialie
|
|
* - global. WASMGlobalIntance and global data
|
|
* - memory. WAAMMemoryInstance, memory_data and heap
|
|
* - table. WASMTableInstance, table_data
|
|
* - exports. all global, memory and table
|
|
*
|
|
* it is almost everything in wasm_instantiate() except funciton.
|
|
*/
|
|
(void)base;
|
|
printf("Unsupported\n");
|
|
return NULL;
|
|
}
|
|
|
|
wasm_vm_t *
|
|
vm_clone(const wasm_vm_t *base, clone_level level)
|
|
{
|
|
if (level == not_cloneable)
|
|
return NULL;
|
|
|
|
if (level == compiled_bytecode)
|
|
return vm_clone_from_module(base);
|
|
else
|
|
return vm_clone_from_instance(base);
|
|
}
|
|
|
|
wasm_vm_t *
|
|
vm_release(wasm_vm_t *vm)
|
|
{
|
|
if (!vm)
|
|
return NULL;
|
|
|
|
if (vm->function_list) {
|
|
free(vm->function_list);
|
|
vm->function_list = NULL;
|
|
}
|
|
|
|
if (vm->exports) {
|
|
wasm_extern_vec_delete(vm->exports);
|
|
free(vm->exports);
|
|
vm->exports = NULL;
|
|
}
|
|
|
|
wasm_instance_delete(vm->instance);
|
|
vm->instance = NULL;
|
|
|
|
wasm_module_delete(vm->module);
|
|
vm->module = NULL;
|
|
|
|
wasm_store_delete(vm->store);
|
|
vm->store = NULL;
|
|
|
|
wasm_engine_delete(vm->engine);
|
|
vm->engine = NULL;
|
|
|
|
free(vm);
|
|
return NULL;
|
|
}
|
|
|
|
bool
|
|
vm_memory_set_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t byte)
|
|
{
|
|
byte_t *data = wasm_memory_data(vm->memory);
|
|
assert(data);
|
|
*(data + offset) = byte;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
vm_memory_get_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t *byte)
|
|
{
|
|
byte_t *data = wasm_memory_data(vm->memory);
|
|
assert(data);
|
|
*byte = *(data + offset);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
vm_function_set_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t byte)
|
|
{
|
|
wasm_val_t a_v[2] = { WASM_I32_VAL(offset), WASM_I32_VAL(byte) };
|
|
wasm_val_vec_t args = WASM_ARRAY_VEC(a_v);
|
|
wasm_val_vec_t results = WASM_EMPTY_VEC;
|
|
wasm_trap_t *trap = wasm_func_call(vm->function_list[0], &args, &results);
|
|
if (trap)
|
|
printf("call wasm_set_byte failed");
|
|
|
|
return trap != NULL;
|
|
}
|
|
|
|
bool
|
|
vm_function_get_byte(const wasm_vm_t *vm, uint32_t offset, uint8_t *byte)
|
|
{
|
|
wasm_val_t a_v[1] = { WASM_I32_VAL(offset) };
|
|
wasm_val_vec_t args = WASM_ARRAY_VEC(a_v);
|
|
wasm_val_t r_v[1] = { WASM_INIT_VAL };
|
|
wasm_val_vec_t results = WASM_ARRAY_VEC(r_v);
|
|
wasm_trap_t *trap = wasm_func_call(vm->function_list[1], &args, &results);
|
|
if (trap) {
|
|
printf("call wasm_get_byte failed");
|
|
return false;
|
|
}
|
|
|
|
assert(results.data->kind == WASM_I32);
|
|
*byte = results.data->of.i32;
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
load_wasm_file_content(const char *file_name, wasm_byte_vec_t *out)
|
|
{
|
|
bool ret = false;
|
|
#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0
|
|
FILE *file = fopen(file_name, "rb");
|
|
#else
|
|
FILE *file = fopen(file_name, "rb");
|
|
#endif
|
|
if (!file) {
|
|
printf("> Error loading .wasm!\n");
|
|
goto quit;
|
|
}
|
|
|
|
int offset = fseek(file, 0L, SEEK_END);
|
|
if (offset == -1) {
|
|
printf("> Error loading .wasm!\n");
|
|
goto close_file;
|
|
}
|
|
|
|
long file_size = ftell(file);
|
|
if (file_size == -1) {
|
|
printf("> Error loading .wasm!\n");
|
|
goto close_file;
|
|
}
|
|
|
|
offset = fseek(file, 0L, SEEK_SET);
|
|
if (offset == -1) {
|
|
printf("> Error loading .wasm!\n");
|
|
goto close_file;
|
|
}
|
|
|
|
wasm_byte_vec_new_uninitialized(out, file_size);
|
|
if (fread(out->data, file_size, 1, file) != 1) {
|
|
printf("> Error loading content!\n");
|
|
goto close_file;
|
|
}
|
|
|
|
ret = true;
|
|
close_file:
|
|
fclose(file);
|
|
quit:
|
|
return ret;
|
|
}
|
|
|
|
static pthread_key_t name_key;
|
|
|
|
wasm_trap_t *
|
|
report_cb(const wasm_val_vec_t *args, wasm_val_vec_t *results)
|
|
{
|
|
(void)results;
|
|
|
|
assert(args->data[0].kind == WASM_I32);
|
|
uint32_t chk_pnt_no = args->data[0].of.i32;
|
|
|
|
char *name = pthread_getspecific(name_key);
|
|
printf("[%s] Pass CHK POINT #%u\n", name, chk_pnt_no);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool
|
|
run_code_start(wasm_vm_t **out)
|
|
{
|
|
bool ret = false;
|
|
|
|
printf("Initializing...\n");
|
|
wasm_vm_t *vm = vm_new();
|
|
if (!vm)
|
|
goto fail;
|
|
|
|
printf("Loading binary...\n");
|
|
wasm_byte_vec_t binary = { 0 };
|
|
#if WASM_ENABLE_AOT != 0 && WASM_ENABLE_INTERP == 0
|
|
const char *file_name = "clone.aot";
|
|
#else
|
|
const char *file_name = "clone.wasm";
|
|
#endif
|
|
if (!load_wasm_file_content(file_name, &binary))
|
|
goto release_vm;
|
|
|
|
printf("Compiling module...\n");
|
|
ret = vm_load(vm, &binary);
|
|
wasm_byte_vec_delete(&binary);
|
|
if (!ret)
|
|
goto release_vm;
|
|
|
|
printf("Creating callback...\n");
|
|
wasm_functype_t *callback_type =
|
|
wasm_functype_new_1_0(wasm_valtype_new_i32());
|
|
if (!callback_type)
|
|
goto release_vm;
|
|
|
|
wasm_func_t *callback = wasm_func_new(vm->store, callback_type, report_cb);
|
|
wasm_functype_delete(callback_type);
|
|
if (!callback)
|
|
goto release_vm;
|
|
|
|
printf("Instantiating module...\n");
|
|
wasm_extern_t *externs[] = { wasm_func_as_extern(callback) };
|
|
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
|
ret = vm_link(vm, &imports);
|
|
wasm_func_delete(callback);
|
|
if (!ret)
|
|
goto release_vm;
|
|
|
|
*out = vm;
|
|
return true;
|
|
|
|
release_vm:
|
|
vm_release(vm);
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
run_warm_start_w_compiled_bytecode(const wasm_vm_t *first, wasm_vm_t **out)
|
|
{
|
|
bool ret;
|
|
wasm_vm_t *secondary = vm_clone(first, compiled_bytecode);
|
|
if (!secondary)
|
|
goto fail;
|
|
|
|
printf("Creating callback...\n");
|
|
wasm_functype_t *callback_type =
|
|
wasm_functype_new_1_0(wasm_valtype_new_i32());
|
|
if (!callback_type)
|
|
goto release_vm;
|
|
|
|
wasm_func_t *callback =
|
|
wasm_func_new(secondary->store, callback_type, report_cb);
|
|
wasm_functype_delete(callback_type);
|
|
if (!callback)
|
|
goto release_vm;
|
|
|
|
printf("Instantiating module...\n");
|
|
wasm_extern_t *externs[] = { wasm_func_as_extern(callback) };
|
|
wasm_extern_vec_t imports = WASM_ARRAY_VEC(externs);
|
|
ret = vm_link(secondary, &imports);
|
|
wasm_func_delete(callback);
|
|
if (!ret)
|
|
goto release_vm;
|
|
|
|
*out = secondary;
|
|
return true;
|
|
|
|
release_vm:
|
|
vm_release(secondary);
|
|
fail:
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
run_warm_start_w_instantiated_module(const wasm_vm_t *first, wasm_vm_t **out)
|
|
{
|
|
wasm_vm_t *secondary = vm_clone(first, instantiated_module);
|
|
if (!secondary)
|
|
return false;
|
|
|
|
*out = secondary;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
run_test(const wasm_vm_t *vm)
|
|
{
|
|
uint8_t byte = 0xFF;
|
|
|
|
/* read initialization */
|
|
vm_function_get_byte(vm, 10, &byte);
|
|
assert(byte == 0x0);
|
|
vm_memory_get_byte(vm, 10, &byte);
|
|
assert(byte == 0x0);
|
|
|
|
/* read after writing */
|
|
vm_function_set_byte(vm, 16, 0xab);
|
|
vm_function_get_byte(vm, 16, &byte);
|
|
assert(byte == 0xab);
|
|
|
|
vm_memory_set_byte(vm, 16, 0xcd);
|
|
vm_memory_get_byte(vm, 16, &byte);
|
|
assert(byte == 0xcd);
|
|
|
|
/* reading and writing across */
|
|
vm_function_set_byte(vm, 16, 0xef);
|
|
vm_memory_get_byte(vm, 16, &byte);
|
|
assert(byte == 0xef);
|
|
|
|
vm_memory_set_byte(vm, 16, 0x67);
|
|
vm_function_get_byte(vm, 16, &byte);
|
|
assert(byte == 0x67);
|
|
|
|
printf("All Passed ...\n");
|
|
}
|
|
|
|
static void *
|
|
thrd_func(void *arg)
|
|
{
|
|
thread_arg_t *thrd_arg = (thread_arg_t *)arg;
|
|
printf("Running warm start at %s...\n", thrd_arg->name);
|
|
|
|
pthread_setspecific(name_key, thrd_arg->name);
|
|
|
|
wasm_vm_t *vm;
|
|
if (!run_warm_start_w_compiled_bytecode(thrd_arg->base_vm, &vm))
|
|
return NULL;
|
|
|
|
pthread_mutex_trylock(thrd_arg->ready_go_lock);
|
|
while (!(*thrd_arg->ready_go_flag)) {
|
|
pthread_cond_wait(thrd_arg->ready_go_cond, thrd_arg->ready_go_lock);
|
|
}
|
|
pthread_mutex_unlock(thrd_arg->ready_go_lock);
|
|
|
|
printf("Running test at %s...\n", thrd_arg->name);
|
|
run_test(vm);
|
|
|
|
vm_release(vm);
|
|
pthread_exit(NULL);
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
main()
|
|
{
|
|
int ret = EXIT_FAILURE;
|
|
bool ready_go_flag = false;
|
|
pthread_mutex_t ready_go_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
pthread_cond_t ready_go_cond = PTHREAD_COND_INITIALIZER;
|
|
pthread_key_create(&name_key, NULL);
|
|
pthread_setspecific(name_key, "Execution Thread");
|
|
|
|
printf("Running cold start at the execution thread...\n");
|
|
wasm_vm_t *base_vm;
|
|
if (!run_code_start(&base_vm))
|
|
goto quit;
|
|
run_test(base_vm);
|
|
|
|
printf("Running warm start at other threads...\n");
|
|
pthread_mutex_trylock(&ready_go_lock);
|
|
|
|
pthread_t tids[WORKER_NUMBER] = { 0 };
|
|
thread_arg_t thrd_args[WORKER_NUMBER] = { 0 };
|
|
for (size_t i = 0; i < sizeof(tids) / sizeof(tids[0]); i++) {
|
|
thread_arg_t *thrd_arg = thrd_args + i;
|
|
|
|
snprintf(thrd_arg->name, 32, "Worker#%lu", i);
|
|
thrd_arg->ready_go_cond = &ready_go_cond;
|
|
thrd_arg->ready_go_lock = &ready_go_lock;
|
|
thrd_arg->ready_go_flag = &ready_go_flag;
|
|
thrd_arg->base_vm = base_vm;
|
|
|
|
int ret = pthread_create(&tids[i], NULL, thrd_func, thrd_arg);
|
|
if (ret != 0)
|
|
break;
|
|
}
|
|
|
|
sleep(5);
|
|
|
|
ready_go_flag = true;
|
|
pthread_mutex_unlock(&ready_go_lock);
|
|
pthread_cond_broadcast(&ready_go_cond);
|
|
|
|
for (size_t i = 0; i < sizeof(tids) / sizeof(tids[0]); i++) {
|
|
if (tids[i] != 0)
|
|
pthread_join(tids[i], NULL);
|
|
}
|
|
vm_release(base_vm);
|
|
ret = EXIT_SUCCESS;
|
|
quit:
|
|
return ret;
|
|
}
|