Restore the Hare-faithful floatinfo struct defs (ref/hare/math/floats.ha
:117,126), removing the Drew-flattened-(b) #129-era bypass (F64_EXPBIAS:int
scalar). #149 (db7523e) now lowers cross-module &def to LEAQ, so the struct
exports are addressable via the stof consumer pattern (&math.f64info ->
f: *floatinfo -> f.expbias). First real consumer of A.2 struct-composite
static-init (DATA byte-validated: f64info/f32info 40B each).
ADD export def f64info/f32info: floatinfo (hex masks value-identical to
Hare's (1<<52)-1 etc.; A.2 helper folds bare literals only, documented
at-site). DELETE export def F64_EXPBIAS (zero consumers). KEEP NAN_BITS/
INF_BITS u64 sentinels (ruling b: def NAN=0.0/0.0 blocked by #147) +
F64_EXPONENT_BIAS:u64 (bit-ops alias, Hare keeps both).
Test 952: pointer-param row reads &math.f64info + &math.f32info through
*math.floatinfo, all 5 fields each (pointer-param not direct field-read,
dodges #150). Make test 184/184 incl 990-997 byte-id + combined_ww_fresh.
lib/math not compiler-imported -> no regen.
Unblocks fold-4 stof.ha.
421 lines
17 KiB
C
421 lines
17 KiB
C
/*
|
|
* 952_floats_run — runtime regression net for lib/math/floats fold-1
|
|
* (f64bits, f64frombits, isnan, isinf, signf64, absf64, copysignf64,
|
|
* ispositivef64, isnegativef64) plus the fold-2a decompose step
|
|
* (issubnormalf64, normalizef64, frexpf64). The classify/sign/bits surface rides
|
|
* the f64-codegen paths fixed by #96 (deref-load -> MOVSD/X0) and #97
|
|
* (compare consults PF for NaN); normalizef64 additionally rides the #102
|
|
* 16B-tuple-from-call receive (its (f64, i64) return) and the f64 multiply
|
|
* on the subnormal path. Both stages emit byte-identical asm, so the
|
|
* 990-997 byte-id gates can't catch a reintroduction — only an
|
|
* executed-and-checked runtime probe can. This file is that probe.
|
|
*
|
|
* frexpf64 rides two now-landed cgen fixes: the `n == 0f64` zero guard
|
|
* (#103 — a no-decimal f64 literal now reaches XMM) and its (f64, i64)
|
|
* tuple return + .0/.1 destructure (#105 — the tuple f64-word read). The
|
|
* frexpf64(0.0) row is the key regressor: it exercises both the early
|
|
* return AND the tuple receive. As with the normalizef64 rows, tuple
|
|
* fields are spilled to a let before any f64-literal comparison (a
|
|
* tuple-field f64 compared directly against a literal `r.0 == 0.0` was the
|
|
* #105 mis-load); the .1 i64 field is compared in place.
|
|
*
|
|
* Table-driven like 951_f64cgen_run: each row is a self-contained ww
|
|
* program importing math; the C-side cstage `ww build -I lib` compiles
|
|
* it, we run the binary and assert the exit code. Self-checking rows
|
|
* return 0 on pass / a locator code on the first failing assertion;
|
|
* value-propagation rows return a computed result compared to want_exit.
|
|
*
|
|
* cstage-only by design (mirrors 951 + 969_checked_run): `ww_ww run` is
|
|
* broken (#95) and per-program wwstage byte-id is the 990-997 gates' job.
|
|
*
|
|
* NaN/INF operands are built at runtime through opaque zero()/one() fns
|
|
* so the checker can't const-fold them away (matches 951's #97 rows).
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
|
|
static int
|
|
runwait(const char *cmd)
|
|
{
|
|
int rc = system(cmd);
|
|
if (rc == -1) return -1;
|
|
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
|
return 1;
|
|
}
|
|
|
|
struct row { const char *src; int want_exit; };
|
|
|
|
static const struct row rows[] = {
|
|
/* f64bits + f64frombits round-trip. 0x3FF0000000000000 == 1.0,
|
|
* 0x4045000000000000 == 42.0. Round-trip through tobits/frombits
|
|
* must preserve the bit pattern (both u64 and f64 deref-loads). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" if (math.f64bits(1.0) != 0x3FF0000000000000u64) { return 1; };\n"
|
|
" if (math.f64bits(0.0) != 0u64) { return 2; };\n"
|
|
" let rt: f64 = math.f64frombits(math.f64bits(-2.5));\n"
|
|
" if (rt != -2.5) { return 3; };\n"
|
|
" if (math.f64frombits(0x4045000000000000u64) != 42.0) { return 4; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* f64frombits value propagation: a u64 bit pattern reinterpreted as
|
|
* f64 then truncated to i32. On #96 the deref strands the value in a
|
|
* GPR and the i32 cast reads stale X0. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" return math.f64frombits(0x4045000000000000u64): i32;\n"
|
|
"};\n", 42 },
|
|
/* isnan + isinf. Runtime NaN/INF via opaque zero()/one() so the
|
|
* checker can't const-fold. isnan rides #97 (self-inequality must
|
|
* be true for NaN); isinf rides #96 (f64bits deref-load). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"fn zero() f64 = { return 0.0; };\n"
|
|
"fn one() f64 = { return 1.0; };\n"
|
|
"export fn main() i32 = {\n"
|
|
" let nan: f64 = zero() / zero();\n"
|
|
" if (!math.isnan(nan)) { return 1; };\n"
|
|
" if (math.isnan(1.0)) { return 2; };\n"
|
|
" let inf: f64 = one() / zero();\n"
|
|
" if (!math.isinf(inf)) { return 3; };\n"
|
|
" if (!math.isinf(-inf)) { return 4; };\n"
|
|
" if (math.isinf(nan)) { return 5; };\n"
|
|
" if (math.isinf(1.23)) { return 6; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* signf64 + ispositivef64 + isnegativef64. +0.0 has a clear sign
|
|
* bit so signf64(0.0) == 1 (zero is positive). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" if (math.signf64(3.0) != 1i64) { return 1; };\n"
|
|
" if (math.signf64(-3.0) != -1i64) { return 2; };\n"
|
|
" if (math.signf64(0.0) != 1i64) { return 3; };\n"
|
|
" if (!math.ispositivef64(3.0)) { return 4; };\n"
|
|
" if (math.ispositivef64(-3.0)) { return 5; };\n"
|
|
" if (!math.isnegativef64(-3.0)) { return 6; };\n"
|
|
" if (math.isnegativef64(3.0)) { return 7; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* absf64 + copysignf64. absf64(NaN) stays NaN (early return before
|
|
* the bit-mask). copysign transfers y's sign onto x's magnitude. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"fn zero() f64 = { return 0.0; };\n"
|
|
"export fn main() i32 = {\n"
|
|
" if (math.absf64(-3.0) != 3.0) { return 1; };\n"
|
|
" if (math.absf64(3.0) != 3.0) { return 2; };\n"
|
|
" let nan: f64 = zero() / zero();\n"
|
|
" if (!math.isnan(math.absf64(nan))) { return 3; };\n"
|
|
" if (math.copysignf64(3.0, -1.0) != -3.0) { return 4; };\n"
|
|
" if (math.copysignf64(-3.0, 1.0) != 3.0) { return 5; };\n"
|
|
" if (math.copysignf64(3.0, 1.0) != 3.0) { return 6; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* absf64 value propagation: f64 result truncated to i32 (rides #96
|
|
* f64-return through X0). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" return math.absf64(-7.0): i32;\n"
|
|
"};\n", 7 },
|
|
/* issubnormalf64: normals and zero are not subnormal; the smallest
|
|
* positive f64 (bits == 1, ~5e-324) is. Built via f64frombits so the
|
|
* checker can't fold a subnormal literal (which it would flush). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" if (math.issubnormalf64(16.0)) { return 1; };\n"
|
|
" if (math.issubnormalf64(0.0)) { return 2; };\n"
|
|
" if (math.issubnormalf64(1.0)) { return 3; };\n"
|
|
" let s: f64 = math.f64frombits(1u64);\n"
|
|
" if (!math.issubnormalf64(s)) { return 4; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* normalizef64 on a normal value returns (n, 0). The (f64, i64) tuple
|
|
* rides the #102 16B-tuple-from-call receive; r.0 is spilled to a let
|
|
* before comparison (see header). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" const r = math.normalizef64(16.0);\n"
|
|
" const m: f64 = r.0;\n"
|
|
" if (m != 16.0) { return 1; };\n"
|
|
" if (r.1 != 0i64) { return 2; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* normalizef64 on a subnormal multiplies by 2^52 (f64 multiply inside a
|
|
* tuple return) and returns exp -52; the result is no longer subnormal
|
|
* and is nonzero. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" let s: f64 = math.f64frombits(1u64);\n"
|
|
" const r = math.normalizef64(s);\n"
|
|
" if (r.1 != -52i64) { return 1; };\n"
|
|
" const m: f64 = r.0;\n"
|
|
" if (math.issubnormalf64(m)) { return 2; };\n"
|
|
" if (m == 0.0) { return 3; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* normalizef64 value propagation: normal-path .0 truncated to i32
|
|
* (spilled to a let to dodge the tuple-field-compare mis-load; the
|
|
* cast itself reads the field fine). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" const r = math.normalizef64(42.0);\n"
|
|
" const m: f64 = r.0;\n"
|
|
" return m: i32;\n"
|
|
"};\n", 42 },
|
|
/* frexpf64 exact (mantissa, exp): 8.0 -> (0.5, 4), 1.0 -> (0.5, 1),
|
|
* 0.75 -> (0.75, 0), and the key 0.0 -> (0.0, 0) early-return row
|
|
* (rides #103 `n == 0f64` + #105 tuple receive). Tuple .0 spilled to a
|
|
* let before the f64-literal compare; .1 compared in place. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" const a = math.frexpf64(8.0);\n"
|
|
" const am: f64 = a.0;\n"
|
|
" if (am != 0.5) { return 1; };\n"
|
|
" if (a.1 != 4i64) { return 2; };\n"
|
|
" const b = math.frexpf64(1.0);\n"
|
|
" const bm: f64 = b.0;\n"
|
|
" if (bm != 0.5) { return 3; };\n"
|
|
" if (b.1 != 1i64) { return 4; };\n"
|
|
" const c = math.frexpf64(0.0);\n"
|
|
" const cm: f64 = c.0;\n"
|
|
" if (cm != 0.0) { return 5; };\n"
|
|
" if (c.1 != 0i64) { return 6; };\n"
|
|
" const d = math.frexpf64(0.75);\n"
|
|
" const dm: f64 = d.0;\n"
|
|
" if (dm != 0.75) { return 7; };\n"
|
|
" if (d.1 != 0i64) { return 8; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* frexpf64 mantissa range + reconstruction: for a nonzero input the
|
|
* mantissa is in [0.5, 1) and mantissa * 2^exp reproduces n. 12.0 ->
|
|
* (0.75, 4); 0.75 * 16 (== 2^4) == 12. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" const r = math.frexpf64(12.0);\n"
|
|
" const m: f64 = r.0;\n"
|
|
" if (m < 0.5) { return 1; };\n"
|
|
" if (m >= 1.0) { return 2; };\n"
|
|
" if (r.1 != 4i64) { return 3; };\n"
|
|
" if (m * 16.0 != 12.0) { return 4; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* frexpf64 value propagation: 1024.0 == 2^10 -> (0.5, 11); the i64 exp
|
|
* field cast to i32 propagates as 11. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" const r = math.frexpf64(1024.0);\n"
|
|
" return r.1: i32;\n"
|
|
"};\n", 11 },
|
|
/* --- strconv-foundation fold-1: F32 width constants. Mirror
|
|
* ref/hare/math/floats.ha:27,30,33,43,46. The shape values (23/8/
|
|
* 127, 0x7FFFFF, 0xFF) are facts of IEEE 754 binary32 — any drift
|
|
* here means F32_* def-folding broke. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" if (math.F32_MANTISSA_BITS != 23u32) { return 1; };\n"
|
|
" if (math.F32_EXPONENT_BITS != 8u32) { return 2; };\n"
|
|
" if (math.F32_EXPONENT_BIAS != 127u32) { return 3; };\n"
|
|
" if (math.F32_MANTISSA_MASK != 0x7FFFFFu32) { return 4; };\n"
|
|
" if (math.F32_EXPONENT_MASK != 0xFFu32) { return 5; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* f32bits / f32frombits round-trip. Mirror
|
|
* ref/hare/math/floats.ha:8,14 + ref/hare/math/+test/floats_test.ha:4
|
|
* (the f32 leg). 1.0f32 -> 0x3F800000, 2.0f32 -> 0x40000000,
|
|
* -1.0f32 -> 0xBF800000, 0.0f32 -> 0u32. Rides the f32-literal
|
|
* materialise path (#104 fold-1) and the f32 ptr-deref read/write. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" if (math.f32bits(1.0f32) != 0x3F800000u32) { return 1; };\n"
|
|
" if (math.f32bits(2.0f32) != 0x40000000u32) { return 2; };\n"
|
|
" if (math.f32bits(-1.0f32) != 0xBF800000u32) { return 3; };\n"
|
|
" if (math.f32bits(0.0f32) != 0u32) { return 4; };\n"
|
|
" if (math.f32frombits(0x3F800000u32) != 1.0f32) { return 5; };\n"
|
|
" if (math.f32frombits(0x40000000u32) != 2.0f32) { return 6; };\n"
|
|
" if (math.f32frombits(0xBF800000u32) != -1.0f32) { return 7; };\n"
|
|
" if (math.f32frombits(0u32) != 0.0f32) { return 8; };\n"
|
|
" let v: f32 = 123456.0f32;\n"
|
|
" if (math.f32frombits(math.f32bits(v)) != v) { return 9; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* f32bits value propagation: bit pattern 0x42280000 == 42.0f32; the
|
|
* u32 cast to i32 propagates as 42. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" return math.f32frombits(0x42280000u32): i32;\n"
|
|
"};\n", 42 },
|
|
/* floatinfo struct: f64-shape instance, address-of must round-trip
|
|
* through a *floatinfo pointer parameter — that's the call shape
|
|
* stof's eisel_lemire (`f: *floatinfo`) uses in fold-4. Mirrors
|
|
* ref/hare/math/floats.ha:103. Stack-local because module-level
|
|
* struct lets-with-init don't lower yet (gap reported with lead).
|
|
* Field assignments rather than a struct literal: ww's N_DOT-qualified
|
|
* `math.floatinfo { ... }` literal trips the cross-module resolver
|
|
* (#16/#17, task list #12). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"fn readbits(p: *math.floatinfo) u64 = { return p.mantbits; };\n"
|
|
"export fn main() i32 = {\n"
|
|
" let info: math.floatinfo;\n"
|
|
" info.mantbits = 52u64;\n"
|
|
" info.expbits = 11u64;\n"
|
|
" info.expbias = 1023;\n"
|
|
" info.mantmask = 0xFFFFFFFFFFFFFu64;\n"
|
|
" info.expmask = 0x7FFu64;\n"
|
|
" if (info.mantbits != 52u64) { return 1; };\n"
|
|
" if (info.expbits != 11u64) { return 2; };\n"
|
|
" if (info.expbias != 1023) { return 3; };\n"
|
|
" if (info.mantmask != 0xFFFFFFFFFFFFFu64) { return 4; };\n"
|
|
" if (info.expmask != 0x7FFu64) { return 5; };\n"
|
|
" if (readbits(&info) != 52u64) { return 6; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* floatinfo f32-shape instance. Same fold as above; verifies the
|
|
* struct type is width-agnostic (Hare exports both f64info and
|
|
* f32info, ref/hare/math/floats.ha:117,126). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"fn readbits(p: *math.floatinfo) u64 = { return p.mantbits; };\n"
|
|
"export fn main() i32 = {\n"
|
|
" let info: math.floatinfo;\n"
|
|
" info.mantbits = 23u64;\n"
|
|
" info.expbits = 8u64;\n"
|
|
" info.expbias = 127;\n"
|
|
" info.mantmask = 0x7FFFFFu64;\n"
|
|
" info.expmask = 0xFFu64;\n"
|
|
" if (info.mantbits != 23u64) { return 1; };\n"
|
|
" if (info.expbias != 127) { return 2; };\n"
|
|
" if (readbits(&info) != 23u64) { return 3; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* --- strconv-foundation fold-1b: NAN_BITS / INF_BITS sentinels.
|
|
* Hare exports `def NAN = 0.0/0.0;` / `def INF = 1.0/0.0;` (ref/hare/
|
|
* math/floats.ha:137,141); ww materializes via f64frombits(NAN_BITS)
|
|
* / f64frombits(INF_BITS) until #129 closes. The bit values are
|
|
* IEEE-754 facts (quiet-NaN: exp-all-ones + mantissa-MSB; +Inf:
|
|
* exp-all-ones + mantissa-zero) — any drift means the def-folded
|
|
* sentinels broke. */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"export fn main() i32 = {\n"
|
|
" if (math.NAN_BITS != 0x7FF8000000000000u64) { return 1; };\n"
|
|
" if (math.INF_BITS != 0x7FF0000000000000u64) { return 2; };\n"
|
|
" let n: f64 = math.f64frombits(math.NAN_BITS);\n"
|
|
" if (!math.isnan(n)) { return 3; };\n"
|
|
" if (math.isnan(1.0)) { return 4; };\n"
|
|
" let i: f64 = math.f64frombits(math.INF_BITS);\n"
|
|
" if (!math.isinf(i)) { return 5; };\n"
|
|
" if (!math.isinf(-i)) { return 6; };\n"
|
|
" if (math.isinf(1.23)) { return 7; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
/* f64info / f32info module-scope instances, read cross-module through
|
|
* a *floatinfo pointer parameter — the exact call shape stof's
|
|
* eisel_lemire (`f: *floatinfo`) uses in fold-4. Address-of a
|
|
* module-level def lowers to LEAQ since #149; the field reads
|
|
* round-trip the static-init DATA the #129 A.2 path emits. Mirrors
|
|
* ref/hare/math/floats.ha:117,126. Pointer-param (not direct
|
|
* math.f64info.expbias) because a cross-module struct-field direct read
|
|
* leaks the field name as an extern (#150, task list #48). */
|
|
{ "package main;\n"
|
|
"import math;\n"
|
|
"fn rdbias(f: *math.floatinfo) int = { return f.expbias; };\n"
|
|
"fn rdmant(f: *math.floatinfo) u64 = { return f.mantbits; };\n"
|
|
"fn rdmask(f: *math.floatinfo) u64 = { return f.mantmask; };\n"
|
|
"fn rdebits(f: *math.floatinfo) u64 = { return f.expbits; };\n"
|
|
"fn rdemask(f: *math.floatinfo) u64 = { return f.expmask; };\n"
|
|
"export fn main() i32 = {\n"
|
|
" if (rdbias(&math.f64info) != 1023) { return 1; };\n"
|
|
" if (rdmant(&math.f64info) != 52u64) { return 2; };\n"
|
|
" if (rdmask(&math.f64info) != 0xFFFFFFFFFFFFFu64) { return 3; };\n"
|
|
" if (rdbias(&math.f32info) != 127) { return 4; };\n"
|
|
" if (rdmant(&math.f32info) != 23u64) { return 5; };\n"
|
|
" if (rdmask(&math.f32info) != 0x7FFFFFu64) { return 6; };\n"
|
|
" if (rdebits(&math.f64info) != 11u64) { return 7; };\n"
|
|
" if (rdemask(&math.f64info) != 0x7FFu64) { return 8; };\n"
|
|
" if (rdebits(&math.f32info) != 8u64) { return 9; };\n"
|
|
" if (rdemask(&math.f32info) != 0xFFu64) { return 10; };\n"
|
|
" return 0;\n"
|
|
"};\n", 0 },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
const char *bin = getenv("BIN");
|
|
if (!bin) bin = "out/bin";
|
|
char cwd[1024];
|
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
|
char absbin[1024];
|
|
if (bin[0] != '/') {
|
|
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
|
bin = absbin;
|
|
}
|
|
|
|
int n = 0, fail = 0;
|
|
for (int i = 0; rows[i].src; i++, n++) {
|
|
char src[64];
|
|
snprintf(src, sizeof src, "/tmp/wwflt_%d_%d.ww", getpid(), i);
|
|
|
|
FILE *f = fopen(src, "wb");
|
|
if (f == NULL) { fail++; continue; }
|
|
fputs(rows[i].src, f);
|
|
fclose(f);
|
|
|
|
char tmpdir[64];
|
|
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwflt_%d_d_%d", getpid(), i);
|
|
mkdir(tmpdir, 0755);
|
|
|
|
char cmd[2048];
|
|
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build -I %s/lib %s",
|
|
tmpdir, bin, cwd, src);
|
|
if (runwait(cmd) != 0) {
|
|
fprintf(stderr, "row %d: build failed\n src: %s\n",
|
|
i, rows[i].src);
|
|
fail++;
|
|
unlink(src); rmdir(tmpdir);
|
|
continue;
|
|
}
|
|
|
|
char outbin[128];
|
|
const char *base = strrchr(src, '/');
|
|
base = base ? base + 1 : src;
|
|
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
|
char *dot = strrchr(outbin, '.');
|
|
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
|
|
|
int got = runwait(outbin);
|
|
if (got != rows[i].want_exit) {
|
|
fprintf(stderr, "row %d: exit %d, want %d\n src: %s\n",
|
|
i, got, rows[i].want_exit, rows[i].src);
|
|
fail++;
|
|
}
|
|
unlink(src); unlink(outbin); rmdir(tmpdir);
|
|
}
|
|
if (fail) {
|
|
fprintf(stderr, "%d/%d floats tests failed\n", fail, n);
|
|
return 1;
|
|
}
|
|
printf("floats: %d/%d ok\n", n, n);
|
|
return 0;
|
|
}
|