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:
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;