commit 472d97ae4494c11c424d81095648ee83c1a50dd2
parent 2e3bc93c4d520c5d535c59d1e6a0a972017921b8
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Mon, 18 May 2026 01:00:20 -0700
Polish WASM trap lowering
Diffstat:
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))