lib/fmt: #6 formattable += int|uint — bare-int print, the types::numeric widen (both stages)
ww had narrowed Hare's formattable (types::numeric) to a lone i64 arm, so a bare int/uint was not printable: cstage CORRECTLY rejected it, wwstage leniently accepted via the #128 size-keying. Append int|uint LAST (existing tags 0-4 frozen, zero byte-id churn) so BOTH stages accept by membership — closing the #128 int-path leniency by construction. int renders via the signed i64 path; uint via strconv.u64tos unsigned (+ a rawlenu64 width twin) — i64dec would render a high-bit uint negative. Staged cut: narrower widths + full types::numeric graduate per-caller (mirrors the f32-arm precedent). fmt is NOT embedded in the selfhost compiler tools (grep-verified: 0 fmt fn-defs in w6c/wwdump combined.ww) — no combined.ww regen, compiler binaries unchanged. Pin: table-driven test/wcc/815_fmt_int_run (bare int, high-bit uint 2^63+1 positive, i64/str regression guards, byte-id).
This commit is contained in:
9
Makefile
9
Makefile
@@ -402,6 +402,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_fmt_handle_run \
|
||||
$(BIN)/test_fmt_mods_run \
|
||||
$(BIN)/test_fmt_compositions_run \
|
||||
$(BIN)/test_fmt_int_run \
|
||||
$(BIN)/test_fieldfn_leaf_collide_run \
|
||||
$(BIN)/test_amp_fn_assign_run \
|
||||
$(BIN)/test_type_value_shadow_run \
|
||||
@@ -1245,6 +1246,14 @@ $(BIN)/test_fmt_compositions_run: test/wcc/781_fmt_compositions_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_fmt_int_run: test/wcc/815_fmt_int_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
lib/fmt/fmt.ww \
|
||||
lib/memio/memio.ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_bufio_vstream_run: test/wcc/778_bufio_vstream_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
|
||||
127
lib/fmt/fmt.ww
127
lib/fmt/fmt.ww
@@ -73,7 +73,18 @@ fn i64dec(v: i64) str = {
|
||||
// caller. Callers with an `f32` cast at the call site (`myf: f64`),
|
||||
// mirroring how `i64` covers every int width today. Ship the `f32`
|
||||
// arm when the first in-tree caller needs it.
|
||||
export type formattable = (i64 | str | bool | rune | f64);
|
||||
//
|
||||
// `int`/`uint` appended LAST: variant tags follow declaration order
|
||||
// (cstage cg_tag_for_variant / wwstage flatvariantidxt), so the
|
||||
// pre-existing tags i64=0,str=1,bool=2,rune=3,f64=4 stay frozen and
|
||||
// int=5,uint=6 — zero byte-id churn for existing callers (#6). This is a
|
||||
// STAGED step toward Hare's full `types::numeric` (ref/hare/types/
|
||||
// classes.ha:5-17 → ref/hare/fmt/iter.ha:14): int/uint are the machine-
|
||||
// word types apps actually print; narrower widths (i8/i16/i32, u8/u16/
|
||||
// u32, size) graduate when a caller lands. Making them real members (not
|
||||
// a size/name-keyed coercion) closes the #128 int-path leniency by
|
||||
// construction — both stages now accept bare int by MEMBERSHIP.
|
||||
export type formattable = (i64 | str | bool | rune | f64 | int | uint);
|
||||
|
||||
// ---- internal stream formatters --------------------------------------
|
||||
|
||||
@@ -111,6 +122,15 @@ fn writeone(s: io.handle, a: formattable) (size | io.error) = {
|
||||
let v2: str = strconv.f64tos(v);
|
||||
return putbytes(s, v2.ptr, v2.len);
|
||||
};
|
||||
case let n: int => {
|
||||
let v: str = i64dec(n: i64);
|
||||
return putbytes(s, v.ptr, v.len);
|
||||
};
|
||||
case let n: uint => {
|
||||
// unsigned path: i64dec would render a high-bit value negative.
|
||||
let v: str = strconv.u64tos(n: u64, strconv.base.DEC);
|
||||
return putbytes(s, v.ptr, v.len);
|
||||
};
|
||||
};
|
||||
return 0: size;
|
||||
};
|
||||
@@ -281,6 +301,19 @@ fn rawleni64(v: i64, m: *mods) i32 = {
|
||||
return signlen + inner;
|
||||
};
|
||||
|
||||
// rawlenu64 — bytes the raw render of unsigned `v` under `m` would
|
||||
// emit. uint twin of [[rawleni64]]: no value-derived sign (uint is
|
||||
// never negative), so neg_flag is fixed false — only an explicit
|
||||
// PLUS/SPACE mod adds a sign byte. Mirror print.ha.
|
||||
fn rawlenu64(v: u64, m: *mods) i32 = {
|
||||
let signlen: i32 = 0;
|
||||
if (signof(false, m) != 0u8) { signlen = 1; };
|
||||
let dlen: i32 = digitsu64(v, basenum(m.base));
|
||||
let inner: i32 = dlen;
|
||||
if (m.prec > signlen + dlen) { inner = m.prec - signlen; };
|
||||
return signlen + inner;
|
||||
};
|
||||
|
||||
// rawlenstr — bytes the raw render of `s` would emit (after `prec`
|
||||
// truncation, per Hare print.ha:86).
|
||||
fn rawlenstr(s: str, m: *mods) i32 = {
|
||||
@@ -314,6 +347,8 @@ fn rawlen(arg: formattable, m: *mods) i32 = {
|
||||
case let b: bool => { if (b) { return 4; }; return 5; };
|
||||
case let r: rune => return 1;
|
||||
case let v: f64 => return rawlenf64(v, m);
|
||||
case let v: int => return rawleni64(v: i64, m);
|
||||
case let v: uint => return rawlenu64(v: u64, m);
|
||||
};
|
||||
return 0; // unreachable — match is exhaustive
|
||||
};
|
||||
@@ -408,6 +443,88 @@ fn formatraw(s: io.handle, arg: formattable, m: *mods) (size | io.error) = {
|
||||
};
|
||||
return total;
|
||||
};
|
||||
case let vi: int => {
|
||||
// int is the signed machine word; widen to i64 and emit the
|
||||
// signed render — identical to the i64 arm above.
|
||||
let v: i64 = vi: i64;
|
||||
let neg_flag: bool = v < 0;
|
||||
let u: u64 = v: u64;
|
||||
if (neg_flag) { u = (-v): u64; };
|
||||
let sb: u8 = signof(neg_flag, m);
|
||||
let total: size = 0;
|
||||
if (sb != 0u8) {
|
||||
let buf: [1]u8;
|
||||
buf[0] = sb;
|
||||
let r: (size | io.error) = putbytes(s, &buf[0], 1);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
};
|
||||
let dlen: i32 = digitsu64(u, basenum(m.base));
|
||||
let signlen: i32 = 0;
|
||||
if (sb != 0u8) { signlen = 1; };
|
||||
let pad0: i32 = 0;
|
||||
if (m.prec > signlen + dlen) { pad0 = m.prec - signlen - dlen; };
|
||||
let pi: i32 = 0;
|
||||
for (pi < pad0) {
|
||||
let buf: [1]u8;
|
||||
buf[0] = '0';
|
||||
let r: (size | io.error) = putbytes(s, &buf[0], 1);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
pi += 1;
|
||||
};
|
||||
let view: str = strconv.u64tos(u, m.base);
|
||||
let r: (size | io.error) = putbytes(s, view.ptr, view.len);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
case let vu: uint => {
|
||||
// unsigned path: no value-derived sign (uint never negative), so
|
||||
// neg_flag is fixed false — a high-bit value renders as its true
|
||||
// unsigned decimal, not negative. strconv.u64tos, not i64dec.
|
||||
let u: u64 = vu: u64;
|
||||
let sb: u8 = signof(false, m);
|
||||
let total: size = 0;
|
||||
if (sb != 0u8) {
|
||||
let buf: [1]u8;
|
||||
buf[0] = sb;
|
||||
let r: (size | io.error) = putbytes(s, &buf[0], 1);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
};
|
||||
let dlen: i32 = digitsu64(u, basenum(m.base));
|
||||
let signlen: i32 = 0;
|
||||
if (sb != 0u8) { signlen = 1; };
|
||||
let pad0: i32 = 0;
|
||||
if (m.prec > signlen + dlen) { pad0 = m.prec - signlen - dlen; };
|
||||
let pi: i32 = 0;
|
||||
for (pi < pad0) {
|
||||
let buf: [1]u8;
|
||||
buf[0] = '0';
|
||||
let r: (size | io.error) = putbytes(s, &buf[0], 1);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
pi += 1;
|
||||
};
|
||||
let view: str = strconv.u64tos(u, m.base);
|
||||
let r: (size | io.error) = putbytes(s, view.ptr, view.len);
|
||||
match (r) {
|
||||
case let n: size => { total += n; };
|
||||
case let e: io.error => return e;
|
||||
};
|
||||
return total;
|
||||
};
|
||||
};
|
||||
let z: size = 0; return z; // unreachable — match is exhaustive
|
||||
};
|
||||
@@ -485,6 +602,14 @@ fn formatfield(s: io.handle, f: field, m: *mods) (size | io.error) = {
|
||||
let a: formattable = v;
|
||||
return formatone(s, a, m);
|
||||
};
|
||||
case let v: int => {
|
||||
let a: formattable = v;
|
||||
return formatone(s, a, m);
|
||||
};
|
||||
case let v: uint => {
|
||||
let a: formattable = v;
|
||||
return formatone(s, a, m);
|
||||
};
|
||||
case let p: *mods => { fmtabort(); let z: size = 0; return z; };
|
||||
};
|
||||
};
|
||||
|
||||
398
test/wcc/815_fmt_int_run.c
Normal file
398
test/wcc/815_fmt_int_run.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* 815_fmt_int_run — #6 fmt.formattable widen (bare int / uint). Pins
|
||||
* that lib/fmt/fmt.ww now renders bare `int` and `uint` as REAL union
|
||||
* members (formattable = (i64|str|bool|rune|f64|int|uint), int=5,
|
||||
* uint=6 appended last). Before #6 cstage LOUD-rejected a bare int arg
|
||||
* to the fmt family (no `int` arm to widen into) while wwstage leniently
|
||||
* accepted via the #128 size/name-keyed int->i64 coercion — cs!=ww.
|
||||
* Making int/uint real members makes BOTH accept by membership; this
|
||||
* test is the cstage-now-accepts + correct-render + cs==ww byte-id gate.
|
||||
*
|
||||
* row | what it pins
|
||||
* ---------------+--------------------------------------------------
|
||||
* bare_int | fprint(int 42) over the writeone path -> "42"
|
||||
* bare_int_neg | fprint(int -7) -> "-7" (signed i64dec path)
|
||||
* bare_uint | fprint(uint 42) -> "42"
|
||||
* uint_highbit | fprint(uint 2^63+1) -> "9223372036854775809"
|
||||
* | LOAD-BEARING: proves the strconv.u64tos UNSIGNED
|
||||
* | route (len 19, first byte '9' not '-') — i64dec
|
||||
* | would render this negative ("-9223372036854775807",
|
||||
* | len 20). Also proves wwstage tags it uint (6), not
|
||||
* | i64, under the widened union.
|
||||
* int_printf | bsprintf("{}", int 42) -> "42" (formatfield ->
|
||||
* | formatone -> formatraw placeholder path)
|
||||
* unaffected_i64 | fprint(42i64) -> "42" (existing arm regression guard)
|
||||
* unaffected_str | fprint("hi") -> "hi" (existing arm regression guard)
|
||||
* mixed | fprint(1:int, 2i64, true) -> "1 2 true": int(tag5)
|
||||
* | + i64(tag0) + bool(tag2) coexist in one call,
|
||||
* | distinct tags, ww auto-space separator.
|
||||
*
|
||||
* Each row builds + runs through BOTH `ww` (cstage) and `ww_ww`
|
||||
* (wwstage) via the driver (-I lib for the fmt import), then a byte-id
|
||||
* pass builds the combined.ww via the cstage driver and diffs the asm
|
||||
* w6c emits against w6c_ww — any cs!=ww tag/render divergence reds here.
|
||||
*
|
||||
* BOOTSTRAP-EMBED: fmt is NOT embedded in any selfhost combined.ww —
|
||||
* w6c/wwdump main.ww never reach wcc/err.ww (the lone `import fmt`), so
|
||||
* neither selfhost/cmd/{w6c,wwdump}/main.combined.ww carries an fmt fn
|
||||
* (grep-verified: 0 putbytes/writeone/formatraw; the 2 "formattable"
|
||||
* hits are cgen comment text). #6 therefore rides NO combined.ww regen.
|
||||
* This test covers the user-program path, which is the only fmt consumer.
|
||||
*
|
||||
* GATE POLARITY: must stay GREEN. A red means the int/uint arm dropped,
|
||||
* uint mis-routed through the signed renderer (highbit prints negative),
|
||||
* the wwstage mis-tagged uint as i64, or an existing i64/str arm
|
||||
* regressed.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.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 *label;
|
||||
const char *src;
|
||||
int want_exit;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "bare_int",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import fmt;\n"
|
||||
"import io;\n"
|
||||
"import memio;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [32]u8;\n"
|
||||
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
|
||||
" let vs: io.stream = &st.vt;\n"
|
||||
" let x: int = 42;\n"
|
||||
" match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n"
|
||||
" let v: str = memio.string(&st);\n"
|
||||
" if (v.len != 2) { return 92; };\n"
|
||||
" if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n"
|
||||
" return 50;\n"
|
||||
"};\n",
|
||||
50 },
|
||||
{ "bare_int_neg",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import fmt;\n"
|
||||
"import io;\n"
|
||||
"import memio;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [32]u8;\n"
|
||||
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
|
||||
" let vs: io.stream = &st.vt;\n"
|
||||
" let x: int = -7;\n"
|
||||
" match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n"
|
||||
" let v: str = memio.string(&st);\n"
|
||||
" if (v.len != 2) { return 92; };\n"
|
||||
" if (v[0] != 45u8 || v[1] != 55u8) { return 93; };\n"
|
||||
" return 51;\n"
|
||||
"};\n",
|
||||
51 },
|
||||
{ "bare_uint",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import fmt;\n"
|
||||
"import io;\n"
|
||||
"import memio;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [32]u8;\n"
|
||||
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
|
||||
" let vs: io.stream = &st.vt;\n"
|
||||
" let x: uint = 42u64: uint;\n"
|
||||
" match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n"
|
||||
" let v: str = memio.string(&st);\n"
|
||||
" if (v.len != 2) { return 92; };\n"
|
||||
" if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n"
|
||||
" return 52;\n"
|
||||
"};\n",
|
||||
52 },
|
||||
{ "uint_highbit",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import fmt;\n"
|
||||
"import io;\n"
|
||||
"import memio;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [32]u8;\n"
|
||||
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
|
||||
" let vs: io.stream = &st.vt;\n"
|
||||
" let x: uint = 9223372036854775809u64: uint;\n"
|
||||
" match (fmt.fprint(vs, x)) { case size => {}; case io.error => { return 91; }; };\n"
|
||||
" let v: str = memio.string(&st);\n"
|
||||
" if (v.len != 19) { return 92; };\n"
|
||||
" if (v[0] != 57u8) { return 93; };\n" /* '9', not '-' (45) */
|
||||
" if (v[18] != 57u8) { return 94; };\n" /* trailing '9' */
|
||||
" return 53;\n"
|
||||
"};\n",
|
||||
53 },
|
||||
{ "int_printf",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import fmt;\n"
|
||||
"import io;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [16]u8;\n"
|
||||
" let x: int = 42;\n"
|
||||
" let v: str = \"\";\n"
|
||||
" match (fmt.bsprintf(buf[0:16], \"{}\", x)) { case let s: str => { v = s; }; case io.error => { return 91; }; };\n"
|
||||
" if (v.len != 2) { return 92; };\n"
|
||||
" if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n"
|
||||
" return 54;\n"
|
||||
"};\n",
|
||||
54 },
|
||||
{ "unaffected_i64",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import fmt;\n"
|
||||
"import io;\n"
|
||||
"import memio;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [32]u8;\n"
|
||||
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
|
||||
" let vs: io.stream = &st.vt;\n"
|
||||
" match (fmt.fprint(vs, 42i64)) { case size => {}; case io.error => { return 91; }; };\n"
|
||||
" let v: str = memio.string(&st);\n"
|
||||
" if (v.len != 2) { return 92; };\n"
|
||||
" if (v[0] != 52u8 || v[1] != 50u8) { return 93; };\n"
|
||||
" return 55;\n"
|
||||
"};\n",
|
||||
55 },
|
||||
{ "unaffected_str",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import fmt;\n"
|
||||
"import io;\n"
|
||||
"import memio;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [32]u8;\n"
|
||||
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
|
||||
" let vs: io.stream = &st.vt;\n"
|
||||
" match (fmt.fprint(vs, \"hi\")) { case size => {}; case io.error => { return 91; }; };\n"
|
||||
" let v: str = memio.string(&st);\n"
|
||||
" if (v.len != 2) { return 92; };\n"
|
||||
" if (v[0] != 104u8 || v[1] != 105u8) { return 93; };\n"
|
||||
" return 56;\n"
|
||||
"};\n",
|
||||
56 },
|
||||
{ "mixed",
|
||||
"package main;\n"
|
||||
"import os;\n"
|
||||
"import fmt;\n"
|
||||
"import io;\n"
|
||||
"import memio;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [32]u8;\n"
|
||||
" let st: memio.stream = memio.fixed(buf[0:32]);\n"
|
||||
" let vs: io.stream = &st.vt;\n"
|
||||
" let x: int = 1;\n"
|
||||
" match (fmt.fprint(vs, x, 2i64, true)) { case size => {}; case io.error => { return 91; }; };\n"
|
||||
" let v: str = memio.string(&st);\n"
|
||||
" if (v.len != 8) { return 92; };\n" /* "1 2 true" */
|
||||
" if (v[0] != 49u8 || v[1] != 32u8) { return 93; };\n" /* '1' ' ' */
|
||||
" if (v[2] != 50u8 || v[3] != 32u8) { return 94; };\n" /* '2' ' ' */
|
||||
" if (v[4] != 116u8 || v[7] != 101u8) { return 95; };\n" /* 't' .. 'e' */
|
||||
" return 57;\n"
|
||||
"};\n",
|
||||
57 },
|
||||
};
|
||||
|
||||
static int
|
||||
write_source(const char *path, const char *src)
|
||||
{
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(src, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Per-row tmpdir cleanup. ww_ww writes intermediates next to the source
|
||||
* (task #15), so each row's build leaves <src>.{combined.ww,s,o} + bare
|
||||
* exe alongside. Mirror of 781's. */
|
||||
static void
|
||||
cleanup_tmp(const char *tmpdir, const char *base)
|
||||
{
|
||||
char p[640];
|
||||
snprintf(p, sizeof p, "%s/%s.ww", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s.s", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s.o", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s.combined.ww", tmpdir, base); unlink(p);
|
||||
snprintf(p, sizeof p, "%s/%s", tmpdir, base); unlink(p);
|
||||
rmdir(tmpdir);
|
||||
}
|
||||
|
||||
static int
|
||||
build_via_driver(const char *driver, const char *tmpdir, const char *cwd,
|
||||
const char *src)
|
||||
{
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && timeout 180 %s build -I %s/lib %s 2>/dev/null",
|
||||
tmpdir, driver, cwd, src);
|
||||
return runwait(cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
run_row(const char *driver, const char *cwd, const struct row *r, int seq)
|
||||
{
|
||||
char tmpdir[256], src[512], base[64], outbin[768];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/fic_%d_d_%d", getpid(), seq);
|
||||
snprintf(base, sizeof base, "main815");
|
||||
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
|
||||
mkdir(tmpdir, 0755);
|
||||
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
|
||||
int rc;
|
||||
int br = build_via_driver(driver, tmpdir, cwd, src);
|
||||
if (br == 0) {
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||
rc = runwait(outbin);
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
cleanup_tmp(tmpdir, base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* asm_byte_identical — build combined.ww via the cstage driver (needs
|
||||
* the -I lib fmt import resolution that raw w6c can't do), then compile
|
||||
* that combined.ww with both w6c and w6c_ww and diff the asm. Proves no
|
||||
* cs!=ww tag/render divergence on the widened union. */
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const char *cdrv, const char *cwd,
|
||||
const struct row *r, int seq)
|
||||
{
|
||||
char tmpdir[256], base[64], src[512], combined[640], cs[640], ws[640];
|
||||
char cmd[2048];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/fic_%d_b_%d", getpid(), seq);
|
||||
snprintf(base, sizeof base, "main815b");
|
||||
snprintf(src, sizeof src, "%s/%s.ww", tmpdir, base);
|
||||
snprintf(combined, sizeof combined, "%s/%s.combined.ww", tmpdir, base);
|
||||
snprintf(cs, sizeof cs, "%s/%s_c.s", tmpdir, base);
|
||||
snprintf(ws, sizeof ws, "%s/%s_w.s", tmpdir, base);
|
||||
mkdir(tmpdir, 0755);
|
||||
if (write_source(src, r->src) != 0) { cleanup_tmp(tmpdir, base); return -1; }
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && timeout 180 %s build -I %s/lib %s 2>/dev/null",
|
||||
tmpdir, cdrv, cwd, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "fmt_int_run[byteid][%s]: combined.ww build failed\n",
|
||||
r->label);
|
||||
cleanup_tmp(tmpdir, base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, combined);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "fmt_int_run[byteid][%s]: w6c errored\n", r->label);
|
||||
rc = -1;
|
||||
}
|
||||
if (rc == 0) {
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, combined);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "fmt_int_run[byteid][%s]: w6c_ww errored\n",
|
||||
r->label);
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
if (rc == 0) {
|
||||
FILE *fc = fopen(cs, "rb");
|
||||
FILE *fw = fopen(ws, "rb");
|
||||
if (!fc || !fw) {
|
||||
rc = -1;
|
||||
} else {
|
||||
for (;;) {
|
||||
int a = fgetc(fc);
|
||||
int b = fgetc(fw);
|
||||
if (a != b) { rc = -1; break; }
|
||||
if (a == EOF) break;
|
||||
}
|
||||
}
|
||||
if (fc) fclose(fc);
|
||||
if (fw) fclose(fw);
|
||||
if (rc != 0)
|
||||
fprintf(stderr,
|
||||
"fmt_int_run[byteid][%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
}
|
||||
|
||||
unlink(cs); unlink(ws);
|
||||
cleanup_tmp(tmpdir, base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640], wdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
int wwpresent = (access(wdrv, X_OK) == 0);
|
||||
int seq = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int got = run_row(cdrv, cwd, &rows[i], seq++);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "fmt_int_run[cs][%s]: exit=%d want=%d\n",
|
||||
rows[i].label, got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
if (wwpresent) {
|
||||
total++;
|
||||
int gw = run_row(wdrv, cwd, &rows[i], seq++);
|
||||
if (gw != rows[i].want_exit) {
|
||||
fprintf(stderr, "fmt_int_run[ww][%s]: exit=%d want=%d\n",
|
||||
rows[i].label, gw, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wwpresent) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (asm_byte_identical(bin, cdrv, cwd, &rows[i], seq++) != 0)
|
||||
fail++;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "fmt_int_run: skip wwstage (no %s)\n", wdrv);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "fmt_int_run: %d/%d fixtures failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("fmt_int_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user