kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 472d97ae4494c11c424d81095648ee83c1a50dd2
parent 2e3bc93c4d520c5d535c59d1e6a0a972017921b8
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 18 May 2026 01:00:20 -0700

Polish WASM trap lowering

Diffstat:
Mdoc/WASM.md | 6+++---
Mlang/wasm/wasm.c | 301+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mtest/wasm/run.sh | 32++++++++++++++++++++++++++++++++
Atest/wasm/trap/bounds.wat | 5+++++
Atest/wasm/trap/div_overflow.wat | 5+++++
Atest/wasm/trap/div_zero.wat | 5+++++
Atest/wasm/trap/invalid_conversion.wat | 4++++
Atest/wasm/trap/signature.wat | 10++++++++++
Atest/wasm/trap/table_null.wat | 6++++++
Atest/wasm/trap/unreachable.wat | 3+++
10 files changed, 337 insertions(+), 40 deletions(-)

diff --git a/doc/WASM.md b/doc/WASM.md @@ -847,8 +847,8 @@ slots and runtime table storage remain open work. and MVP conversions. - [x] Lower f32/f64 arithmetic, comparisons, constants, conversions, and reinterpret ops. -- [ ] Add checked traps for `i32/i64.trunc_f32/f64_{s,u}` NaN/out-of-range - cases; current lowering relies on target float-to-int conversion behavior. +- [x] Add checked traps for `i32/i64.trunc_f32/f64_{s,u}` NaN/out-of-range + cases. - [x] Lower single linear memory load/store operations, `memory.size`, and active data initialization. - [x] Implement checked single-memory loads/stores and growable storage. @@ -877,7 +877,7 @@ slots and runtime table storage remain open work. - [x] Add internal `CfreeWasmTable` and import-slot structs under `lang/wasm` or `rt`, and use them for imported functions/globals/memories and active element initialization. -- [ ] Add trap helpers for unreachable, division traps, invalid conversion, +- [x] Add trap helpers for unreachable, division traps, invalid conversion, bounds checks, table checks, and indirect-call signature checks. - [x] Define ownership and initialization for runtime tables, active elements, and imported state. diff --git a/lang/wasm/wasm.c b/lang/wasm/wasm.c @@ -3767,16 +3767,15 @@ static uint32_t wasm_mem_width(uint8_t kind) { } } -static void wasm_cg_trap(CfreeCg* cg) { - cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE); - cfree_cg_unreachable(cg); -} - -static void wasm_cg_trap_unreachable(CfreeCg* cg) { wasm_cg_trap(cg); } -static void wasm_cg_trap_division(CfreeCg* cg) { wasm_cg_trap(cg); } -static void wasm_cg_trap_bounds(CfreeCg* cg) { wasm_cg_trap(cg); } -static void wasm_cg_trap_table(CfreeCg* cg) { wasm_cg_trap(cg); } -static void wasm_cg_trap_signature(CfreeCg* cg) { wasm_cg_trap(cg); } +typedef enum WasmTrapKind { + WASM_TRAP_UNREACHABLE, + WASM_TRAP_DIVISION, + WASM_TRAP_INVALID_CONVERSION, + WASM_TRAP_BOUNDS, + WASM_TRAP_TABLE, + WASM_TRAP_SIGNATURE, + WASM_TRAP_COUNT, +} WasmTrapKind; typedef struct WasmCgRuntime { CfreeCgTypeId i8_ptr_ty; @@ -3803,8 +3802,64 @@ typedef struct WasmCgRuntime { uint32_t table_entries_ptr_field; uint32_t table_len_field; uint32_t table_max_field; + CfreeCgTypeId trap_func_ty; + CfreeCgSym trap_syms[WASM_TRAP_COUNT]; } WasmCgRuntime; +static const char* wasm_trap_name(WasmTrapKind kind) { + switch (kind) { + case WASM_TRAP_UNREACHABLE: + return "__cfree_wasm_trap_unreachable"; + case WASM_TRAP_DIVISION: + return "__cfree_wasm_trap_division"; + case WASM_TRAP_INVALID_CONVERSION: + return "__cfree_wasm_trap_invalid_conversion"; + case WASM_TRAP_BOUNDS: + return "__cfree_wasm_trap_bounds"; + case WASM_TRAP_TABLE: + return "__cfree_wasm_trap_table"; + case WASM_TRAP_SIGNATURE: + return "__cfree_wasm_trap_signature"; + default: + return "__cfree_wasm_trap"; + } +} + +static void wasm_cg_emit_raw_trap(CfreeCg* cg) { + cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE); + cfree_cg_unreachable(cg); +} + +static void wasm_cg_trap(CfreeCg* cg, const WasmCgRuntime* rt, + WasmTrapKind kind) { + if (rt && kind < WASM_TRAP_COUNT && rt->trap_syms[kind]) + cfree_cg_call_symbol(cg, rt->trap_syms[kind], 0, + (CfreeCgCallAttrs){.flags = CFREE_CG_CALL_COLD}); + else + cfree_cg_intrinsic(cg, CFREE_CG_INTRIN_TRAP, 0, CFREE_CG_TYPE_NONE); + cfree_cg_unreachable(cg); +} + +static void wasm_cg_trap_unreachable(CfreeCg* cg, const WasmCgRuntime* rt) { + wasm_cg_trap(cg, rt, WASM_TRAP_UNREACHABLE); +} +static void wasm_cg_trap_division(CfreeCg* cg, const WasmCgRuntime* rt) { + wasm_cg_trap(cg, rt, WASM_TRAP_DIVISION); +} +static void wasm_cg_trap_invalid_conversion(CfreeCg* cg, + const WasmCgRuntime* rt) { + wasm_cg_trap(cg, rt, WASM_TRAP_INVALID_CONVERSION); +} +static void wasm_cg_trap_bounds(CfreeCg* cg, const WasmCgRuntime* rt) { + wasm_cg_trap(cg, rt, WASM_TRAP_BOUNDS); +} +static void wasm_cg_trap_table(CfreeCg* cg, const WasmCgRuntime* rt) { + wasm_cg_trap(cg, rt, WASM_TRAP_TABLE); +} +static void wasm_cg_trap_signature(CfreeCg* cg, const WasmCgRuntime* rt) { + wasm_cg_trap(cg, rt, WASM_TRAP_SIGNATURE); +} + static void wasm_indexed_name(char* name, size_t cap, const char* prefix, uint32_t index) { size_t pos = 0; @@ -4001,7 +4056,7 @@ static void wasm_cg_push_global_value_lvalue(CfreeCompiler* c, CfreeCg* cg, { CfreeCgLabel ok = cfree_cg_label_new(cg); cfree_cg_branch_true(cg, ok); - wasm_cg_trap_table(cg); + wasm_cg_trap_table(cg, rt); cfree_cg_label_place(cg, ok); } cfree_cg_bitcast(cg, ptr_ty); @@ -4049,7 +4104,7 @@ static void wasm_cg_memory_check(CfreeCompiler* c, CfreeCg* cg, uint32_t max_pages = m->memory.has_max ? m->memory.max_pages : m->memory.min_pages; if (end > (uint64_t)max_pages * 65536u) { - wasm_cg_trap_bounds(cg); + wasm_cg_trap_bounds(cg, rt); return; } (void)c; @@ -4062,7 +4117,7 @@ static void wasm_cg_memory_check(CfreeCompiler* c, CfreeCg* cg, cfree_cg_int_binop(cg, CFREE_CG_INT_SUB, 0); cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U); cfree_cg_branch_true(cg, ok); - wasm_cg_trap_bounds(cg); + wasm_cg_trap_bounds(cg, rt); cfree_cg_label_place(cg, ok); } @@ -4113,7 +4168,8 @@ static void wasm_cg_rotate(CfreeCompiler* c, CfreeCg* cg, CfreeCgBuiltinTypes b, } static void wasm_cg_checked_divrem(CfreeCompiler* c, CfreeCg* cg, - CfreeCgBuiltinTypes b, WasmValType vt, + CfreeCgBuiltinTypes b, + const WasmCgRuntime* rt, WasmValType vt, CfreeCgIntBinOp op) { CfreeCgTypeId ty = wasm_cg_type(c, b, vt); CfreeCgMemAccess mem = wasm_cg_mem(c, b, vt); @@ -4135,7 +4191,7 @@ static void wasm_cg_checked_divrem(CfreeCompiler* c, CfreeCg* cg, cfree_cg_push_int(cg, 0, ty); cfree_cg_int_cmp(cg, CFREE_CG_INT_NE); cfree_cg_branch_true(cg, ok); - wasm_cg_trap_division(cg); + wasm_cg_trap_division(cg, rt); cfree_cg_label_place(cg, ok); if (op == CFREE_CG_INT_SDIV) { CfreeCgLabel no_overflow = cfree_cg_label_new(cg); @@ -4151,7 +4207,7 @@ static void wasm_cg_checked_divrem(CfreeCompiler* c, CfreeCg* cg, cfree_cg_push_int(cg, UINT64_MAX, ty); cfree_cg_int_cmp(cg, CFREE_CG_INT_NE); cfree_cg_branch_true(cg, no_overflow); - wasm_cg_trap_division(cg); + wasm_cg_trap_division(cg, rt); cfree_cg_label_place(cg, no_overflow); } cfree_cg_push_local(cg, lhs); @@ -4161,6 +4217,129 @@ static void wasm_cg_checked_divrem(CfreeCompiler* c, CfreeCg* cg, cfree_cg_int_binop(cg, op, 0); } +static void wasm_cg_checked_trunc(CfreeCompiler* c, CfreeCg* cg, + CfreeCgBuiltinTypes b, + const WasmCgRuntime* rt, WasmValType src, + WasmValType dst, int is_unsigned) { + CfreeCgTypeId src_ty = wasm_cg_type(c, b, src); + CfreeCgTypeId dst_ty = wasm_cg_type(c, b, dst); + CfreeCgTypeId bit_ty = b.id[src == WASM_VAL_F32 ? CFREE_CG_BUILTIN_I32 + : CFREE_CG_BUILTIN_I64]; + CfreeCgMemAccess src_mem = wasm_cg_mem(c, b, src); + CfreeCgMemAccess bit_mem = wasm_cg_mem_type(bit_ty); + CfreeCgLocalAttrs attrs; + CfreeCgLocal value, bits, abs_bits; + uint64_t abs_mask = src == WASM_VAL_F32 ? UINT64_C(0x7fffffff) + : UINT64_C(0x7fffffffffffffff); + uint64_t sign_mask = src == WASM_VAL_F32 ? UINT64_C(0x80000000) + : UINT64_C(0x8000000000000000); + uint64_t inf_bits = src == WASM_VAL_F32 ? UINT64_C(0x7f800000) + : UINT64_C(0x7ff0000000000000); + uint64_t limit_bits; + memset(&attrs, 0, sizeof attrs); + attrs.flags = CFREE_CG_LOCAL_COMPILER_TEMP; + value = cfree_cg_local(cg, src_ty, attrs); + bits = cfree_cg_local(cg, bit_ty, attrs); + abs_bits = cfree_cg_local(cg, bit_ty, attrs); + cfree_cg_push_local(cg, value); + cfree_cg_swap(cg); + cfree_cg_store(cg, src_mem); + + cfree_cg_push_local(cg, bits); + cfree_cg_push_local(cg, value); + cfree_cg_load(cg, src_mem); + cfree_cg_bitcast(cg, bit_ty); + cfree_cg_store(cg, bit_mem); + + cfree_cg_push_local(cg, abs_bits); + cfree_cg_push_local(cg, bits); + cfree_cg_load(cg, bit_mem); + cfree_cg_push_int(cg, abs_mask, bit_ty); + cfree_cg_int_binop(cg, CFREE_CG_INT_AND, 0); + cfree_cg_store(cg, bit_mem); + + { + CfreeCgLabel finite = cfree_cg_label_new(cg); + cfree_cg_push_local(cg, abs_bits); + cfree_cg_load(cg, bit_mem); + cfree_cg_push_int(cg, inf_bits, bit_ty); + cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U); + cfree_cg_branch_true(cg, finite); + wasm_cg_trap_invalid_conversion(cg, rt); + cfree_cg_label_place(cg, finite); + } + + if (src == WASM_VAL_F32) { + if (dst == WASM_VAL_I32) + limit_bits = is_unsigned ? UINT64_C(0x4f800000) : UINT64_C(0x4f000000); + else + limit_bits = is_unsigned ? UINT64_C(0x5f800000) : UINT64_C(0x5f000000); + } else { + if (dst == WASM_VAL_I32) + limit_bits = is_unsigned ? UINT64_C(0x41f0000000000000) + : UINT64_C(0x41e0000000000000); + else + limit_bits = is_unsigned ? UINT64_C(0x43f0000000000000) + : UINT64_C(0x43e0000000000000); + } + + if (is_unsigned) { + CfreeCgLabel nonnegative = cfree_cg_label_new(cg); + cfree_cg_push_local(cg, abs_bits); + cfree_cg_load(cg, bit_mem); + cfree_cg_push_int(cg, 0, bit_ty); + cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ); + cfree_cg_branch_true(cg, nonnegative); + cfree_cg_push_local(cg, bits); + cfree_cg_load(cg, bit_mem); + cfree_cg_push_int(cg, sign_mask, bit_ty); + cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U); + cfree_cg_branch_true(cg, nonnegative); + wasm_cg_trap_invalid_conversion(cg, rt); + cfree_cg_label_place(cg, nonnegative); + + { + CfreeCgLabel in_range = cfree_cg_label_new(cg); + cfree_cg_push_local(cg, abs_bits); + cfree_cg_load(cg, bit_mem); + cfree_cg_push_int(cg, limit_bits, bit_ty); + cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U); + cfree_cg_branch_true(cg, in_range); + wasm_cg_trap_invalid_conversion(cg, rt); + cfree_cg_label_place(cg, in_range); + } + } else { + CfreeCgLabel negative = cfree_cg_label_new(cg); + CfreeCgLabel in_range = cfree_cg_label_new(cg); + cfree_cg_push_local(cg, bits); + cfree_cg_load(cg, bit_mem); + cfree_cg_push_int(cg, sign_mask, bit_ty); + cfree_cg_int_cmp(cg, CFREE_CG_INT_GE_U); + cfree_cg_branch_true(cg, negative); + cfree_cg_push_local(cg, abs_bits); + cfree_cg_load(cg, bit_mem); + cfree_cg_push_int(cg, limit_bits, bit_ty); + cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U); + cfree_cg_branch_true(cg, in_range); + wasm_cg_trap_invalid_conversion(cg, rt); + cfree_cg_label_place(cg, negative); + cfree_cg_push_local(cg, abs_bits); + cfree_cg_load(cg, bit_mem); + cfree_cg_push_int(cg, limit_bits, bit_ty); + cfree_cg_int_cmp(cg, CFREE_CG_INT_LE_U); + cfree_cg_branch_true(cg, in_range); + wasm_cg_trap_invalid_conversion(cg, rt); + cfree_cg_label_place(cg, in_range); + } + + cfree_cg_push_local(cg, value); + cfree_cg_load(cg, src_mem); + if (is_unsigned) + cfree_cg_float_to_uint(cg, dst_ty, CFREE_CG_ROUND_TOWARD_ZERO); + else + cfree_cg_float_to_sint(cg, dst_ty, CFREE_CG_ROUND_TOWARD_ZERO); +} + static int wasm_int_cmp_op(uint8_t kind, CfreeCgIntCmpOp* out) { switch (kind) { case WASM_INSN_I32_EQ: @@ -4846,7 +5025,7 @@ static void wasm_cg_call_func(CfreeCompiler* c, CfreeCg* cg, cfree_cg_push_null(cg, rt->void_ptr_ty); cfree_cg_int_cmp(cg, CFREE_CG_INT_NE); cfree_cg_branch_true(cg, ok); - wasm_cg_trap_table(cg); + wasm_cg_trap_table(cg, rt); cfree_cg_label_place(cg, ok); cfree_cg_push_local(cg, callee); cfree_cg_load(cg, wasm_cg_mem_type(rt->void_ptr_ty)); @@ -4883,6 +5062,29 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, memset(syms, 0, sizeof syms); memset(func_types, 0, sizeof func_types); { + CfreeCgFuncSig sig; + CfreeCgDecl decl; + memset(&sig, 0, sizeof sig); + sig.ret = b.id[CFREE_CG_BUILTIN_VOID]; + sig.call_conv = CFREE_CG_CC_TARGET_C; + rt.trap_func_ty = cfree_cg_type_func(c, sig); + if (!rt.trap_func_ty) + wasm_error(c, wasm_loc(0, 0), "wasm: failed to create trap type"); + for (uint32_t k = 0; k < WASM_TRAP_COUNT; ++k) { + CfreeSym source_name = cfree_sym_intern(c, wasm_trap_name(k)); + memset(&decl, 0, sizeof decl); + decl.kind = CFREE_CG_DECL_FUNC; + decl.linkage_name = cfree_cg_c_linkage_name(c, source_name); + decl.display_name = source_name; + decl.type = rt.trap_func_ty; + decl.sym.bind = CFREE_SB_LOCAL; + decl.as.func.flags = CFREE_CG_FUNC_NORETURN | CFREE_CG_FUNC_COLD; + rt.trap_syms[k] = cfree_cg_decl(cg, decl); + if (!rt.trap_syms[k]) + wasm_error(c, wasm_loc(0, 0), "wasm: failed to declare trap helper"); + } + } + { CfreeCgDecl decl; CfreeCgFuncParam init_param; CfreeCgFuncSig sig; @@ -4954,6 +5156,11 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, if (!syms[i]) wasm_error(c, wasm_loc(0, 0), "wasm: failed to declare function"); } + for (uint32_t k = 0; k < WASM_TRAP_COUNT; ++k) { + cfree_cg_func_begin(cg, rt.trap_syms[k]); + wasm_cg_emit_raw_trap(cg); + cfree_cg_func_end(cg); + } if (init_sym) { CfreeCgLocalAttrs attrs; CfreeCgLocal instance_local; @@ -5087,7 +5294,7 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, WasmInsn in = f->insns[j]; switch (in.kind) { case WASM_INSN_UNREACHABLE: - wasm_cg_trap_unreachable(cg); + wasm_cg_trap_unreachable(cg, &rt); break; case WASM_INSN_NOP: break; @@ -5317,7 +5524,7 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, cfree_cg_load(cg, i32_mem); cfree_cg_int_cmp(cg, CFREE_CG_INT_LT_U); cfree_cg_branch_true(cg, ok); - wasm_cg_trap_table(cg); + wasm_cg_trap_table(cg, &rt); cfree_cg_label_place(cg, ok); ok = cfree_cg_label_new(cg); @@ -5332,7 +5539,7 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, cfree_cg_push_null(cg, rt.void_ptr_ty); cfree_cg_int_cmp(cg, CFREE_CG_INT_NE); cfree_cg_branch_true(cg, ok); - wasm_cg_trap_table(cg); + wasm_cg_trap_table(cg, &rt); cfree_cg_label_place(cg, ok); ok = cfree_cg_label_new(cg); @@ -5343,7 +5550,7 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, cfree_cg_push_int(cg, (uint32_t)in.imm, b.id[CFREE_CG_BUILTIN_I32]); cfree_cg_int_cmp(cg, CFREE_CG_INT_EQ); cfree_cg_branch_true(cg, ok); - wasm_cg_trap_signature(cg); + wasm_cg_trap_signature(cg, &rt); cfree_cg_label_place(cg, ok); cfree_cg_push_local(cg, callee); @@ -5527,28 +5734,36 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, cfree_cg_int_binop(cg, CFREE_CG_INT_MUL, 0); break; case WASM_INSN_I32_DIV_S: - wasm_cg_checked_divrem(c, cg, b, WASM_VAL_I32, CFREE_CG_INT_SDIV); + wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32, + CFREE_CG_INT_SDIV); break; case WASM_INSN_I32_DIV_U: - wasm_cg_checked_divrem(c, cg, b, WASM_VAL_I32, CFREE_CG_INT_UDIV); + wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32, + CFREE_CG_INT_UDIV); break; case WASM_INSN_I32_REM_S: - wasm_cg_checked_divrem(c, cg, b, WASM_VAL_I32, CFREE_CG_INT_SREM); + wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32, + CFREE_CG_INT_SREM); break; case WASM_INSN_I32_REM_U: - wasm_cg_checked_divrem(c, cg, b, WASM_VAL_I32, CFREE_CG_INT_UREM); + wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I32, + CFREE_CG_INT_UREM); break; case WASM_INSN_I64_DIV_S: - wasm_cg_checked_divrem(c, cg, b, WASM_VAL_I64, CFREE_CG_INT_SDIV); + wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64, + CFREE_CG_INT_SDIV); break; case WASM_INSN_I64_DIV_U: - wasm_cg_checked_divrem(c, cg, b, WASM_VAL_I64, CFREE_CG_INT_UDIV); + wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64, + CFREE_CG_INT_UDIV); break; case WASM_INSN_I64_REM_S: - wasm_cg_checked_divrem(c, cg, b, WASM_VAL_I64, CFREE_CG_INT_SREM); + wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64, + CFREE_CG_INT_SREM); break; case WASM_INSN_I64_REM_U: - wasm_cg_checked_divrem(c, cg, b, WASM_VAL_I64, CFREE_CG_INT_UREM); + wasm_cg_checked_divrem(c, cg, b, &rt, WASM_VAL_I64, + CFREE_CG_INT_UREM); break; case WASM_INSN_I32_AND: case WASM_INSN_I64_AND: @@ -5628,23 +5843,35 @@ static void wasm_emit_cg(CfreeCompiler* c, const CfreeCompileOptions* opts, break; case WASM_INSN_I32_TRUNC_F32_S: case WASM_INSN_I32_TRUNC_F64_S: - cfree_cg_float_to_sint(cg, b.id[CFREE_CG_BUILTIN_I32], - CFREE_CG_ROUND_TOWARD_ZERO); + wasm_cg_checked_trunc(c, cg, b, &rt, + in.kind == WASM_INSN_I32_TRUNC_F32_S + ? WASM_VAL_F32 + : WASM_VAL_F64, + WASM_VAL_I32, 0); break; case WASM_INSN_I32_TRUNC_F32_U: case WASM_INSN_I32_TRUNC_F64_U: - cfree_cg_float_to_uint(cg, b.id[CFREE_CG_BUILTIN_I32], - CFREE_CG_ROUND_TOWARD_ZERO); + wasm_cg_checked_trunc(c, cg, b, &rt, + in.kind == WASM_INSN_I32_TRUNC_F32_U + ? WASM_VAL_F32 + : WASM_VAL_F64, + WASM_VAL_I32, 1); break; case WASM_INSN_I64_TRUNC_F32_S: case WASM_INSN_I64_TRUNC_F64_S: - cfree_cg_float_to_sint(cg, b.id[CFREE_CG_BUILTIN_I64], - CFREE_CG_ROUND_TOWARD_ZERO); + wasm_cg_checked_trunc(c, cg, b, &rt, + in.kind == WASM_INSN_I64_TRUNC_F32_S + ? WASM_VAL_F32 + : WASM_VAL_F64, + WASM_VAL_I64, 0); break; case WASM_INSN_I64_TRUNC_F32_U: case WASM_INSN_I64_TRUNC_F64_U: - cfree_cg_float_to_uint(cg, b.id[CFREE_CG_BUILTIN_I64], - CFREE_CG_ROUND_TOWARD_ZERO); + wasm_cg_checked_trunc(c, cg, b, &rt, + in.kind == WASM_INSN_I64_TRUNC_F32_U + ? WASM_VAL_F32 + : WASM_VAL_F64, + WASM_VAL_I64, 1); break; case WASM_INSN_F32_CONVERT_I32_S: case WASM_INSN_F32_CONVERT_I64_S: diff --git a/test/wasm/run.sh b/test/wasm/run.sh @@ -5,6 +5,7 @@ ROOT=$(cd "$(dirname "$0")/../.." && pwd) BUILD_DIR="$ROOT/build/test/wasm" CASES_DIR="$ROOT/test/wasm/cases" ERR_DIR="$ROOT/test/wasm/err" +TRAP_DIR="$ROOT/test/wasm/trap" META_DIR="$ROOT/test/wasm/meta" CFREE_BIN="${CFREE:-$ROOT/build/cfree}" WASM_TOOL="$ROOT/build/test/wasm-tool" @@ -137,6 +138,19 @@ run_expect_fail() { fi } +run_expect_fail_quiet() { + local label=$1 + local out="$BUILD_DIR/${label//\//_}.out" + local err="$BUILD_DIR/${label//\//_}.err" + shift + if bash -c 'out=$1; err=$2; shift 2; "$@" >"$out" 2>"$err"' \ + bash "$out" "$err" "$@" >/dev/null 2>/dev/null; then + note_fail "$label expected failure" + else + note_pass "$label" + fi +} + printf 'test-wasm-front target=%s obj=%s\n' "$target_triple" "$TEST_OBJ" for wat in "$CASES_DIR"/*.wat; do @@ -196,6 +210,24 @@ for wat in "$CASES_DIR"/*.wat; do fi done +for wat in "$TRAP_DIR"/*.wat; do + [ -e "$wat" ] || continue + name=$(basename "$wat" .wat) + work="$BUILD_DIR/trap-$name" + mkdir -p "$work" + wasm="$work/$name.wasm" + + if [ "$have_wasm_tool" -eq 1 ]; then + run_expect_zero "trap/$name/W" "$WASM_TOOL" --wat2wasm "$wat" "$wasm" + else + note_skip "trap/$name/W" "no wasm-tool" + continue + fi + + run_expect_fail_quiet "trap/$name/D-wat" "$CFREE_BIN" run -e test_main "$wat" + run_expect_fail_quiet "trap/$name/D-wasm" "$CFREE_BIN" run -e test_main "$wasm" +done + for wat in "$ERR_DIR"/*.wat; do [ -e "$wat" ] || continue name=$(basename "$wat" .wat) diff --git a/test/wasm/trap/bounds.wat b/test/wasm/trap/bounds.wat @@ -0,0 +1,5 @@ +(module + (memory 1) + (func (export "test_main") (result i32) + i32.const 65536 + i32.load)) diff --git a/test/wasm/trap/div_overflow.wat b/test/wasm/trap/div_overflow.wat @@ -0,0 +1,5 @@ +(module + (func (export "test_main") (result i32) + i32.const -2147483648 + i32.const -1 + i32.div_s)) diff --git a/test/wasm/trap/div_zero.wat b/test/wasm/trap/div_zero.wat @@ -0,0 +1,5 @@ +(module + (func (export "test_main") (result i32) + i32.const 7 + i32.const 0 + i32.div_s)) diff --git a/test/wasm/trap/invalid_conversion.wat b/test/wasm/trap/invalid_conversion.wat @@ -0,0 +1,4 @@ +(module + (func (export "test_main") (result i32) + f64.const 4294967296 + i32.trunc_f64_u)) diff --git a/test/wasm/trap/signature.wat b/test/wasm/trap/signature.wat @@ -0,0 +1,10 @@ +(module + (type $wanted (func (result i32))) + (type $actual (func (param i32) (result i32))) + (table 1 funcref) + (func $target (type $actual) + local.get 0) + (elem (i32.const 0) $target) + (func (export "test_main") (result i32) + i32.const 0 + call_indirect (type $wanted))) diff --git a/test/wasm/trap/table_null.wat b/test/wasm/trap/table_null.wat @@ -0,0 +1,6 @@ +(module + (type $callee (func (result i32))) + (table 1 funcref) + (func (export "test_main") (result i32) + i32.const 0 + call_indirect (type $callee))) diff --git a/test/wasm/trap/unreachable.wat b/test/wasm/trap/unreachable.wat @@ -0,0 +1,3 @@ +(module + (func (export "test_main") (result i32) + unreachable))