Merge main into dev/gc

This commit is contained in:
Wenyong Huang 2021-12-07 03:53:46 +08:00
commit a7dab94e93
38 changed files with 944 additions and 210 deletions

View File

@ -29,6 +29,7 @@ iwasm VM core
- [Thread management and pthread library](./doc/pthread_library.md), ref to [sample](samples/multi-thread)
- [Linux SGX (Intel Software Guard Extension) support](./doc/linux_sgx.md)
- [Source debugging](./doc/source_debugging.md)
- [XIP (Execution In Place) support](./doc/xip.md)
### post-MVP features
- [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions)

View File

@ -61,9 +61,13 @@ if (CMAKE_SIZEOF_VOID_P EQUAL 8)
set (CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -fPIC")
endif ()
else ()
add_definitions (-m32)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32")
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32")
include(CheckCCompilerFlag)
Check_C_Compiler_Flag(-m32 M32_OK)
if (M32_OK OR WAMR_BUILD_TARGET STREQUAL "X86_32")
add_definitions (-m32)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m32")
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -m32")
endif ()
endif ()
endif ()

View File

@ -53,7 +53,8 @@ static const aot_intrinsic g_intrinsic_mapping[] = {
{ "f64_convert_i64_u", "aot_intrinsic_u64_to_f64", AOT_INTRINSIC_FLAG_U64_TO_F64 },
{ "f32_convert_i64_s", "aot_intrinsic_i64_to_f32", AOT_INTRINSIC_FLAG_I64_TO_F32 },
{ "f32_convert_i64_u", "aot_intrinsic_u64_to_f32", AOT_INTRINSIC_FLAG_U64_TO_F32 },
{ "i32_trunc_f64_u", "aot_intrinsic_f64_to_u32", AOT_INTRINSIC_FLAG_I32_TO_F64 },
{ "i32_trunc_f64_u", "aot_intrinsic_f64_to_u32", AOT_INTRINSIC_FLAG_F64_TO_U32 },
{ "i32_trunc_f64_s", "aot_intrinsic_f64_to_i32", AOT_INTRINSIC_FLAG_F64_TO_I32 },
{ "f32_demote_f64", "aot_intrinsic_f64_to_f32", AOT_INTRINSIC_FLAG_F64_TO_F32 },
{ "f64_promote_f32", "aot_intrinsic_f32_to_f64", AOT_INTRINSIC_FLAG_F32_TO_F64 },
{ "f32_cmp", "aot_intrinsic_f32_cmp", AOT_INTRINSIC_FLAG_F32_CMP },

View File

@ -90,7 +90,7 @@ static bool
check_buf(const uint8 *buf, const uint8 *buf_end, uint32 length,
char *error_buf, uint32 error_buf_size)
{
if (buf + length > buf_end) {
if (buf + length < buf || buf + length > buf_end) {
set_error_buf(error_buf, error_buf_size, "unexpect end");
return false;
}
@ -166,6 +166,7 @@ GET_U64_FROM_ADDR(uint32 *addr)
#define BIN_TYPE_ELF32B 1 /* 32-bit big endian */
#define BIN_TYPE_ELF64L 2 /* 64-bit little endian */
#define BIN_TYPE_ELF64B 3 /* 64-bit big endian */
#define BIN_TYPE_COFF32 4 /* 32-bit little endian */
#define BIN_TYPE_COFF64 6 /* 64-bit little endian */
/* Legal values for e_type (object file type). */
@ -188,7 +189,8 @@ GET_U64_FROM_ADDR(uint32 *addr)
#define E_MACHINE_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */
#define E_MACHINE_XTENSA 94 /* Tensilica Xtensa Architecture */
#define E_MACHINE_RISCV 243 /* RISC-V 32/64 */
#define E_MACHINE_WIN_X86_64 0x8664 /* Windowx x86-64 architecture */
#define E_MACHINE_WIN_I386 0x14c /* Windows i386 architecture */
#define E_MACHINE_WIN_X86_64 0x8664 /* Windows x86-64 architecture */
/* Legal values for e_version */
#define E_VERSION_CURRENT 1 /* Current version */
@ -317,6 +319,7 @@ get_aot_file_target(AOTTargetInfo *target_info, char *target_buf,
machine_type = "x86_64";
break;
case E_MACHINE_386:
case E_MACHINE_WIN_I386:
machine_type = "i386";
break;
case E_MACHINE_ARM:
@ -2194,6 +2197,8 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end,
|| !strcmp(group->section_name, ".text")
#endif
) {
#if !defined(BH_PLATFORM_LINUX) && !defined(BH_PLATFORM_LINUX_SGX) \
&& !defined(BH_PLATFORM_DARWIN)
if (module->native_symbol_count > 0) {
set_error_buf(error_buf, error_buf_size,
"cannot apply relocation to text section "
@ -2201,6 +2206,7 @@ load_relocation_section(const uint8 *buf, const uint8 *buf_end,
"\"--enable-indirect-mode\" flag");
goto fail;
}
#endif
if (!do_text_relocation(module, group, error_buf, error_buf_size))
goto fail;
}

View File

@ -99,6 +99,7 @@ typedef struct {
REG_SYM(aot_intrinsic_i64_to_f64), \
REG_SYM(aot_intrinsic_u64_to_f64), \
REG_SYM(aot_intrinsic_f64_to_f32), \
REG_SYM(aot_intrinsic_f64_to_i32), \
REG_SYM(aot_intrinsic_f64_to_u32), \
REG_SYM(aot_intrinsic_f32_to_f64), \
REG_SYM(aot_intrinsic_f32_cmp), \

View File

@ -1774,7 +1774,7 @@ aot_module_malloc(AOTModuleInstance *module_inst, uint32 size,
aot_set_exception(module_inst, "app heap corrupted");
}
else {
aot_set_exception(module_inst, "out of memory");
LOG_WARNING("warning: allocate %u bytes memory failed", size);
}
return 0;
}

View File

@ -148,9 +148,6 @@ wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst,
wasm_exec_env_destroy_internal(exec_env);
return NULL;
}
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_debug_instance_create(cluster);
#endif
#endif /* end of WASM_ENABLE_THREAD_MGR */
return exec_env;
@ -165,7 +162,6 @@ wasm_exec_env_destroy(WASMExecEnv *exec_env)
if (cluster) {
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_cluster_thread_exited(exec_env);
wasm_debug_instance_destroy(cluster);
#endif
wasm_cluster_terminate_all_except_self(cluster, exec_env);
wasm_cluster_del_exec_env(cluster, exec_env);

View File

@ -294,6 +294,84 @@ get_package_type(const uint8 *buf, uint32 size)
return Package_Type_Unknown;
}
#if WASM_ENABLE_AOT != 0
static uint8 *
align_ptr(const uint8 *p, uint32 b)
{
uintptr_t v = (uintptr_t)p;
uintptr_t m = b - 1;
return (uint8 *)((v + m) & ~m);
}
#define CHECK_BUF(buf, buf_end, length) \
do { \
if (buf + length < buf || buf + length > buf_end) \
return false; \
} while (0)
#define read_uint32(p, p_end, res) \
do { \
p = (uint8 *)align_ptr(p, sizeof(uint32)); \
CHECK_BUF(p, p_end, sizeof(uint32)); \
res = *(uint32 *)p; \
p += sizeof(uint32); \
} while (0)
bool
wasm_runtime_is_xip_file(const uint8 *buf, uint32 size)
{
const uint8 *p = buf, *p_end = buf + size;
uint32 section_type, sub_section_type, section_size;
if (get_package_type(buf, size) != Wasm_Module_AoT)
return false;
CHECK_BUF(p, p_end, 8);
p += 8;
while (p < p_end) {
read_uint32(p, p_end, section_type);
read_uint32(p, p_end, section_size);
CHECK_BUF(p, p_end, section_size);
if (section_type == AOT_SECTION_TYPE_CUSTOM) {
read_uint32(p, p_end, sub_section_type);
if (sub_section_type == AOT_CUSTOM_SECTION_NATIVE_SYMBOL) {
return true;
}
else {
p -= sizeof(uint32);
}
}
else if (section_type >= AOT_SECTION_TYPE_SIGANATURE) {
return false;
}
p += section_size;
}
return false;
}
#endif /* end of WASM_ENABLE_AOT */
#if (WASM_ENABLE_THREAD_MGR != 0) && (WASM_ENABLE_DEBUG_INTERP != 0)
uint32
wasm_runtime_start_debug_instance(WASMExecEnv *exec_env)
{
WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster);
if (cluster->debug_inst) {
LOG_WARNING("Cluster already bind to a debug instance");
return cluster->debug_inst->control_thread->port;
}
if (wasm_debug_instance_create(cluster)) {
return cluster->debug_inst->control_thread->port;
}
return 0;
}
#endif
#if WASM_ENABLE_MULTI_MODULE != 0
static module_reader reader;
static module_destroyer destroyer;
@ -1431,7 +1509,7 @@ wasm_runtime_create_exec_env_and_call_wasm(
if (module_inst->module_type == Wasm_Module_Bytecode)
ret = wasm_create_exec_env_and_call_function(
(WASMModuleInstance *)module_inst, (WASMFunctionInstance *)function,
argc, argv);
argc, argv, true);
#endif
#if WASM_ENABLE_AOT != 0
if (module_inst->module_type == Wasm_Module_AoT)

View File

@ -406,6 +406,10 @@ wasm_runtime_destroy(void);
WASM_RUNTIME_API_EXTERN PackageType
get_package_type(const uint8 *buf, uint32 size);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_is_xip_file(const uint8 *buf, uint32 size);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN WASMModuleCommon *
wasm_runtime_load(const uint8 *buf, uint32 size, char *error_buf,
@ -494,6 +498,12 @@ wasm_runtime_call_wasm_v(WASMExecEnv *exec_env,
uint32 num_results, wasm_val_t *results,
uint32 num_args, ...);
#if WASM_ENABLE_DEBUG_INTERP != 0
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN uint32
wasm_runtime_start_debug_instance(WASMExecEnv *exec_env);
#endif
/**
* Call a function reference of a given WASM runtime instance with
* arguments.

View File

@ -224,7 +224,8 @@ acquire_wait_info(void *address, bool create)
AtomicWaitInfo *wait_info = NULL;
bh_list_status ret;
wait_info = (AtomicWaitInfo *)bh_hash_map_find(wait_map, address);
if (address)
wait_info = (AtomicWaitInfo *)bh_hash_map_find(wait_map, address);
if (!create)
return wait_info;

View File

@ -2539,6 +2539,39 @@ fail:
return false;
}
/* Check whether the target supports hardware atomic instructions */
static bool
aot_require_lower_atomic_pass(AOTCompContext *comp_ctx)
{
bool ret = false;
if (!strncmp(comp_ctx->target_arch, "riscv", 5)) {
char *feature =
LLVMGetTargetMachineFeatureString(comp_ctx->target_machine);
if (feature) {
if (!strstr(feature, "+a")) {
ret = true;
}
LLVMDisposeMessage(feature);
}
}
return ret;
}
/* Check whether the target needs to expand switch to if/else */
static bool
aot_require_lower_switch_pass(AOTCompContext *comp_ctx)
{
bool ret = false;
/* IR switch/case will cause .rodata relocation on riscv */
if (!strncmp(comp_ctx->target_arch, "riscv", 5)) {
ret = true;
}
return ret;
}
bool
aot_compile_wasm(AOTCompContext *comp_ctx)
{
@ -2626,6 +2659,27 @@ aot_compile_wasm(AOTCompContext *comp_ctx)
LLVMPassManagerBuilderDispose(pass_mgr_builder);
}
if (comp_ctx->optimize && comp_ctx->is_indirect_mode) {
LLVMPassManagerRef common_pass_mgr = NULL;
if (!(common_pass_mgr = LLVMCreatePassManager())) {
aot_set_last_error("create pass manager failed");
return false;
}
aot_add_expand_memory_op_pass(common_pass_mgr);
if (aot_require_lower_atomic_pass(comp_ctx))
LLVMAddLowerAtomicPass(common_pass_mgr);
if (aot_require_lower_switch_pass(comp_ctx))
LLVMAddLowerSwitchPass(common_pass_mgr);
LLVMRunPassManager(common_pass_mgr, comp_ctx->module);
LLVMDisposePassManager(common_pass_mgr);
}
return true;
}

View File

@ -1913,7 +1913,8 @@ struct coff_hdr {
#define IMAGE_FILE_MACHINE_I386 0x014c
#define IMAGE_FILE_MACHINE_IA64 0x0200
#define AOT_COFF_BIN_TYPE 6
#define AOT_COFF32_BIN_TYPE 4 /* 32-bit little endian */
#define AOT_COFF64_BIN_TYPE 6 /* 64-bit little endian */
#define EI_NIDENT 16
@ -2029,8 +2030,11 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data)
obj_data->target_info.e_version = 1;
obj_data->target_info.e_flags = 0;
if (coff_header->u16Machine == IMAGE_FILE_MACHINE_AMD64)
obj_data->target_info.bin_type = AOT_COFF_BIN_TYPE;
if (coff_header->u16Machine == IMAGE_FILE_MACHINE_AMD64
|| coff_header->u16Machine == IMAGE_FILE_MACHINE_IA64)
obj_data->target_info.bin_type = AOT_COFF64_BIN_TYPE;
else if (coff_header->u16Machine == IMAGE_FILE_MACHINE_I386)
obj_data->target_info.bin_type = AOT_COFF32_BIN_TYPE;
}
else if (bin_type == LLVMBinaryTypeELF32L
|| bin_type == LLVMBinaryTypeELF32B) {

View File

@ -6,6 +6,7 @@
#include "aot_emit_memory.h"
#include "aot_emit_exception.h"
#include "../aot/aot_runtime.h"
#include "aot_intrinsic.h"
#define BUILD_ICMP(op, left, right, res, name) \
do { \
@ -951,13 +952,66 @@ aot_compile_op_memory_copy(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
if (!(dst_addr = check_bulk_memory_overflow(comp_ctx, func_ctx, dst, len)))
return false;
/* TODO: lookup func ptr of "memmove" to call for XIP mode */
if (comp_ctx->is_indirect_mode) {
LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type;
LLVMValueRef func, params[3];
int32 func_idx;
if (!(res = LLVMBuildMemMove(comp_ctx->builder, dst_addr, 1, src_addr, 1,
len))) {
aot_set_last_error("llvm build memmove failed.");
return false;
if (!(dst_addr =
LLVMBuildBitCast(comp_ctx->builder, dst_addr, INT32_PTR_TYPE,
"memmove dst addr cast type"))) {
aot_set_last_error("llvm cast memmove dst addr type failed.");
return false;
}
if (!(src_addr =
LLVMBuildBitCast(comp_ctx->builder, src_addr, INT32_PTR_TYPE,
"memmove src addr cast type"))) {
aot_set_last_error("llvm cast memmove src addr type failed.");
return false;
}
param_types[0] = INT32_PTR_TYPE;
param_types[1] = INT32_PTR_TYPE;
param_types[2] = I32_TYPE;
ret_type = INT32_PTR_TYPE;
if (!(func_type = LLVMFunctionType(ret_type, param_types, 3, false))) {
aot_set_last_error("create LLVM function type failed.");
return false;
}
if (!(func_ptr_type = LLVMPointerType(func_type, 0))) {
aot_set_last_error("create LLVM function pointer type failed.");
return false;
}
func_idx = aot_get_native_symbol_index(comp_ctx, "memmove");
if (func_idx < 0) {
return false;
}
if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->native_symbol,
func_ptr_type, func_idx))) {
return false;
}
params[0] = dst_addr;
params[1] = src_addr;
params[2] = len;
if (!(res = LLVMBuildCall(comp_ctx->builder, func, params, 3,
"call memmove"))) {
aot_set_last_error("llvm build memmove failed.");
return false;
}
}
else {
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;
@ -981,11 +1035,57 @@ aot_compile_op_memory_fill(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
return false;
}
/* TODO: lookup func ptr of "memset" to call for XIP mode */
if (comp_ctx->is_indirect_mode) {
LLVMTypeRef param_types[3], ret_type, func_type, func_ptr_type;
LLVMValueRef func, params[3];
int32 func_idx;
if (!(res = LLVMBuildMemSet(comp_ctx->builder, dst_addr, val, len, 1))) {
aot_set_last_error("llvm build memset failed.");
return false;
if (!(dst_addr =
LLVMBuildBitCast(comp_ctx->builder, dst_addr, INT32_PTR_TYPE,
"memset dst addr cast type"))) {
aot_set_last_error("llvm cast memset dst addr type failed.");
return false;
}
param_types[0] = INT32_PTR_TYPE;
param_types[1] = INT8_TYPE;
param_types[2] = I32_TYPE;
ret_type = INT32_PTR_TYPE;
if (!(func_type = LLVMFunctionType(ret_type, param_types, 3, false))) {
aot_set_last_error("create LLVM function type failed.");
return false;
}
if (!(func_ptr_type = LLVMPointerType(func_type, 0))) {
aot_set_last_error("create LLVM function pointer type failed.");
return false;
}
func_idx = aot_get_native_symbol_index(comp_ctx, "memset");
if (func_idx < 0) {
return false;
}
if (!(func = aot_get_func_from_table(comp_ctx, func_ctx->native_symbol,
func_ptr_type, func_idx))) {
return false;
}
params[0] = dst_addr;
params[1] = val;
params[2] = len;
if (!(res = LLVMBuildCall(comp_ctx->builder, func, params, 3,
"call memset"))) {
aot_set_last_error("llvm build memset failed.");
return false;
}
}
else {
if (!(res =
LLVMBuildMemSet(comp_ctx->builder, dst_addr, val, len, 1))) {
aot_set_last_error("llvm build memset failed.");
return false;
}
}
return true;
fail:
@ -1127,16 +1227,20 @@ aot_compile_op_atomic_cmpxchg(AOTCompContext *comp_ctx,
}
if (op_type == VALUE_TYPE_I32) {
if (!(result = LLVMBuildZExt(comp_ctx->builder, result, I32_TYPE,
"result_i32"))) {
goto fail;
if (LLVMTypeOf(result) != I32_TYPE) {
if (!(result = LLVMBuildZExt(comp_ctx->builder, result, I32_TYPE,
"result_i32"))) {
goto fail;
}
}
PUSH_I32(result);
}
else {
if (!(result = LLVMBuildZExt(comp_ctx->builder, result, I64_TYPE,
"result_i64"))) {
goto fail;
if (LLVMTypeOf(result) != I64_TYPE) {
if (!(result = LLVMBuildZExt(comp_ctx->builder, result, I64_TYPE,
"result_i64"))) {
goto fail;
}
}
PUSH_I64(result);
}

View File

@ -445,6 +445,9 @@ aot_get_func_from_table(const AOTCompContext *comp_ctx, LLVMValueRef base,
bool
aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str);
void
aot_add_expand_memory_op_pass(LLVMPassManagerRef pass);
#if WASM_ENABLE_LAZY_JIT != 0
void
aot_handle_llvm_errmsg(char *error_buf, uint32 error_buf_size,

View File

@ -6,20 +6,29 @@
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/Twine.h>
#include <llvm/ADT/Triple.h>
#include <llvm/Analysis/TargetTransformInfo.h>
#include <llvm/CodeGen/TargetPassConfig.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/MC/MCSubtargetInfo.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm-c/Core.h>
#include <llvm-c/ExecutionEngine.h>
#include <llvm-c/Initialization.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/JITEventListener.h>
#include <llvm/ExecutionEngine/RTDyldMemoryManager.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Target/CodeGenCWrappers.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/Transforms/Utils/LowerMemIntrinsics.h>
#include <cstring>
using namespace llvm;
@ -33,6 +42,9 @@ WAMRCreateMCJITCompilerForModule(LLVMExecutionEngineRef *OutJIT,
extern "C" bool
aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str);
extern "C" void
aot_add_expand_memory_op_pass(LLVMPassManagerRef pass);
extern "C" void
aot_func_disable_tce(LLVMValueRef func);
@ -106,6 +118,109 @@ WAMRCreateMCJITCompilerForModule(LLVMExecutionEngineRef *OutJIT,
return 1;
}
class ExpandMemoryOpPass : public llvm::ModulePass
{
public:
static char ID;
ExpandMemoryOpPass()
: ModulePass(ID)
{}
bool runOnModule(Module &M) override;
bool expandMemIntrinsicUses(Function &F);
StringRef getPassName() const override
{
return "Expand memory operation intrinsics";
}
void getAnalysisUsage(AnalysisUsage &AU) const override
{
AU.addRequired<TargetTransformInfoWrapperPass>();
}
};
char ExpandMemoryOpPass::ID = 0;
bool
ExpandMemoryOpPass::expandMemIntrinsicUses(Function &F)
{
Intrinsic::ID ID = F.getIntrinsicID();
bool Changed = false;
for (auto I = F.user_begin(), E = F.user_end(); I != E;) {
Instruction *Inst = cast<Instruction>(*I);
++I;
switch (ID) {
case Intrinsic::memcpy:
{
auto *Memcpy = cast<MemCpyInst>(Inst);
Function *ParentFunc = Memcpy->getParent()->getParent();
const TargetTransformInfo &TTI =
getAnalysis<TargetTransformInfoWrapperPass>().getTTI(
*ParentFunc);
expandMemCpyAsLoop(Memcpy, TTI);
Changed = true;
Memcpy->eraseFromParent();
break;
}
case Intrinsic::memmove:
{
auto *Memmove = cast<MemMoveInst>(Inst);
expandMemMoveAsLoop(Memmove);
Changed = true;
Memmove->eraseFromParent();
break;
}
case Intrinsic::memset:
{
auto *Memset = cast<MemSetInst>(Inst);
expandMemSetAsLoop(Memset);
Changed = true;
Memset->eraseFromParent();
break;
}
default:
break;
}
}
return Changed;
}
bool
ExpandMemoryOpPass::runOnModule(Module &M)
{
bool Changed = false;
for (Function &F : M) {
if (!F.isDeclaration())
continue;
switch (F.getIntrinsicID()) {
case Intrinsic::memcpy:
case Intrinsic::memmove:
case Intrinsic::memset:
if (expandMemIntrinsicUses(F))
Changed = true;
break;
default:
break;
}
}
return Changed;
}
void
aot_add_expand_memory_op_pass(LLVMPassManagerRef pass)
{
unwrap(pass)->add(new ExpandMemoryOpPass());
}
bool
aot_check_simd_compatibility(const char *arch_c_str, const char *cpu_c_str)
{

View File

@ -16,3 +16,11 @@ endif()
set (IWASM_COMPL_SOURCE ${source_all})
# Disalbe rtti to works with LLVM
if (MSVC)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
else()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif()

View File

@ -235,6 +235,17 @@ wasm_runtime_free(void *ptr);
WASM_RUNTIME_API_EXTERN package_type_t
get_package_type(const uint8_t *buf, uint32_t size);
/**
* Check whether a file is an AOT XIP (Execution In Place) file
*
* @param buf the package buffer
* @param size the package buffer size
*
* @return true if success, false otherwise
*/
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_is_xip_file(const uint8_t *buf, uint32_t size);
/**
* It is a callback for WAMR providing by embedding to load a module file
* into a buffer
@ -408,6 +419,26 @@ wasm_runtime_create_exec_env(wasm_module_inst_t module_inst,
WASM_RUNTIME_API_EXTERN void
wasm_runtime_destroy_exec_env(wasm_exec_env_t exec_env);
/**
* Start debug instance based on given execution environment.
* Note:
* The debug instance will be destroyed during destroying the
* execution environment, developers don't need to destroy it
* manually.
* If the cluster of this execution environment has already
* been bound to a debug instance, this function will return true
* directly.
* If developer spawns some exec_env by wasm_runtime_spawn_exec_env,
* don't need to call this function for every spawned exec_env as
* they are sharing the same cluster with the main exec_env.
*
* @param exec_env the execution environment to start debug instance
*
* @return debug port if success, 0 otherwise.
*/
WASM_RUNTIME_API_EXTERN uint32_t
wasm_runtime_start_debug_instance(wasm_exec_env_t exec_env);
/**
* Initialize thread environment.
* Note:

View File

@ -695,6 +695,20 @@ struct WASMModule {
HashMap *ref_type_set;
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
/**
* Count how many instances reference this module. When source
* debugging feature enabled, the debugger may modify the code
* section of the module, so we need to report a warning if user
* create several instances based on the same module
*
* Sub_instances created by lib-pthread or spawn API will not
* influence or check the ref count
*/
uint32 ref_count;
korp_mutex ref_count_lock;
#endif
#if WASM_ENABLE_CUSTOM_NAME_SECTION != 0
const uint8 *name_section_buf;
const uint8 *name_section_buf_end;

View File

@ -55,7 +55,7 @@ static bool
check_buf(const uint8 *buf, const uint8 *buf_end, uint32 length,
char *error_buf, uint32 error_buf_size)
{
if (buf + length > buf_end) {
if (buf + length < buf || buf + length > buf_end) {
set_error_buf(error_buf, error_buf_size,
"unexpected end of section or function");
return false;
@ -67,7 +67,7 @@ static bool
check_buf1(const uint8 *buf, const uint8 *buf_end, uint32 length,
char *error_buf, uint32 error_buf_size)
{
if (buf + length > buf_end) {
if (buf + length < buf || buf + length > buf_end) {
set_error_buf(error_buf, error_buf_size, "unexpected end");
return false;
}
@ -1747,7 +1747,6 @@ load_function_import(const uint8 **p_buf, const uint8 *buf_end,
bool linked_call_conv_raw = false;
bool is_native_symbol = false;
CHECK_BUF(p, p_end, 1);
read_leb_uint32(p, p_end, declare_type_index);
*p_buf = p;
@ -3901,6 +3900,18 @@ load_from_sections(WASMModule *module, WASMSection *sections,
break;
}
}
if (!aux_stack_top_global) {
/* Auxiliary stack global isn't found, it must be unused
in the wasm app, as if it is used, the global must be
defined. Here we set it to __heap_base global and set
its size to 0. */
aux_stack_top_global = aux_heap_base_global;
aux_stack_top = aux_heap_base;
module->aux_stack_top_global_index =
module->aux_heap_base_global_index;
module->aux_stack_bottom = aux_stack_top;
module->aux_stack_size = 0;
}
break;
}
}
@ -3945,7 +3956,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
export->name, export->index);
/* resolve retain function.
If not find, reset malloc function index */
If not found, reset malloc function index */
export_tmp = module->exports;
for (j = 0; j < module->export_count; j++, export_tmp++) {
if ((export_tmp->kind == EXPORT_KIND_FUNC)
@ -4096,6 +4107,10 @@ create_module(char *error_buf, uint32 error_buf_size)
#if WASM_ENABLE_DEBUG_INTERP != 0
bh_list_init(&module->fast_opcode_list);
if (os_mutex_init(&module->ref_count_lock) != 0) {
wasm_runtime_free(module);
return NULL;
}
#endif
return module;
}
@ -4205,7 +4220,6 @@ create_sections(const uint8 *buf, uint32 size, WASMSection **p_section_list,
}
last_section_index = section_index;
}
CHECK_BUF1(p, p_end, 1);
read_leb_uint32(p, p_end, section_size);
CHECK_BUF1(p, p_end, section_size);
@ -4439,6 +4453,7 @@ wasm_loader_unload(WASMModule *module)
wasm_runtime_free(fast_opcode);
fast_opcode = next;
}
os_mutex_destroy(&module->ref_count_lock);
#endif
wasm_runtime_free(module);

View File

@ -25,14 +25,14 @@ set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
string);
}
#define CHECK_BUF(buf, buf_end, length) \
do { \
bh_assert(buf + length <= buf_end); \
#define CHECK_BUF(buf, buf_end, length) \
do { \
bh_assert(buf + length >= buf && buf + length <= buf_end); \
} while (0)
#define CHECK_BUF1(buf, buf_end, length) \
do { \
bh_assert(buf + length <= buf_end); \
#define CHECK_BUF1(buf, buf_end, length) \
do { \
bh_assert(buf + length >= buf && buf + length <= buf_end); \
} while (0)
#define skip_leb(p) while (*p++ & 0x80)
@ -45,7 +45,7 @@ is_32bit_type(uint8 type)
{
if (type == VALUE_TYPE_I32 || type == VALUE_TYPE_F32
#if WASM_ENABLE_REF_TYPES != 0
|| type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF)
|| type == VALUE_TYPE_FUNCREF || type == VALUE_TYPE_EXTERNREF
#endif
)
return true;
@ -412,7 +412,6 @@ load_function_import(const uint8 **p_buf, const uint8 *buf_end,
void *linked_attachment = NULL;
bool linked_call_conv_raw = false;
CHECK_BUF(p, p_end, 1);
read_leb_uint32(p, p_end, declare_type_index);
*p_buf = p;
@ -1962,6 +1961,18 @@ load_from_sections(WASMModule *module, WASMSection *sections,
break;
}
}
if (!aux_stack_top_global) {
/* Auxiliary stack global isn't found, it must be unused
in the wasm app, as if it is used, the global must be
defined. Here we set it to __heap_base global and set
its size to 0. */
aux_stack_top_global = aux_heap_base_global;
aux_stack_top = aux_heap_base;
module->aux_stack_top_global_index =
module->aux_heap_base_global_index;
module->aux_stack_bottom = aux_stack_top;
module->aux_stack_size = 0;
}
break;
}
}
@ -2005,7 +2016,7 @@ load_from_sections(WASMModule *module, WASMSection *sections,
export->name, export->index);
/* resolve retain function.
If not find, reset malloc function index */
If not found, reset malloc function index */
export_tmp = module->exports;
for (j = 0; j < module->export_count; j++, export_tmp++) {
if ((export_tmp->kind == EXPORT_KIND_FUNC)
@ -2234,7 +2245,6 @@ create_sections(const uint8 *buf, uint32 size, WASMSection **p_section_list,
|| last_section_index < section_index);
last_section_index = section_index;
}
CHECK_BUF1(p, p_end, 1);
read_leb_uint32(p, p_end, section_size);
CHECK_BUF1(p, p_end, section_size);

View File

@ -900,7 +900,7 @@ execute_post_inst_function(WASMModuleInstance *module_inst)
return true;
return wasm_create_exec_env_and_call_function(module_inst, post_inst_func,
0, NULL);
0, NULL, false);
}
#if WASM_ENABLE_BULK_MEMORY != 0
@ -929,7 +929,7 @@ execute_memory_init_function(WASMModuleInstance *module_inst)
return true;
return wasm_create_exec_env_and_call_function(module_inst, memory_init_func,
0, NULL);
0, NULL, false);
}
#endif
@ -944,7 +944,8 @@ execute_start_function(WASMModuleInstance *module_inst)
bh_assert(!func->is_import_func && func->param_cell_num == 0
&& func->ret_cell_num == 0);
return wasm_create_exec_env_and_call_function(module_inst, func, 0, NULL);
return wasm_create_exec_env_and_call_function(module_inst, func, 0, NULL,
false);
}
static bool
@ -972,11 +973,11 @@ execute_malloc_function(WASMModuleInstance *module_inst,
}
ret = wasm_create_exec_env_and_call_function(module_inst, malloc_func, argc,
argv);
argv, false);
if (retain_func && ret) {
ret = wasm_create_exec_env_and_call_function(module_inst, retain_func,
1, argv);
1, argv, false);
}
if (ret)
@ -992,7 +993,7 @@ execute_free_function(WASMModuleInstance *module_inst,
argv[0] = offset;
return wasm_create_exec_env_and_call_function(module_inst, free_func, 1,
argv);
argv, false);
}
#if WASM_ENABLE_MULTI_MODULE != 0
@ -1125,6 +1126,19 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size,
if (!module)
return NULL;
#if WASM_ENABLE_DEBUG_INTERP != 0
if (!is_sub_inst) {
os_mutex_lock(&module->ref_count_lock);
if (module->ref_count != 0) {
LOG_WARNING(
"warning: multiple instances referencing the same module may "
"cause unexpected behaviour during debugging");
}
module->ref_count++;
os_mutex_unlock(&module->ref_count_lock);
}
#endif
/* Check heap size */
heap_size = align_uint(heap_size, 8);
if (heap_size > APP_HEAP_SIZE_MAX)
@ -1133,6 +1147,13 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size,
/* Allocate the memory */
if (!(module_inst = runtime_malloc(sizeof(WASMModuleInstance), error_buf,
error_buf_size))) {
#if WASM_ENABLE_DEBUG_INTERP != 0
if (!is_sub_inst) {
os_mutex_lock(&module->ref_count_lock);
module->ref_count--;
os_mutex_unlock(&module->ref_count_lock);
}
#endif
return NULL;
}
@ -1525,7 +1546,9 @@ wasm_instantiate(WASMModule *module, bool is_sub_inst, uint32 stack_size,
(WASMModuleInstanceCommon *)module_inst);
#endif
(void)global_data_end;
return module_inst;
fail:
wasm_deinstantiate(module_inst, false);
return NULL;
@ -1582,6 +1605,14 @@ wasm_deinstantiate(WASMModuleInstance *module_inst, bool is_sub_inst)
}
#endif
#if WASM_ENABLE_DEBUG_INTERP != 0
if (!is_sub_inst) {
os_mutex_lock(&module_inst->module->ref_count_lock);
module_inst->module->ref_count--;
os_mutex_unlock(&module_inst->module->ref_count_lock);
}
#endif
wasm_runtime_free(module_inst);
}
@ -1667,7 +1698,8 @@ wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function,
bool
wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst,
WASMFunctionInstance *func,
unsigned argc, uint32 argv[])
unsigned argc, uint32 argv[],
bool enable_debug)
{
WASMExecEnv *exec_env;
bool ret;
@ -1686,6 +1718,11 @@ wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst,
}
#if WASM_ENABLE_THREAD_MGR != 0
if (enable_debug) {
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_runtime_start_debug_instance(exec_env);
#endif
}
}
#endif
@ -1836,7 +1873,7 @@ wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size,
wasm_set_exception(module_inst, "app heap corrupted");
}
else {
wasm_set_exception(module_inst, "out of memory");
LOG_WARNING("warning: allocate %u bytes memory failed", size);
}
return 0;
}

View File

@ -320,7 +320,8 @@ wasm_call_function(WASMExecEnv *exec_env, WASMFunctionInstance *function,
bool
wasm_create_exec_env_and_call_function(WASMModuleInstance *module_inst,
WASMFunctionInstance *function,
unsigned argc, uint32 argv[]);
unsigned argc, uint32 argv[],
bool enable_debug);
bool
wasm_create_exec_env_singleton(WASMModuleInstance *module_inst);

View File

@ -7,7 +7,7 @@
#include "bh_log.h"
#include "gdbserver.h"
#include "platform_api_extension.h"
#include "bh_platform.h"
#include "wasm_interp.h"
#include "wasm_opcode.h"
#include "wasm_runtime.h"
@ -54,20 +54,11 @@ control_thread_routine(void *arg)
{
WASMDebugInstance *debug_inst = (WASMDebugInstance *)arg;
WASMDebugControlThread *control_thread = NULL;
WASMCluster *cluster = NULL;
WASMExecEnv *exec_env;
bh_assert(debug_inst);
control_thread = debug_inst->control_thread;
bh_assert(control_thread);
cluster = debug_inst->cluster;
bh_assert(cluster);
exec_env = bh_list_first_elem(&cluster->exec_env_list);
bh_assert(exec_env);
os_mutex_lock(&exec_env->wait_lock);
os_mutex_lock(&debug_inst->wait_lock);
control_thread->status = RUNNING;
@ -84,19 +75,31 @@ control_thread_routine(void *arg)
LOG_WARNING("control thread of debug object %p start\n", debug_inst);
control_thread->server =
wasm_launch_gdbserver(control_thread->ip_addr, control_thread->port);
wasm_create_gdbserver(control_thread->ip_addr, &control_thread->port);
if (!control_thread->server) {
LOG_ERROR("Failed to create debug server\n");
os_cond_signal(&exec_env->wait_cond);
os_mutex_unlock(&exec_env->wait_lock);
os_cond_signal(&debug_inst->wait_cond);
os_mutex_unlock(&debug_inst->wait_lock);
return NULL;
}
control_thread->server->thread = control_thread;
/* control thread ready, notify main thread */
os_cond_signal(&exec_env->wait_cond);
os_mutex_unlock(&exec_env->wait_lock);
/*
* wasm gdbserver created, the execution thread
* doesn't need to wait for the debugger connection,
* so we wake up the execution thread before listen
*/
os_cond_signal(&debug_inst->wait_cond);
os_mutex_unlock(&debug_inst->wait_lock);
/* wait lldb client to connect */
if (!wasm_gdbserver_listen(control_thread->server)) {
LOG_ERROR("Failed while connecting debugger\n");
wasm_runtime_free(control_thread->server);
return NULL;
}
while (true) {
os_mutex_lock(&control_thread->wait_lock);
@ -112,7 +115,7 @@ control_thread_routine(void *arg)
os_mutex_unlock(&control_thread->wait_lock);
}
LOG_VERBOSE("control thread of debug object %p stop\n", debug_inst);
LOG_VERBOSE("control thread of debug object [%p] stopped\n", debug_inst);
return NULL;
}
@ -120,12 +123,6 @@ static WASMDebugControlThread *
wasm_debug_control_thread_create(WASMDebugInstance *debug_instance)
{
WASMDebugControlThread *control_thread;
WASMCluster *cluster = debug_instance->cluster;
WASMExecEnv *exec_env;
bh_assert(cluster);
exec_env = bh_list_first_elem(&cluster->exec_env_list);
bh_assert(exec_env);
if (!(control_thread =
wasm_runtime_malloc(sizeof(WASMDebugControlThread)))) {
@ -139,18 +136,18 @@ wasm_debug_control_thread_create(WASMDebugInstance *debug_instance)
debug_instance->control_thread = control_thread;
os_mutex_lock(&exec_env->wait_lock);
os_mutex_lock(&debug_instance->wait_lock);
if (0
!= os_thread_create(&control_thread->tid, control_thread_routine,
debug_instance, APP_THREAD_STACK_SIZE_MAX)) {
os_mutex_unlock(&control_thread->wait_lock);
os_mutex_unlock(&debug_instance->wait_lock);
goto fail1;
}
/* wait until the debug control thread ready */
os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock);
os_mutex_unlock(&exec_env->wait_lock);
os_cond_wait(&debug_instance->wait_cond, &debug_instance->wait_lock);
os_mutex_unlock(&debug_instance->wait_lock);
if (!control_thread->server)
goto fail1;
@ -174,7 +171,8 @@ static void
wasm_debug_control_thread_destroy(WASMDebugInstance *debug_instance)
{
WASMDebugControlThread *control_thread = debug_instance->control_thread;
LOG_VERBOSE("control thread of debug object %p stop\n", debug_instance);
LOG_VERBOSE("stopping control thread of debug object [%p]\n",
debug_instance);
control_thread->status = STOPPED;
os_mutex_lock(&control_thread->wait_lock);
wasm_close_gdbserver(control_thread->server);
@ -286,6 +284,15 @@ wasm_debug_instance_create(WASMCluster *cluster)
return NULL;
}
memset(instance, 0, sizeof(WASMDebugInstance));
if (os_mutex_init(&instance->wait_lock) != 0) {
goto fail1;
}
if (os_cond_init(&instance->wait_cond) != 0) {
goto fail2;
}
bh_list_init(&instance->break_point_list);
instance->cluster = cluster;
@ -297,29 +304,21 @@ wasm_debug_instance_create(WASMCluster *cluster)
if (!wasm_debug_control_thread_create(instance)) {
LOG_ERROR("WASM Debug Engine error: failed to create control thread");
wasm_runtime_free(instance);
return NULL;
goto fail3;
}
return instance;
}
static WASMDebugInstance *
wasm_cluster_get_debug_instance(WASMDebugEngine *engine, WASMCluster *cluster)
{
WASMDebugInstance *instance;
os_mutex_lock(&g_debug_engine->instance_list_lock);
instance = bh_list_first_elem(&engine->debug_instance_list);
while (instance) {
if (instance->cluster == cluster) {
os_mutex_unlock(&g_debug_engine->instance_list_lock);
return instance;
}
instance = bh_list_elem_next(instance);
}
os_mutex_unlock(&g_debug_engine->instance_list_lock);
wasm_cluster_set_debug_inst(cluster, instance);
return instance;
fail3:
os_cond_destroy(&instance->wait_cond);
fail2:
os_mutex_destroy(&instance->wait_lock);
fail1:
wasm_runtime_free(instance);
return NULL;
}
static void
@ -347,7 +346,7 @@ wasm_debug_instance_destroy(WASMCluster *cluster)
return;
}
instance = wasm_cluster_get_debug_instance(g_debug_engine, cluster);
instance = cluster->debug_inst;
if (instance) {
/* destroy control thread */
wasm_debug_control_thread_destroy(instance);
@ -359,7 +358,11 @@ wasm_debug_instance_destroy(WASMCluster *cluster)
/* destroy all breakpoints */
wasm_debug_instance_destroy_breakpoints(instance);
os_mutex_destroy(&instance->wait_lock);
os_cond_destroy(&instance->wait_cond);
wasm_runtime_free(instance);
cluster->debug_inst = NULL;
}
}
@ -434,47 +437,78 @@ wasm_debug_instance_get_tids(WASMDebugInstance *instance, uint64 tids[],
int len)
{
WASMExecEnv *exec_env;
int i = 0;
int i = 0, threads_num = 0;
if (!instance)
return 0;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env && i < len) {
tids[i++] = exec_env->handle;
/* Some threads may not be ready */
if (exec_env->handle != 0) {
tids[i++] = exec_env->handle;
threads_num++;
}
exec_env = bh_list_elem_next(exec_env);
}
LOG_VERBOSE("find %d tids\n", i);
return i;
LOG_VERBOSE("find %d tids\n", threads_num);
return threads_num;
}
static WASMExecEnv *
get_stopped_thread(WASMCluster *cluster)
{
WASMExecEnv *exec_env;
exec_env = bh_list_first_elem(&cluster->exec_env_list);
while (exec_env) {
if (exec_env->current_status->running_status != STATUS_RUNNING) {
return exec_env;
}
exec_env = bh_list_elem_next(exec_env);
}
return NULL;
}
uint64
wasm_debug_instance_wait_thread(WASMDebugInstance *instance, uint64 tid,
uint32 *status)
{
WASMExecEnv *exec_env;
WASMExecEnv *last_exec_env = NULL;
WASMExecEnv *exec_env = NULL;
os_mutex_lock(&instance->wait_lock);
while ((instance->cluster->exec_env_list.len != 0)
&& ((exec_env = get_stopped_thread(instance->cluster)) == NULL)) {
os_cond_wait(&instance->wait_cond, &instance->wait_lock);
}
os_mutex_unlock(&instance->wait_lock);
/* If cluster has no exec_env, then this whole cluster is exiting */
if (instance->cluster->exec_env_list.len == 0) {
*status = 0;
return 0;
}
instance->current_tid = exec_env->handle;
*status = exec_env->current_status->signal_flag;
return exec_env->handle;
}
uint32
wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, uint64 tid)
{
WASMExecEnv *exec_env = NULL;
exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
while (exec_env) {
last_exec_env = exec_env;
if (instance->current_tid != 0
&& last_exec_env->handle == instance->current_tid) {
break;
if (exec_env->handle == tid) {
return exec_env->current_status->signal_flag;
}
exec_env = bh_list_elem_next(exec_env);
}
if (last_exec_env) {
wasm_cluster_wait_thread_status(last_exec_env, status);
if (instance->current_tid == 0)
instance->current_tid = last_exec_env->handle;
return last_exec_env->handle;
}
else {
*status = ~0;
return 0;
}
return 0;
}
void

View File

@ -42,6 +42,8 @@ typedef struct WASMDebugInstance {
WASMCluster *cluster;
uint32 id;
korp_tid current_tid;
korp_mutex wait_lock;
korp_cond wait_cond;
} WASMDebugInstance;
typedef enum WASMDebugEventKind {
@ -160,6 +162,9 @@ uint64
wasm_debug_instance_wait_thread(WASMDebugInstance *instance, uint64 tid,
uint32 *status);
uint32
wasm_debug_instance_get_thread_status(WASMDebugInstance *instance, uint64 tid);
bool
wasm_debug_instance_singlestep(WASMDebugInstance *instance, uint64 tid);

View File

@ -51,17 +51,18 @@ static struct packet_handler_elem packet_handler_table[255] = {
};
WASMGDBServer *
wasm_launch_gdbserver(char *host, int port)
wasm_create_gdbserver(char *host, int *port)
{
int listen_fd = -1;
const int one = 1;
struct sockaddr_in addr;
socklen_t socklen;
int ret;
int sockt_fd = 0;
WASMGDBServer *server;
bh_assert(port);
if (!(server = wasm_runtime_malloc(sizeof(WASMGDBServer)))) {
LOG_ERROR("wasm gdb server error: failed to allocate memory");
return NULL;
@ -90,7 +91,7 @@ wasm_launch_gdbserver(char *host, int port)
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(host);
addr.sin_port = htons(port);
addr.sin_port = htons(*port);
ret = bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
@ -103,25 +104,12 @@ wasm_launch_gdbserver(char *host, int port)
LOG_ERROR("%s", strerror(errno));
goto fail;
}
LOG_WARNING("Debug server listening on %s:%d\n", host,
ntohs(addr.sin_port));
LOG_WARNING("Listening on %s:%d\n", host, ntohs(addr.sin_port));
ret = listen(listen_fd, 1);
if (ret < 0) {
LOG_ERROR("wasm gdb server error: listen() failed");
goto fail;
}
*port = ntohs(addr.sin_port);
server->listen_fd = listen_fd;
sockt_fd = accept(listen_fd, NULL, NULL);
if (sockt_fd < 0) {
LOG_ERROR("wasm gdb server error: accept() failed");
goto fail;
}
LOG_VERBOSE("accept gdb client");
server->socket_fd = sockt_fd;
server->noack = false;
return server;
fail:
@ -134,6 +122,35 @@ fail:
return NULL;
}
bool
wasm_gdbserver_listen(WASMGDBServer *server)
{
int ret;
int sockt_fd = 0;
ret = listen(server->listen_fd, 1);
if (ret < 0) {
LOG_ERROR("wasm gdb server error: listen() failed");
goto fail;
}
sockt_fd = accept(server->listen_fd, NULL, NULL);
if (sockt_fd < 0) {
LOG_ERROR("wasm gdb server error: accept() failed");
goto fail;
}
LOG_VERBOSE("accept gdb client");
server->socket_fd = sockt_fd;
server->noack = false;
return true;
fail:
shutdown(server->listen_fd, SHUT_RDWR);
close(server->listen_fd);
return false;
}
void
wasm_close_gdbserver(WASMGDBServer *server)
{

View File

@ -33,7 +33,10 @@ typedef struct WASMGDBServer {
} WASMGDBServer;
WASMGDBServer *
wasm_launch_gdbserver(char *addr, int port);
wasm_create_gdbserver(char *addr, int *port);
bool
wasm_gdbserver_listen(WASMGDBServer *server);
void
wasm_close_gdbserver(WASMGDBServer *server);

View File

@ -17,6 +17,9 @@
#define MAX_PACKET_SIZE (0x20000)
static char tmpbuf[MAX_PACKET_SIZE];
static void
send_thread_stop_status(WASMGDBServer *server, uint32_t status, uint64_t tid);
void
handle_generay_set(WASMGDBServer *server, char *payload)
{
@ -261,6 +264,20 @@ handle_generay_query(WASMGDBServer *server, char *payload)
if (args && (!strcmp(name, "WasmGlobal"))) {
porcess_wasm_global(server, args);
}
if (!strcmp(name, "Offsets")) {
write_packet(server, "");
}
if (!strncmp(name, "ThreadStopInfo", strlen("ThreadStopInfo"))) {
int32 prefix_len = strlen("ThreadStopInfo");
uint64 tid = strtol(name + prefix_len, NULL, 16);
uint32 status = wasm_debug_instance_get_thread_status(
server->thread->debug_instance, tid);
send_thread_stop_status(server, status, tid);
}
}
static void
@ -332,19 +349,32 @@ handle_v_packet(WASMGDBServer *server, char *payload)
write_packet(server, "vCont;c;C;s;S;");
if (!strcmp("Cont", name)) {
if (args && args[0] == 's') {
char *numstring = strchr(args, ':');
if (numstring) {
*numstring++ = '\0';
uint64_t tid = strtol(numstring, NULL, 16);
wasm_debug_instance_set_cur_thread(
(WASMDebugInstance *)server->thread->debug_instance, tid);
wasm_debug_instance_singlestep(
(WASMDebugInstance *)server->thread->debug_instance, tid);
tid = wasm_debug_instance_wait_thread(
(WASMDebugInstance *)server->thread->debug_instance, tid,
&status);
send_thread_stop_status(server, status, tid);
if (args) {
if (args[0] == 's' || args[0] == 'c') {
char *numstring = strchr(args, ':');
if (numstring) {
*numstring++ = '\0';
uint64_t tid = strtol(numstring, NULL, 16);
wasm_debug_instance_set_cur_thread(
(WASMDebugInstance *)server->thread->debug_instance,
tid);
if (args[0] == 's') {
wasm_debug_instance_singlestep(
(WASMDebugInstance *)server->thread->debug_instance,
tid);
}
else {
wasm_debug_instance_continue(
(WASMDebugInstance *)
server->thread->debug_instance);
}
tid = wasm_debug_instance_wait_thread(
(WASMDebugInstance *)server->thread->debug_instance,
tid, &status);
send_thread_stop_status(server, status, tid);
}
}
}
}

View File

@ -332,7 +332,7 @@ create_cluster_info(WASMCluster *cluster)
if (!(node = wasm_runtime_malloc(sizeof(ClusterInfoNode)))) {
return NULL;
}
memset(node, 0, sizeof(WASMCluster));
memset(node, 0, sizeof(ClusterInfoNode));
node->thread_list = &node->thread_list_head;
ret = bh_list_init(node->thread_list);

View File

@ -5,6 +5,10 @@
#include "thread_manager.h"
#if WASM_ENABLE_DEBUG_INTERP != 0
#include "debug_engine.h"
#endif
typedef struct {
bh_list_link l;
void (*destroy_cb)(WASMCluster *);
@ -227,6 +231,11 @@ wasm_cluster_destroy(WASMCluster *cluster)
wasm_runtime_free(cluster->stack_tops);
if (cluster->stack_segment_occupied)
wasm_runtime_free(cluster->stack_segment_occupied);
#if WASM_ENABLE_DEBUG_INTERP != 0
wasm_debug_instance_destroy(cluster);
#endif
wasm_runtime_free(cluster);
}
@ -488,31 +497,18 @@ wasm_cluster_create_exenv_status()
WASMCurrentEnvStatus *status;
if (!(status = wasm_runtime_malloc(sizeof(WASMCurrentEnvStatus)))) {
goto fail;
return NULL;
}
if (os_mutex_init(&status->wait_lock) != 0)
goto fail1;
if (os_cond_init(&status->wait_cond) != 0)
goto fail2;
status->step_count = 0;
status->signal_flag = 0;
status->running_status = 0;
return status;
fail2:
os_mutex_destroy(&status->wait_lock);
fail1:
wasm_runtime_free(status);
fail:
return NULL;
}
void
wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status)
{
os_mutex_destroy(&status->wait_lock);
os_cond_destroy(&status->wait_cond);
wasm_runtime_free(status);
}
@ -529,29 +525,34 @@ wasm_cluster_clear_thread_signal(WASMExecEnv *exec_env)
exec_env->current_status->signal_flag = 0;
}
void
wasm_cluster_wait_thread_status(WASMExecEnv *exec_env, uint32 *status)
{
os_mutex_lock(&exec_env->current_status->wait_lock);
while (wasm_cluster_thread_is_running(exec_env)) {
os_cond_wait(&exec_env->current_status->wait_cond,
&exec_env->current_status->wait_lock);
}
*status = exec_env->current_status->signal_flag;
os_mutex_unlock(&exec_env->current_status->wait_lock);
}
void
wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo)
{
exec_env->current_status->signal_flag = signo;
}
static void
notify_debug_instance(WASMExecEnv *exec_env)
{
WASMCluster *cluster;
cluster = wasm_exec_env_get_cluster(exec_env);
bh_assert(cluster);
if (!cluster->debug_inst) {
return;
}
os_mutex_lock(&cluster->debug_inst->wait_lock);
os_cond_signal(&cluster->debug_inst->wait_cond);
os_mutex_unlock(&cluster->debug_inst->wait_lock);
}
void
wasm_cluster_thread_stopped(WASMExecEnv *exec_env)
{
exec_env->current_status->running_status = STATUS_STOP;
os_cond_signal(&exec_env->current_status->wait_cond);
notify_debug_instance(exec_env);
}
void
@ -578,7 +579,7 @@ void
wasm_cluster_thread_exited(WASMExecEnv *exec_env)
{
exec_env->current_status->running_status = STATUS_EXIT;
os_cond_signal(&exec_env->current_status->wait_cond);
notify_debug_instance(exec_env);
}
void
@ -595,7 +596,14 @@ wasm_cluster_thread_step(WASMExecEnv *exec_env)
exec_env->current_status->running_status = STATUS_STEP;
os_cond_signal(&exec_env->wait_cond);
}
#endif
void
wasm_cluster_set_debug_inst(WASMCluster *cluster, WASMDebugInstance *inst)
{
cluster->debug_inst = inst;
}
#endif /* end of WASM_ENABLE_DEBUG_INTERP */
int32
wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val)

View File

@ -39,6 +39,8 @@ typedef struct WASMCurrentEnvStatus {
korp_cond wait_cond;
} WASMCurrentEnvStatus;
typedef struct WASMDebugInstance WASMDebugInstance;
WASMCurrentEnvStatus *
wasm_cluster_create_exenv_status();
@ -69,6 +71,9 @@ wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo);
void
wasm_cluster_thread_step(WASMExecEnv *exec_env);
void
wasm_cluster_set_debug_inst(WASMCluster *cluster, WASMDebugInstance *inst);
#endif
typedef struct WASMCluster {
struct WASMCluster *next;
@ -84,6 +89,9 @@ typedef struct WASMCluster {
uint32 stack_size;
/* Record which segments are occupied */
bool *stack_segment_occupied;
#if WASM_ENABLE_DEBUG_INTERP != 0
WASMDebugInstance *debug_inst;
#endif
} WASMCluster;
void

View File

@ -146,6 +146,10 @@ Currently we only profile the memory consumption of module, module_instance and
> Note: The WAMR application entry (`core/iwasm/common/wasm_application.c`) encapsulate some common process to instantiate, execute the wasm functions and print the results. Some platform related APIs are used in these functions, so you can enable this flag to exclude this file if your platform doesn't support those APIs.
> *Don't enable this flag if you are building `product-mini`*
#### **Enable source debugging features**
- **WAMR_BUILD_DEBUG_INTERP**=1/0, default to 0 if not set
> Note: There are some other setup required by source debugging, please refer to [source_debugging.md](./source_debugging.md) for more details.
**Combination of configurations:**
We can combine the configurations. For example, if we want to disable interpreter, enable AOT and WASI, we can run command:

View File

@ -89,3 +89,51 @@ lldb-12 iwasm -- test.aot
```
Then you can use lldb commands to debug both wamr runtime and your wasm application in ***current terminal***
## Enable debugging in embedders (for interpreter)
There are three steps to enable debugging in embedders
1. Set the debug parameters when initializing the runtime environment:
``` c
RuntimeInitArgs init_args;
memset(&init_args, 0, sizeof(RuntimeInitArgs));
/* ... */
strcpy(init_args.ip_addr, "127.0.0.1");
init_args.instance_port = 1234;
/*
* Or set port to 0 to use a port assigned by os
* init_args.instance_port = 0;
*/
if (!wasm_runtime_full_init(&init_args)) {
return false;
}
```
2. Use `wasm_runtime_start_debug_instance` to create the debug instance:
``` c
/*
initialization, loading and instantiating
...
*/
exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
uint32_t debug_port = wasm_runtime_start_debug_instance();
```
3. Enable source debugging features during building
You can use `-DWAMR_BUILD_DEBUG_INTERP=1` during cmake configuration
Or you can set it directly in `cmake` files:
``` cmake
set (WAMR_BUILD_DEBUG_INTERP 1)
```
### Attentions
- Debugging `multi-thread wasm module` is not supported, if your wasm module use pthread APIs (see [pthread_library.md](./pthread_library.md)), or the embedder use `wasm_runtime_spawn_thread` to create new wasm threads, then there may be **unexpected behaviour** during debugging.
> Note: This attention is about "wasm thread" rather than native threads. Executing wasm functions in several different native threads will **not** affect the normal behaviour of debugging feature.
- When using source debugging features, **don't** create multiple `wasm_instance` from the same `wasm_module`, because the debugger may change the bytecode (set/unset breakpoints) of the `wasm_module`. If you do need several instance from the same bytecode, you need to copy the bytecode to a new butter, then load a new `wasm_module`, and then instantiate the new wasm module to get the new instance.

14
doc/xip.md Normal file
View File

@ -0,0 +1,14 @@
# WAMR XIP (Execution In Place) feature introduction
Some IoT devices may require to run the AOT file from flash or ROM which is read-only, so as to reduce the memory consumption, or resolve the issue that there is no executable memory available to run AOT code. In such case, the AOT code inside the AOT file shouldn't be duplicated into memory and shouldn't be modified (or patched) by the AOT relocations. To address this, WAMR implements the XIP (Execution In Place) feature, which generates the AOT relocations as few as possible:
- In the AOT code, an AOT function calls other functions with indirect mode: it doesn't call other functions directly, but looks up their pointers from the function pointer table passed by its first argument exec_env, and then calls the function pointer found. By this way the relocations to other functions are eliminated.
- Eliminate the calls to the LLVM intrinsic functions, or, replace calling them with calling runtime self implemented functions instead, e.g. the calling to `llvm.experimental.constrained.fadd.f32` is replaced by the calling to `aot_intrinsic_fadd_f32`.
The XIP file is an AOT file without (or with few) relocations to patch the AOT code (or text section). Developer can use the option `--enable-indirect-mode --disable-llvm-intrinsics` for wamrc to generate the AOT file, e.g.:
```bash
wamrc --enable-indirect-mode --disable-llvm-intrinsics -o <aot_file> <wasm_file>
```
## Known issues
There may be some relocations to the ".rodata" like sections which require to patch the AOT code. More work will be done to resolve it in the future.

View File

@ -52,6 +52,8 @@ typedef struct EnclaveModule {
uint32 wasi_env_list_size;
char **wasi_argv;
uint32 wasi_argc;
bool is_xip_file;
uint32 total_size_mapped;
} EnclaveModule;
#if WASM_ENABLE_SPEC_TEST == 0
@ -127,28 +129,57 @@ handle_cmd_load_module(uint64 *args, uint32 argc)
uint32 error_buf_size = *(uint32 *)args++;
uint64 total_size = sizeof(EnclaveModule) + (uint64)wasm_file_size;
EnclaveModule *enclave_module;
bool is_xip_file = false;
bh_assert(argc == 4);
if (total_size >= UINT32_MAX
|| !(enclave_module =
(EnclaveModule *)wasm_runtime_malloc((uint32)total_size))) {
set_error_buf(error_buf, error_buf_size,
"WASM module load failed: "
"allocate memory failed.");
*(void **)args_org = NULL;
return;
#if WASM_ENABLE_AOT != 0
is_xip_file = wasm_runtime_is_xip_file((uint8 *)wasm_file, wasm_file_size);
#endif
if (!is_xip_file) {
if (total_size >= UINT32_MAX
|| !(enclave_module = (EnclaveModule *)wasm_runtime_malloc(
(uint32)total_size))) {
set_error_buf(error_buf, error_buf_size,
"WASM module load failed: "
"allocate memory failed.");
*(void **)args_org = NULL;
return;
}
memset(enclave_module, 0, (uint32)total_size);
}
else {
int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC;
int map_flags = MMAP_MAP_NONE;
if (total_size >= UINT32_MAX
|| !(enclave_module = (EnclaveModule *)os_mmap(
NULL, (uint32)total_size, map_prot, map_flags))) {
set_error_buf(error_buf, error_buf_size,
"WASM module load failed: mmap memory failed.");
*(void **)args_org = NULL;
return;
}
memset(enclave_module, 0, (uint32)total_size);
enclave_module->is_xip_file = true;
enclave_module->total_size_mapped = (uint32)total_size;
}
memset(enclave_module, 0, (uint32)total_size);
enclave_module->wasm_file = (uint8 *)enclave_module + sizeof(EnclaveModule);
bh_memcpy_s(enclave_module->wasm_file, wasm_file_size, wasm_file,
wasm_file_size);
if (is_xip_file) {
enclave_module->is_xip_file = true;
}
if (!(enclave_module->module =
wasm_runtime_load(enclave_module->wasm_file, wasm_file_size,
error_buf, error_buf_size))) {
wasm_runtime_free(enclave_module);
if (!is_xip_file)
wasm_runtime_free(enclave_module);
else
os_munmap(enclave_module, (uint32)total_size);
*(void **)args_org = NULL;
return;
}
@ -170,7 +201,10 @@ handle_cmd_unload_module(uint64 *args, uint32 argc)
wasm_runtime_free(enclave_module->wasi_arg_buf);
wasm_runtime_unload(enclave_module->module);
wasm_runtime_free(enclave_module);
if (!enclave_module->is_xip_file)
wasm_runtime_free(enclave_module);
else
os_munmap(enclave_module, enclave_module->total_size_mapped);
LOG_VERBOSE("Unload module success.\n");
}

View File

@ -34,8 +34,6 @@ print_help()
printf(" --heap-size=n Set maximum heap size in bytes, default is 16 KB\n");
printf(" --repl Start a very simple REPL (read-eval-print-loop) mode\n"
" that runs commands in the form of \"FUNC ARG...\"\n");
printf(" --xip Enable XIP (Execution In Place) mode to run AOT file\n"
" generated with \"--enable-indirect-mode\" flag\n");
#if WASM_ENABLE_LIBC_WASI != 0
printf(" --env=<env> Pass wasi environment variables with \"key=value\"\n");
printf(" to the program, for example:\n");
@ -240,7 +238,7 @@ main(int argc, char *argv[])
int log_verbose_level = 2;
#endif
bool is_repl_mode = false;
bool is_xip_mode = false;
bool is_xip_file = false;
#if WASM_ENABLE_LIBC_WASI != 0
const char *dir_list[8] = { NULL };
uint32 dir_list_size = 0;
@ -273,9 +271,6 @@ main(int argc, char *argv[])
else if (!strcmp(argv[0], "--repl")) {
is_repl_mode = true;
}
else if (!strcmp(argv[0], "--xip")) {
is_xip_mode = true;
}
else if (!strncmp(argv[0], "--stack-size=", 13)) {
if (argv[0][13] == '\0')
return print_help();
@ -392,10 +387,11 @@ main(int argc, char *argv[])
(uint8 *)bh_read_file_to_buffer(wasm_file, &wasm_file_size)))
goto fail1;
if (is_xip_mode) {
#if WASM_ENABLE_AOT != 0
if (wasm_runtime_is_xip_file(wasm_file_buf, wasm_file_size)) {
uint8 *wasm_file_mapped;
int map_prot = MMAP_PROT_READ | MMAP_PROT_WRITE | MMAP_PROT_EXEC;
int map_flags = MMAP_MAP_NONE;
int map_flags = MMAP_MAP_32BIT;
if (!(wasm_file_mapped =
os_mmap(NULL, (uint32)wasm_file_size, map_prot, map_flags))) {
@ -408,7 +404,9 @@ main(int argc, char *argv[])
wasm_file_size);
wasm_runtime_free(wasm_file_buf);
wasm_file_buf = wasm_file_mapped;
is_xip_file = true;
}
#endif
#if WASM_ENABLE_MULTI_MODULE != 0
wasm_runtime_set_module_reader(module_reader_callback, moudle_destroyer);
@ -450,7 +448,7 @@ fail3:
fail2:
/* free the file buffer */
if (!is_xip_mode)
if (!is_xip_file)
wasm_runtime_free(wasm_file_buf);
else
os_munmap(wasm_file_buf, wasm_file_size);

View File

@ -51,6 +51,7 @@ def ignore_the_case(
multi_module_flag=False,
multi_thread_flag=False,
simd_flag=False,
xip_flag=False,
):
if case_name in ["comments", "inline-module", "names"]:
return True
@ -100,6 +101,7 @@ def test_case(
multi_module_flag=False,
multi_thread_flag=False,
simd_flag=False,
xip_flag=False,
clean_up_flag=True,
verbose_flag=True,
):
@ -114,6 +116,7 @@ def test_case(
multi_module_flag,
multi_thread_flag,
simd_flag,
xip_flag,
):
return True
@ -139,6 +142,9 @@ def test_case(
if simd_flag:
CMD.append("--simd")
if xip_flag:
CMD.append("--xip")
if not clean_up_flag:
CMD.append("--no_cleanup")
@ -195,6 +201,7 @@ def test_suite(
multi_module_flag=False,
multi_thread_flag=False,
simd_flag=False,
xip_flag=False,
clean_up_flag=True,
verbose_flag=True,
):
@ -217,6 +224,7 @@ def test_suite(
multi_module_flag,
multi_thread_flag,
simd_flag,
xip_flag,
clean_up_flag,
verbose_flag,
)
@ -239,6 +247,7 @@ def test_suite_parallelly(
multi_module_flag=False,
multi_thread_flag=False,
simd_flag=False,
xip_flag=False,
clean_up_flag=False,
verbose_flag=False,
):
@ -266,6 +275,7 @@ def test_suite_parallelly(
multi_module_flag,
multi_thread_flag,
simd_flag,
xip_flag,
clean_up_flag,
verbose_flag,
],
@ -323,6 +333,13 @@ def main():
dest="simd_flag",
help="Running with the SIMD feature",
)
parser.add_argument(
"-X",
action="store_true",
default=False,
dest="xip_flag",
help="Running with the XIP feature",
)
parser.add_argument(
"-t",
action="store_true",
@ -387,6 +404,7 @@ def main():
options.multi_module_flag,
options.multi_thread_flag,
options.simd_flag,
options.xip_flag,
options.clean_up_flag,
options.verbose_flag,
)
@ -403,6 +421,7 @@ def main():
options.multi_module_flag,
options.multi_thread_flag,
options.simd_flag,
options.xip_flag,
options.clean_up_flag,
options.verbose_flag,
)
@ -419,6 +438,7 @@ def main():
options.multi_module_flag,
options.multi_thread_flag,
options.simd_flag,
options.xip_flag,
options.clean_up_flag,
options.verbose_flag,
)

View File

@ -210,6 +210,9 @@ parser.add_argument('--sgx', action='store_true',
parser.add_argument('--simd', default=False, action='store_true',
help="Enable SIMD")
parser.add_argument('--xip', default=False, action='store_true',
help="Enable XIP")
parser.add_argument('--multi-thread', default=False, action='store_true',
help="Enable Multi-thread")
@ -942,6 +945,10 @@ def compile_wasm_to_aot(wasm_tempfile, aot_tempfile, runner, opts, r):
if not opts.simd:
cmd.append("--disable-simd")
if opts.xip:
cmd.append("--enable-indirect-mode")
cmd.append("--disable-llvm-intrinsics")
if opts.multi_thread:
cmd.append("--enable-multi-thread")

View File

@ -17,10 +17,11 @@ function help()
echo "-s {suite_name} test only one suite (spec)"
echo "-m set compile target of iwasm(x86_64\x86_32\armv7_vfp\thumbv7_vfp\riscv64_lp64d\riscv64_lp64)"
echo "-t set compile type of iwasm(classic-interp\fast-interp\jit\aot)"
echo "-M enable the multi module feature"
echo "-M enable multi module feature"
echo "-p enable multi thread feature"
echo "-S enable SIMD feature"
echo "-G enable GC feature"
echo "-X enable XIP feature"
echo "-x test SGX"
echo "-b use the wabt binary release package instead of compiling from the source code"
echo "-P run the spec test parallelly"
@ -37,13 +38,14 @@ ENABLE_MULTI_THREAD=0
COLLECT_CODE_COVERAGE=0
ENABLE_SIMD=0
ENABLE_GC=0
ENABLE_XIP=0
#unit test case arrary
TEST_CASE_ARR=()
SGX_OPT=""
PLATFORM=$(uname -s | tr A-Z a-z)
PARALLELISM=0
while getopts ":s:cabt:m:MCpSxPG" opt
while getopts ":s:cabt:m:MCpSXxPG" opt
do
OPT_PARSED="TRUE"
case $opt in
@ -108,6 +110,10 @@ do
echo "enable SIMD feature"
ENABLE_SIMD=1
;;
X)
echo "enable XIP feature"
ENABLE_XIP=1
;;
x)
echo "test SGX"
SGX_OPT="--sgx"
@ -403,7 +409,11 @@ function spec_test()
fi
if [[ ${ENABLE_MULTI_THREAD} == 1 ]]; then
ARGS_FOR_SPEC_TEST+="-p "
ARGS_FOR_SPEC_TEST+="-p "
fi
if [[ ${ENABLE_XIP} == 1 ]]; then
ARGS_FOR_SPEC_TEST+="-X "
fi
# require warmc only in aot mode