kit

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

commit a7a3338d08760d46faf61a0fc6f82f01b62c8cc1
parent c5166d0e42265a19c8f18223e0cb43bb35497e5b
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu,  4 Jun 2026 01:39:37 -0700

rv32 parse: resolve LP64-assumption corpus failures

The rv32 (ilp32) parse lane had 3 cases failing because they bake in
LP64 (64-bit long/size_t) assumptions. kit's rv32 codegen is correct —
clang as an independent ilp32 oracle returns the identical values
(10, 16, 9), confirming the test expectations, not the codegen, are
target-specific.

- builtin_26_sadd_overflow: gate the long-width (*l_overflow) assertions
  behind #if __SIZEOF_LONG__ == 8. They hard-code 64-bit long boundary
  constants; under ilp32 `long` is a 32-bit width-duplicate of the int
  builtins. The case previously bailed in this block, so the 64-bit
  long long overflow builtins were never reached on rv32 — they now run
  and pass, adding real 64-bit-overflow-on-32-bit-target coverage.
  Unchanged on LP64 (verified 42 on rv32 kit+clang and native LP64).

- 6_5_64_unsigned_size_division.rv32.skip: the 64-bit size_t
  overflow-check idiom; the clamp legitimately fires under ilp32.

- rv64_atomic_widths_orders.rv32.skip: rv64-named 64-bit atomics via
  64-bit long; rv32's 32-bit atomics stay covered by builtin_06..25.

- test/parse/run.sh: set KIT_SKIP_IS_FAILURE=0 to match the toy runner,
  so the corpus's permanent ilp32 skip floor (i128_*/ldbl128_*, ...) no
  longer gates the exit; only a real FAIL or XPASS does.

Diffstat:
Atest/parse/cases/6_5_64_unsigned_size_division.rv32.skip | 1+
Mtest/parse/cases/builtin_26_sadd_overflow.c | 28+++++++++++++++++++---------
Atest/parse/cases/rv64_atomic_widths_orders.rv32.skip | 1+
Mtest/parse/run.sh | 6++++++
4 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/test/parse/cases/6_5_64_unsigned_size_division.rv32.skip b/test/parse/cases/6_5_64_unsigned_size_division.rv32.skip @@ -0,0 +1 @@ +This case is the 64-bit size_t overflow-check idiom: it asserts that 2^30 elements of a 24-byte struct do NOT exceed MAX_SIZE_T (so the LIMIT_N clamp is a no-op and limit == n). That holds only when size_t/unsigned long is 64-bit (LP64). On rv32 ILP32 unsigned long is 32-bit, MAX_SIZE_T/sizeof == 0xFFFFFFFF/24 < 2^30, so the clamp legitimately fires and limit != n (kit and clang both return 10). Data-model-dependent, like the i128_* cases. diff --git a/test/parse/cases/builtin_26_sadd_overflow.c b/test/parse/cases/builtin_26_sadd_overflow.c @@ -1,24 +1,16 @@ int test_main(void) { int r = 0; unsigned ur = 0; - long lr = 0; - unsigned long ulr = 0; long long llr = 0; unsigned long long ullr = 0; - long l_hi = 4611686018427387904L; - unsigned long ul_hi = 9223372036854775808UL; long long ll_hi = 4611686018427387904LL; unsigned long long ull_hi = 9223372036854775808ULL; - long l_max = 9223372036854775807L; - long l_min = (long)9223372036854775808UL; - unsigned long ul_max = 18446744073709551615UL; long long ll_max = 9223372036854775807LL; long long ll_min = (long long)9223372036854775808ULL; unsigned long long ull_max = 18446744073709551615ULL; - if ((unsigned long)l_max != ul_hi - 1UL) return 78; - if ((unsigned long)l_min != ul_hi) return 79; if ((unsigned long long)ll_max != ull_hi - 1ULL) return 80; if ((unsigned long long)ll_min != ull_hi) return 81; + _Bool ov = __builtin_sadd_overflow(40, 2, &r); if (ov || r != 42) return 1; ov = __builtin_sadd_overflow(2147483647, 1, &r); @@ -37,6 +29,23 @@ int test_main(void) { ov = __builtin_umul_overflow(2147483648u, 2u, &ur); if (!ov || ur != 0u) return 8; + /* The *l_overflow assertions hard-code 64-bit `long` boundary constants, so + * they only hold under LP64. On an ILP32 target (`long` is 32-bit) those + * literals truncate and `long` is just a width-duplicate of the `int` + * builtins above; rv32's 32-bit overflow path is covered by the `int` + * variants and its 64-bit path by the `long long` variants below, so gate + * the `long`-width block out where `long` is not 64-bit. */ +#if __SIZEOF_LONG__ == 8 + long lr = 0; + unsigned long ulr = 0; + long l_hi = 4611686018427387904L; + unsigned long ul_hi = 9223372036854775808UL; + long l_max = 9223372036854775807L; + long l_min = (long)9223372036854775808UL; + unsigned long ul_max = 18446744073709551615UL; + if ((unsigned long)l_max != ul_hi - 1UL) return 78; + if ((unsigned long)l_min != ul_hi) return 79; + ov = __builtin_saddl_overflow(40L, 2L, &lr); if (ov || lr != 42L) return 15; ov = __builtin_saddl_overflow(l_max, 1L, &lr); @@ -55,6 +64,7 @@ int test_main(void) { if (!ov || ulr != ul_max) return 21; ov = __builtin_umull_overflow(ul_hi, 2UL, &ulr); if (!ov || ulr != 0UL) return 22; +#endif ov = __builtin_saddll_overflow(ll_max, 1LL, &llr); if (!ov) return 9; diff --git a/test/parse/cases/rv64_atomic_widths_orders.rv32.skip b/test/parse/cases/rv64_atomic_widths_orders.rv32.skip @@ -0,0 +1 @@ +This rv64-targeted case exercises 64-bit atomics through `long`, relying on long being 64-bit (LP64) so i64_loc can hold values like 0x100000000. On rv32 ILP32 long is 32-bit: the store of the long long literal 0x100000000L truncates to 0, so the != comparison against the untruncated 64-bit literal fails and the case returns 9 (kit and clang agree). rv32's 32-bit atomic codegen (lr.w/sc.w, amoswap.w/amoadd.w, CAS) is covered by builtin_06..25, which pass on rv32. diff --git a/test/parse/run.sh b/test/parse/run.sh @@ -603,5 +603,11 @@ KIT_LABEL=test-parse-ok KIT_BUILD_DIR="$BUILD_DIR/parse" \ KIT_OPT0ONLY="C W" KIT_TARGETS_EXT="" KIT_PARALLELIZABLE="$PAR" \ kit_corpus_run +# Treat skips (cross-target without a runner, the data-model-bound .skip +# sidecars — i128_*/ldbl128_* under ILP32, long-double-aliased targets, ...) as +# non-fatal, matching the toy runner: only a real FAIL (wrong exit code) or an +# XPASS (stale skip) gates the exit. Per-lane opt-ins like KIT_TEST_ALLOW_SKIP=1 +# are now redundant but harmless. +KIT_SKIP_IS_FAILURE=0 kit_summary test-parse-ok kit_exit