Implement stack frame GC object tracking (#1065)

Implement stack frame GC object tracking for the reclaim root set traversing
And update wamr-test-suites for latest spec cases:
- Update GC spec cases to latest version
- Patch spec interpreter to support building it with ocaml 4.0.8
- Fix runtest.py to ref.null result
- Comment some supported cases in type-cannon and type-equivalence
This commit is contained in:
Wenyong Huang 2022-04-08 14:07:22 +08:00 committed by GitHub
parent baca3148e4
commit 68cdf30476
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 583 additions and 97 deletions

View File

@ -223,11 +223,15 @@ wasm_array_obj_new(void *heap_handle, WASMRttObjectRef rtt_obj, uint32 length,
PUT_REF_TO_ADDR(elem_addr, init_value->gc_obj);
}
else if (array_type->elem_type == VALUE_TYPE_I32
|| array_type->elem_type == VALUE_TYPE_F32
|| array_type->elem_type == PACKED_TYPE_I8
|| array_type->elem_type == PACKED_TYPE_I16) {
|| array_type->elem_type == VALUE_TYPE_F32) {
((int32 *)array_obj->elem_data)[i] = init_value->i32;
}
else if (array_type->elem_type == PACKED_TYPE_I8) {
((int8 *)array_obj->elem_data)[i] = (int8)init_value->i32;
}
else if (array_type->elem_type == PACKED_TYPE_I16) {
((int16 *)array_obj->elem_data)[i] = (int16)init_value->i32;
}
else {
uint32 *elem_addr = (uint32 *)array_obj->elem_data + 2 * i;
PUT_I64_TO_ADDR(elem_addr, init_value->i64);

View File

@ -205,6 +205,80 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign)
return result;
}
#if WASM_ENABLE_GC != 0
static uint8 *
get_frame_ref(WASMInterpFrame *frame)
{
WASMFunctionInstance *cur_func = frame->function;
unsigned all_cell_num;
if (!cur_func) {
/* it's a glue frame created in wasm_interp_call_wasm,
set all_cell_num since frame->sp == frame->lp and
no GC object will be traversed */
all_cell_num = 0;
}
else if (!frame->ip) {
/* it's a native method frame created in
wasm_interp_call_func_native */
all_cell_num =
cur_func->param_cell_num > 2 ? cur_func->param_cell_num : 2;
}
else {
/* it's a wasm bytecode function frame */
WASMFunction *cur_wasm_func = cur_func->u.func;
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;
}
return (uint8 *)(frame->lp + all_cell_num);
}
static void
init_frame_refs(uint8 *frame_ref, uint32 cell_num, WASMFuncType *func_type)
{
uint32 i, j;
memset(frame_ref, 0, cell_num);
for (i = 0, j = 0; i < func_type->param_count; i++) {
if (wasm_is_type_reftype(func_type->types[i])) {
frame_ref[j++] = 1;
#if UINTPTR_MAX == UINT64_MAX
frame_ref[j++] = 1;
#endif
}
else if (func_type->types[i] == VALUE_TYPE_I32
|| func_type->types[i] == VALUE_TYPE_F32)
j++;
else
j += 2;
}
}
/* Return the corresponding ref slot of the given address of local
variable or stack pointer. */
#define COMPUTE_FRAME_REF(ref, lp, p) (ref + (unsigned)((uint32 *)p - lp))
#define FRAME_REF(p) COMPUTE_FRAME_REF(frame_ref, frame_lp, p)
#define FRAME_REF_FOR(frame, p) \
COMPUTE_FRAME_REF(get_frame_ref(frame), frame->lp, p)
#define CLEAR_FRAME_REF(p, n) \
do { \
int32 ref_i, ref_n = (int32)(n); \
uint8 *ref = FRAME_REF(p); \
for (ref_i = 0; ref_i < ref_n; ref_i++) \
ref[ref_i] = 0; \
} while (0)
#else
#define CLEAR_FRAME_REF(p, n) (void)0
#endif /* end of WASM_ENABLE_GC != 0 */
#define skip_leb(p) while (*p++ & 0x80)
#define PUSH_I32(value) \
@ -229,11 +303,23 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign)
frame_sp += 2; \
} while (0)
#define PUSH_REF(value) \
do { \
PUT_REF_TO_ADDR(frame_sp, value); \
frame_sp += 2; \
#if UINTPTR_MAX == UINT64_MAX
#define PUSH_REF(value) \
do { \
PUT_REF_TO_ADDR(frame_sp, value); \
frame_ref_tmp = FRAME_REF(frame_sp); \
*frame_ref_tmp = *(frame_ref_tmp + 1) = 1; \
frame_sp += 2; \
} while (0)
#else
#define PUSH_REF(value) \
do { \
PUT_REF_TO_ADDR(frame_sp, value); \
frame_ref_tmp = FRAME_REF(frame_sp); \
*frame_ref_tmp = 1; \
frame_sp++; \
} while (0)
#endif
#define PUSH_CSP(_label_type, param_cell_num, cell_num, _target_addr) \
do { \
@ -254,9 +340,15 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign)
#define POP_F64() (frame_sp -= 2, GET_F64_FROM_ADDR(frame_sp))
#define POP_REF() \
(frame_sp -= sizeof(uintptr_t) / sizeof(uint32), \
#if UINTPTR_MAX == UINT64_MAX
#define POP_REF() \
(frame_sp -= 2, frame_ref_tmp = FRAME_REF(frame_sp), \
*frame_ref_tmp = *(frame_ref_tmp + 1) = 0, GET_REF_FROM_ADDR(frame_sp))
#else
#define POP_REF() \
(frame_sp--, frame_ref_tmp = FRAME_REF(frame_sp), *frame_ref_tmp = 0, \
GET_REF_FROM_ADDR(frame_sp))
#endif
#define POP_CSP_CHECK_OVERFLOW(n) \
do { \
@ -269,26 +361,31 @@ read_leb(const uint8 *buf, uint32 *p_offset, uint32 maxbits, bool sign)
--frame_csp; \
} while (0)
#define POP_CSP_N(n) \
do { \
uint32 *frame_sp_old = frame_sp; \
uint32 cell_num_to_copy; \
POP_CSP_CHECK_OVERFLOW(n + 1); \
frame_csp -= n; \
frame_ip = (frame_csp - 1)->target_addr; \
/* copy arity values of block */ \
frame_sp = (frame_csp - 1)->frame_sp; \
cell_num_to_copy = (frame_csp - 1)->cell_num; \
word_copy(frame_sp, frame_sp_old - cell_num_to_copy, \
cell_num_to_copy); \
frame_sp += cell_num_to_copy; \
#define POP_CSP_N(n) \
do { \
uint32 *frame_sp_old = frame_sp; \
uint32 cell_num_to_copy; \
POP_CSP_CHECK_OVERFLOW(n + 1); \
frame_csp -= n; \
frame_ip = (frame_csp - 1)->target_addr; \
/* copy arity values of block */ \
frame_sp = (frame_csp - 1)->frame_sp; \
cell_num_to_copy = (frame_csp - 1)->cell_num; \
word_copy(frame_sp, frame_sp_old - cell_num_to_copy, \
cell_num_to_copy); \
frame_ref_copy(FRAME_REF(frame_sp), \
FRAME_REF(frame_sp_old - cell_num_to_copy), \
cell_num_to_copy); \
frame_sp += cell_num_to_copy; \
CLEAR_FRAME_REF(frame_sp, frame_sp_old - frame_sp); \
} while (0)
/* Pop the given number of elements from the given frame's stack. */
#define POP(N) \
do { \
int n = (N); \
frame_sp -= n; \
#define POP(N) \
do { \
int n = (N); \
frame_sp -= n; \
CLEAR_FRAME_REF(frame_sp, n); \
} while (0)
#define SYNC_ALL_TO_FRAME() \
@ -730,10 +827,23 @@ sign_ext_32_64(int32 val)
static inline void
word_copy(uint32 *dest, uint32 *src, unsigned num)
{
for (; num > 0; num--)
*dest++ = *src++;
if (dest != src)
for (; num > 0; num--)
*dest++ = *src++;
}
#if WASM_ENABLE_GC != 0
static inline void
frame_ref_copy(uint8 *frame_ref_dest, uint8 *frame_ref_src, unsigned num)
{
if (frame_ref_dest != frame_ref_src)
for (; num > 0; num--)
*frame_ref_dest++ = *frame_ref_src++;
}
#else
#define frame_ref_copy(frame_ref_dst, frame_ref_src, num) (void)0
#endif
static inline WASMInterpFrame *
ALLOC_FRAME(WASMExecEnv *exec_env, uint32 size, WASMInterpFrame *prev_frame)
{
@ -773,20 +883,35 @@ wasm_interp_call_func_native(WASMModuleInstance *module_inst,
WASMInterpFrame *prev_frame)
{
WASMFunctionImport *func_import = cur_func->u.func_import;
unsigned local_cell_num = 2;
unsigned local_cell_num =
cur_func->param_cell_num > 2 ? cur_func->param_cell_num : 2;
unsigned all_cell_num;
WASMInterpFrame *frame;
uint32 argv_ret[2];
char buf[128];
bool ret;
#if WASM_ENABLE_GC != 0
uint8 *frame_ref;
#endif
if (!(frame = ALLOC_FRAME(exec_env,
wasm_interp_interp_frame_size(local_cell_num),
prev_frame)))
all_cell_num = local_cell_num;
#if WASM_ENABLE_GC != 0
all_cell_num += (local_cell_num + 3) / 4;
#endif
if (!(frame =
ALLOC_FRAME(exec_env, wasm_interp_interp_frame_size(all_cell_num),
prev_frame)))
return;
frame->function = cur_func;
frame->ip = NULL;
frame->sp = frame->lp + local_cell_num;
#if WASM_ENABLE_GC != 0
/* native function doesn't have operand stack and label stack */
frame_ref = (uint8 *)frame->sp;
init_frame_refs(frame_ref, local_cell_num, func_import->func_type);
#endif
wasm_exec_env_set_cur_frame(exec_env, frame);
@ -994,6 +1119,10 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
register uint8 *frame_ip = &opcode_IMPDEP; /* cache of frame->ip */
register uint32 *frame_lp = NULL; /* cache of frame->lp */
register uint32 *frame_sp = NULL; /* cache of frame->sp */
#if WASM_ENABLE_GC != 0
register uint8 *frame_ref = NULL; /* cache of frame->ref */
uint8 *frame_ref_tmp;
#endif
WASMBranchBlock *frame_csp = NULL;
BlockAddr *cache_items;
uint8 *frame_ip_end = frame_ip + 1;
@ -1092,7 +1221,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
{
value_type = *frame_ip++;
param_cell_num = 0;
cell_num = wasm_value_type_cell_num(value_type);
cell_num = 0;
handle_op_loop:
PUSH_CSP(LABEL_TYPE_LOOP, param_cell_num, cell_num, frame_ip);
HANDLE_OP_END();
@ -1168,6 +1297,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
else { /* end of function, treat as WASM_OP_RETURN */
frame_sp -= cur_func->ret_cell_num;
for (i = 0; i < cur_func->ret_cell_num; i++) {
#if WASM_ENABLE_GC != 0
if (prev_frame->ip) {
/* prev frame is not a glue frame and has
the frame ref area */
*FRAME_REF_FOR(prev_frame, prev_frame->sp) =
*FRAME_REF(frame_sp + i);
}
#endif
*prev_frame->sp++ = frame_sp[i];
}
goto return_func;
@ -1227,6 +1364,14 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
{
frame_sp -= cur_func->ret_cell_num;
for (i = 0; i < cur_func->ret_cell_num; i++) {
#if WASM_ENABLE_GC != 0
if (prev_frame->ip) {
/* prev frame is not a glue frame and has
the frame ref area */
*FRAME_REF_FOR(prev_frame, prev_frame->sp) =
*FRAME_REF(frame_sp + i);
}
#endif
*prev_frame->sp++ = frame_sp[i];
}
goto return_func;
@ -1573,6 +1718,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
gc_obj = GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM);
if (gc_obj == NULL_REF) {
frame_sp -= REF_CELL_NUM;
CLEAR_FRAME_REF(frame_sp, REF_CELL_NUM);
goto label_pop_csp_n;
}
HANDLE_OP_END();
@ -1590,6 +1736,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
}
else {
frame_sp -= REF_CELL_NUM;
CLEAR_FRAME_REF(frame_sp, REF_CELL_NUM);
}
HANDLE_OP_END();
}
@ -1939,7 +2086,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
case WASM_OP_REF_CAST:
{
rtt_obj = POP_REF();
gc_obj = GET_REF_FROM_ADDR(frame_sp - 2);
gc_obj = GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM);
if (gc_obj
&& (!rtt_obj
@ -1957,7 +2104,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
#endif
read_leb_uint32(frame_ip, frame_ip_end, depth);
rtt_obj = POP_REF();
gc_obj = GET_REF_FROM_ADDR(frame_sp - 2);
gc_obj = GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM);
if (gc_obj
&& (rtt_obj
@ -1974,7 +2121,7 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
#endif
read_leb_uint32(frame_ip, frame_ip_end, depth);
rtt_obj = POP_REF();
gc_obj = GET_REF_FROM_ADDR(frame_sp - 2);
gc_obj = GET_REF_FROM_ADDR(frame_sp - REF_CELL_NUM);
if (!gc_obj
|| (!rtt_obj
@ -4431,14 +4578,22 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
else {
WASMFunction *cur_wasm_func = cur_func->u.func;
WASMFuncType *func_type;
uint64 cell_num_of_local_stack;
func_type = cur_wasm_func->func_type;
all_cell_num = (uint64)cur_func->param_cell_num
+ (uint64)cur_func->local_cell_num
+ (uint64)cur_wasm_func->max_stack_cell_num
cell_num_of_local_stack =
(uint64)cur_func->param_cell_num
+ (uint64)cur_func->local_cell_num
+ (uint64)cur_wasm_func->max_stack_cell_num;
all_cell_num = cell_num_of_local_stack
+ ((uint64)cur_wasm_func->max_block_num)
* sizeof(WASMBranchBlock) / 4;
#if WASM_ENABLE_GC != 0
/* area of frame_ref */
all_cell_num += (cell_num_of_local_stack + 3) / 4;
#endif
if (all_cell_num >= UINT32_MAX) {
wasm_set_exception(module, "wasm operand stack overflow");
goto got_exception;
@ -4466,6 +4621,12 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
frame->csp_boundary =
frame->csp_bottom + cur_wasm_func->max_block_num;
#if WASM_ENABLE_GC != 0
frame_ref = (uint8 *)frame->csp_boundary;
init_frame_refs(frame_ref, (uint32)cell_num_of_local_stack,
func_type);
#endif
/* Initialize the local variables */
memset(frame_lp + cur_func->param_cell_num, 0,
(uint32)(cur_func->local_cell_num * 4));
@ -4515,6 +4676,31 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
#endif
}
#if WASM_ENABLE_GC != 0
void
vmci_gc_rootset_elem(void *heap, WASMObjectRef obj)
{}
void
wasm_interp_traverse_gc_rootset(WASMInterpFrame *frame, void *heap)
{
int i;
for (; frame; frame = frame->prev_frame) {
for (i = 0; i < frame->sp - frame->lp; i++) {
uint8 *frame_ref = get_frame_ref(frame);
if (frame_ref[i]) {
vmci_gc_rootset_elem(heap, (WASMObjectRef)&frame->lp[i]);
#if UINTPTR_MAX == UINT64_MAX
bh_assert(frame_ref[i + 1]);
i++;
#endif
}
}
}
}
#endif
void
wasm_interp_call_wasm(WASMModuleInstance *module_inst, WASMExecEnv *exec_env,
WASMFunctionInstance *function, uint32 argc,

View File

@ -787,8 +787,9 @@ sign_ext_32_64(int32 val)
static inline void
word_copy(uint32 *dest, uint32 *src, unsigned num)
{
for (; num > 0; num--)
*dest++ = *src++;
if (dest != src)
for (; num > 0; num--)
*dest++ = *src++;
}
static inline WASMInterpFrame *

View File

@ -5970,8 +5970,9 @@ check_stack_push(WASMLoaderContext *ctx, uint8 type, char *error_buf,
if (wasm_is_type_multi_byte_type(type)
&& ctx->frame_reftype_map >= ctx->frame_reftype_map_boundary) {
/* Increase the frame reftype map stack */
bh_assert(ctx->frame_reftype_map - ctx->frame_reftype_map_bottom
== ctx->frame_reftype_map_size);
bh_assert(
(uint32)(ctx->frame_reftype_map - ctx->frame_reftype_map_bottom)
== ctx->frame_reftype_map_size);
MEM_REALLOC(ctx->frame_reftype_map_bottom, ctx->frame_reftype_map_size,
ctx->frame_reftype_map_size
+ (uint32)sizeof(WASMRefTypeMap) * 8);
@ -12219,8 +12220,15 @@ re_scan:
}
if (loader_ctx->csp_num > 0) {
set_error_buf(error_buf, error_buf_size,
"function body must end with END opcode");
if (cur_func_idx
< module->import_function_count + module->function_count - 1) {
set_error_buf(error_buf, error_buf_size, "END opcode expected");
}
else {
set_error_buf(error_buf, error_buf_size,
"unexpected end of section or function, "
"or section size mismatch");
}
goto fail;
}

View File

@ -2739,3 +2739,31 @@ wasm_interp_dump_call_stack(struct WASMExecEnv *exec_env)
os_printf("\n");
}
#endif /* end of WASM_ENABLE_DUMP_CALL_STACK */
#if WASM_ENABLE_GC != 0
bool
wasm_runtime_traverse_gc_rootset(WASMExecEnv *exec_env, void *heap)
{
return false;
}
void
wasm_runtime_gc_prepare(WASMExecEnv *exec_env)
{
#if 0
exec_env->is_gc_reclaiming = false;
wasm_thread_suspend_all();
exec_env->is_gc_reclaim = 1;
exec_env->requesting_suspend = 0;
#endif
}
void
wasm_runtime_gc_finished(WASMExecEnv *exec_env)
{
#if 0
wasm_thread_resume_all();
exec_env->doing_gc_reclaim = 0;
#endif
}
#endif /* end of WASM_ENABLE_GC != 0 */

View File

@ -19,10 +19,18 @@ import time
"""
The script itself has to be put under the same directory with the "spec".
To run single spec case:
To run a single non-GC case with interpreter mode:
cd workspace
python2.7 runtest.py --wast2wasm spec/interpreter/wasm --interpreter iwasm
--aot-compiler wamrc --gc --loader-only spec/test/core/xxx.wast
python2.7 runtest.py --wast2wasm wabt/bin/wat2wasm --interpreter iwasm \
spec/test/core/xxx.wast
To run a single non-GC case with aot mode:
cd workspace
python2.7 runtest.py --aot --wast2wasm wabt/bin/wat2wasm --interpreter iwasm \
--aot-compiler wamrc spec/test/core/xxx.wast
To run a single GC case:
cd workspace
python2.7 runtest.py --wast2wasm spec/interpreter/wasm --interpreter iwasm \
--aot-compiler wamrc --gc spec/test/core/xxx.wast
"""
PLATFORM_NAME = os.uname().sysname.lower()
@ -65,7 +73,7 @@ def ignore_the_case(
if not multi_module_flag and case_name in ["imports", "linking"]:
return True
if gc_flag and case_name in ["func_bind", "let"]:
if gc_flag and case_name in ["func_bind", "let", "type-canon"]:
return True
if "i386" == target and case_name in ["float_exprs"]:

View File

@ -1,5 +1,18 @@
diff --git a/interpreter/exec/eval.ml b/interpreter/exec/eval.ml
index 1dc208bf..3e3ff5e6 100644
--- a/interpreter/exec/eval.ml
+++ b/interpreter/exec/eval.ml
@@ -1232,7 +1232,7 @@ let init (m : module_) (exts : extern list) : module_inst =
in
if List.length exts <> List.length imports then
Link.error m.at "wrong number of imports provided for initialisation";
- let inst0 = {empty_module_inst with types = List.concat_map create_type types} in
+ let inst0 = {empty_module_inst with types = List.concat (List.map create_type types)} in
ignore (List.fold_left (init_type inst0) (0l, inst0.types) types);
let inst1 = List.fold_right2 (add_import m) exts imports inst0 in
let fs = List.map (create_func inst1) funcs in
diff --git a/test/core/binary.wast b/test/core/binary.wast
index 1f76dc13..f4512ade 100644
index fbca3d94..a592bfdc 100644
--- a/test/core/binary.wast
+++ b/test/core/binary.wast
@@ -161,7 +161,7 @@
@ -20,7 +33,7 @@ index 1f76dc13..f4512ade 100644
;; Unsigned LEB128 must not be overlong
(assert_malformed
@@ -1582,7 +1582,7 @@
@@ -1638,7 +1638,7 @@
)
;; 2 elem segment declared, 1 given
@ -29,7 +42,7 @@ index 1f76dc13..f4512ade 100644
(module binary
"\00asm" "\01\00\00\00"
"\01\04\01" ;; type section
@@ -1595,7 +1595,7 @@
@@ -1651,7 +1651,7 @@
;; "\00\41\00\0b\01\00" ;; elem 1 (missed)
)
"unexpected end"
@ -171,7 +184,7 @@ index 35e8c917..a7a459df 100644
;; Syntax errors
diff --git a/test/core/linking.wast b/test/core/linking.wast
index 85de43d8..6b218e3b 100644
index 6a8ba1d0..e2721631 100644
--- a/test/core/linking.wast
+++ b/test/core/linking.wast
@@ -64,6 +64,7 @@
@ -190,7 +203,7 @@ index 85de43d8..6b218e3b 100644
(assert_unlinkable
@@ -317,6 +319,7 @@
@@ -300,6 +302,7 @@
)
)
@ -198,7 +211,7 @@ index 85de43d8..6b218e3b 100644
(assert_return (invoke $Mt "call" (i32.const 2)) (i32.const 4))
(assert_return (invoke $Nt "Mt.call" (i32.const 2)) (i32.const 4))
(assert_return (invoke $Nt "call" (i32.const 2)) (i32.const 5))
@@ -339,6 +342,7 @@
@@ -322,6 +325,7 @@
(assert_return (invoke $Nt "call" (i32.const 3)) (i32.const -4))
(assert_trap (invoke $Nt "call" (i32.const 4)) "indirect call type mismatch")
@ -206,7 +219,7 @@ index 85de43d8..6b218e3b 100644
(module $Ot
(type (func (result i32)))
@@ -353,6 +357,7 @@
@@ -336,6 +340,7 @@
)
)
@ -214,7 +227,7 @@ index 85de43d8..6b218e3b 100644
(assert_return (invoke $Mt "call" (i32.const 3)) (i32.const 4))
(assert_return (invoke $Nt "Mt.call" (i32.const 3)) (i32.const 4))
(assert_return (invoke $Nt "call Mt.call" (i32.const 3)) (i32.const 4))
@@ -377,6 +382,7 @@
@@ -360,6 +365,7 @@
(assert_trap (invoke $Ot "call" (i32.const 0)) "uninitialized element")
(assert_trap (invoke $Ot "call" (i32.const 20)) "undefined element")
@ -222,7 +235,7 @@ index 85de43d8..6b218e3b 100644
(module
(table (import "Mt" "tab") 0 funcref)
@@ -415,6 +421,7 @@
@@ -398,6 +404,7 @@
;; Unlike in the v1 spec, active element segments stored before an
;; out-of-bounds access persist after the instantiation failure.
@ -230,7 +243,7 @@ index 85de43d8..6b218e3b 100644
(assert_trap
(module
(table (import "Mt" "tab") 10 funcref)
@@ -426,7 +433,9 @@
@@ -409,7 +416,9 @@
)
(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))
(assert_trap (invoke $Mt "call" (i32.const 8)) "uninitialized element")
@ -240,7 +253,7 @@ index 85de43d8..6b218e3b 100644
(assert_trap
(module
(table (import "Mt" "tab") 10 funcref)
@@ -438,6 +447,7 @@
@@ -421,6 +430,7 @@
"out of bounds memory access"
)
(assert_return (invoke $Mt "call" (i32.const 7)) (i32.const 0))
@ -248,7 +261,7 @@ index 85de43d8..6b218e3b 100644
(module $Mtable_ex
@@ -455,6 +465,7 @@
@@ -438,6 +448,7 @@
(table (import "Mtable_ex" "t-extern") 1 externref)
)
@ -256,7 +269,7 @@ index 85de43d8..6b218e3b 100644
(assert_unlinkable
(module (table (import "Mtable_ex" "t-refnull") 1 (ref null func)))
"incompatible import type"
@@ -481,6 +492,7 @@
@@ -464,6 +475,7 @@
(module (table (import "Mtable_ex" "t-refnull") 1 externref))
"incompatible import type"
)
@ -264,7 +277,7 @@ index 85de43d8..6b218e3b 100644
;; Memories
@@ -520,10 +532,12 @@
@@ -503,10 +515,12 @@
)
)
@ -277,7 +290,7 @@ index 85de43d8..6b218e3b 100644
(module
(memory (import "Mm" "mem") 0)
@@ -546,6 +560,7 @@
@@ -529,6 +543,7 @@
)
)
@ -285,7 +298,7 @@ index 85de43d8..6b218e3b 100644
(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 1))
(assert_return (invoke $Pm "grow" (i32.const 2)) (i32.const 1))
(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 3))
@@ -554,6 +569,7 @@
@@ -537,6 +552,7 @@
(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5))
(assert_return (invoke $Pm "grow" (i32.const 1)) (i32.const -1))
(assert_return (invoke $Pm "grow" (i32.const 0)) (i32.const 5))
@ -293,7 +306,7 @@ index 85de43d8..6b218e3b 100644
(assert_unlinkable
(module
@@ -577,8 +593,10 @@
@@ -560,8 +576,10 @@
)
"out of bounds memory access"
)
@ -304,7 +317,7 @@ index 85de43d8..6b218e3b 100644
(assert_trap
(module
@@ -590,7 +608,9 @@
@@ -573,7 +591,9 @@
)
"out of bounds table access"
)
@ -314,7 +327,7 @@ index 85de43d8..6b218e3b 100644
;; Store is modified if the start function traps.
(module $Ms
@@ -606,6 +626,7 @@
@@ -589,6 +609,7 @@
)
(register "Ms" $Ms)
@ -322,7 +335,7 @@ index 85de43d8..6b218e3b 100644
(assert_trap
(module
(import "Ms" "memory" (memory 1))
@@ -625,3 +646,4 @@
@@ -608,3 +629,4 @@
(assert_return (invoke $Ms "get memory[0]") (i32.const 104)) ;; 'h'
(assert_return (invoke $Ms "get table[0]") (i32.const 0xdead))
@ -342,9 +355,18 @@ index adb5cb78..590f6262 100644
(i32.add (local.get $x) (i32.const 1))
)
diff --git a/test/core/select.wast b/test/core/select.wast
index fbe51be1..bb209d9b 100644
index 94aa8605..baf0f9c5 100644
--- a/test/core/select.wast
+++ b/test/core/select.wast
@@ -277,7 +277,7 @@
(assert_return (invoke "select-f64-t" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304))
(assert_return (invoke "join-funcnull" (i32.const 1)) (ref.func))
-(assert_return (invoke "join-funcnull" (i32.const 0)) (ref.null))
+(assert_return (invoke "join-funcnull" (i32.const 0)) (ref.null func))
(assert_trap (invoke "select-trap-left" (i32.const 1)) "unreachable")
(assert_trap (invoke "select-trap-left" (i32.const 0)) "unreachable")
@@ -368,6 +368,7 @@
(module (func $arity-0 (select (result) (nop) (nop) (i32.const 1))))
"invalid result arity"
@ -784,3 +806,225 @@ index 0b2d26f7..bdab6a01 100644
(table $t0 30 30 funcref)
(table $t1 30 30 funcref)
(elem (table $t1) (i32.const 2) func 3 1 4 1)
diff --git a/test/core/type-equivalence.wast b/test/core/type-equivalence.wast
index ed7e1a51..be10fd5a 100644
--- a/test/core/type-equivalence.wast
+++ b/test/core/type-equivalence.wast
@@ -28,8 +28,8 @@
;; Recursive types.
(module
- (rec (type $t1 (func (param i32 (ref $t1)))))
- (rec (type $t2 (func (param i32 (ref $t2)))))
+ (type $t1 (func (param i32 (ref $t1))))
+ (type $t2 (func (param i32 (ref $t2))))
(func $f1 (param $r (ref $t1)) (call $f2 (local.get $r)))
(func $f2 (param $r (ref $t2)) (call $f1 (local.get $r)))
@@ -47,14 +47,10 @@
;; Isomorphic recursive types.
(module
- (rec
- (type $t0 (func (param i32 (ref $t1))))
- (type $t1 (func (param i32 (ref $t0))))
- )
- (rec
- (type $t2 (func (param i32 (ref $t3))))
- (type $t3 (func (param i32 (ref $t2))))
- )
+ (type $t0 (func (param i32 (ref $t1))))
+ (type $t1 (func (param i32 (ref $t0))))
+ (type $t2 (func (param i32 (ref $t3))))
+ (type $t3 (func (param i32 (ref $t2))))
(func $f0 (param $r (ref $t0))
(call $f2 (local.get $r))
@@ -73,6 +69,7 @@
;; Invalid recursion.
+(;
(assert_invalid
(module
(type $t1 (func (param (ref $t2))))
@@ -80,6 +77,7 @@
)
"unknown type"
)
+;)
;; Semantic types (run time)
@@ -134,8 +132,8 @@
;; Recursive types.
(module
- (rec (type $t1 (func (result (ref null $t1)))))
- (rec (type $t2 (func (result (ref null $t2)))))
+ (type $t1 (func (result (ref null $t1))))
+ (type $t2 (func (result (ref null $t2))))
(func $f1 (type $t1) (ref.null $t1))
(func $f2 (type $t2) (ref.null $t2))
@@ -155,21 +153,16 @@
)
(assert_return (invoke "run"))
-
;; Isomorphic recursive types.
(module
- (rec
- (type $t1 (func (param i32 (ref $t1))))
- (type $t2 (func (param i32 (ref $t3))))
- (type $t3 (func (param i32 (ref $t2))))
- )
+ (type $t1 (func (param i32 (ref $t1))))
+ (type $t2 (func (param i32 (ref $t3))))
+ (type $t3 (func (param i32 (ref $t2))))
- (rec
- (type $u1 (func (param i32 (ref $u1))))
- (type $u2 (func (param i32 (ref $u3))))
- (type $u3 (func (param i32 (ref $u2))))
- )
+ (type $u1 (func (param i32 (ref $u1))))
+ (type $u2 (func (param i32 (ref $u3))))
+ (type $u3 (func (param i32 (ref $u2))))
(func $f1 (type $t1))
(func $f2 (type $t2))
@@ -187,7 +180,6 @@
)
(assert_return (invoke "run"))
-
;; Semantic types (link time)
;; Simple types.
@@ -196,11 +188,13 @@
(type $t1 (func (param f32 f32) (result f32)))
(func (export "f") (param (ref $t1)))
)
+(;
(register "M")
(module
(type $t2 (func (param $x f32) (param $y f32) (result f32)))
(func (import "M" "f") (param (ref $t2)))
)
+;)
;; Indirect types.
@@ -214,6 +208,7 @@
(func (export "f1") (param (ref $t1)))
(func (export "f2") (param (ref $t1)))
)
+(;
(register "M")
(module
(type $s0 (func (param i32) (result f32)))
@@ -226,33 +221,35 @@
(func (import "M" "f2") (param (ref $t1)))
(func (import "M" "f2") (param (ref $t1)))
)
+;)
;; Recursive types.
(module
- (rec (type $t1 (func (param i32 (ref $t1)))))
+ (type $t1 (func (param i32 (ref $t1))))
(func (export "f") (param (ref $t1)))
)
+(;
(register "M")
(module
(rec (type $t2 (func (param i32 (ref $t2)))))
(func (import "M" "f") (param (ref $t2)))
)
+;)
;; Isomorphic recursive types.
(module
- (rec
- (type $t1 (func (param i32 (ref $t1))))
- (type $t2 (func (param i32 (ref $t3))))
- (type $t3 (func (param i32 (ref $t2))))
- )
+ (type $t1 (func (param i32 (ref $t1))))
+ (type $t2 (func (param i32 (ref $t3))))
+ (type $t3 (func (param i32 (ref $t2))))
(func (export "f1") (param (ref $t1)))
(func (export "f2") (param (ref $t2)))
(func (export "f3") (param (ref $t3)))
)
+(;
(register "M")
(module
(rec
@@ -264,17 +261,17 @@
(func (import "M" "f2") (param (ref $t2)))
(func (import "M" "f3") (param (ref $t3)))
)
+;)
(module
- (rec
- (type $t1 (func (param i32 (ref $t3))))
- (type $t2 (func (param i32 (ref $t1))))
- (type $t3 (func (param i32 (ref $t2))))
- )
+ (type $t1 (func (param i32 (ref $t3))))
+ (type $t2 (func (param i32 (ref $t1))))
+ (type $t3 (func (param i32 (ref $t2))))
(func (export "f1") (param (ref $t1)))
(func (export "f2") (param (ref $t2)))
(func (export "f3") (param (ref $t3)))
)
+(;
(register "M")
(module
(rec
@@ -286,24 +283,22 @@
(func (import "M" "f2") (param (ref $t2)))
(func (import "M" "f3") (param (ref $t3)))
)
+;)
(module
- (rec
- (type $t1 (func (param i32 (ref $u1))))
- (type $u1 (func (param f32 (ref $t1))))
- )
+ (type $t1 (func (param i32 (ref $u1))))
+ (type $u1 (func (param f32 (ref $t1))))
- (rec
- (type $t2 (func (param i32 (ref $u3))))
- (type $u2 (func (param f32 (ref $t3))))
- (type $t3 (func (param i32 (ref $u2))))
- (type $u3 (func (param f32 (ref $t2))))
- )
+ (type $t2 (func (param i32 (ref $u3))))
+ (type $u2 (func (param f32 (ref $t3))))
+ (type $t3 (func (param i32 (ref $u2))))
+ (type $u3 (func (param f32 (ref $t2))))
(func (export "f1") (param (ref $t1)))
(func (export "f2") (param (ref $t2)))
(func (export "f3") (param (ref $t3)))
)
+(;
(register "M")
(module
(rec
@@ -322,3 +317,4 @@
(func (import "M" "f2") (param (ref $t2)))
(func (import "M" "f3") (param (ref $t3)))
)
+;)

View File

@ -409,8 +409,12 @@ def parse_simple_const_w_type(number, type):
number = float.fromhex(number) if '0x' in number else float(number)
return number, "{:.7g}:{}".format(number, type)
elif type == "ref.null":
# hard coding
return "extern", "extern:ref.null"
if number == "func":
return "func", "func:ref.null"
elif number == "extern":
return "extern", "extern:ref.null"
else:
raise Exception("invalid value {} and type {}".format(number, type))
elif type == "ref.extern":
number = int(number, 16) if '0x' in number else int(number)
return number, "0x{:x}:ref.extern".format(number)
@ -429,6 +433,10 @@ def parse_assertion_value(val):
type.const val
ref.extern val
ref.null ref_type
ref.array
ref.data
ref.func
ref.i31
"""
if not val:
return None, ""
@ -442,6 +450,8 @@ def parse_assertion_value(val):
if type in ["i32", "i64", "f32", "f64"]:
return parse_simple_const_w_type(numbers[0], type)
elif type == "ref":
if splitted[0] in ["ref.array", "ref.data", "ref.func", "ref.i31"]:
return splitted[0]
# need to distinguish between "ref.null" and "ref.extern"
return parse_simple_const_w_type(numbers[0], splitted[0])
else:
@ -570,6 +580,9 @@ def simple_value_comparison(out, expected):
# the add result in x86_32 is inf
return True
if out == "ref.array":
return expected in ["ref.array", "ref.data"]
out_val, out_type = out.split(':')
expected_val, expected_type = expected.split(':')
@ -626,8 +639,10 @@ def value_comparison(out, expected):
if not expected:
return False
assert(':' in out), "out should be in a form likes numbers:type, but {}".format(out)
assert(':' in expected), "expected should be in a form likes numbers:type, but {}".format(expected)
if not out in ["ref.array", "ref.data", "ref.func", "ref.i31"]:
assert(':' in out), "out should be in a form likes numbers:type, but {}".format(out)
if not expected in ["ref.array", "ref.data", "ref.func", "ref.i31"]:
assert(':' in expected), "expected should be in a form likes numbers:type, but {}".format(expected)
if 'v128' in out:
return vector_value_comparison(out, expected)
@ -758,7 +773,10 @@ def test_assert_return(r, opts, form):
else:
returns = re.split("\)\s*\(", m.group(3)[1:-1])
# processed numbers in strings
expected = [parse_assertion_value(v)[1] for v in returns]
if len(returns) == 1 and returns[0] in ["ref.array", "ref.data", "ref.func", "ref.i31", "ref.null"]:
expected = [returns[0]]
else:
expected = [parse_assertion_value(v)[1] for v in returns]
test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), ",".join(expected))
elif not m and n:
module = os.path.join(temp_file_directory,n.group(1))
@ -789,10 +807,10 @@ def test_assert_return(r, opts, form):
if n.group(3) == '':
args=[]
else:
args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n.group(3)[1:-1])]
# a workaround for "ref.null extern" and "ref.null func"
args = [ arg.replace('extern', 'null').replace('func', 'null') for arg in args]
# convert (ref.null extern/func) into (ref.null null)
n1 = n.group(3).replace("(ref.null extern)", "(ref.null null)")
n1 = n1.replace("ref.null func)", "(ref.null null)")
args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", n1[1:-1])]
_, expected = parse_assertion_value(n.group(4)[1:-1])
test_assert(r, opts, "return", "%s %s" % (func, " ".join(args)), expected)
@ -817,10 +835,10 @@ def test_assert_trap(r, opts, form):
if m.group(2) == '':
args = []
else:
args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m.group(2)[1:-1])]
# workaround for "ref.null extern"
args = [ arg.replace('extern', 'null').replace('func', 'null') for arg in args]
# convert (ref.null extern/func) into (ref.null null)
m1 = m.group(2).replace("(ref.null extern)", "(ref.null null)")
m1 = m1.replace("ref.null func)", "(ref.null null)")
args = [re.split(' +', v)[1] for v in re.split("\)\s*\(", m1[1:-1])]
expected = "Exception: %s" % m.group(3)
test_assert(r, opts, "trap", "%s %s" % (func, " ".join(args)), expected)
@ -1110,19 +1128,13 @@ if __name__ == "__main__":
elif skip_test(form, SKIP_TESTS):
log("Skipping test: %s" % form[0:60])
elif re.match("^\(assert_trap\s+\(module", form):
if opts.loader_only:
continue
if test_aot:
test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile, opts, r)
else:
test_assert_with_exception(form, wast_tempfile, wasm_tempfile, None, opts, r)
elif re.match("^\(assert_exhaustion\\b.*", form):
if opts.loader_only:
continue
test_assert_exhaustion(r, opts, form)
elif re.match("^\(assert_unlinkable\\b.*", form):
if opts.loader_only:
continue
if test_aot:
test_assert_with_exception(form, wast_tempfile, wasm_tempfile, aot_tempfile, opts, r)
else:
@ -1193,8 +1205,6 @@ if __name__ == "__main__":
log("ignoring assert_return_.*_nan")
pass
elif re.match(".*\(invoke\s+\$\\b.*", form):
if opts.loader_only:
continue
# invoke a particular named module's function
if form.startswith("(assert_return"):
test_assert_return(r,opts,form)
@ -1248,17 +1258,11 @@ if __name__ == "__main__":
(repr(exc), r.buf))
elif re.match("^\(assert_return\\b.*", form):
if opts.loader_only:
continue
assert(r), "iwasm repl runtime should be not null"
test_assert_return(r, opts, form)
elif re.match("^\(assert_trap\\b.*", form):
if opts.loader_only:
continue
test_assert_trap(r, opts, form)
elif re.match("^\(invoke\\b.*", form):
if opts.loader_only:
continue
assert(r), "iwasm repl runtime should be not null"
do_invoke(r, opts, form)
elif re.match("^\(assert_invalid\\b.*", form):

View File

@ -320,6 +320,7 @@ function spec_test()
git restore . && git clean -ffd .
git fetch gc
git checkout -B gc_spec --track gc/master
git reset --hard c486ace0c751ebfc66dfcc59c8c224db29cedbe5
git apply ../../spec-test-script/gc_ignore_cases.patch
echo "compile the reference intepreter"
@ -373,6 +374,7 @@ function spec_test()
make -C wabt gcc-release -j 4
fi
ln -sf ${WORK_DIR}/../spec-test-script/all.sh .
ln -sf ${WORK_DIR}/../spec-test-script/all.py .
ln -sf ${WORK_DIR}/../spec-test-script/runtest.py .
@ -421,6 +423,7 @@ function spec_test()
fi
cd ${WORK_DIR}
#./all.sh ${ARGS_FOR_SPEC_TEST} | tee -a ${REPORT_DIR}/spec_test_report.txt
python3 ./all.py ${ARGS_FOR_SPEC_TEST} | tee -a ${REPORT_DIR}/spec_test_report.txt
[[ ${PIPESTATUS[0]} -ne 0 ]] && exit 1