wcc: kind-agnostic typeeq dispatch in cgmatch non-nullable arm (#179)
cgmatch's non-nullable variant-tag synthesis gated flatvariantidx on pat.kind == N_TNAME (with N_TSLICE else-branch for #19's untyped-elem fallback). N_TPTR / N_TFN / N_TPTR(N_TFN) case-patterns fell through both, leaving r=-1 → want=0 so every variant past 0 silently collapsed to tag 0 — runtime-passes only when the value happens to sit on variant 0 (zero-coincidence miscompile). Cstage cg_tag_for_variant works on resolved Type and is kind- agnostic; harec stores `_case->type = ctype` (ref/harec check.c:2527). #66 Phase-N already flipped match dispatch to typeeq; #179 is the last site still keyed on AST kind. Memory: project_tinfo_lossy_nominal + feedback "Hare = resolved-type-only match dispatch". Route through flatvariantidxt(scrutt.type_, pat.type_) directly, guarded by istaggedtype + typeisslice(pattype) for the slice axis. No new helper — existing flatvariantidxt / flatslicevariantidx wire up unchanged. 767 probe locks the fix across 5 rows: (1) nullable *fn branched store (ken's verify gate — proves the nullable arm at line 1484 isn't perturbed); (2) *i32|*i64 storing &i64 — pre-fix wwstage emitted CMPQ $0 for *i64 arm, post-fix CMPQ $1; (3) *fn(i32)|*fn(i64) via intermediate local; (4) branched runtime variant choice defeats const-fold; (5) aliased ptr variants (typeeq through TY_NAMED). Per row: cs runtime, ww runtime, cs.s == ww.s byte-id. Sibling filed inline: widening `&fn` INLINE into a fn-ptr-only tagged union picks tag 0 in wwstage's cgwidentaggedstorebp (out-of-scope; row 3 dodges via intermediate ident store).
This commit is contained in:
7
Makefile
7
Makefile
@@ -317,6 +317,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_amp_fn_ident \
|
||||
$(BIN)/test_star_fn_deref \
|
||||
$(BIN)/test_star_fn_deref_call \
|
||||
$(BIN)/test_match_variant_dispatch \
|
||||
$(BIN)/test_use_promote_alias \
|
||||
$(BIN)/test_field_signed $(BIN)/test_frame_argcount \
|
||||
$(BIN)/test_selfhost $(BIN)/test_w6a_ww $(BIN)/test_w6l_ww \
|
||||
@@ -625,6 +626,12 @@ $(BIN)/test_star_fn_deref_call: test/wcc/766_star_fn_deref_call.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_match_variant_dispatch: test/wcc/767_match_variant_dispatch.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_arrlit_str_full: test/wcc/711_arrlit_str_full.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
|
||||
@@ -19069,16 +19069,22 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// rather than the resolved N_TTAGGED node.
|
||||
if (istaggedtype(c, scrutt)) {
|
||||
let r: i32 = -1;
|
||||
if (pat.kind == nkind.N_TNAME) {
|
||||
r = flatvariantidx(c, scrutt, pat);
|
||||
} else { if (pat.kind == nkind.N_TSLICE) {
|
||||
// `case let s: []T =>` — pat.str is empty
|
||||
// because the variant is a composite, so
|
||||
// route through the slice-shape helper.
|
||||
// Without this every (scalar | []T) match
|
||||
// arm collapses to tag 0 (task #19).
|
||||
r = flatslicevariantidx(c, scrutt, pat.lhs);
|
||||
}; };
|
||||
let pattype: *tinfo = pat.type_: *tinfo;
|
||||
if (pattype != nil) {
|
||||
// #179: kind-agnostic dispatch on the resolved
|
||||
// tinfo. Cstage cg_tag_for_variant works on
|
||||
// Type, so N_TPTR / N_TFN / N_TPTR(N_TFN) case-
|
||||
// patterns all reach typeeq; the prior pat.kind
|
||||
// gate dropped them to r=-1 → tag 0 collapse.
|
||||
// Slice arm still goes through flatslicevariantidx
|
||||
// (task #19 untyped-elem fallback when typeeq
|
||||
// can't match a (scalar | []T) shape).
|
||||
if (typeisslice(pattype)) {
|
||||
r = flatslicevariantidx(c, scrutt, pat.lhs);
|
||||
} else {
|
||||
r = flatvariantidxt(scrutt.type_: *tinfo, pattype);
|
||||
};
|
||||
};
|
||||
if (r >= 0) { want = r; };
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1509,16 +1509,22 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// rather than the resolved N_TTAGGED node.
|
||||
if (istaggedtype(c, scrutt)) {
|
||||
let r: i32 = -1;
|
||||
if (pat.kind == nkind.N_TNAME) {
|
||||
r = flatvariantidx(c, scrutt, pat);
|
||||
} else { if (pat.kind == nkind.N_TSLICE) {
|
||||
// `case let s: []T =>` — pat.str is empty
|
||||
// because the variant is a composite, so
|
||||
// route through the slice-shape helper.
|
||||
// Without this every (scalar | []T) match
|
||||
// arm collapses to tag 0 (task #19).
|
||||
r = flatslicevariantidx(c, scrutt, pat.lhs);
|
||||
}; };
|
||||
let pattype: *tinfo = pat.type_: *tinfo;
|
||||
if (pattype != nil) {
|
||||
// #179: kind-agnostic dispatch on the resolved
|
||||
// tinfo. Cstage cg_tag_for_variant works on
|
||||
// Type, so N_TPTR / N_TFN / N_TPTR(N_TFN) case-
|
||||
// patterns all reach typeeq; the prior pat.kind
|
||||
// gate dropped them to r=-1 → tag 0 collapse.
|
||||
// Slice arm still goes through flatslicevariantidx
|
||||
// (task #19 untyped-elem fallback when typeeq
|
||||
// can't match a (scalar | []T) shape).
|
||||
if (typeisslice(pattype)) {
|
||||
r = flatslicevariantidx(c, scrutt, pat.lhs);
|
||||
} else {
|
||||
r = flatvariantidxt(scrutt.type_: *tinfo, pattype);
|
||||
};
|
||||
};
|
||||
if (r >= 0) { want = r; };
|
||||
};
|
||||
};
|
||||
|
||||
@@ -19069,16 +19069,22 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
// rather than the resolved N_TTAGGED node.
|
||||
if (istaggedtype(c, scrutt)) {
|
||||
let r: i32 = -1;
|
||||
if (pat.kind == nkind.N_TNAME) {
|
||||
r = flatvariantidx(c, scrutt, pat);
|
||||
} else { if (pat.kind == nkind.N_TSLICE) {
|
||||
// `case let s: []T =>` — pat.str is empty
|
||||
// because the variant is a composite, so
|
||||
// route through the slice-shape helper.
|
||||
// Without this every (scalar | []T) match
|
||||
// arm collapses to tag 0 (task #19).
|
||||
r = flatslicevariantidx(c, scrutt, pat.lhs);
|
||||
}; };
|
||||
let pattype: *tinfo = pat.type_: *tinfo;
|
||||
if (pattype != nil) {
|
||||
// #179: kind-agnostic dispatch on the resolved
|
||||
// tinfo. Cstage cg_tag_for_variant works on
|
||||
// Type, so N_TPTR / N_TFN / N_TPTR(N_TFN) case-
|
||||
// patterns all reach typeeq; the prior pat.kind
|
||||
// gate dropped them to r=-1 → tag 0 collapse.
|
||||
// Slice arm still goes through flatslicevariantidx
|
||||
// (task #19 untyped-elem fallback when typeeq
|
||||
// can't match a (scalar | []T) shape).
|
||||
if (typeisslice(pattype)) {
|
||||
r = flatslicevariantidx(c, scrutt, pat.lhs);
|
||||
} else {
|
||||
r = flatvariantidxt(scrutt.type_: *tinfo, pattype);
|
||||
};
|
||||
};
|
||||
if (r >= 0) { want = r; };
|
||||
};
|
||||
};
|
||||
|
||||
337
test/wcc/767_match_variant_dispatch.c
Normal file
337
test/wcc/767_match_variant_dispatch.c
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* 767_match_variant_dispatch — project #179: wwstage cgmatch's
|
||||
* non-nullable arm gated its flat-tag dispatch on `pat.kind ==
|
||||
* N_TNAME` (with an `N_TSLICE` else-branch for task #19's untyped
|
||||
* elem fallback). N_TPTR / N_TFN / N_TPTR(N_TFN) case-patterns fell
|
||||
* through both arms, leaving the local `r` at -1 → `want = 0`, so
|
||||
* every variant past 0 silently collapsed to tag 0 — the kind of
|
||||
* miscompile that runtime-passes only when the value happens to be
|
||||
* variant 0 (zero-coincidence).
|
||||
*
|
||||
* Cstage cg_tag_for_variant (cmd/w6c/cgen.c:624) walks Type directly
|
||||
* and is kind-agnostic; it has never had this gap. Drew's settled
|
||||
* principle: match dispatch is resolved-type-only (harec
|
||||
* check.c:2527 stores `_case->type = ctype`). Project #66 Phase-N
|
||||
* already flipped the typeeq compare; #179 was the one site that
|
||||
* still keyed on AST kind. Memory: project_tinfo_lossy_nominal.
|
||||
*
|
||||
* Fix: selfhost/cmd/wcc/cgenexpr.ww cgmatch non-nullable arm —
|
||||
* route through `flatvariantidxt(scrutt.type_, pat.type_)` directly,
|
||||
* guarded only by `istaggedtype(scrutt)` + `typeisslice(pattype)`
|
||||
* for the slice axis. No new helper; existing flatvariantidxt /
|
||||
* flatslicevariantidx wire up unchanged.
|
||||
*
|
||||
* Coverage (5 rows):
|
||||
* 1. nullable_ptr_fn — `(*fn(i32) i32 | void)`, branched
|
||||
* store (#105 lesson: two distinct
|
||||
* fns picked at runtime), match via
|
||||
* indirect call. KEN'S VERIFY GATE:
|
||||
* row 1 byte-id proves wwstage's
|
||||
* nullable-fold ordering matches
|
||||
* cstage cg_tag_for_variant — fail
|
||||
* here flags a separate-signoff fold.
|
||||
* Goes through the nullable disc path
|
||||
* (line 1484-1502, untouched by #179).
|
||||
* 2. ptr_variants_past_zero — `(*i32 | *i64)` storing &i64.
|
||||
* Pre-fix wwstage emits CMPQ $0 for
|
||||
* the *i64 arm; post-fix CMPQ $1.
|
||||
* 3. fn_ptr_variants_via_local — `(*fn(i32) i32 | *fn(i64) i64)`
|
||||
* built from a local. Direct N_TPTR
|
||||
* over N_TFN case-pattern is the new-
|
||||
* shape exercise; store path goes via
|
||||
* ident (not `&fn` inline) to dodge
|
||||
* the fn-variant store-side sibling
|
||||
* bug (see below).
|
||||
* 4. stored_variant1_roundtrip — `(*i32 | *i64)` with branched
|
||||
* runtime variant choice (pick = 1
|
||||
* forces variant 1; defeats const-
|
||||
* fold to variant 0). Mirrors #105.
|
||||
* 5. aliased_ptr_variants — `type pa = *i32; type pb = *i64;
|
||||
* (pa | pb)`. typeeq through TY_NAMED
|
||||
* wrappers — the alias-aware path that
|
||||
* the surface-name compare couldn't
|
||||
* key on. OR-pattern was the original
|
||||
* row 5, dropped because the wwstage
|
||||
* parser doesn't accept `case T1 | T2`.
|
||||
*
|
||||
* Per-row gates: cstage runtime exit, wwstage runtime exit,
|
||||
* cs.s == ww.s byte-identical (rule-10 stage symmetry).
|
||||
*
|
||||
* Sibling bug surfaced (not fixed here, filed inline): widening
|
||||
* `&fn` INLINE into a `(*fn(...) | *fn(...))` slot computes the
|
||||
* wrong tag in wwstage — taggedvariantindext / typeeq on the
|
||||
* synthetic fn-ptr tinfo built from N_UN TK_AMP IDENT-of-FN doesn't
|
||||
* match the variant tinfo, so tag collapses to 0. Storing via an
|
||||
* intermediate ident-typed local (row 3 shape) routes through a
|
||||
* different store path and tags correctly. cstage cg_widen_tagged_
|
||||
* store hits the same node with the same Type and tags correctly,
|
||||
* so the divergence is wwstage-only on the inline-amp shape. Out
|
||||
* of scope for #179 (which is the match-DISPATCH side, not the
|
||||
* widen-STORE side).
|
||||
*
|
||||
* GATE POLARITY: must stay GREEN. A red here means the cgmatch
|
||||
* non-nullable dispatch regressed, the nullable arm got perturbed,
|
||||
* or stage symmetry drifted on the new typeeq route.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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;
|
||||
}
|
||||
|
||||
#define STAGE_CS 1
|
||||
#define STAGE_WW 2
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int expected_exit;
|
||||
int stage_mask;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* KEN'S VERIFY GATE: nullable disc path must byte-id even
|
||||
* though #179 didn't touch it. A divergence here flags a
|
||||
* separate flatvariantidxt nullable-fold ordering issue. */
|
||||
{ "nullable_ptr_fn",
|
||||
"fn ai(x: i32) i32 = { return x + 1; };\n"
|
||||
"fn bi(x: i32) i32 = { return x + 100; };\n"
|
||||
"fn indi(f: *fn(x: i32) i32, x: i32) i32 = { return (*f)(x); };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let pick: i32 = 1;\n"
|
||||
" let v: (*fn(x: i32) i32 | void) = &ai;\n"
|
||||
" if (pick != 0) { v = &bi; };\n"
|
||||
" let r: i32 = match (v) {\n"
|
||||
" case void => yield 99: i32;\n"
|
||||
" case let f: *fn(x: i32) i32 => yield indi(f, 7);\n"
|
||||
" };\n"
|
||||
" return r;\n"
|
||||
"};\n",
|
||||
107,
|
||||
STAGE_CS | STAGE_WW },
|
||||
{ "ptr_variants_past_zero",
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: i64 = 42;\n"
|
||||
" let v: (*i32 | *i64) = &a;\n"
|
||||
" let r: i32 = match (v) {\n"
|
||||
" case *i32 => yield 11: i32;\n"
|
||||
" case *i64 => yield 22: i32;\n"
|
||||
" };\n"
|
||||
" return r;\n"
|
||||
"};\n",
|
||||
22,
|
||||
STAGE_CS | STAGE_WW },
|
||||
{ "fn_ptr_variants_via_local",
|
||||
"fn ai(x: i32) i32 = { return x + 1; };\n"
|
||||
"fn bi(x: i64) i64 = { return x + 1; };\n"
|
||||
"fn indl(f: *fn(x: i64) i64, x: i64) i64 = { return (*f)(x); };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let g: *fn(x: i64) i64 = &bi;\n"
|
||||
" let v: (*fn(x: i32) i32 | *fn(x: i64) i64) = g;\n"
|
||||
" let r: i32 = match (v) {\n"
|
||||
" case let p: *fn(x: i32) i32 => yield 1: i32;\n"
|
||||
" case let q: *fn(x: i64) i64 => yield indl(q, 9): i32;\n"
|
||||
" };\n"
|
||||
" return r;\n"
|
||||
"};\n",
|
||||
10,
|
||||
STAGE_CS | STAGE_WW },
|
||||
{ "stored_variant1_roundtrip",
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: i32 = 5;\n"
|
||||
" let b: i64 = 9;\n"
|
||||
" let pick: i32 = 1;\n"
|
||||
" let v: (*i32 | *i64) = &a;\n"
|
||||
" if (pick != 0) { v = &b; };\n"
|
||||
" let r: i32 = match (v) {\n"
|
||||
" case *i32 => yield 11: i32;\n"
|
||||
" case *i64 => yield 22: i32;\n"
|
||||
" };\n"
|
||||
" return r;\n"
|
||||
"};\n",
|
||||
22,
|
||||
STAGE_CS | STAGE_WW },
|
||||
{ "aliased_ptr_variants",
|
||||
"type pa = *i32;\n"
|
||||
"type pb = *i64;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: i64 = 100;\n"
|
||||
" let g: pb = &a;\n"
|
||||
" let v: (pa | pb) = g;\n"
|
||||
" let r: i32 = match (v) {\n"
|
||||
" case pa => yield 11: i32;\n"
|
||||
" case pb => yield 22: i32;\n"
|
||||
" };\n"
|
||||
" return r;\n"
|
||||
"};\n",
|
||||
22,
|
||||
STAGE_CS | STAGE_WW },
|
||||
};
|
||||
|
||||
static int
|
||||
write_source(const char *src_path, const char *src)
|
||||
{
|
||||
FILE *f = fopen(src_path, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(src, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_tmp(const char *tmpdir, const char *base)
|
||||
{
|
||||
char p[512];
|
||||
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 *src)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof cmd, "cd %s && timeout 180 %s build %s 2>/dev/null",
|
||||
tmpdir, driver, src);
|
||||
return runwait(cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
run_row(const char *driver, const struct row *r, int seq)
|
||||
{
|
||||
char tmpdir[256], src[256], base[64], outbin[512];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/mvd_%d_d_%d", getpid(), seq);
|
||||
snprintf(src, sizeof src, "%s/main767.ww", tmpdir);
|
||||
snprintf(base, sizeof base, "main767");
|
||||
mkdir(tmpdir, 0755);
|
||||
if (write_source(src, r->src) != 0) {
|
||||
cleanup_tmp(tmpdir, base);
|
||||
return -1;
|
||||
}
|
||||
int rc = -1;
|
||||
if (build_via_driver(driver, tmpdir, src) == 0) {
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||
rc = runwait(outbin);
|
||||
}
|
||||
cleanup_tmp(tmpdir, base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* asm_byte_identical — diff cstage vs wwstage .s. Parallel trees so
|
||||
* ww_ww writing intermediates next to the source doesn't clobber the
|
||||
* cstage .s (filed bug per CLAUDE.md rule 14 phase split). */
|
||||
static int
|
||||
asm_byte_identical(const char *cdrv, const char *wdrv,
|
||||
const struct row *r, int seq)
|
||||
{
|
||||
char src[256], tdc[256], tdw[256], base[64], cs[512], ws[512];
|
||||
snprintf(tdc, sizeof tdc, "/tmp/mvd_%d_c_%d", getpid(), seq);
|
||||
snprintf(tdw, sizeof tdw, "/tmp/mvd_%d_w_%d", getpid(), seq);
|
||||
snprintf(base, sizeof base, "main767");
|
||||
mkdir(tdc, 0755);
|
||||
mkdir(tdw, 0755);
|
||||
snprintf(src, sizeof src, "%s/main767.ww", tdc);
|
||||
if (write_source(src, r->src) != 0) { cleanup_tmp(tdc, base); cleanup_tmp(tdw, base); return -1; }
|
||||
int rc = -1;
|
||||
if (build_via_driver(cdrv, tdc, src) != 0) goto out;
|
||||
snprintf(cs, sizeof cs, "%s/%s.s", tdc, base);
|
||||
|
||||
snprintf(src, sizeof src, "%s/main767.ww", tdw);
|
||||
if (write_source(src, r->src) != 0) goto out;
|
||||
if (build_via_driver(wdrv, tdw, src) != 0) goto out;
|
||||
snprintf(ws, sizeof ws, "%s/%s.s", tdw, base);
|
||||
|
||||
FILE *fc = fopen(cs, "rb");
|
||||
FILE *fw = fopen(ws, "rb");
|
||||
if (fc && fw) {
|
||||
rc = 0;
|
||||
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);
|
||||
out:
|
||||
cleanup_tmp(tdc, base);
|
||||
cleanup_tmp(tdw, base);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
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++) {
|
||||
if (rows[i].stage_mask & STAGE_CS) {
|
||||
total++;
|
||||
int got = run_row(cdrv, &rows[i], seq++);
|
||||
if (got != rows[i].expected_exit) {
|
||||
fprintf(stderr,
|
||||
"match_variant_dispatch[cstage run][%s]: exit=%d want=%d\n",
|
||||
rows[i].label, got, rows[i].expected_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (wwpresent && (rows[i].stage_mask & STAGE_WW)) {
|
||||
total++;
|
||||
int got = run_row(wdrv, &rows[i], seq++);
|
||||
if (got != rows[i].expected_exit) {
|
||||
fprintf(stderr,
|
||||
"match_variant_dispatch[wwstage run][%s]: exit=%d want=%d\n",
|
||||
rows[i].label, got, rows[i].expected_exit);
|
||||
fail++;
|
||||
}
|
||||
total++;
|
||||
if (asm_byte_identical(cdrv, wdrv, &rows[i], seq++) != 0) {
|
||||
fprintf(stderr,
|
||||
"match_variant_dispatch[byte-id][%s]: cstage vs wwstage asm differs\n",
|
||||
rows[i].label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!wwpresent)
|
||||
fprintf(stderr, "match_variant_dispatch: skip wwstage (no %s)\n", wdrv);
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "match_variant_dispatch: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("match_variant_dispatch: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user