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