kit

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

commit a7d17edda7d748ce58062ed593e03bd03dea52ce
parent addc00d93e9b67a981889840a231276aa207e5a8
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 27 May 2026 16:02:27 -0700

wasm: make long double binary128 and report it unsupported

Match clang/LLVM's wasm32 convention: long double is IEEE binary128, not
f64. TY_LDOUBLE now maps to CFREE_CG_BUILTIN_F128 on wasm and the __LDBL_*
predefined macros advertise MANT_DIG=113. The backend has no 128-bit float
support, so valtype_for_size_kind and the ABI scalar classifier now emit a
specific 'wasm: long double not supported' fatal when a value is actually
materialized.

Previously long double was silently f64 with MANT_DIG=53, so the ldbl128_*
parse fixtures' '__LDBL_MANT_DIG__ != 113' guard compiled their bodies out
to 'return 0' and reported as FAIL (wrong exit code). They now compile-fail
with a phased-rollout message and report SKIP; ldbl128_01_layout_macros
still PASSES (compile-time layout checks only).

Adds doc/WASM_PARSE_CHECKLIST.md tracking W-path status across the
test/parse suite (405 pass / 7 fail / 11 compile-fail / 41 skip / 1 hang).

Diffstat:
Adoc/WASM_PARSE_CHECKLIST.md | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlang/c/type/type.c | 8+++++---
Mlang/cpp/pp/pp.c | 8+++++---
Msrc/arch/wasm/abi.c | 5+++++
Msrc/arch/wasm/emit.c | 4++++
5 files changed, 122 insertions(+), 6 deletions(-)

diff --git a/doc/WASM_PARSE_CHECKLIST.md b/doc/WASM_PARSE_CHECKLIST.md @@ -0,0 +1,103 @@ +# Wasm backend — `test/parse` W-path checklist + +Status of the Wasm CGTarget against the `test/parse` C suite, path **W** +(`cfree cc -O0 -target wasm32-none -c case.c` → `cfree run -e test_main case.wasm`). + +- Host: arm64 (native JIT for the re-lowering). Opt level 0. +- 465 cases: **405 pass · 7 fail · 11 compile-fail · 41 skip · 1 hang**. +- Only the 26 skips below match run.sh's phased-rollout regex (reported SKIP). + The fails/compile-fails fall outside it and report as **FAIL** in the harness. +- Reproduce / re-probe: `build/wasm_probe.sh [filter]`; results in + `build/wasm_probe/results.tsv`, per-case logs alongside. + +## ⏳ Hang (blocks `make test-parse`) + +- [ ] **`6_8_19_switch_nested_dup_case`** — `.wasm` compiles clean; `cfree run` + spins at ~100% CPU forever. Infinite loop is in JIT'd code (nested-switch + lowering emits a backward branch turning the switch into a loop). One stalled + parallel worker prevents the suite from completing. + +## ❌ Fail — wrong exit code (7) + +### Decoder UB (LEB128 sign-extend shifts into sign bit) +- [ ] `6_5_58_large_integer_immediates` — exp 42, got 134; UBSan at + `src/wasm/decode.c:54:36` (`left shift of 127 by 63 places ... int64_t`) +- [ ] `rv64_large_imm_li` — exp 42, got 134; same `src/wasm/decode.c:54` UB + +### Misc lowering mismatches +- [ ] `attr_p2_10_alias` — exp 42, got 1; `fatal: wasm: function result type mismatch` +- [ ] `builtin_22_ctz_long_widths` — exp 42, got 1; `unary operand type mismatch (expected i32 got i64)` +- [ ] `builtin_24_atomic_lock_free` — exp 42, got 34 +- [ ] `builtin_clear_cache_01` — exp 42, got 134; `AddressSanitizer: DEADLYSIGNAL` +- [ ] `6_8_31_switch_char_extremes` — exp 5, got 20 + +## ❌ Compile-fail — `cc -target wasm32-none` fatal (11) + +- [ ] `6_7_1_03_thread_local_basic` — `obj section name '.tdata' for target obj=3 not yet implemented` (TLS) +- [ ] `gnu_thread_storage_01` — `obj section name '.tbss' for target obj=3 not yet implemented` (TLS) +- [ ] `call_indirect_arg_struct_byval` — `wasm: void value type requested` +- [ ] `call_indirect_arg_struct_field` — `wasm: void value type requested` +- [ ] `call_indirect_arg_struct_field_two_parts` — `wasm: void value type requested` +- [ ] `call_indirect_ret_struct_byval` — `wasm: void value type requested` +- [ ] `call_indirect_ret_struct_direct` — `wasm: void value type requested` +- [ ] `call_large_const_global_struct_byval` — `wasm: void value type requested` +- [ ] `6_8_26_switch_many_cases` — `wasm: too many switch cases for br_table` +- [ ] `builtin_25_atomic_fetch_nand` — `wasm target: atomic NAND has no native wasm-threads opcode` +- [ ] `rv64_atomic_widths_orders` — `wasm: reg 22 used before being defined` (use-before-def in lowering) + +## ⏭️ Skip — phased-rollout (41, reported SKIP) + +### `long double` — `wasm: long double not supported` +Wasm now advertises binary128 `long double` (clang/LLVM convention); the backend +fatals when a value is materialized, so these report SKIP instead of silently +returning 0. `ldbl128_01_layout_macros` still PASSES (compile-time layout checks only). +- [ ] `6_7_2_12_long_double` +- [ ] `ldbl128_02_literal_to_int` +- [ ] `ldbl128_03_arith` +- [ ] `ldbl128_04_conversions` +- [ ] `ldbl128_05_compare` +- [ ] `ldbl128_06_call_return` +- [ ] `ldbl128_07_struct_storage` +- [ ] `ldbl128_08_literal_bits` +- [ ] `ldbl128_09_global_init` +- [ ] `ldbl128_10_unary_neg` +- [ ] `ldbl128_11_array_copy` +- [ ] `ldbl128_12_stack_args` +- [ ] `ldbl128_13_mixed_arith` +- [ ] `ldbl128_14_struct_return` +- [ ] `ldbl128_15_arbitrary_mul` + + +### `__int128` ABI — `wasm32 ABI: scalar 16-byte values are not supported` +- [ ] `i128_02_literal_storage` +- [ ] `i128_03_add_sub_carry` +- [ ] `i128_04_mul_high_half` +- [ ] `i128_05_div_mod` +- [ ] `i128_06_shifts_bitwise` +- [ ] `i128_07_compare` +- [ ] `i128_08_signed_shift_convert` +- [ ] `i128_09_call_return` +- [ ] `i128_10_struct_storage` +- [ ] `i128_11_union_lanes` +- [ ] `i128_12_global_init` +- [ ] `i128_13_signed_div_mod` +- [ ] `i128_14_arbitrary_mul` + +### Bitfields — `wasm target: bitfield_store/bitfield_load not yet implemented` +- [ ] `6_7_2_1_01_bitfield` (store) +- [ ] `6_7_2_1_07_signed_bitfield` (store) +- [ ] `6_7_2_1_08_zero_width_bitfield` (store) +- [ ] `6_7_2_1_09_bool_bitfield` (store) +- [ ] `6_7_2_1_10_static_bitfield_pack` (load) + +### FP negate — `wasm: fneg via 0-x not supported` +- [ ] `6_5_70_fp_unary_neg_struct_field` +- [ ] `6_5_71_fp_unary_neg_zero` +- [ ] `builtin_28_fabs_inf` +- [ ] `rv64_fp_round_trip` + +### Other +- [ ] `asm_01_grammar` — `wasm target: asm register clobbers not yet supported` +- [ ] `asm_02_file_scope` — `wasm target: address of undefined symbol not yet implemented` +- [ ] `attr_p2_08_weak_undef` — `wasm target: address of undefined symbol not yet implemented` +- [ ] `builtin_26_sadd_overflow` — `wasm target: 64-bit checked-overflow multiply is not yet supported` diff --git a/lang/c/type/type.c b/lang/c/type/type.c @@ -517,9 +517,11 @@ static CfreeCgTypeId type_cg_builtin(CfreeCompiler* c, TypeKind kind) { case TY_DOUBLE: return b.id[CFREE_CG_BUILTIN_F64]; case TY_LDOUBLE: - /* RV64 long double = double per the locked decision. Only - * aarch64-linux still uses binary128 here. */ - if (target.arch == CFREE_ARCH_ARM_64 && target.os == CFREE_OS_LINUX) { + /* binary128 long double: aarch64-linux, and wasm32 (matching + * clang/LLVM's wasm convention). RV64 long double = double per the + * locked decision; everything else uses double too. */ + if ((target.arch == CFREE_ARCH_ARM_64 && target.os == CFREE_OS_LINUX) || + target.arch == CFREE_ARCH_WASM) { return b.id[CFREE_CG_BUILTIN_F128]; } return b.id[CFREE_CG_BUILTIN_F64]; diff --git a/lang/cpp/pp/pp.c b/lang/cpp/pp/pp.c @@ -670,9 +670,11 @@ static void pp_register_target_predefined(Pp* pp) { pp_define(pp, "__DBL_DENORM_MIN__", "0x1p-1074"); /* RV64 long double = double per the locked decision (matches RV64 - * musl/glibc default). Only aarch64-linux still gets binary128 - * long double. */ - if (target.arch == CFREE_ARCH_ARM_64 && target.os == CFREE_OS_LINUX) { + * musl/glibc default). aarch64-linux and wasm32 (matching clang/LLVM's + * wasm convention) get binary128 long double; the wasm backend then + * reports it as unsupported when a value is actually materialized. */ + if ((target.arch == CFREE_ARCH_ARM_64 && target.os == CFREE_OS_LINUX) || + target.arch == CFREE_ARCH_WASM) { pp_define(pp, "__LDBL_HAS_DENORM__", "1"); pp_define(pp, "__LDBL_MANT_DIG__", "113"); pp_define(pp, "__LDBL_DECIMAL_DIG__", "36"); diff --git a/src/arch/wasm/abi.c b/src/arch/wasm/abi.c @@ -33,6 +33,11 @@ static void classify_scalar(TargetABI* a, CfreeCgTypeId t, ABIArgInfo* out) { if (ti.size > 8) { SrcLoc loc = {0, 0, 0}; + /* A >8-byte float is binary128 long double (the only such C scalar); + * report it specifically. Wider integers are __int128. */ + if (ti.scalar_kind == ABI_SC_FLOAT) { + compiler_panic(a->c, loc, "wasm: long double not supported"); + } compiler_panic(a->c, loc, "wasm32 ABI: scalar %u-byte values are not supported", (unsigned)ti.size); diff --git a/src/arch/wasm/emit.c b/src/arch/wasm/emit.c @@ -73,6 +73,10 @@ static WasmValType valtype_for_size_kind(WTarget* t, u32 size, u8 scalar_kind) { if (scalar_kind == ABI_SC_FLOAT) { if (size == 4) return WASM_VAL_F32; if (size == 8) return WASM_VAL_F64; + /* The only C float wider than f64 is binary128 long double, which has + * no wasm value type. Report it specifically rather than as a generic + * size error. */ + if (size == 16) wfail(t, "wasm: long double not supported"); wfail(t, "wasm: unsupported float size %u", size); } if (size <= 4) return WASM_VAL_I32;