Add initial support for WASM exceptions (#2382)

This PR adds the initial support for WASM exceptions, it is an initial contribution,
as part 1 of 2 contributions. Refer to:
  https://github.com/bytecodealliance/wasm-micro-runtime/pull/2382

### Included in this contribution
* Inside the classic interpreter only:
  * Initial handling of Tags
  * Initial handling of Exceptions based on W3C Exception Proposal
* Add `cmake -DWAMR_BUILD_EXCE_HANDLING=1/0` option to enable/disable
  the feature, and update the wamr-test-suites scripts to test the feature.

### To be included in Part 2
* Inside the classic interpreter only:
  * Bug fixes and naming convention improvements
  * Additional CI /CD changes to validate ENABLE_EXCE_HANDLING switch builds
    OK on all platforms
  * Import and Export of Exceptions and Tags

Signed-off-by: Chris Woods <chris.woods@siemens.com>
Co-authored-by: Ricardo Aguilar <ricardoaguilar@siemens.com>
Co-authored-by: Rene Ermler <rene.ermler@siemens.com>
This commit is contained in:
Chris Woods 2023-09-27 19:46:45 -05:00 committed by GitHub
parent 95b19a587e
commit 8bebfe9ad7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1303 additions and 57 deletions

View File

@ -319,6 +319,7 @@ else ()
endif ()
if (WAMR_BUILD_EXCE_HANDLING EQUAL 1)
add_definitions (-DWASM_ENABLE_EXCE_HANDLING=1)
add_definitions (-DWASM_ENABLE_TAGS=1)
message (" Exception Handling enabled")
endif ()
if (DEFINED WAMR_BH_VPRINTF)

View File

@ -445,6 +445,10 @@
#define WASM_ENABLE_EXCE_HANDLING 0
#endif
#ifndef WASM_ENABLE_TAGS
#define WASM_ENABLE_TAGS 0
#endif
#ifndef WASM_ENABLE_SGX_IPFS
#define WASM_ENABLE_SGX_IPFS 0
#endif

View File

@ -14,6 +14,13 @@
extern "C" {
#endif
#if WASM_ENABLE_EXCE_HANDLING != 0
#define _EXCEWARNING \
LOG_WARNING /* for exception handling misbehavior logging */
#define _EXCEVERBOSE \
LOG_VERBOSE /* more excessive tracing of tagbrowsing and stack pointers */
#endif
/** Value Type */
#define VALUE_TYPE_I32 0x7F
#define VALUE_TYPE_I64 0X7E
@ -65,6 +72,9 @@ extern "C" {
#if WASM_ENABLE_BULK_MEMORY != 0
#define SECTION_TYPE_DATACOUNT 12
#endif
#if WASM_ENABLE_TAGS != 0
#define SECTION_TYPE_TAG 13
#endif
#define SUB_SECTION_TYPE_MODULE 0
#define SUB_SECTION_TYPE_FUNC 1
@ -74,20 +84,34 @@ extern "C" {
#define IMPORT_KIND_TABLE 1
#define IMPORT_KIND_MEMORY 2
#define IMPORT_KIND_GLOBAL 3
#if WASM_ENABLE_TAGS != 0
#define IMPORT_KIND_TAG 4
#endif
#define EXPORT_KIND_FUNC 0
#define EXPORT_KIND_TABLE 1
#define EXPORT_KIND_MEMORY 2
#define EXPORT_KIND_GLOBAL 3
#if WASM_ENABLE_TAGS != 0
#define EXPORT_KIND_TAG 4
#endif
#define LABEL_TYPE_BLOCK 0
#define LABEL_TYPE_LOOP 1
#define LABEL_TYPE_IF 2
#define LABEL_TYPE_FUNCTION 3
#if WASM_ENABLE_EXCE_HANDLING != 0
#define LABEL_TYPE_TRY 4
#define LABEL_TYPE_CATCH 5
#define LABEL_TYPE_CATCH_ALL 6
#endif
typedef struct WASMModule WASMModule;
typedef struct WASMFunction WASMFunction;
typedef struct WASMGlobal WASMGlobal;
#if WASM_ENABLE_TAGS != 0
typedef struct WASMTag WASMTag;
#endif
typedef union V128 {
int8 i8x16[16];
@ -197,6 +221,21 @@ typedef struct WASMFunctionImport {
bool call_conv_wasm_c_api;
} WASMFunctionImport;
#if WASM_ENABLE_TAGS != 0
typedef struct WASMTagImport {
uint8 attribute; /* the type of the tag (numerical) */
uint32 type; /* the type of the catch function (numerical)*/
WASMType *tag_type;
uint32 tag_index_linked;
#if WASM_ENABLE_MULTI_MODULE != 0
/* imported function pointer after linked */
/* TODO: remove if not needed */
WASMModule *import_module;
WASMTag *import_tag_linked;
#endif
} WASMTagImport;
#endif
typedef struct WASMGlobalImport {
char *module_name;
char *field_name;
@ -223,6 +262,9 @@ typedef struct WASMImport {
WASMFunctionImport function;
WASMTableImport table;
WASMMemoryImport memory;
#if WASM_ENABLE_TAGS != 0
WASMTagImport tag;
#endif
WASMGlobalImport global;
struct {
char *module_name;
@ -261,6 +303,10 @@ struct WASMFunction {
uint32 const_cell_num;
#endif
#if WASM_ENABLE_EXCE_HANDLING != 0
uint32 exception_handler_count;
#endif
#if WASM_ENABLE_FAST_JIT != 0 || WASM_ENABLE_JIT != 0 \
|| WASM_ENABLE_WAMR_COMPILER != 0
/* Whether function has opcode memory.grow */
@ -290,6 +336,13 @@ struct WASMFunction {
#endif
};
#if WASM_ENABLE_TAGS != 0
struct WASMTag {
uint8 attribute; /* the attribute property of the tag (expected to be 0) */
uint32 type; /* the type of the tag (expected valid inden in type table) */
};
#endif
struct WASMGlobal {
uint8 type;
bool is_mutable;
@ -417,6 +470,9 @@ struct WASMModule {
uint32 function_count;
uint32 table_count;
uint32 memory_count;
#if WASM_ENABLE_TAGS != 0
uint32 tag_count;
#endif
uint32 global_count;
uint32 export_count;
uint32 table_seg_count;
@ -430,11 +486,17 @@ struct WASMModule {
uint32 import_function_count;
uint32 import_table_count;
uint32 import_memory_count;
#if WASM_ENABLE_TAGS != 0
uint32 import_tag_count;
#endif
uint32 import_global_count;
WASMImport *import_functions;
WASMImport *import_tables;
WASMImport *import_memories;
#if WASM_ENABLE_TAGS != 0
WASMImport *import_tags;
#endif
WASMImport *import_globals;
WASMType **types;
@ -442,6 +504,9 @@ struct WASMModule {
WASMFunction **functions;
WASMTable *tables;
WASMMemory *memories;
#if WASM_ENABLE_TAGS != 0
WASMTag *tags;
#endif
WASMGlobal *globals;
WASMExport *exports;
WASMTableSeg *table_segments;
@ -625,6 +690,11 @@ typedef struct WASMBranchBlock {
uint8 *target_addr;
uint32 *frame_sp;
uint32 cell_num;
#if WASM_ENABLE_EXCE_HANDLING != 0
/* in exception handling, label_type needs to be stored to lookup exception
* handlers */
uint8 label_type;
#endif
} WASMBranchBlock;
/**

View File

@ -34,6 +34,14 @@ typedef struct WASMInterpFrame {
uint64 time_started;
#endif
#if WASM_ENABLE_EXCE_HANDLING != 0
/* set to true if the callee returns an exception rather than
* result values on the stack
*/
bool exception_raised;
uint32 tag_index;
#endif
#if WASM_ENABLE_FAST_INTERP != 0
/* Return offset of the first return value of current frame,
the callee will put return values here continuously */

View File

@ -338,10 +338,19 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign)
frame_sp += 2; \
} while (0)
/* in exception handling, label_type needs to be stored to lookup exception
* handlers */
#if WASM_ENABLE_EXCE_HANDLING != 0
#define SET_LABEL_TYPE(_label_type) frame_csp->label_type = _label_type
#else
#define SET_LABEL_TYPE(_label_type) (void)0
#endif
#define PUSH_CSP(_label_type, param_cell_num, cell_num, _target_addr) \
do { \
bh_assert(frame_csp < frame->csp_boundary); \
/* frame_csp->label_type = _label_type; */ \
SET_LABEL_TYPE(_label_type); \
frame_csp->cell_num = cell_num; \
frame_csp->begin_addr = frame_ip; \
frame_csp->target_addr = _target_addr; \
@ -392,6 +401,18 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign)
frame_sp -= n; \
} while (0)
#if WASM_ENABLE_EXCE_HANDLING != 0
/* unwind the CSP to a given label and optionally modify the labeltype */
#define UNWIND_CSP(N, T) \
do { \
/* unwind to function frame */ \
frame_csp -= N; \
/* drop handlers and values pushd in try block */ \
frame_sp = (frame_csp - 1)->frame_sp; \
(frame_csp - 1)->label_type = T ? T : (frame_csp - 1)->label_type; \
} while (0)
#endif
#define SYNC_ALL_TO_FRAME() \
do { \
frame->sp = frame_sp; \
@ -1184,6 +1205,9 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
uint32 local_idx, local_offset, global_idx;
uint8 local_type, *global_addr;
uint32 cache_index, type_index, param_cell_num, cell_num;
#if WASM_ENABLE_EXCE_HANDLING != 0
int32_t exception_tag_index;
#endif
uint8 value_type;
#if !defined(OS_ENABLE_HW_BOUND_CHECK) \
|| WASM_CPU_SUPPORTS_UNALIGNED_ADDR_ACCESS == 0
@ -1226,6 +1250,404 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
HANDLE_OP(WASM_OP_NOP) { HANDLE_OP_END(); }
#if WASM_ENABLE_EXCE_HANDLING != 0
HANDLE_OP(WASM_OP_RETHROW)
{
int32_t relative_depth;
read_leb_int32(frame_ip, frame_ip_end, relative_depth);
/* No frame found with exception handler; validation should
* catch it */
bh_assert(frame_csp >= frame->csp_bottom + relative_depth);
/* go up the frame stack */
WASMBranchBlock *tgtframe = (frame_csp - 1) - relative_depth;
bh_assert(tgtframe->label_type == LABEL_TYPE_CATCH
|| tgtframe->label_type == LABEL_TYPE_CATCH_ALL);
/* tgtframe points to the frame containing a thrown
* exception */
uint32 *tgtframe_sp = tgtframe->frame_sp;
/* frame sp of tgtframe points to catched exception */
exception_tag_index = *((uint32 *)tgtframe_sp);
tgtframe_sp++;
/* get tag type */
uint8 tag_type_index =
module->module->tags[exception_tag_index].type;
uint32 cell_num_to_copy =
wasm_types[tag_type_index]->param_cell_num;
/* move exception parameters (if there are any) onto top
* of stack */
if (cell_num_to_copy > 0) {
word_copy(frame_sp, tgtframe_sp - cell_num_to_copy,
cell_num_to_copy);
}
frame_sp += cell_num_to_copy;
goto find_a_catch_handler;
}
HANDLE_OP(WASM_OP_THROW)
{
read_leb_int32(frame_ip, frame_ip_end, exception_tag_index);
/* landig pad for the rethrow ? */
find_a_catch_handler:
{
/* browse through frame stack */
uint32 relative_depth = 0;
do {
POP_CSP_CHECK_OVERFLOW(relative_depth - 1);
WASMBranchBlock *tgtframe = frame_csp - relative_depth - 1;
switch (tgtframe->label_type) {
case LABEL_TYPE_BLOCK:
case LABEL_TYPE_IF:
case LABEL_TYPE_LOOP:
case LABEL_TYPE_CATCH:
case LABEL_TYPE_CATCH_ALL:
/*
* skip that blocks in search
* BLOCK, IF and LOOP do not contain handlers and
* cannot catch exceptions blocks marked as CATCH or
* CATCH_ALL did already caugth an exception and can
* only be a target for RETHROW, but cannot catch an
* exception again
*/
break;
case LABEL_TYPE_TRY:
{
uint32 handler_number = 0;
uint8 **handlers = (uint8 **)tgtframe->frame_sp;
uint8 *handler = NULL;
while ((handler = handlers[handler_number]) != 0) {
uint8 handler_opcode = *handler;
uint8 *target_addr =
handler
+ 1; /* first instruction or leb-immediate
behind the handler opcode */
switch (handler_opcode) {
case WASM_OP_CATCH:
{
int32 lookup_index = 0;
/* read the tag_index and advance
* target_addr to the first instruction
* in the block */
read_leb_int32(target_addr, 0,
lookup_index);
if (exception_tag_index
== lookup_index) {
/* set ip */
frame_ip = target_addr;
/* save frame_sp (points to
* exception values) */
uint32 *frame_sp_old = frame_sp;
UNWIND_CSP(relative_depth,
LABEL_TYPE_CATCH);
/* transfer exception values */
uint8 tag_type_index =
module->module
->tags[exception_tag_index]
.type;
uint32 cell_num_to_copy =
wasm_types[tag_type_index]
->param_cell_num;
/* push exception_tag_index and
* exception values for rethrow */
PUSH_I32(exception_tag_index);
if (cell_num_to_copy > 0) {
word_copy(
frame_sp,
frame_sp_old
- cell_num_to_copy,
cell_num_to_copy);
}
frame_sp += cell_num_to_copy;
/* push exception values for catch
*/
if (cell_num_to_copy > 0) {
word_copy(
frame_sp,
frame_sp_old
- cell_num_to_copy,
cell_num_to_copy);
}
frame_sp += cell_num_to_copy;
/* advance to handler */
HANDLE_OP_END();
}
break;
}
case WASM_OP_DELEGATE:
{
int32 lookup_depth = 0;
/* read the depth */
read_leb_int32(target_addr, 0,
lookup_depth);
/* save frame_sp (points to exception
* values) */
uint32 *frame_sp_old = frame_sp;
UNWIND_CSP(relative_depth,
LABEL_TYPE_CATCH);
/* leave the block (the delegate is
* technically not inside the frame) */
frame_csp--;
/* unwind to delegated frame */
frame_csp -= lookup_depth;
/* transfer exception values */
uint8 tag_type_index =
module->module
->tags[exception_tag_index]
.type;
uint32 cell_num_to_copy =
wasm_types[tag_type_index]
->param_cell_num;
/* push exception values for catch */
if (cell_num_to_copy > 0) {
word_copy(frame_sp,
frame_sp_old
- cell_num_to_copy,
cell_num_to_copy);
}
frame_sp += cell_num_to_copy;
/* tag_index is already stored in
* exception_tag_index */
goto find_a_catch_handler;
}
case WASM_OP_CATCH_ALL:
{
/* no immediate */
/* save frame_sp (points to exception
* values) */
uint32 *frame_sp_old = frame_sp;
/* set ip */
frame_ip = target_addr;
UNWIND_CSP(relative_depth,
LABEL_TYPE_CATCH_ALL);
/* push exception_tag_index and
* exception values for rethrow */
PUSH_I32(exception_tag_index);
if (exception_tag_index
!= (int32_t)0xFFFFFFFF) {
/* transfer exception values */
uint8 tag_type_index =
module->module
->tags[exception_tag_index]
.type;
uint32 cell_num_to_copy =
wasm_types[tag_type_index]
->param_cell_num;
if (cell_num_to_copy > 0) {
word_copy(
frame_sp,
frame_sp_old
- cell_num_to_copy,
cell_num_to_copy);
}
frame_sp += cell_num_to_copy;
}
/* catch_all has no exception values */
/* advance to handler */
HANDLE_OP_END();
}
default:
wasm_set_exception(
module, "WASM_OP_THROW found "
"unexpected handler type");
goto got_exception;
}
handler_number++;
}
/* exception not catched in this frame */
break;
}
case LABEL_TYPE_FUNCTION:
{
/* save frame_sp (points to exception values) */
uint32 *frame_sp_old = frame_sp;
UNWIND_CSP(relative_depth, LABEL_TYPE_FUNCTION);
if (exception_tag_index
>= (int32_t)module->module->tag_count) {
wasm_set_exception(module, "invalid tag index");
goto got_exception;
}
/* transfer exception values */
uint8 tag_type_index =
module->module->tags[exception_tag_index].type;
uint32 cell_num_to_copy =
wasm_types[tag_type_index]->param_cell_num;
/* push exception values for catch
* The values are copied to the CALLER FRAME
* (prev_frame->sp) same behvior ad WASM_OP_RETURN
*/
if (cell_num_to_copy > 0) {
word_copy(prev_frame->sp,
frame_sp_old - cell_num_to_copy,
cell_num_to_copy);
}
prev_frame->sp += cell_num_to_copy;
*((int32 *)(prev_frame->sp)) = exception_tag_index;
(int32 *)(prev_frame->sp++);
/* mark frame as raised exception */
wasm_set_exception(module,
"uncaught wasm exception");
/* end of function, treat as WASM_OP_RETURN */
goto return_func;
}
default:
wasm_set_exception(
module,
"unexpected or invalid label in THROW or "
"RETHROW when searching a catch handler");
goto got_exception;
}
relative_depth++;
} while (1);
}
/* something went wrong. normally, we should always find the
* func label. if not, stop the interpreter */
wasm_set_exception(
module, "WASM_OP_THROW hit the bottom of the frame stack");
goto got_exception;
}
HANDLE_OP(EXT_OP_TRY)
{
/* read the blocktype */
read_leb_uint32(frame_ip, frame_ip_end, type_index);
param_cell_num = wasm_types[type_index]->param_cell_num;
cell_num = wasm_types[type_index]->ret_cell_num;
goto handle_op_try;
}
HANDLE_OP(WASM_OP_TRY)
{
value_type = *frame_ip++;
param_cell_num = 0;
cell_num = wasm_value_type_cell_num(value_type);
handle_op_try:
cache_index = ((uintptr_t)frame_ip)
& (uintptr_t)(BLOCK_ADDR_CACHE_SIZE - 1);
cache_items = exec_env->block_addr_cache[cache_index];
if (cache_items[0].start_addr == frame_ip) {
cache_items[0].start_addr = 0;
}
if (cache_items[1].start_addr == frame_ip) {
cache_items[1].start_addr = 0;
}
/* start at the first opcode following the try and its blocktype
*/
uint8 *lookup_cursor = frame_ip;
uint8 handler_opcode = WASM_OP_UNREACHABLE;
/* target_addr filled in when END or DELEGATE is found */
PUSH_CSP(LABEL_TYPE_TRY, param_cell_num, cell_num, 0);
/* reset to begin of block */
lookup_cursor = frame_ip;
do {
/* lookup the next CATCH, CATCH_ALL or END for this TRY */
if (!wasm_loader_find_block_addr(
exec_env, (BlockAddr *)exec_env->block_addr_cache,
lookup_cursor, (uint8 *)-1, LABEL_TYPE_TRY,
&else_addr, &end_addr)) {
/* something went wrong */
wasm_set_exception(module, "find block address failed");
goto got_exception;
}
/* place cursor for continuation past opcode */
lookup_cursor = end_addr + 1;
/* end_addr points to CATCH, CATCH_ALL, DELEGATE or END */
handler_opcode = *end_addr;
switch (handler_opcode) {
case WASM_OP_CATCH:
skip_leb(lookup_cursor); /* skip tag_index */
PUSH_I64(end_addr);
break;
case WASM_OP_CATCH_ALL:
PUSH_I64(end_addr);
break;
case WASM_OP_DELEGATE:
skip_leb(lookup_cursor); /* skip depth */
PUSH_I64(end_addr);
/* patch target_addr */
(frame_csp - 1)->target_addr = lookup_cursor;
break;
case WASM_OP_END:
PUSH_I64(0);
/* patch target_addr */
(frame_csp - 1)->target_addr = end_addr;
break;
default:
/* something went wrong */
wasm_set_exception(module,
"find block address returned an "
"unexpected opcode");
goto got_exception;
}
/* ... search until the returned address is the END of the
* TRY block */
} while (handler_opcode != WASM_OP_END
&& handler_opcode != WASM_OP_DELEGATE);
/* handler setup on stack complete */
HANDLE_OP_END();
}
HANDLE_OP(WASM_OP_CATCH)
{
/* skip the tag_index */
skip_leb(frame_ip);
/* leave the frame */
POP_CSP_N(0);
HANDLE_OP_END();
}
HANDLE_OP(WASM_OP_CATCH_ALL)
{
/* leave the frame */
POP_CSP_N(0);
HANDLE_OP_END();
}
HANDLE_OP(WASM_OP_DELEGATE)
{
/* skip the delegate depth */
skip_leb(frame_ip);
/* leave the frame like WASM_OP_END */
POP_CSP();
HANDLE_OP_END();
}
#endif
HANDLE_OP(EXT_OP_BLOCK)
{
read_leb_uint32(frame_ip, frame_ip_end, type_index);
@ -1544,20 +1966,6 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
goto call_func_from_interp;
}
#if WASM_ENABLE_EXCE_HANDLING != 0
HANDLE_OP(WASM_OP_TRY)
HANDLE_OP(WASM_OP_CATCH)
HANDLE_OP(WASM_OP_THROW)
HANDLE_OP(WASM_OP_RETHROW)
HANDLE_OP(WASM_OP_DELEGATE)
HANDLE_OP(WASM_OP_CATCH_ALL)
{
/* TODO */
wasm_set_exception(module, "unsupported opcode");
goto got_exception;
}
#endif
/* parametric instructions */
HANDLE_OP(WASM_OP_DROP)
{
@ -3845,6 +4253,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
HANDLE_OP(WASM_OP_RETHROW)
HANDLE_OP(WASM_OP_DELEGATE)
HANDLE_OP(WASM_OP_CATCH_ALL)
HANDLE_OP(EXT_OP_TRY)
#endif
HANDLE_OP(WASM_OP_UNUSED_0x14)
HANDLE_OP(WASM_OP_UNUSED_0x15)
@ -3900,6 +4309,50 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
if (cur_func->import_func_inst) {
wasm_interp_call_func_import(module, exec_env, cur_func,
prev_frame);
#if WASM_ENABLE_EXCE_HANDLING != 0
char uncaught_exception[128] = { 0 };
bool has_exception =
wasm_copy_exception(module, uncaught_exception);
if (has_exception
&& strstr(uncaught_exception, "uncaught wasm exception")) {
/* fix framesp */
UPDATE_ALL_FROM_FRAME();
uint32 import_exception =
0xFFFFFFFF; /* initialize imported exception index to be
invalid */
/* pull external exception */
uint32 ext_exception = POP_I32();
WASMModule *im_mod = cur_func->u.func_import->import_module;
/* external function came back with an exception or trap */
/* lookup exception in import tags */
uint32 import_tag_index;
for (import_tag_index = 0;
import_tag_index < module->module->import_tag_count;
import_tag_index++) {
WASMTagImport *im_tag =
&(module->module->import_tags[import_tag_index]
.u.tag);
/* compare the module and the external index with the
* imort tag data */
if ((im_mod == im_tag->import_module)
&& (ext_exception == im_tag->tag_index_linked)) {
/* set the import_exception to the import tag */
import_exception = import_tag_index;
break;
}
}
/*
* push the internal exception index to stack,
* or 0xffffffff in case, the external exception
* is not in the import list
*/
PUSH_I32(import_exception);
}
#endif
}
else
#endif
@ -3920,19 +4373,57 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
if (memory)
linear_mem_size = memory->memory_data_size;
#endif
if (wasm_copy_exception(module, NULL))
if (wasm_copy_exception(module, NULL)) {
#if WASM_ENABLE_EXCE_HANDLING != 0
/* the caller raised an exception */
char uncaught_exception[128] = { 0 };
bool has_exception =
wasm_copy_exception(module, uncaught_exception);
/* libc_builtin signaled a "exception thrown by stdc++" trap */
if (has_exception
&& strstr(uncaught_exception,
"exception thrown by stdc++")) {
wasm_set_exception(module, NULL);
/* setup internal c++ rethrow */
exception_tag_index = 0;
goto find_a_catch_handler;
}
/* when throw hits the end of a function it signalles with a
* "uncaught wasm exception" trap */
if (has_exception
&& strstr(uncaught_exception, "uncaught wasm exception")) {
wasm_set_exception(module, NULL);
exception_tag_index = POP_I32();
/* rethrow the exception into that frame */
goto find_a_catch_handler;
}
#endif
goto got_exception;
}
}
else {
WASMFunction *cur_wasm_func = cur_func->u.func;
WASMType *func_type;
#if WASM_ENABLE_EXCE_HANDLING != 0
/* account for exception handlers */
/* bundle them here */
uint32 eh_size =
cur_wasm_func->exception_handler_count * sizeof(uint8 *);
cur_wasm_func->max_stack_cell_num += eh_size;
#endif
func_type = cur_wasm_func->func_type;
all_cell_num = cur_func->param_cell_num + cur_func->local_cell_num
+ cur_wasm_func->max_stack_cell_num
+ cur_wasm_func->max_block_num
* (uint32)sizeof(WASMBranchBlock) / 4;
/* param_cell_num, local_cell_num, max_stack_cell_num and
max_block_num are all no larger than UINT16_MAX (checked
in loader), all_cell_num must be smaller than 1MB */
@ -3981,11 +4472,19 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
FREE_FRAME(exec_env, frame);
wasm_exec_env_set_cur_frame(exec_env, prev_frame);
if (!prev_frame->ip)
if (!prev_frame->ip) {
/* Called from native. */
return;
}
RECOVER_CONTEXT(prev_frame);
#if WASM_ENABLE_EXCE_HANDLING != 0
if (wasm_get_exception(module)) {
wasm_set_exception(module, NULL);
exception_tag_index = POP_I32();
goto find_a_catch_handler;
}
#endif
HANDLE_OP_END();
}

View File

@ -1425,8 +1425,8 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
HANDLE_OP(WASM_OP_RETHROW)
HANDLE_OP(WASM_OP_DELEGATE)
HANDLE_OP(WASM_OP_CATCH_ALL)
HANDLE_OP(EXT_OP_TRY)
{
/* TODO */
wasm_set_exception(module, "unsupported opcode");
goto got_exception;
}
@ -3684,12 +3684,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
HANDLE_OP(WASM_OP_REF_FUNC)
#endif
#if WASM_ENABLE_EXCE_HANDLING == 0
/* if exception handling is disabled, these opcodes issue a trap */
HANDLE_OP(WASM_OP_TRY)
HANDLE_OP(WASM_OP_CATCH)
HANDLE_OP(WASM_OP_THROW)
HANDLE_OP(WASM_OP_RETHROW)
HANDLE_OP(WASM_OP_DELEGATE)
HANDLE_OP(WASM_OP_CATCH_ALL)
HANDLE_OP(EXT_OP_TRY)
#endif
/* SELECT_T is converted to SELECT or SELECT_64 */
HANDLE_OP(WASM_OP_SELECT_T)

View File

@ -1386,6 +1386,99 @@ fail:
return false;
}
#if WASM_ENABLE_TAGS != 0
static bool
load_tag_import(const uint8 **p_buf, const uint8 *buf_end,
const WASMModule *parent_module, /* this module ! */
const char *sub_module_name, const char *tag_name,
WASMTagImport *tag, /* structure to fill */
char *error_buf, uint32 error_buf_size)
{
WASMExport *export = 0;
WASMModule *sub_module = NULL;
#if WASM_ENABLE_MULTI_MODULE != 0
if (!wasm_runtime_is_built_in_module(sub_module_name)) {
sub_module = load_depended_module(parent_module, sub_module_name,
error_buf, error_buf_size);
if (!sub_module) {
return false;
}
}
#endif
WASMModuleCommon *module_reg =
wasm_runtime_find_module_registered(sub_module_name);
if (!module_reg) {
set_error_buf(error_buf, error_buf_size,
"load_tag_import: registered module not found");
goto fail;
}
sub_module = (WASMModule *)module_reg;
uint32 i;
export = sub_module->exports;
for (i = 0; i < sub_module->export_count; i++, export ++) {
if (export->kind == EXPORT_KIND_TAG
&& strcmp(export->name, tag_name) == 0) {
WASMTag *imp_tag = (WASMTag *)&sub_module->tags[export->index];
WASMType *imp_tag_type =
(WASMType *)sub_module->types[imp_tag->type];
/* fill import tag*/
tag->tag_index_linked = export->index;
tag->tag_type = (WASMType *)sub_module->types[imp_tag->type];
#if WASM_ENABLE_MULTI_MODULE != 0
tag->import_module = (WASMModule *)module_reg;
tag->import_tag_linked = &sub_module->tags[export->index];
#endif
}
}
uint8 tag_attribute;
uint32 tag_type;
const uint8 *p = *p_buf, *p_end = buf_end;
/* get the one byte attribute */
CHECK_BUF(p, p_end, 1);
tag_attribute = read_uint8(p);
if (tag_attribute != 0) {
set_error_buf(error_buf, error_buf_size, "unknown tag attribute");
goto fail;
}
/* get type */
read_leb_uint32(p, p_end, tag_type);
/* compare against module->types */
if (tag_type >= parent_module->type_count) {
set_error_buf(error_buf, error_buf_size, "unknown tag type");
goto fail;
}
/* check, that the type of the referred tag returns void */
WASMType *func_type = (WASMType *)parent_module->types[tag_type];
if (func_type->result_count != 0) {
set_error_buf(error_buf, error_buf_size,
"tag type signature does not return void");
goto fail;
}
/* store to module tag declarations */
tag->attribute = tag_attribute;
tag->type = tag_type;
*p_buf = p;
(void)parent_module;
LOG_VERBOSE("Load tag import success\n");
return true;
fail:
return false;
}
#endif
static bool
load_global_import(const uint8 **p_buf, const uint8 *buf_end,
const WASMModule *parent_module, char *sub_module_name,
@ -1598,6 +1691,9 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
WASMImport *import;
WASMImport *import_functions = NULL, *import_tables = NULL;
WASMImport *import_memories = NULL, *import_globals = NULL;
#if WASM_ENABLE_TAGS != 0
WASMImport *import_tags = NULL;
#endif
char *sub_module_name, *field_name;
uint8 u8, kind;
@ -1626,7 +1722,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
p += name_len;
CHECK_BUF(p, p_end, 1);
/* 0x00/0x01/0x02/0x03 */
/* 0x00/0x01/0x02/0x03/0x04 */
kind = read_uint8(p);
switch (kind) {
@ -1667,6 +1763,16 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
}
break;
#if WASM_ENABLE_TAGS != 0
case IMPORT_KIND_TAG: /* import tags */
/* it only counts the number of tags to import */
module->import_tag_count++;
CHECK_BUF(p, p_end, 1);
u8 = read_uint8(p);
read_leb_uint32(p, p_end, type_index);
break;
#endif
case IMPORT_KIND_GLOBAL: /* import global */
CHECK_BUF(p, p_end, 2);
p += 2;
@ -1689,10 +1795,23 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
import_memories = module->import_memories =
module->imports + module->import_function_count
+ module->import_table_count;
#if WASM_ENABLE_TAGS != 0
if (module->import_tag_count)
import_tags = module->import_tags =
module->imports + module->import_function_count
+ module->import_table_count + module->import_memory_count;
if (module->import_global_count)
import_globals = module->import_globals =
module->imports + module->import_function_count
+ module->import_table_count + module->import_memory_count
+ module->import_tag_count;
#else
if (module->import_global_count)
import_globals = module->import_globals =
module->imports + module->import_function_count
+ module->import_table_count + module->import_memory_count;
#endif
p = p_old;
@ -1719,7 +1838,7 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
p += name_len;
CHECK_BUF(p, p_end, 1);
/* 0x00/0x01/0x02/0x03 */
/* 0x00/0x01/0x02/0x03/0x4 */
kind = read_uint8(p);
switch (kind) {
@ -1755,6 +1874,18 @@ load_import_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
}
break;
#if WASM_ENABLE_TAGS != 0
case IMPORT_KIND_TAG:
bh_assert(import_tags);
import = import_tags++;
if (!load_tag_import(&p, p_end, module, sub_module_name,
field_name, &import->u.tag, error_buf,
error_buf_size)) {
return false;
}
break;
#endif
case IMPORT_KIND_GLOBAL: /* import global */
bh_assert(import_globals);
import = import_globals++;
@ -2266,6 +2397,16 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
return false;
}
break;
#if WASM_ENABLE_TAGS != 0
/* export tag */
case EXPORT_KIND_TAG:
if (index >= module->tag_count + module->import_tag_count) {
set_error_buf(error_buf, error_buf_size, "unknown tag");
return false;
}
break;
#endif
/* global index */
case EXPORT_KIND_GLOBAL:
if (index
@ -2275,6 +2416,7 @@ load_export_section(const uint8 *buf, const uint8 *buf_end, WASMModule *module,
return false;
}
break;
default:
set_error_buf(error_buf, error_buf_size,
"invalid export kind");
@ -2681,6 +2823,75 @@ fail:
}
#endif
#if WASM_ENABLE_TAGS != 0
static bool
load_tag_section(const uint8 *buf, const uint8 *buf_end, const uint8 *buf_code,
const uint8 *buf_code_end, WASMModule *module, char *error_buf,
uint32 error_buf_size)
{
LOG_VERBOSE("In %s\n", __FUNCTION__);
const uint8 *p = buf, *p_end = buf_end;
size_t total_size = 0;
/* number of tags defined in the section */
uint32 section_tag_count = 0;
uint8 tag_attribute;
uint32 tag_type;
/* get tag count */
read_leb_uint32(p, p_end, section_tag_count);
module->tag_count = module->import_tag_count + section_tag_count;
if (section_tag_count) {
total_size = sizeof(WASMTag) * module->tag_count;
if (!(module->tags =
loader_malloc(total_size, error_buf, error_buf_size))) {
return false;
}
/* load each tag, imported tags precede the tags */
uint32 tag_index;
for (tag_index = module->import_tag_count;
tag_index < module->tag_count; tag_index++) {
/* get the one byte attribute */
CHECK_BUF(p, p_end, 1);
tag_attribute = read_uint8(p);
/* get type */
read_leb_uint32(p, p_end, tag_type);
/* compare against module->types */
if (tag_type >= module->type_count) {
set_error_buf(error_buf, error_buf_size, "unknown type");
return false;
}
/* get return type (must be 0) */
/* check, that the type of the referred tag returns void */
WASMType *func_type = (WASMType *)module->types[tag_type];
if (func_type->result_count != 0) {
set_error_buf(error_buf, error_buf_size,
"non-empty tag result type");
goto fail;
}
/* store to module tag declarations */
module->tags[tag_index].attribute = tag_attribute;
module->tags[tag_index].type = tag_type;
}
}
if (p != p_end) {
set_error_buf(error_buf, error_buf_size, "section size mismatch");
return false;
}
LOG_VERBOSE("Load tag section success.\n");
return true;
fail:
return false;
}
#endif
static bool
load_code_section(const uint8 *buf, const uint8 *buf_end, const uint8 *buf_func,
const uint8 *buf_func_end, WASMModule *module,
@ -3471,6 +3682,14 @@ load_from_sections(WASMModule *module, WASMSection *sections,
error_buf_size))
return false;
break;
#if WASM_ENABLE_TAGS != 0
case SECTION_TYPE_TAG:
/* load tag declaration section */
if (!load_tag_section(buf, buf_end, buf_code, buf_code_end,
module, error_buf, error_buf_size))
return false;
break;
#endif
case SECTION_TYPE_GLOBAL:
if (!load_global_section(buf, buf_end, module, error_buf,
error_buf_size))
@ -3940,6 +4159,9 @@ static uint8 section_ids[] = {
SECTION_TYPE_FUNC,
SECTION_TYPE_TABLE,
SECTION_TYPE_MEMORY,
#if WASM_ENABLE_TAGS != 0
SECTION_TYPE_TAG,
#endif
SECTION_TYPE_GLOBAL,
SECTION_TYPE_EXPORT,
SECTION_TYPE_START,
@ -4490,6 +4712,64 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
case WASM_OP_NOP:
break;
#if WASM_ENABLE_EXCE_HANDLING != 0
case WASM_OP_TRY:
u8 = read_uint8(p);
if (block_nested_depth
< sizeof(block_stack) / sizeof(BlockAddr)) {
block_stack[block_nested_depth].start_addr = p;
block_stack[block_nested_depth].else_addr = NULL;
}
block_nested_depth++;
break;
case EXT_OP_TRY:
skip_leb_uint32(p, p_end);
if (block_nested_depth
< sizeof(block_stack) / sizeof(BlockAddr)) {
block_stack[block_nested_depth].start_addr = p;
block_stack[block_nested_depth].else_addr = NULL;
}
block_nested_depth++;
break;
case WASM_OP_CATCH:
if (block_nested_depth == 1) {
*p_end_addr = (uint8 *)(p - 1);
/* stop search and return the address of the catch block */
return true;
}
break;
case WASM_OP_CATCH_ALL:
if (block_nested_depth == 1) {
*p_end_addr = (uint8 *)(p - 1);
/* stop search and return the address of the catch_all block
*/
return true;
}
break;
case WASM_OP_THROW:
/* skip tag_index */
skip_leb(p);
break;
case WASM_OP_RETHROW:
/* skip depth */
skip_leb(p);
break;
case WASM_OP_DELEGATE:
if (block_nested_depth == 1) {
*p_end_addr = (uint8 *)(p - 1);
return true;
}
else {
/* the DELEGATE opcode ends the tryblock, */
block_nested_depth--;
if (block_nested_depth
< sizeof(block_stack) / sizeof(BlockAddr))
block_stack[block_nested_depth].end_addr =
(uint8 *)(p - 1);
}
break;
#endif
case WASM_OP_BLOCK:
case WASM_OP_LOOP:
case WASM_OP_IF:
@ -4612,17 +4892,6 @@ wasm_loader_find_block_addr(WASMExecEnv *exec_env, BlockAddr *block_addr_cache,
u8 = read_uint8(p); /* 0x00 */
break;
#if WASM_ENABLE_EXCE_HANDLING != 0
case WASM_OP_TRY:
case WASM_OP_CATCH:
case WASM_OP_THROW:
case WASM_OP_RETHROW:
case WASM_OP_DELEGATE:
case WASM_OP_CATCH_ALL:
/* TODO */
return false;
#endif
case WASM_OP_DROP:
case WASM_OP_SELECT:
case WASM_OP_DROP_64:
@ -5369,6 +5638,10 @@ wasm_loader_ctx_init(WASMFunction *func, char *error_buf, uint32 error_buf_size)
goto fail;
loader_ctx->frame_csp_boundary = loader_ctx->frame_csp_bottom + 8;
#if WASM_ENABLE_EXCE_HANDLING != 0
func->exception_handler_count = 0;
#endif
#if WASM_ENABLE_FAST_INTERP != 0
loader_ctx->frame_offset_size = sizeof(int16) * 32;
if (!(loader_ctx->frame_offset_bottom = loader_ctx->frame_offset =
@ -6982,9 +7255,25 @@ check_block_stack(WASMLoaderContext *loader_ctx, BranchBlock *block,
/* Check stack cell num equals return cell num */
if (available_stack_cell != return_cell_num) {
#if WASM_ENABLE_EXCE_HANDLING != 0
/* testspec: this error message format is expected by try_catch.wast */
snprintf(
error_buf, error_buf_size, "type mismatch: %s requires [%s]%s[%s]",
block->label_type == LABEL_TYPE_TRY
|| (block->label_type == LABEL_TYPE_CATCH
&& return_cell_num > 0)
? "instruction"
: "block",
return_cell_num > 0 ? type2str(return_types[0]) : "",
" but stack has ",
available_stack_cell > 0 ? type2str(*(loader_ctx->frame_ref - 1))
: "");
goto fail;
#else
set_error_buf(error_buf, error_buf_size,
"type mismatch: stack size does not match block type");
goto fail;
#endif
}
/* Check stack values match return types */
@ -7298,6 +7587,24 @@ re_scan:
goto handle_op_block_and_loop;
case WASM_OP_BLOCK:
case WASM_OP_LOOP:
#if WASM_ENABLE_EXCE_HANDLING != 0
case WASM_OP_TRY:
if (opcode == WASM_OP_TRY) {
/*
* keep track of exception handlers to account for
* memory allocation
*/
func->exception_handler_count++;
/*
* try is a block
* do nothing special, but execution continues to
* to handle_op_block_and_loop,
* and that be pushes the csp
*/
}
#endif
#if WASM_ENABLE_FAST_INTERP != 0
PRESERVE_LOCAL_FOR_BLOCK();
#endif
@ -7349,7 +7656,6 @@ re_scan:
POP_TYPE(
wasm_type->types[wasm_type->param_count - i - 1]);
}
PUSH_CSP(LABEL_TYPE_BLOCK + (opcode - WASM_OP_BLOCK),
block_type, p);
@ -7363,6 +7669,11 @@ re_scan:
if (opcode == WASM_OP_BLOCK) {
skip_label();
}
#if WASM_ENABLE_EXCE_HANDLING != 0
else if (opcode == WASM_OP_TRY) {
skip_label();
}
#endif
else if (opcode == WASM_OP_LOOP) {
skip_label();
if (BLOCK_HAS_PARAM(block_type)) {
@ -7430,7 +7741,182 @@ re_scan:
#endif
break;
}
#if WASM_ENABLE_EXCE_HANDLING != 0
case WASM_OP_THROW:
{
SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true);
BranchBlock *cur_block = loader_ctx->frame_csp - 1;
uint8 label_type = cur_block->label_type;
uint32 tag_index = 0;
read_leb_int32(p, p_end, tag_index);
/* check validity of tag_index against module->tag_count */
/* check tag index is within the tag index space */
if (tag_index >= module->tag_count) {
set_error_buf(error_buf, error_buf_size,
"unknown tag index");
goto fail;
}
/* the index of the type stored in the tag declaration */
uint8 tag_type_index = module->tags[tag_index].type;
/* check validity of tag_type_index */
if (tag_type_index >= module->type_count) {
set_error_buf(error_buf, error_buf_size,
"unknown tag type index");
goto fail;
}
/* check, that the type of the referred tag returns void */
WASMType *func_type = (WASMType *)module->types[tag_type_index];
if (func_type->result_count != 0) {
set_error_buf(error_buf, error_buf_size,
"tag type signature does not return void");
goto fail;
}
/* throw is stack polymorphic */
(void)label_type;
RESET_STACK();
break;
}
case WASM_OP_RETHROW:
{
/* must be done before checking branch block */
SET_CUR_BLOCK_STACK_POLYMORPHIC_STATE(true);
/* check the target catching block: LABEL_TYPE_CATCH */
if (!(frame_csp_tmp = check_branch_block(
loader_ctx, &p, p_end, error_buf, error_buf_size)))
goto fail;
if (frame_csp_tmp->label_type != LABEL_TYPE_CATCH
&& frame_csp_tmp->label_type != LABEL_TYPE_CATCH_ALL) {
/* trap according to spectest (rethrow.wast) */
set_error_buf(error_buf, error_buf_size,
"invalid rethrow label");
goto fail;
}
BranchBlock *cur_block = loader_ctx->frame_csp - 1;
uint8 label_type = cur_block->label_type;
(void)label_type;
/* rethrow is stack polymorphic */
RESET_STACK();
break;
}
case WASM_OP_DELEGATE:
{
/* check target block is valid */
if (!(frame_csp_tmp = check_branch_block(
loader_ctx, &p, p_end, error_buf, error_buf_size)))
goto fail;
/* valid types */
if (LABEL_TYPE_TRY != frame_csp_tmp->label_type) {
snprintf(error_buf, error_buf_size, "unknown label");
goto fail;
}
BranchBlock *cur_block = loader_ctx->frame_csp - 1;
uint8 label_type = cur_block->label_type;
(void)label_type;
/* DELEGATE ends the block */
POP_CSP();
break;
}
case WASM_OP_CATCH:
{
BranchBlock *cur_block = loader_ctx->frame_csp - 1;
uint8 label_type = cur_block->label_type;
uint32 tag_index = 0;
read_leb_int32(p, p_end, tag_index);
/* check validity of tag_index against module->tag_count */
/* check tag index is within the tag index space */
if (tag_index >= module->tag_count) {
set_error_buf(error_buf, error_buf_size, "unknown tag");
goto fail;
}
/* the index of the type stored in the tag declaration */
uint8 tag_type_index = module->tags[tag_index].type;
/* check validity of tag_type_index */
if (tag_type_index >= module->type_count) {
set_error_buf(error_buf, error_buf_size,
"unknown tag type index");
goto fail;
}
/* check, that the type of the referred tag returns void */
WASMType *func_type = module->types[tag_type_index];
if (func_type->result_count != 0) {
set_error_buf(error_buf, error_buf_size,
"tag type signature does not return void");
goto fail;
}
/* check validity of current label (expect LABEL_TYPE_TRY or
* LABEL_TYPE_CATCH) */
if ((LABEL_TYPE_CATCH != label_type)
&& (LABEL_TYPE_TRY != label_type)) {
set_error_buf(error_buf, error_buf_size,
"Unexpected block sequence encountered.");
goto fail;
}
BlockType new_block_type;
new_block_type.is_value_type = false;
new_block_type.u.type = module->types[tag_type_index];
/*
* replace frame_csp by LABEL_TYPE_CATCH
*/
cur_block->label_type = LABEL_TYPE_CATCH;
/* RESET_STACK removes the values pushed in TRY or pervious
* CATCH Blocks */
RESET_STACK();
/* push types on the stack according to catched type */
if (BLOCK_HAS_PARAM(new_block_type)) {
for (i = 0; i < new_block_type.u.type->param_count; i++)
PUSH_TYPE(new_block_type.u.type->types[i]);
}
break;
}
case WASM_OP_CATCH_ALL:
{
BranchBlock *cur_block = loader_ctx->frame_csp - 1;
/* expecting a TRY or CATCH, anything else will be considered an
* error */
if ((LABEL_TYPE_CATCH != cur_block->label_type)
&& (LABEL_TYPE_TRY != cur_block->label_type)) {
set_error_buf(error_buf, error_buf_size,
"Unexpected block sequence encountered.");
goto fail;
}
/* no immediates */
/* replace frame_csp by LABEL_TYPE_CATCH_ALL */
cur_block->label_type = LABEL_TYPE_CATCH_ALL;
/* RESET_STACK removes the values pushed in TRY or pervious
* CATCH Blocks */
RESET_STACK();
/* catch_all has no tagtype and therefore no parameters */
break;
}
#endif
case WASM_OP_ELSE:
{
BlockType block_type = (loader_ctx->frame_csp - 1)->block_type;
@ -7898,19 +8384,6 @@ re_scan:
break;
}
#if WASM_ENABLE_EXCE_HANDLING != 0
case WASM_OP_TRY:
case WASM_OP_CATCH:
case WASM_OP_THROW:
case WASM_OP_RETHROW:
case WASM_OP_DELEGATE:
case WASM_OP_CATCH_ALL:
/* TODO */
set_error_buf_v(error_buf, error_buf_size, "%s %02x",
"unsupported opcode", opcode);
goto fail;
#endif
case WASM_OP_DROP:
{
BranchBlock *cur_block = loader_ctx->frame_csp - 1;

View File

@ -20,12 +20,10 @@ typedef enum WASMOpcode {
WASM_OP_LOOP = 0x03, /* loop */
WASM_OP_IF = 0x04, /* if */
WASM_OP_ELSE = 0x05, /* else */
WASM_OP_TRY = 0x06, /* try */
WASM_OP_CATCH = 0x07, /* catch */
WASM_OP_THROW = 0x08, /* throw */
WASM_OP_RETHROW = 0x09, /* rethrow */
WASM_OP_TRY = 0x06, /* try */
WASM_OP_CATCH = 0x07, /* catch* */
WASM_OP_THROW = 0x08, /* throw of a try catch */
WASM_OP_RETHROW = 0x09, /* rethrow of a try catch */
WASM_OP_UNUSED_0x0a = 0x0a,
WASM_OP_END = 0x0b, /* end */
@ -43,8 +41,8 @@ typedef enum WASMOpcode {
WASM_OP_UNUSED_0x16 = 0x16,
WASM_OP_UNUSED_0x17 = 0x17,
WASM_OP_DELEGATE = 0x18, /* delegate */
WASM_OP_CATCH_ALL = 0x19, /* catch_all */
WASM_OP_DELEGATE = 0x18, /* delegate block of the try catch*/
WASM_OP_CATCH_ALL = 0x19, /* a catch_all handler in a try block */
/* parametric instructions */
WASM_OP_DROP = 0x1a, /* drop */
@ -270,8 +268,10 @@ typedef enum WASMOpcode {
EXT_OP_IF = 0xd5, /* if with blocktype */
EXT_OP_BR_TABLE_CACHE = 0xd6, /* br_table from cache */
EXT_OP_TRY = 0xd7, /* try block with blocktype */
#if WASM_ENABLE_DEBUG_INTERP != 0
DEBUG_OP_BREAK = 0xd7, /* debug break point */
DEBUG_OP_BREAK = 0xd8, /* debug break point */
#endif
/* Post-MVP extend op prefix */
@ -907,6 +907,7 @@ typedef enum WASMAtomicEXTOpcode {
HANDLE_OPCODE(EXT_OP_LOOP), /* 0xd4 */ \
HANDLE_OPCODE(EXT_OP_IF), /* 0xd5 */ \
HANDLE_OPCODE(EXT_OP_BR_TABLE_CACHE), /* 0xd6 */ \
HANDLE_OPCODE(EXT_OP_TRY), /* 0xd7 */ \
SET_GOTO_TABLE_ELEM(WASM_OP_MISC_PREFIX), /* 0xfc */ \
SET_GOTO_TABLE_ELEM(WASM_OP_ATOMIC_PREFIX), /* 0xfe */ \
DEF_DEBUG_BREAK_HANDLE() \

View File

@ -338,6 +338,14 @@ else
CFLAGS += -DWASM_ENABLE_REF_TYPES=0
endif
ifeq ($(CONFIG_INTERPRETERS_WAMR_ENABLE_EXCE_HANDLING),y)
CFLAGS += -DWASM_ENABLE_EXCE_HANDLING=1
CFLAGS += -DWASM_ENABLE_TAGS=1
else
CFLAGS += -DWASM_ENABLE_EXCE_HANDLING=0
CFLAGS += -DWASM_ENABLE_TAGS=0
endif
CFLAGS += -Wno-strict-prototypes -Wno-shadow -Wno-unused-variable
CFLAGS += -Wno-int-conversion -Wno-implicit-function-declaration

View File

@ -68,7 +68,15 @@ def ignore_the_case(
gc_flag=False,
xip_flag=False,
qemu_flag=False,
eh_flag=False,
):
# print(f"case_name {case_name}\n")
if eh_flag and case_name in [ "tag", "try_catch", "rethrow", "try_delegate" ]:
return False
else:
return True
if case_name in ["comments", "inline-module", "names"]:
return True
@ -115,7 +123,11 @@ def ignore_the_case(
return False
def preflight_check(aot_flag):
def preflight_check(aot_flag, eh_flag):
global SPEC_TEST_DIR
if eh_flag:
SPEC_TEST_DIR="exception-handling/test/core"
if not pathlib.Path(SPEC_TEST_DIR).resolve().exists():
print(f"Can not find {SPEC_TEST_DIR}")
return False
@ -140,6 +152,7 @@ def test_case(
multi_thread_flag=False,
simd_flag=False,
xip_flag=False,
eh_flag=False,
clean_up_flag=True,
verbose_flag=True,
gc_flag=False,
@ -147,6 +160,7 @@ def test_case(
qemu_firmware="",
log="",
):
CMD = ["python3", "runtest.py"]
CMD.append("--wast2wasm")
CMD.append(WAST2WASM_CMD if not gc_flag else SPEC_INTERPRETER_CMD)
@ -181,6 +195,9 @@ def test_case(
if xip_flag:
CMD.append("--xip")
if eh_flag:
CMD.append("--eh")
if qemu_flag:
CMD.append("--qemu")
CMD.append("--qemu-firmware")
@ -253,6 +270,7 @@ def test_suite(
multi_thread_flag=False,
simd_flag=False,
xip_flag=False,
eh_flag=False,
clean_up_flag=True,
verbose_flag=True,
gc_flag=False,
@ -261,6 +279,10 @@ def test_suite(
qemu_firmware="",
log="",
):
global SPEC_TEST_DIR
if eh_flag:
SPEC_TEST_DIR="exception-handling/test/core"
suite_path = pathlib.Path(SPEC_TEST_DIR).resolve()
if not suite_path.exists():
print(f"can not find spec test cases at {suite_path}")
@ -290,6 +312,7 @@ def test_suite(
gc_flag,
xip_flag,
qemu_flag,
eh_flag,
):
filtered_case_list.append(case_path)
print(f"---> {len(case_list)} --filter--> {len(filtered_case_list)}")
@ -315,6 +338,7 @@ def test_suite(
multi_thread_flag,
simd_flag,
xip_flag,
eh_flag,
clean_up_flag,
verbose_flag,
gc_flag,
@ -352,6 +376,7 @@ def test_suite(
multi_thread_flag,
simd_flag,
xip_flag,
eh_flag,
clean_up_flag,
verbose_flag,
gc_flag,
@ -411,6 +436,14 @@ def main():
dest="xip_flag",
help="Running with the XIP feature",
)
# added to support WASM_ENABLE_EXCE_HANDLING
parser.add_argument(
"-e",
action="store_true",
default=False,
dest="eh_flag",
help="Running with the exception-handling feature",
)
parser.add_argument(
"-t",
action="store_true",
@ -483,7 +516,8 @@ def main():
options = parser.parse_args()
print(options)
if not preflight_check(options.aot_flag):
if not preflight_check(options.aot_flag, options.eh_flag):
return False
if not options.cases:
@ -502,6 +536,7 @@ def main():
options.multi_thread_flag,
options.simd_flag,
options.xip_flag,
options.eh_flag,
options.clean_up_flag,
options.verbose_flag,
options.gc_flag,
@ -526,6 +561,7 @@ def main():
options.multi_thread_flag,
options.simd_flag,
options.xip_flag,
options.eh_flag,
options.clean_up_flag,
options.verbose_flag,
options.gc_flag,

View File

@ -224,6 +224,9 @@ parser.add_argument('--simd', default=False, action='store_true',
parser.add_argument('--xip', default=False, action='store_true',
help="Enable XIP")
parser.add_argument('--eh', default=False, action='store_true',
help="Enable Exception Handling")
parser.add_argument('--multi-module', default=False, action='store_true',
help="Enable Multi-thread")
@ -692,6 +695,13 @@ def test_assert(r, opts, mode, cmd, expected):
if o.find(e) >= 0 or e.find(o) >= 0:
return True
# wasm-exception thrown out of function call, not a trap
if mode=='wasmexception':
o = re.sub('^Exception: ', '', out)
e = re.sub('^Exception: ', '', expected)
if o.find(e) >= 0 or e.find(o) >= 0:
return True
## 0x9:i32,-0x1:i32 -> ['0x9:i32', '-0x1:i32']
expected_list = re.split(',', expected)
out_list = re.split(',', out)
@ -917,6 +927,42 @@ def test_assert_exhaustion(r,opts,form):
expected = "Exception: %s\n" % m.group(3)
test_assert(r, opts, "exhaustion", "%s %s" % (func, " ".join(args)), expected)
# added to support WASM_ENABLE_EXCE_HANDLING
def test_assert_wasmexception(r,opts,form):
# params
# ^
# \(assert_exception\s+
# \(invoke\s+"([^"]+)"\s+
# (\(.*\))\s*
# ()
# \)\s*
# \)\s*
# $
m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*\)\s*$', form)
if not m:
# no params
# ^
# \(assert_exception\s+
# \(invoke\s+"([^"]+)"\s*
# ()
# \)\s*
# \)\s*
# $
m = re.search('^\(assert_exception\s+\(invoke\s+"([^"]+)"\s*()\)\s*\)\s*$', form)
if not m:
raise Exception("unparsed assert_exception: '%s'" % form)
func = m.group(1) # function name
if m.group(2) == '': # arguments
args = []
else:
args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
expected = "Exception: uncaught wasm exception\n"
test_assert(r, opts, "wasmexception", "%s %s" % (func, " ".join(args)), expected)
def do_invoke(r, opts, form):
# params
m = re.search('^\(invoke\s+"([^"]+)"\s+(\(.*\))\s*\)\s*$', form)
@ -954,6 +1000,8 @@ def compile_wast_to_wasm(form, wast_tempfile, wasm_tempfile, opts):
# default arguments
if opts.gc:
cmd = [opts.wast2wasm, "-u", "-d", wast_tempfile, "-o", wasm_tempfile]
elif opts.eh:
cmd = [opts.wast2wasm, "--enable-thread", "--no-check", "--enable-exceptions", "--enable-tail-call", wast_tempfile, "-o", wasm_tempfile ]
else:
cmd = [opts.wast2wasm, "--enable-thread", "--no-check",
wast_tempfile, "-o", wasm_tempfile ]
@ -1168,6 +1216,8 @@ if __name__ == "__main__":
test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r)
elif re.match("^\(assert_exhaustion\\b.*", form):
test_assert_exhaustion(r, opts, form)
elif re.match("^\(assert_exception\\b.*", form):
test_assert_wasmexception(r, opts, form)
elif re.match("^\(assert_unlinkable\\b.*", form):
test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile if test_aot else None, opts, r, False)
elif re.match("^\(assert_malformed\\b.*", form):

View File

@ -22,6 +22,8 @@ function help()
echo "-S enable SIMD feature"
echo "-G enable GC feature"
echo "-X enable XIP feature"
# added to support WASM_ENABLE_EXCE_HANDLING
echo "-e enable exception handling"
echo "-x test SGX"
echo "-w enable WASI threads"
echo "-b use the wabt binary release package instead of compiling from the source code"
@ -46,6 +48,7 @@ COLLECT_CODE_COVERAGE=0
ENABLE_SIMD=0
ENABLE_GC=0
ENABLE_XIP=0
ENABLE_EH=0
ENABLE_DEBUG_VERSION=0
ENABLE_GC_HEAP_VERIFY=0
#unit test case arrary
@ -58,7 +61,7 @@ QEMU_FIRMWARE=""
# prod/testsuite-all branch
WASI_TESTSUITE_COMMIT="cf64229727f71043d5849e73934e249e12cb9e06"
while getopts ":s:cabgvt:m:MCpSXxwPGQF:" opt
while getopts ":s:cabgvt:m:MCpSXexwPGQF:" opt
do
OPT_PARSED="TRUE"
case $opt in
@ -133,6 +136,10 @@ do
echo "enable XIP feature"
ENABLE_XIP=1
;;
e)
echo "enable exception handling feature"
ENABLE_EH=1
;;
x)
echo "test SGX"
SGX_OPT="--sgx"
@ -487,6 +494,88 @@ function spec_test()
echo -e "\nFinish spec tests" | tee -a ${REPORT_DIR}/spec_test_report.txt
}
function exception_test()
{
echo "Now start exception tests"
touch ${REPORT_DIR}/exception_test_report.txt
cd ${WORK_DIR}
if [ ! -d "exception-handling" ];then
echo "exception-handling not exist, clone it from github"
git clone -b master --single-branch https://github.com/WebAssembly/exception-handling
fi
pushd exception-handling
# restore and clean everything
git reset --hard HEAD
popd
echo $(pwd)
if [ ${WABT_BINARY_RELEASE} == "YES" ]; then
echo "download a binary release and install"
local WAT2WASM=${WORK_DIR}/wabt/out/gcc/Release/wat2wasm
if [ ! -f ${WAT2WASM} ]; then
case ${PLATFORM} in
linux)
WABT_PLATFORM=ubuntu
;;
darwin)
WABT_PLATFORM=macos
;;
*)
echo "wabt platform for ${PLATFORM} in unknown"
exit 1
;;
esac
if [ ! -f /tmp/wabt-1.0.31-${WABT_PLATFORM}.tar.gz ]; then
wget \
https://github.com/WebAssembly/wabt/releases/download/1.0.31/wabt-1.0.31-${WABT_PLATFORM}.tar.gz \
-P /tmp
fi
cd /tmp \
&& tar zxf wabt-1.0.31-${WABT_PLATFORM}.tar.gz \
&& mkdir -p ${WORK_DIR}/wabt/out/gcc/Release/ \
&& install wabt-1.0.31/bin/wa* ${WORK_DIR}/wabt/out/gcc/Release/ \
&& cd -
fi
else
echo "download source code and compile and install"
if [ ! -d "wabt" ];then
echo "wabt not exist, clone it from github"
git clone --recursive https://github.com/WebAssembly/wabt
fi
echo "upate wabt"
cd wabt
git pull
git reset --hard origin/main
cd ..
make -C wabt gcc-release -j 4
fi
ln -sf ${WORK_DIR}/../spec-test-script/all.py .
ln -sf ${WORK_DIR}/../spec-test-script/runtest.py .
local ARGS_FOR_SPEC_TEST="-e --no_clean_up "
# set log directory
ARGS_FOR_SPEC_TEST+="--log ${REPORT_DIR}"
cd ${WORK_DIR}
echo "python3 ./all.py ${ARGS_FOR_SPEC_TEST} | tee -a ${REPORT_DIR}/exception_test_report.txt"
python3 ./all.py ${ARGS_FOR_SPEC_TEST} | tee -a ${REPORT_DIR}/exception_test_report.txt
if [[ ${PIPESTATUS[0]} -ne 0 ]];then
echo -e "\nspec tests FAILED" | tee -a ${REPORT_DIR}/exception_test_report.txt
exit 1
fi
cd -
echo -e "\nFinish exception tests" | tee -a ${REPORT_DIR}/exception_test_report.txt
}
function wasi_test()
{
echo "Now start wasi tests"
@ -755,6 +844,11 @@ function trigger()
EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_LIB_WASI_THREADS=1"
fi
if [[ ${ENABLE_EH} == 1 ]]; then
EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_EXCE_HANDLING=1"
EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_TAIL_CALL=1"
EXTRA_COMPILE_FLAGS+=" -DWAMR_BUILD_MULTI_MODULE=1"
fi
echo "SANITIZER IS" $WAMR_BUILD_SANITIZER
if [[ "$WAMR_BUILD_SANITIZER" == "ubsan" ]]; then