cstage+test: graduate hidden-name mklabel sites to @-prefix SSoT (#26)
Class A frame-layout landmine pre-located; #26c queued for size-
strategy convergence per rule 10.
Cstage's tagged-scratch sites previously stamped per-call labels
via mklabel "tagbase"/"tagscr"/"argscr"/"idxscr", bumping labelseq
once per call and allocating a fresh frame slot. Wwstage routes
the same sites through localadd("@tagbase", ...) and
localadd("@tagscr", c.tagscrsz, nil) — the @-dedup shares ONE
slot per name per fn and never touches labelseq. @tagscr is
shared across THREE wwstage sites: cgenutil.ww:180 pushargsrev
struct-payload widen, cgenutil.ww:2918 cgwidentaggedstore
via_outer, cgenexpr.ww:3524 cgindex tagged-element. Worker's
initial draft introduced cg_argscr / cg_idxscr as separate
cache vars — names that don't exist in wwstage. Per rob's rule-10
amendment those collapsed to a single cg_tagscr shared across
the 3 sites, matching wwstage's @tagscr SSoT exactly.
Cstage now caches two slots matching wwstage's namespace exactly:
cg_tagbase (8B base spill, 1 site at cgwidentaggedstore via_outer)
and cg_tagscr (sized scratch shared across the 3 sites above).
Eliminates per-call labelseq bumps and per-call frame churn.
Class A byte-id drift (silent corpus-coverage-blind landmine)
closed for the 1-name shape match. Model: STATUS-3 #15 commit
987391b routed @retscr through the same SSoT via cg_retscr;
this commit extends the carve-out to @tagbase and @tagscr.
Size strategy: cstage has no scanlocals pre-pass (wwstage's
c.tagscrsz pre-pass at cgendecl.ww:32 tagscrbump computes the
per-fn max). First call across the 3 @tagscr sites sizes the
slot; subsequent calls reuse if sz <= cached, fatal() if larger
(rule 7: surface-don't-silently-corrupt). Long-term rule-10
convergence — wwstage DOWN from scanlocals to first-use+fail-loud
on BOTH stages (per rob: aligning richer DOWN to leaner) — is
filed as #26c, separate concern from #26's name-SSoT graduation.
Tests:
- 736_cstage_label_ssot succ_rows: pins cstage-vs-wwstage cmp -s
byte-id on the canonical pointer-rooted two-tagged-store shape
(two `c.v = (...: bag);` writes through *cell). Pre-fix cstage
frame was 16B+48B larger (2*@tagbase + 2*@tagscr per call);
post-fix single-slot SSoT matches wwstage byte-for-byte.
- 736_cstage_label_ssot fail_rows: pre-locates the size-grow
landmine. A fn with two unions of different slot sizes (16B
then 24B) routed through @tagscr; cstage must fatal() with
"@tagscr cached sz" + size mismatch + #26c follow-up cite.
Gates corpus growth into this shape against silent miscompile.
110/110 ok. 995_self_rebuild byte-id holds (ww2 == ww3 == ww4).
This commit is contained in:
5
Makefile
5
Makefile
@@ -265,6 +265,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
|||||||
$(BIN)/test_enum_modshadow \
|
$(BIN)/test_enum_modshadow \
|
||||||
$(BIN)/test_struct_modshadow \
|
$(BIN)/test_struct_modshadow \
|
||||||
$(BIN)/test_def_modshadow \
|
$(BIN)/test_def_modshadow \
|
||||||
|
$(BIN)/test_cstage_label_ssot \
|
||||||
$(BIN)/test_fnparams_bare_leaf_shadow \
|
$(BIN)/test_fnparams_bare_leaf_shadow \
|
||||||
$(BIN)/test_fnret_bare_leaf_shadow \
|
$(BIN)/test_fnret_bare_leaf_shadow \
|
||||||
$(BIN)/test_param_shadow_mod \
|
$(BIN)/test_param_shadow_mod \
|
||||||
@@ -625,6 +626,10 @@ $(BIN)/test_def_modshadow: test/wcc/735_def_modshadow.c \
|
|||||||
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
|
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
$(BIN)/test_cstage_label_ssot: test/wcc/736_cstage_label_ssot.c \
|
||||||
|
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
$(BIN)/test_fnparams_bare_leaf_shadow: test/wcc/732_fnparams_bare_leaf_shadow.c \
|
$(BIN)/test_fnparams_bare_leaf_shadow: test/wcc/732_fnparams_bare_leaf_shadow.c \
|
||||||
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
|
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|||||||
@@ -39,6 +39,33 @@ static int *cg_frame;
|
|||||||
* semantics for synthetic scratches). 0 means "not yet allocated";
|
* semantics for synthetic scratches). 0 means "not yet allocated";
|
||||||
* negative offsets returned by local_alloc are the live value. */
|
* negative offsets returned by local_alloc are the live value. */
|
||||||
static int cg_retscr;
|
static int cg_retscr;
|
||||||
|
/* Per-fn @-prefix scratch SSoT (task #26, follow-up to #15-cstage's
|
||||||
|
* @retscr). Pre-#26 each site allocated a labelseq-stamped fresh slot
|
||||||
|
* per call (mklabel "tagbase" / "tagscr" / "argscr" / "idxscr"); the
|
||||||
|
* labelseq bumps drifted cstage's ct/ce/end labels ahead of wwstage,
|
||||||
|
* and the per-call frame growth drifted cstage's framesize ahead too.
|
||||||
|
*
|
||||||
|
* Two cached slots match wwstage's `@`-prefix namespace exactly:
|
||||||
|
* cg_tagbase — 8B base-register spill for cg_widen_tagged_store
|
||||||
|
* via_outer (mirrors wwstage @tagbase, 1 site).
|
||||||
|
* cg_tagscr — sized scratch shared across THREE sites: cg_widen_
|
||||||
|
* tagged_store via_outer write target, cg_widen_tagged_
|
||||||
|
* push struct/tagged-source widen, N_INDEX tagged-element
|
||||||
|
* assign. Mirrors wwstage @tagscr (cgenutil.ww:180 +
|
||||||
|
* :2918, cgenexpr.ww:3524). Wwstage shares the slot via
|
||||||
|
* localadd `@`-prefix dedup, sized to `c.tagscrsz`
|
||||||
|
* (per-fn max computed by scanlocals pre-pass).
|
||||||
|
*
|
||||||
|
* Cstage has no pre-pass: first call across the 3 sites sizes the
|
||||||
|
* slot; subsequent calls (any of the 3 sites) reuse if sz ≤ cached,
|
||||||
|
* fatal() if larger. Per rule 7: surface, don't silently corrupt the
|
||||||
|
* frame. The size-strategy convergence (wwstage DOWN to first-use+
|
||||||
|
* fail-loud on BOTH stages, or cstage UP to a scanlocals pre-pass) is
|
||||||
|
* filed as #26c — separate concern. _sz tracks cached allocation size. */
|
||||||
|
static int cg_tagbase;
|
||||||
|
static int cg_tagbase_sz;
|
||||||
|
static int cg_tagscr;
|
||||||
|
static int cg_tagscr_sz;
|
||||||
/* System V AMD64 sret discipline (task #23). Plain TY_STRUCT returns
|
/* System V AMD64 sret discipline (task #23). Plain TY_STRUCT returns
|
||||||
* with size > 24B are passed via a hidden first-arg pointer (RDI) to
|
* with size > 24B are passed via a hidden first-arg pointer (RDI) to
|
||||||
* a caller-prealloc dest; the callee writes through that pointer and
|
* a caller-prealloc dest; the callee writes through that pointer and
|
||||||
@@ -1186,11 +1213,29 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
|||||||
int base_spill = 0;
|
int base_spill = 0;
|
||||||
int write_off = slot_off;
|
int write_off = slot_off;
|
||||||
if (via_outer) {
|
if (via_outer) {
|
||||||
const char *spname = mklabel(c, "tagbase");
|
if (cg_tagbase != 0) {
|
||||||
base_spill = local_alloc(c, locals_p, spname, 8, cg_frame);
|
base_spill = cg_tagbase;
|
||||||
|
} else {
|
||||||
|
base_spill = local_alloc(c, locals_p, "@tagbase", 8,
|
||||||
|
cg_frame);
|
||||||
|
cg_tagbase = base_spill;
|
||||||
|
cg_tagbase_sz = 8;
|
||||||
|
}
|
||||||
ins2(c, A_MOVQ, areg(base_reg), amem(D_BP, base_spill));
|
ins2(c, A_MOVQ, areg(base_reg), amem(D_BP, base_spill));
|
||||||
const char *scname = mklabel(c, "tagscr");
|
if (cg_tagscr != 0) {
|
||||||
write_off = local_alloc(c, locals_p, scname, sz, cg_frame);
|
if (sz > cg_tagscr_sz)
|
||||||
|
fatal("cg_widen_tagged_store: @tagscr "
|
||||||
|
"cached sz %d, need %d (per-fn slot "
|
||||||
|
"growth needs scanlocals pre-pass — "
|
||||||
|
"STATUS-4 #26c follow-up)",
|
||||||
|
cg_tagscr_sz, sz);
|
||||||
|
write_off = cg_tagscr;
|
||||||
|
} else {
|
||||||
|
write_off = local_alloc(c, locals_p, "@tagscr", sz,
|
||||||
|
cg_frame);
|
||||||
|
cg_tagscr = write_off;
|
||||||
|
cg_tagscr_sz = sz;
|
||||||
|
}
|
||||||
/* Pre-zero so str/scalar branches (which leave high words
|
/* Pre-zero so str/scalar branches (which leave high words
|
||||||
* untouched when sz exceeds the variant's footprint) still
|
* untouched when sz exceeds the variant's footprint) still
|
||||||
* deliver a clean slot to the copy-out. */
|
* deliver a clean slot to the copy-out. */
|
||||||
@@ -1469,8 +1514,19 @@ cg_widen_tagged_push(Cg *c, Local **locals_p, Type *dst, Node *src, int sz)
|
|||||||
ins1(c, A_PUSHQ, areg(D_AX)); /* tag at +0 */
|
ins1(c, A_PUSHQ, areg(D_AX)); /* tag at +0 */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const char *scr_name = mklabel(c, "argscr");
|
int scr;
|
||||||
int scr = local_alloc(c, locals_p, scr_name, sz, cg_frame);
|
if (cg_tagscr != 0) {
|
||||||
|
if (sz > cg_tagscr_sz)
|
||||||
|
fatal("cg_widen_tagged_push: @tagscr cached sz %d, "
|
||||||
|
"need %d (per-fn slot growth needs scanlocals "
|
||||||
|
"pre-pass — STATUS-4 #26c follow-up)",
|
||||||
|
cg_tagscr_sz, sz);
|
||||||
|
scr = cg_tagscr;
|
||||||
|
} else {
|
||||||
|
scr = local_alloc(c, locals_p, "@tagscr", sz, cg_frame);
|
||||||
|
cg_tagscr = scr;
|
||||||
|
cg_tagscr_sz = sz;
|
||||||
|
}
|
||||||
/* Zero the scratch slot first so any pad word the store path
|
/* Zero the scratch slot first so any pad word the store path
|
||||||
* leaves untouched (struct payload shorter than the slot's value
|
* leaves untouched (struct payload shorter than the slot's value
|
||||||
* area) reads as 0 on the callee. The store path then writes the
|
* area) reads as 0 on the callee. The store path then writes the
|
||||||
@@ -3443,9 +3499,23 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
* the function frame; no cleanup needed. */
|
* the function frame; no cleanup needed. */
|
||||||
if ((is_arr || is_sl || is_ptr) && elem_tagged) {
|
if ((is_arr || is_sl || is_ptr) && elem_tagged) {
|
||||||
int ssz = esz;
|
int ssz = esz;
|
||||||
const char *scrn = mklabel(c, "idxscr");
|
int scr;
|
||||||
int scr = local_alloc(c, &locals, scrn, ssz,
|
if (cg_tagscr != 0) {
|
||||||
cg_frame);
|
if (ssz > cg_tagscr_sz)
|
||||||
|
fatal("N_INDEX tagged: "
|
||||||
|
"@tagscr cached sz %d, "
|
||||||
|
"need %d (per-fn slot "
|
||||||
|
"growth needs scanlocals "
|
||||||
|
"pre-pass — STATUS-4 #26c "
|
||||||
|
"follow-up)",
|
||||||
|
cg_tagscr_sz, ssz);
|
||||||
|
scr = cg_tagscr;
|
||||||
|
} else {
|
||||||
|
scr = local_alloc(c, &locals,
|
||||||
|
"@tagscr", ssz, cg_frame);
|
||||||
|
cg_tagscr = scr;
|
||||||
|
cg_tagscr_sz = ssz;
|
||||||
|
}
|
||||||
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
ins2(c, A_XORQ, areg(D_AX), areg(D_AX));
|
||||||
for (int k = 0; k < ssz; k += 8)
|
for (int k = 0; k < ssz; k += 8)
|
||||||
ins2(c, A_MOVQ, areg(D_AX),
|
ins2(c, A_MOVQ, areg(D_AX),
|
||||||
@@ -7180,6 +7250,10 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
|||||||
nloops = 0;
|
nloops = 0;
|
||||||
cg_ret_type = fn->type ? fn->type->ret : NULL;
|
cg_ret_type = fn->type ? fn->type->ret : NULL;
|
||||||
cg_retscr = 0;
|
cg_retscr = 0;
|
||||||
|
cg_tagbase = 0;
|
||||||
|
cg_tagbase_sz = 0;
|
||||||
|
cg_tagscr = 0;
|
||||||
|
cg_tagscr_sz = 0;
|
||||||
cg_sret_arg_off = 0;
|
cg_sret_arg_off = 0;
|
||||||
cg_sret_dest_off = 0;
|
cg_sret_dest_off = 0;
|
||||||
cg_sretscr_off = 0;
|
cg_sretscr_off = 0;
|
||||||
|
|||||||
250
test/wcc/736_cstage_label_ssot.c
Normal file
250
test/wcc/736_cstage_label_ssot.c
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* 736_cstage_label_ssot — sentinel for STATUS-4 #26. Cstage's tagged-
|
||||||
|
* scratch sites previously stamped per-call labels via mklabel "tagbase"
|
||||||
|
* / "tagscr" / "argscr" / "idxscr". Each call bumped `labelseq` and
|
||||||
|
* allocated a fresh frame slot. Wwstage's mirrors use `@`-prefix names
|
||||||
|
* routed through `localadd` (selfhost/cmd/wcc/cgen.ww) — the `@`-dedup
|
||||||
|
* shares one slot per name per fn and never touches labelseq. Per-fn
|
||||||
|
* `@tagbase` is one slot (8B); per-fn `@tagscr` is ONE slot shared
|
||||||
|
* across cgwidentaggedstore via_outer (cgenutil.ww:2918), pushargsrev
|
||||||
|
* struct-payload widen (cgenutil.ww:180) and cgindex tagged-element
|
||||||
|
* (cgenexpr.ww:3524).
|
||||||
|
*
|
||||||
|
* Post-#26 cstage caches `cg_tagbase` + `cg_tagscr` (single shared slot
|
||||||
|
* across the three @tagscr sites — matches wwstage's namespace exactly).
|
||||||
|
* The size-strategy gap remains: wwstage's scanlocals pre-pass sizes
|
||||||
|
* `c.tagscrsz` to the per-fn max BEFORE emit; cstage has no pre-pass,
|
||||||
|
* so first call across the 3 sites sizes the slot and subsequent
|
||||||
|
* callers must fit. fatal() per rule 7 if growth is needed. Long-term
|
||||||
|
* convergence (wwstage DOWN to first-use+fail-loud on BOTH stages, or
|
||||||
|
* cstage UP to a scanlocals pre-pass) is filed as #26c — separate
|
||||||
|
* concern, not in #26's scope.
|
||||||
|
*
|
||||||
|
* Two row families:
|
||||||
|
* succ_rows — pin cstage vs wwstage asm `cmp -s` byte-id on the
|
||||||
|
* canonical pointer-rooted two-tagged-store shape.
|
||||||
|
* Pre-#26 cstage's frame was 16B larger (extra @tagbase
|
||||||
|
* + @tagscr per call); post-#26 they match.
|
||||||
|
* fail_rows — pre-locate the Class A landmine: a fn that needs the
|
||||||
|
* @tagscr slot to grow within one cgfn. Cstage must
|
||||||
|
* fatal() with the expected pattern (rule 7: surface,
|
||||||
|
* don't silently corrupt the frame). Tests that the
|
||||||
|
* next stdlib commit landing into this shape gets a
|
||||||
|
* loud failure rather than a silent miscompile.
|
||||||
|
*/
|
||||||
|
#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 *label; const char *src; };
|
||||||
|
|
||||||
|
static const struct row succ_rows[] = {
|
||||||
|
/* Two struct-pointer-rooted tagged stores in the same fn. Each
|
||||||
|
* `c.v = (...: bag);` enters cg_widen_tagged_store with a
|
||||||
|
* non-BP base (D_BX, pointer-rooted dst) which routes through
|
||||||
|
* the @tagbase spill + @tagscr scratch path. Pre-#26: 2 @tagbase
|
||||||
|
* slots + 2 @tagscr slots in cstage's frame; wwstage shares one
|
||||||
|
* @tagbase + one @tagscr via localadd `@`-dedup. Post-#26: cstage
|
||||||
|
* caches both per-fn via cg_tagbase / cg_tagscr (mirrors
|
||||||
|
* cg_retscr from #15-cstage) so the second call reuses, matching
|
||||||
|
* wwstage. */
|
||||||
|
{ "two_struct_ptr_tagged_stores",
|
||||||
|
"type pair = struct { hi: i64, lo: i64 };\n"
|
||||||
|
"type bag = (pair | i64);\n"
|
||||||
|
"type cell = struct { v: bag };\n"
|
||||||
|
"export fn run(c: *cell, p: pair, q: pair) i64 = {\n"
|
||||||
|
" c.v = (p: bag);\n"
|
||||||
|
" c.v = (q: bag);\n"
|
||||||
|
" return 0i64;\n"
|
||||||
|
"};\n" },
|
||||||
|
};
|
||||||
|
|
||||||
|
struct failrow {
|
||||||
|
const char *label;
|
||||||
|
const char *src;
|
||||||
|
const char *want_pattern; /* substring expected in cstage stderr */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct failrow fail_rows[] = {
|
||||||
|
/* Pre-located landmine: two pointer-rooted tagged stores into
|
||||||
|
* differently-sized union slots within ONE fn. First store pins
|
||||||
|
* cg_tagscr_sz to the smaller slot; second store needs a larger
|
||||||
|
* slot than cached → fatal() with the size-grow pattern. Wwstage
|
||||||
|
* handles this via scanlocals pre-pass (c.tagscrsz = max). Cstage
|
||||||
|
* has no pre-pass — fail-loud per rule 7 keeps the next stdlib
|
||||||
|
* commit hitting this shape from silently corrupting the frame.
|
||||||
|
*
|
||||||
|
* Slot sizes: (i64 | i32) → 16B (tag + max(8,4) → 8+8). (i64 | str)
|
||||||
|
* → 24B (tag + max(8, str=16) → 8+16). First write 16B, second
|
||||||
|
* 24B; cstage must fatal on the second call. */
|
||||||
|
{ "tagscr_size_grow_fatal",
|
||||||
|
"type small = (i64 | i32);\n"
|
||||||
|
"type big = (i64 | str);\n"
|
||||||
|
"type cell_a = struct { v: small };\n"
|
||||||
|
"type cell_b = struct { v: big };\n"
|
||||||
|
"export fn run(a: *cell_a, b: *cell_b, x: i64, s: str) i64 = {\n"
|
||||||
|
" a.v = (x: small);\n"
|
||||||
|
" b.v = (s: big);\n"
|
||||||
|
" return 0i64;\n"
|
||||||
|
"};\n",
|
||||||
|
"@tagscr cached sz" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
emit_s(const char *w6c, const char *src_text, int i, char *out_s, size_t cap)
|
||||||
|
{
|
||||||
|
char src[64], cmd[1024];
|
||||||
|
snprintf(src, sizeof src, "/tmp/lbl_ssot_%d_%d.ww", getpid(), i);
|
||||||
|
snprintf(out_s, cap, "/tmp/lbl_ssot_%d_%d_%s.s",
|
||||||
|
getpid(), i, w6c[strlen(w6c) - 1] == 'w' ? "ww" : "c");
|
||||||
|
|
||||||
|
FILE *f = fopen(src, "wb");
|
||||||
|
if (!f) return -1;
|
||||||
|
fputs(src_text, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null", w6c, out_s, src);
|
||||||
|
int rc = runwait(cmd);
|
||||||
|
unlink(src);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run w6c with stderr captured to a tmp file. Returns rc and writes
|
||||||
|
* stderr path into err_out. Caller unlinks. */
|
||||||
|
static int
|
||||||
|
emit_capture_stderr(const char *w6c, const char *src_text, int i,
|
||||||
|
char *err_out, size_t cap)
|
||||||
|
{
|
||||||
|
char src[64], cmd[1024];
|
||||||
|
snprintf(src, sizeof src, "/tmp/lbl_ssot_fail_%d_%d.ww", getpid(), i);
|
||||||
|
snprintf(err_out, cap, "/tmp/lbl_ssot_fail_%d_%d.err", getpid(), i);
|
||||||
|
|
||||||
|
FILE *f = fopen(src, "wb");
|
||||||
|
if (!f) return -1;
|
||||||
|
fputs(src_text, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>%s", w6c, src, err_out);
|
||||||
|
int rc = runwait(cmd);
|
||||||
|
unlink(src);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
stderr_contains(const char *path, const char *needle)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f) return 0;
|
||||||
|
char buf[4096];
|
||||||
|
size_t n = fread(buf, 1, sizeof buf - 1, f);
|
||||||
|
fclose(f);
|
||||||
|
buf[n] = '\0';
|
||||||
|
return strstr(buf, needle) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void)
|
||||||
|
{
|
||||||
|
const char *bin = getenv("BIN");
|
||||||
|
if (!bin) bin = "out/bin";
|
||||||
|
char absbin[512];
|
||||||
|
if (bin[0] != '/') {
|
||||||
|
char cwd[256];
|
||||||
|
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||||
|
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||||
|
bin = absbin;
|
||||||
|
}
|
||||||
|
|
||||||
|
char w6c[640], w6c_ww[640];
|
||||||
|
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
|
||||||
|
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
|
||||||
|
|
||||||
|
int have_ww = (access(w6c_ww, X_OK) == 0);
|
||||||
|
|
||||||
|
int total = 0, fail = 0;
|
||||||
|
|
||||||
|
int nsucc = (int)(sizeof succ_rows / sizeof succ_rows[0]);
|
||||||
|
for (int i = 0; i < nsucc; i++) {
|
||||||
|
char cs_path[128], ws_path[128];
|
||||||
|
|
||||||
|
if (emit_s(w6c, succ_rows[i].src, i, cs_path, sizeof cs_path) != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"cstage_label_ssot[cstage][%s]: w6c failed\n",
|
||||||
|
succ_rows[i].label);
|
||||||
|
fail++; total++; continue;
|
||||||
|
}
|
||||||
|
total++;
|
||||||
|
|
||||||
|
if (!have_ww) { unlink(cs_path); continue; }
|
||||||
|
|
||||||
|
if (emit_s(w6c_ww, succ_rows[i].src, i, ws_path, sizeof ws_path) != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"cstage_label_ssot[wwstage][%s]: w6c_ww failed\n",
|
||||||
|
succ_rows[i].label);
|
||||||
|
fail++; total++;
|
||||||
|
unlink(cs_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
total++;
|
||||||
|
|
||||||
|
/* Byte-id between stages — the fix-pin. Pre-fix cstage's
|
||||||
|
* frame was 16B larger and the second-store offsets pointed
|
||||||
|
* at extra slots; post-fix they match. */
|
||||||
|
total++;
|
||||||
|
char cmd[512];
|
||||||
|
snprintf(cmd, sizeof cmd, "cmp -s %s %s", cs_path, ws_path);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"cstage_label_ssot[%s]: cstage vs wwstage asm differs\n",
|
||||||
|
succ_rows[i].label);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink(cs_path); unlink(ws_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nfail = (int)(sizeof fail_rows / sizeof fail_rows[0]);
|
||||||
|
for (int i = 0; i < nfail; i++) {
|
||||||
|
char err_path[128];
|
||||||
|
int rc = emit_capture_stderr(w6c, fail_rows[i].src, i,
|
||||||
|
err_path, sizeof err_path);
|
||||||
|
total++;
|
||||||
|
if (rc == 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"cstage_label_ssot[fail][%s]: expected cstage "
|
||||||
|
"fatal, got success exit\n", fail_rows[i].label);
|
||||||
|
fail++;
|
||||||
|
unlink(err_path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
total++;
|
||||||
|
if (!stderr_contains(err_path, fail_rows[i].want_pattern)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"cstage_label_ssot[fail][%s]: stderr missing "
|
||||||
|
"pattern \"%s\"\n",
|
||||||
|
fail_rows[i].label, fail_rows[i].want_pattern);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
unlink(err_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fail) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"cstage_label_ssot: %d/%d fixtures failed\n", fail, total);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("cstage_label_ssot: %d/%d ok\n", total, total);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user