w6c+w6c_ww: struct-lit store into indexed/deref/field place fills via resolver (#20)
A struct-LITERAL rhs aimed at an N_INDEX element (a[i] = pt{...},
(*ts)[i].caps[k] = capture{...}), an N_UN deref place (*p = pt{...}),
or an indexed-base FIELD place (a[i].f = pt{...}, reviewer-20 sibling)
fell to a scalar store tail in BOTH stages: cgexpr on a struct
literal emits nothing (AX=0) and one MOVQ zeroed the place's first
word — every field silently dropped, a str-leading element's
content.ptr nulled (downstream SEGFAULT). Byte-identically wrong, so
every byte-id gate was blind; runtime pins added.
Fix: divert struct-lit-rhs INDEX/UN-STAR/DOT-over-INDEX places past
the legacy arms and widen the F6 assign-resolver gate
(N_DOT -> N_DOT|N_INDEX|N_UN); the existing C1.25 aggregate arm
materialises the literal into a fresh per-use @placescr slot and
word-copies to the cgplaceaddr-resolved address. No new path;
@placescr alloc site stays single per stage. Rider (task #32): an
array-LITERAL rhs at assignment — unwired for EVERY place kind, same
silent zero-word tail — now dies loud at one choke-point until the
fill lands; build-fail rows pin it.
Gates regex fold-5a (run_thread groupstart capture store,
regex.ha:643-651). Residual adjacent gaps (deref ident-rhs truncation,
>24B ident reassign cs!=ww, struct compound acceptance, value-global
DATAW, tuple-lit deref truncation, CALL-rhs RAX-only store) probed
pre-existing and filed as tasks #31 A-G / #32.
This commit is contained in:
12
Makefile
12
Makefile
@@ -396,6 +396,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
|||||||
$(BIN)/test_delete_range \
|
$(BIN)/test_delete_range \
|
||||||
$(BIN)/test_insert_elem \
|
$(BIN)/test_insert_elem \
|
||||||
$(BIN)/test_arrlit_overlong \
|
$(BIN)/test_arrlit_overlong \
|
||||||
|
$(BIN)/test_idx_structlit_store \
|
||||||
$(BIN)/test_placeaddr_store \
|
$(BIN)/test_placeaddr_store \
|
||||||
$(BIN)/test_tryprop_multisuccess \
|
$(BIN)/test_tryprop_multisuccess \
|
||||||
$(BIN)/test_append_place \
|
$(BIN)/test_append_place \
|
||||||
@@ -1073,6 +1074,17 @@ $(BIN)/test_placeaddr_store: test/wcc/805_placeaddr_store.c \
|
|||||||
$(LIB)/libwwrt.a | $(BIN)
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
$(CC) $(CFLAGS) -o $@ $<
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
|
# Task #20: struct-LITERAL store into an INDEXED element (`a[i] =
|
||||||
|
# pt{...}`, `(*ts)[i].caps[k] = capture{...}`) and a DEREF place
|
||||||
|
# (`*p = pt{...}`) — pre-fix a byte-identical SILENT no-op in both
|
||||||
|
# stages (gate-blind; the regex fold-5a groupstart capture store).
|
||||||
|
# Runtime readback rows + asm byte-id per row on both drivers.
|
||||||
|
$(BIN)/test_idx_structlit_store: test/wcc/809_idx_structlit_store.c \
|
||||||
|
$(BIN)/ww $(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6l \
|
||||||
|
$(BIN)/ww_ww \
|
||||||
|
$(LIB)/libwwrt.a | $(BIN)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $<
|
||||||
|
|
||||||
# F8+F9 (tasks #5/#12, regex fold-2b): `?` interim single-success gate
|
# F8+F9 (tasks #5/#12, regex fold-2b): `?` interim single-success gate
|
||||||
# (|success| > 1 loud-rejected on BOTH stages until task #14's
|
# (|success| > 1 loud-rejected on BOTH stages until task #14's
|
||||||
# subset-union typing) + direct `f()? is T` / `match (f()?)` reject
|
# subset-union typing) + direct `f()? is T` / `match (f()?)` reject
|
||||||
|
|||||||
@@ -4267,6 +4267,41 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
cgexpr(c, n->rhs, locals);
|
cgexpr(c, n->rhs, locals);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/* #20 (task): struct-lit rhs into an INDEXED struct element —
|
||||||
|
* `a[i] = pt{...}`, `(*ts)[i].caps[k] = capture{...}` — a
|
||||||
|
* DEREF place (`*p = pt{...}`) or an indexed-base FIELD
|
||||||
|
* place (`a[i].f = pt{...}`, same class) skips the legacy
|
||||||
|
* arms and routes to the resolver aggregate arm below (the
|
||||||
|
* single @placescr funnel). The legacy arms' rhs handling
|
||||||
|
* (#270-1b ident/dot/deref gate; deref scalar store; the
|
||||||
|
* a[i].f fldstoreop tail) let the lit fall to a scalar
|
||||||
|
* tail: cgexpr(N_STRUCTLIT) emits nothing (AX=0) and one
|
||||||
|
* MOVQ zeroed the place's first word — every field
|
||||||
|
* silently dropped, a leading str header trashed. */
|
||||||
|
int place_slit = 0;
|
||||||
|
if (n->lhs
|
||||||
|
&& (n->lhs->kind == N_INDEX
|
||||||
|
|| (n->lhs->kind == N_UN && n->lhs->op == TK_STAR)
|
||||||
|
|| (n->lhs->kind == N_DOT && n->lhs->lhs
|
||||||
|
&& n->lhs->lhs->kind == N_INDEX))
|
||||||
|
&& n->op == TK_ASSIGN
|
||||||
|
&& n->rhs && n->rhs->kind == N_STRUCTLIT) {
|
||||||
|
Type *iet = type_chase_named(n->lhs->type);
|
||||||
|
if (iet && iet->kind == TY_STRUCT)
|
||||||
|
place_slit = 1;
|
||||||
|
}
|
||||||
|
/* Task #32: an array-LITERAL rhs at assignment is unwired
|
||||||
|
* for EVERY place kind (ident reassign, index, deref, dot)
|
||||||
|
* — only decl-init fills. Pre-#32 the same scalar tail
|
||||||
|
* zeroed one word silently; die loud until the fill lands.
|
||||||
|
* Slice-typed places are already loud in the checker. */
|
||||||
|
if (n->op == TK_ASSIGN && n->lhs
|
||||||
|
&& n->rhs && n->rhs->kind == N_ARRLIT) {
|
||||||
|
Type *alt = type_chase_named(n->lhs->type);
|
||||||
|
if (alt && alt->kind == TY_ARRAY)
|
||||||
|
fatal("array-literal store at assignment "
|
||||||
|
"unwired (task #32)");
|
||||||
|
}
|
||||||
/* p.x = v or p.x += v where p.x is a struct field
|
/* p.x = v or p.x += v where p.x is a struct field
|
||||||
* (direct or via *struct). For compound ops we read-modify-
|
* (direct or via *struct). For compound ops we read-modify-
|
||||||
* write the field; for plain `=` we just write. The base
|
* write the field; for plain `=` we just write. The base
|
||||||
@@ -4645,7 +4680,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
* fall through and silently drop the store. Placed before the
|
* fall through and silently drop the store. Placed before the
|
||||||
* `!= N_IDENT` branch so both shapes share one path. */
|
* `!= N_IDENT` branch so both shapes share one path. */
|
||||||
if (n->lhs && n->lhs->kind == N_DOT && n->lhs->lhs
|
if (n->lhs && n->lhs->kind == N_DOT && n->lhs->lhs
|
||||||
&& n->lhs->lhs->kind == N_INDEX) {
|
&& n->lhs->lhs->kind == N_INDEX && !place_slit) {
|
||||||
Node *idxbase = n->lhs->lhs->lhs;
|
Node *idxbase = n->lhs->lhs->lhs;
|
||||||
Node *idx = n->lhs->lhs->rhs;
|
Node *idx = n->lhs->lhs->rhs;
|
||||||
if (idxbase && idxbase->kind == N_IDENT && idx) {
|
if (idxbase && idxbase->kind == N_IDENT && idx) {
|
||||||
@@ -5482,7 +5517,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
* ptr local) or a more complex expression like s.ptr where
|
* ptr local) or a more complex expression like s.ptr where
|
||||||
* s: *[]u8. We compute the base address, scale the index by
|
* s: *[]u8. We compute the base address, scale the index by
|
||||||
* elem size, and store with the right size. */
|
* elem size, and store with the right size. */
|
||||||
if (n->lhs->kind == N_INDEX && n->lhs->lhs) {
|
if (n->lhs->kind == N_INDEX && n->lhs->lhs && !place_slit) {
|
||||||
Node *base = n->lhs->lhs;
|
Node *base = n->lhs->lhs;
|
||||||
Type *bt = base->type;
|
Type *bt = base->type;
|
||||||
Type *u = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
|
Type *u = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
|
||||||
@@ -5585,8 +5620,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
* address, then word-copy esz bytes: the WRITE-twin of the
|
* address, then word-copy esz bytes: the WRITE-twin of the
|
||||||
* #268 let-init copy loop. Source shapes mirror that loop
|
* #268 let-init copy loop. Source shapes mirror that loop
|
||||||
* (ident local/global, N_DOT field via cg_dotchain_addr,
|
* (ident local/global, N_DOT field via cg_dotchain_addr,
|
||||||
* `*p` deref); a by-value call result is the deferred #271,
|
* `*p` deref); struct-lit sources divert at the place_slit
|
||||||
* so N_CALL/literal sources fall through unchanged. */
|
* gate above (#20), array-lit dies loud (task #32), and a
|
||||||
|
* by-value call result still falls to the scalar tail —
|
||||||
|
* RAX-only store, task #31-G. */
|
||||||
if ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN
|
if ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN
|
||||||
&& esubu && (esubu->kind == TY_STRUCT
|
&& esubu && (esubu->kind == TY_STRUCT
|
||||||
|| esubu->kind == TY_ARRAY
|
|| esubu->kind == TY_ARRAY
|
||||||
@@ -5943,9 +5980,12 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
}
|
}
|
||||||
/* Deref-target assignment `*p = v;`. The size of the store is
|
/* Deref-target assignment `*p = v;`. The size of the store is
|
||||||
* determined by the type *p points at; the pointer expression
|
* determined by the type *p points at; the pointer expression
|
||||||
* is evaluated after the value so we don't need to spill BX. */
|
* is evaluated after the value so we don't need to spill BX.
|
||||||
|
* Retained gap: an aggregate >8B rhs (ident, tuple-lit, call)
|
||||||
|
* truncates to one word here — task #31 A/E/G; struct-lit
|
||||||
|
* diverts at the place_slit gate, array-lit dies loud (#32). */
|
||||||
if (n->lhs && n->lhs->kind == N_UN && n->lhs->op == TK_STAR
|
if (n->lhs && n->lhs->kind == N_UN && n->lhs->op == TK_STAR
|
||||||
&& n->op == TK_ASSIGN) {
|
&& n->op == TK_ASSIGN && !place_slit) {
|
||||||
Type *pt = n->lhs->lhs ? n->lhs->lhs->type : NULL;
|
Type *pt = n->lhs->lhs ? n->lhs->lhs->type : NULL;
|
||||||
Type *pu = (pt && pt->kind == TY_NAMED) ? pt->under : pt;
|
Type *pu = (pt && pt->kind == TY_NAMED) ? pt->under : pt;
|
||||||
Type *vt = (pu && pu->kind == TY_PTR) ? pu->sub : NULL;
|
Type *vt = (pu && pu->kind == TY_PTR) ? pu->sub : NULL;
|
||||||
@@ -6218,8 +6258,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
|||||||
* routes through cgplaceaddr; the load/store emission stays
|
* routes through cgplaceaddr; the load/store emission stays
|
||||||
* here. Any N_DOT shape the resolver can't address dies
|
* here. Any N_DOT shape the resolver can't address dies
|
||||||
* LOUD below: the pre-C1 dispatch tail silently emitted
|
* LOUD below: the pre-C1 dispatch tail silently emitted
|
||||||
* NOTHING (rhs unevaluated) for every such shape. */
|
* NOTHING (rhs unevaluated) for every such shape.
|
||||||
if (n->lhs && n->lhs->kind == N_DOT) {
|
* #20 (task): N_INDEX and N_UN(STAR) lvalues enroll too —
|
||||||
|
* only the struct-lit-rhs diversion above reaches here
|
||||||
|
* (every other indexed/deref shape broke out of its legacy
|
||||||
|
* arm), and the C1.25 aggregate branch fills via
|
||||||
|
* @placescr. */
|
||||||
|
if (n->lhs && (n->lhs->kind == N_DOT
|
||||||
|
|| n->lhs->kind == N_INDEX
|
||||||
|
|| (n->lhs->kind == N_UN && n->lhs->op == TK_STAR))) {
|
||||||
Type *ft = n->lhs->type;
|
Type *ft = n->lhs->type;
|
||||||
Type *fu = type_chase_named(ft);
|
Type *fu = type_chase_named(ft);
|
||||||
int fsz = (int)(ft ? ft->size : 8);
|
int fsz = (int)(ft ? ft->size : 8);
|
||||||
|
|||||||
@@ -27248,6 +27248,42 @@ fn cgcall(c: *cgen, n: *node) void = {
|
|||||||
|
|
||||||
fn cgassign(c: *cgen, n: *node) void = {
|
fn cgassign(c: *cgen, n: *node) void = {
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
|
// #20 (task): struct-lit rhs into an INDEXED struct element —
|
||||||
|
// `a[i] = pt{...}`, `(*ts)[i].caps[k] = capture{...}` — a
|
||||||
|
// DEREF place (`*p = pt{...}`) or an indexed-base FIELD place
|
||||||
|
// (`a[i].f = pt{...}`, same class) skips the legacy arms and
|
||||||
|
// routes to the resolver aggregate arm below (the single
|
||||||
|
// @placescr funnel). The legacy arms' rhs handling (#270-1b
|
||||||
|
// ident/dot/deref gate; deref scalar store; the a[i].f
|
||||||
|
// fldstoreop tail) let the lit fall to a scalar tail:
|
||||||
|
// cgexpr(N_STRUCTLIT) emits nothing (AX=0) and one MOVQ zeroed
|
||||||
|
// the place's first word — every field silently dropped, a
|
||||||
|
// leading str header trashed.
|
||||||
|
let placeslit: bool = false;
|
||||||
|
let placedotidx: bool = false;
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil) {
|
||||||
|
if (lhs.lhs.kind == nkind.N_INDEX) {
|
||||||
|
placedotidx = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if ((lhs.kind == nkind.N_INDEX
|
||||||
|
|| (lhs.kind == nkind.N_UN && lhs.op == tkind.TK_STAR)
|
||||||
|
|| placedotidx)
|
||||||
|
&& n.op == tkind.TK_ASSIGN && n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_STRUCTLIT) {
|
||||||
|
let iet: *tinfo = lhs.type_: *tinfo;
|
||||||
|
for (iet != nil && iet.kind == tykind.TY_NAMED) {
|
||||||
|
iet = iet.under;
|
||||||
|
};
|
||||||
|
if (iet != nil) {
|
||||||
|
if (iet.kind == tykind.TY_STRUCT) {
|
||||||
|
placeslit = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
|
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
|
||||||
// write nothing. Detected by lhs being an nkind.N_IDENT with empty str
|
// write nothing. Detected by lhs being an nkind.N_IDENT with empty str
|
||||||
// (planted by parseprimary on the tkind.TK_UNDER token).
|
// (planted by parseprimary on the tkind.TK_UNDER token).
|
||||||
@@ -27261,6 +27297,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// Task #32: an array-LITERAL rhs at assignment is unwired for
|
||||||
|
// EVERY place kind (ident reassign, index, deref, dot) — only
|
||||||
|
// decl-init fills. Pre-#32 the same scalar tail zeroed one
|
||||||
|
// word silently; die loud until the fill lands. Slice-typed
|
||||||
|
// places are already loud in the checker.
|
||||||
|
if (n.op == tkind.TK_ASSIGN && lhs != nil && n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_ARRLIT) {
|
||||||
|
let alt: *tinfo = lhs.type_: *tinfo;
|
||||||
|
for (alt != nil && alt.kind == tykind.TY_NAMED) {
|
||||||
|
alt = alt.under;
|
||||||
|
};
|
||||||
|
if (alt != nil) {
|
||||||
|
if (alt.kind == tykind.TY_ARRAY) {
|
||||||
|
let mal: str = "array-literal store at assignment unwired (task #32)\n";
|
||||||
|
os.write(2, mal.ptr, mal.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Tagged-union local reassignment: `r = expr;` where r has a
|
// Tagged-union local reassignment: `r = expr;` where r has a
|
||||||
// tagged-union type. Delegate to cgwidentaggedstore (same path
|
// tagged-union type. Delegate to cgwidentaggedstore (same path
|
||||||
// as cglet's tagged-init). Covers nullable fold, tagged source,
|
// as cglet's tagged-init). Covers nullable fold, tagged source,
|
||||||
@@ -27341,10 +27397,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// and BX if str), push, eval pointer, pop value, store.
|
// and BX if str), push, eval pointer, pop value, store.
|
||||||
// We default to MOVQ (8B) since most fixtures use it; for
|
// We default to MOVQ (8B) since most fixtures use it; for
|
||||||
// `*bool` / `*u8` / `*i32` we narrow via the local's tnode.
|
// `*bool` / `*u8` / `*i32` we narrow via the local's tnode.
|
||||||
|
// Retained gap: an aggregate >8B rhs (ident, tuple-lit, call)
|
||||||
|
// truncates to one word here — task #31 A/E/G; struct-lit
|
||||||
|
// diverts at the placeslit gate, array-lit dies loud (#32).
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_UN) {
|
if (lhs.kind == nkind.N_UN) {
|
||||||
if (lhs.op == tkind.TK_STAR) {
|
if (lhs.op == tkind.TK_STAR) {
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN && !placeslit) {
|
||||||
let inner: *node = lhs.lhs;
|
let inner: *node = lhs.lhs;
|
||||||
let elemstr: bool = false;
|
let elemstr: bool = false;
|
||||||
let elemfloat: bool = false;
|
let elemfloat: bool = false;
|
||||||
@@ -27552,7 +27611,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
||||||
// from base.tnode picks MOVB vs MOVQ.
|
// from base.tnode picks MOVB vs MOVQ.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_INDEX) {
|
if (lhs.kind == nkind.N_INDEX && !placeslit) {
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
let base: *node = lhs.lhs;
|
let base: *node = lhs.lhs;
|
||||||
let idx: *node = lhs.rhs;
|
let idx: *node = lhs.rhs;
|
||||||
@@ -27771,8 +27830,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// address, then word-copy esz bytes: the WRITE-twin of
|
// address, then word-copy esz bytes: the WRITE-twin of
|
||||||
// the #268 let-init copy loop. Source shapes mirror that
|
// the #268 let-init copy loop. Source shapes mirror that
|
||||||
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||||
// deref); a by-value call result is the deferred #271, so
|
// deref); struct-lit sources divert at the placeslit
|
||||||
// N_CALL/literal sources fall through unchanged. esz>8
|
// gate above (#20), array-lit dies loud (task #32), and
|
||||||
|
// a by-value call result still falls to the scalar tail
|
||||||
|
// — RAX-only store, task #31-G. esz>8
|
||||||
// non-str/non-slice IS a struct/array/tuple here (the
|
// non-str/non-slice IS a struct/array/tuple here (the
|
||||||
// tagged element already returned above; floats are ≤8).
|
// tagged element already returned above; floats are ≤8).
|
||||||
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||||
@@ -28196,7 +28257,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// field write).
|
// field write).
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
&& lhs.lhs.kind == nkind.N_INDEX) {
|
&& lhs.lhs.kind == nkind.N_INDEX && !placeslit) {
|
||||||
let idxbase: *node = lhs.lhs.lhs;
|
let idxbase: *node = lhs.lhs.lhs;
|
||||||
let idx: *node = lhs.lhs.rhs;
|
let idx: *node = lhs.lhs.rhs;
|
||||||
let fld2: str = lhs.str;
|
let fld2: str = lhs.str;
|
||||||
@@ -30569,8 +30630,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// N_DOT shape the resolver can't address dies LOUD below: the
|
// N_DOT shape the resolver can't address dies LOUD below: the
|
||||||
// pre-C1 fall-off-the-function tail silently emitted NOTHING
|
// pre-C1 fall-off-the-function tail silently emitted NOTHING
|
||||||
// (rhs unevaluated). Mirror of the cstage cgen.c N_ASSIGN arm.
|
// (rhs unevaluated). Mirror of the cstage cgen.c N_ASSIGN arm.
|
||||||
|
// #20 (task): N_INDEX and N_UN(STAR) lvalues enroll too — only
|
||||||
|
// the struct-lit-rhs diversion above reaches here (every other
|
||||||
|
// indexed/deref shape returned from its legacy arm), and the
|
||||||
|
// C1.25 aggregate branch fills via @placescr.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT || lhs.kind == nkind.N_INDEX
|
||||||
|
|| (lhs.kind == nkind.N_UN && lhs.op == tkind.TK_STAR)) {
|
||||||
let ft: *tinfo = lhs.type_: *tinfo;
|
let ft: *tinfo = lhs.type_: *tinfo;
|
||||||
let fu: *tinfo = ft;
|
let fu: *tinfo = ft;
|
||||||
for (fu != nil && fu.kind == tykind.TY_NAMED) {
|
for (fu != nil && fu.kind == tykind.TY_NAMED) {
|
||||||
|
|||||||
@@ -6814,6 +6814,42 @@ fn cgcall(c: *cgen, n: *node) void = {
|
|||||||
|
|
||||||
fn cgassign(c: *cgen, n: *node) void = {
|
fn cgassign(c: *cgen, n: *node) void = {
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
|
// #20 (task): struct-lit rhs into an INDEXED struct element —
|
||||||
|
// `a[i] = pt{...}`, `(*ts)[i].caps[k] = capture{...}` — a
|
||||||
|
// DEREF place (`*p = pt{...}`) or an indexed-base FIELD place
|
||||||
|
// (`a[i].f = pt{...}`, same class) skips the legacy arms and
|
||||||
|
// routes to the resolver aggregate arm below (the single
|
||||||
|
// @placescr funnel). The legacy arms' rhs handling (#270-1b
|
||||||
|
// ident/dot/deref gate; deref scalar store; the a[i].f
|
||||||
|
// fldstoreop tail) let the lit fall to a scalar tail:
|
||||||
|
// cgexpr(N_STRUCTLIT) emits nothing (AX=0) and one MOVQ zeroed
|
||||||
|
// the place's first word — every field silently dropped, a
|
||||||
|
// leading str header trashed.
|
||||||
|
let placeslit: bool = false;
|
||||||
|
let placedotidx: bool = false;
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil) {
|
||||||
|
if (lhs.lhs.kind == nkind.N_INDEX) {
|
||||||
|
placedotidx = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if ((lhs.kind == nkind.N_INDEX
|
||||||
|
|| (lhs.kind == nkind.N_UN && lhs.op == tkind.TK_STAR)
|
||||||
|
|| placedotidx)
|
||||||
|
&& n.op == tkind.TK_ASSIGN && n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_STRUCTLIT) {
|
||||||
|
let iet: *tinfo = lhs.type_: *tinfo;
|
||||||
|
for (iet != nil && iet.kind == tykind.TY_NAMED) {
|
||||||
|
iet = iet.under;
|
||||||
|
};
|
||||||
|
if (iet != nil) {
|
||||||
|
if (iet.kind == tykind.TY_STRUCT) {
|
||||||
|
placeslit = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
|
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
|
||||||
// write nothing. Detected by lhs being an nkind.N_IDENT with empty str
|
// write nothing. Detected by lhs being an nkind.N_IDENT with empty str
|
||||||
// (planted by parseprimary on the tkind.TK_UNDER token).
|
// (planted by parseprimary on the tkind.TK_UNDER token).
|
||||||
@@ -6827,6 +6863,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// Task #32: an array-LITERAL rhs at assignment is unwired for
|
||||||
|
// EVERY place kind (ident reassign, index, deref, dot) — only
|
||||||
|
// decl-init fills. Pre-#32 the same scalar tail zeroed one
|
||||||
|
// word silently; die loud until the fill lands. Slice-typed
|
||||||
|
// places are already loud in the checker.
|
||||||
|
if (n.op == tkind.TK_ASSIGN && lhs != nil && n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_ARRLIT) {
|
||||||
|
let alt: *tinfo = lhs.type_: *tinfo;
|
||||||
|
for (alt != nil && alt.kind == tykind.TY_NAMED) {
|
||||||
|
alt = alt.under;
|
||||||
|
};
|
||||||
|
if (alt != nil) {
|
||||||
|
if (alt.kind == tykind.TY_ARRAY) {
|
||||||
|
let mal: str = "array-literal store at assignment unwired (task #32)\n";
|
||||||
|
os.write(2, mal.ptr, mal.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Tagged-union local reassignment: `r = expr;` where r has a
|
// Tagged-union local reassignment: `r = expr;` where r has a
|
||||||
// tagged-union type. Delegate to cgwidentaggedstore (same path
|
// tagged-union type. Delegate to cgwidentaggedstore (same path
|
||||||
// as cglet's tagged-init). Covers nullable fold, tagged source,
|
// as cglet's tagged-init). Covers nullable fold, tagged source,
|
||||||
@@ -6907,10 +6963,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// and BX if str), push, eval pointer, pop value, store.
|
// and BX if str), push, eval pointer, pop value, store.
|
||||||
// We default to MOVQ (8B) since most fixtures use it; for
|
// We default to MOVQ (8B) since most fixtures use it; for
|
||||||
// `*bool` / `*u8` / `*i32` we narrow via the local's tnode.
|
// `*bool` / `*u8` / `*i32` we narrow via the local's tnode.
|
||||||
|
// Retained gap: an aggregate >8B rhs (ident, tuple-lit, call)
|
||||||
|
// truncates to one word here — task #31 A/E/G; struct-lit
|
||||||
|
// diverts at the placeslit gate, array-lit dies loud (#32).
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_UN) {
|
if (lhs.kind == nkind.N_UN) {
|
||||||
if (lhs.op == tkind.TK_STAR) {
|
if (lhs.op == tkind.TK_STAR) {
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN && !placeslit) {
|
||||||
let inner: *node = lhs.lhs;
|
let inner: *node = lhs.lhs;
|
||||||
let elemstr: bool = false;
|
let elemstr: bool = false;
|
||||||
let elemfloat: bool = false;
|
let elemfloat: bool = false;
|
||||||
@@ -7118,7 +7177,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
||||||
// from base.tnode picks MOVB vs MOVQ.
|
// from base.tnode picks MOVB vs MOVQ.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_INDEX) {
|
if (lhs.kind == nkind.N_INDEX && !placeslit) {
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
let base: *node = lhs.lhs;
|
let base: *node = lhs.lhs;
|
||||||
let idx: *node = lhs.rhs;
|
let idx: *node = lhs.rhs;
|
||||||
@@ -7337,8 +7396,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// address, then word-copy esz bytes: the WRITE-twin of
|
// address, then word-copy esz bytes: the WRITE-twin of
|
||||||
// the #268 let-init copy loop. Source shapes mirror that
|
// the #268 let-init copy loop. Source shapes mirror that
|
||||||
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||||
// deref); a by-value call result is the deferred #271, so
|
// deref); struct-lit sources divert at the placeslit
|
||||||
// N_CALL/literal sources fall through unchanged. esz>8
|
// gate above (#20), array-lit dies loud (task #32), and
|
||||||
|
// a by-value call result still falls to the scalar tail
|
||||||
|
// — RAX-only store, task #31-G. esz>8
|
||||||
// non-str/non-slice IS a struct/array/tuple here (the
|
// non-str/non-slice IS a struct/array/tuple here (the
|
||||||
// tagged element already returned above; floats are ≤8).
|
// tagged element already returned above; floats are ≤8).
|
||||||
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||||
@@ -7762,7 +7823,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// field write).
|
// field write).
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
&& lhs.lhs.kind == nkind.N_INDEX) {
|
&& lhs.lhs.kind == nkind.N_INDEX && !placeslit) {
|
||||||
let idxbase: *node = lhs.lhs.lhs;
|
let idxbase: *node = lhs.lhs.lhs;
|
||||||
let idx: *node = lhs.lhs.rhs;
|
let idx: *node = lhs.lhs.rhs;
|
||||||
let fld2: str = lhs.str;
|
let fld2: str = lhs.str;
|
||||||
@@ -10135,8 +10196,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// N_DOT shape the resolver can't address dies LOUD below: the
|
// N_DOT shape the resolver can't address dies LOUD below: the
|
||||||
// pre-C1 fall-off-the-function tail silently emitted NOTHING
|
// pre-C1 fall-off-the-function tail silently emitted NOTHING
|
||||||
// (rhs unevaluated). Mirror of the cstage cgen.c N_ASSIGN arm.
|
// (rhs unevaluated). Mirror of the cstage cgen.c N_ASSIGN arm.
|
||||||
|
// #20 (task): N_INDEX and N_UN(STAR) lvalues enroll too — only
|
||||||
|
// the struct-lit-rhs diversion above reaches here (every other
|
||||||
|
// indexed/deref shape returned from its legacy arm), and the
|
||||||
|
// C1.25 aggregate branch fills via @placescr.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT || lhs.kind == nkind.N_INDEX
|
||||||
|
|| (lhs.kind == nkind.N_UN && lhs.op == tkind.TK_STAR)) {
|
||||||
let ft: *tinfo = lhs.type_: *tinfo;
|
let ft: *tinfo = lhs.type_: *tinfo;
|
||||||
let fu: *tinfo = ft;
|
let fu: *tinfo = ft;
|
||||||
for (fu != nil && fu.kind == tykind.TY_NAMED) {
|
for (fu != nil && fu.kind == tykind.TY_NAMED) {
|
||||||
|
|||||||
@@ -27248,6 +27248,42 @@ fn cgcall(c: *cgen, n: *node) void = {
|
|||||||
|
|
||||||
fn cgassign(c: *cgen, n: *node) void = {
|
fn cgassign(c: *cgen, n: *node) void = {
|
||||||
let lhs: *node = n.lhs;
|
let lhs: *node = n.lhs;
|
||||||
|
// #20 (task): struct-lit rhs into an INDEXED struct element —
|
||||||
|
// `a[i] = pt{...}`, `(*ts)[i].caps[k] = capture{...}` — a
|
||||||
|
// DEREF place (`*p = pt{...}`) or an indexed-base FIELD place
|
||||||
|
// (`a[i].f = pt{...}`, same class) skips the legacy arms and
|
||||||
|
// routes to the resolver aggregate arm below (the single
|
||||||
|
// @placescr funnel). The legacy arms' rhs handling (#270-1b
|
||||||
|
// ident/dot/deref gate; deref scalar store; the a[i].f
|
||||||
|
// fldstoreop tail) let the lit fall to a scalar tail:
|
||||||
|
// cgexpr(N_STRUCTLIT) emits nothing (AX=0) and one MOVQ zeroed
|
||||||
|
// the place's first word — every field silently dropped, a
|
||||||
|
// leading str header trashed.
|
||||||
|
let placeslit: bool = false;
|
||||||
|
let placedotidx: bool = false;
|
||||||
|
if (lhs != nil) {
|
||||||
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil) {
|
||||||
|
if (lhs.lhs.kind == nkind.N_INDEX) {
|
||||||
|
placedotidx = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
if ((lhs.kind == nkind.N_INDEX
|
||||||
|
|| (lhs.kind == nkind.N_UN && lhs.op == tkind.TK_STAR)
|
||||||
|
|| placedotidx)
|
||||||
|
&& n.op == tkind.TK_ASSIGN && n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_STRUCTLIT) {
|
||||||
|
let iet: *tinfo = lhs.type_: *tinfo;
|
||||||
|
for (iet != nil && iet.kind == tykind.TY_NAMED) {
|
||||||
|
iet = iet.under;
|
||||||
|
};
|
||||||
|
if (iet != nil) {
|
||||||
|
if (iet.kind == tykind.TY_STRUCT) {
|
||||||
|
placeslit = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
|
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
|
||||||
// write nothing. Detected by lhs being an nkind.N_IDENT with empty str
|
// write nothing. Detected by lhs being an nkind.N_IDENT with empty str
|
||||||
// (planted by parseprimary on the tkind.TK_UNDER token).
|
// (planted by parseprimary on the tkind.TK_UNDER token).
|
||||||
@@ -27261,6 +27297,26 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// Task #32: an array-LITERAL rhs at assignment is unwired for
|
||||||
|
// EVERY place kind (ident reassign, index, deref, dot) — only
|
||||||
|
// decl-init fills. Pre-#32 the same scalar tail zeroed one
|
||||||
|
// word silently; die loud until the fill lands. Slice-typed
|
||||||
|
// places are already loud in the checker.
|
||||||
|
if (n.op == tkind.TK_ASSIGN && lhs != nil && n.rhs != nil) {
|
||||||
|
if (n.rhs.kind == nkind.N_ARRLIT) {
|
||||||
|
let alt: *tinfo = lhs.type_: *tinfo;
|
||||||
|
for (alt != nil && alt.kind == tykind.TY_NAMED) {
|
||||||
|
alt = alt.under;
|
||||||
|
};
|
||||||
|
if (alt != nil) {
|
||||||
|
if (alt.kind == tykind.TY_ARRAY) {
|
||||||
|
let mal: str = "array-literal store at assignment unwired (task #32)\n";
|
||||||
|
os.write(2, mal.ptr, mal.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
// Tagged-union local reassignment: `r = expr;` where r has a
|
// Tagged-union local reassignment: `r = expr;` where r has a
|
||||||
// tagged-union type. Delegate to cgwidentaggedstore (same path
|
// tagged-union type. Delegate to cgwidentaggedstore (same path
|
||||||
// as cglet's tagged-init). Covers nullable fold, tagged source,
|
// as cglet's tagged-init). Covers nullable fold, tagged source,
|
||||||
@@ -27341,10 +27397,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// and BX if str), push, eval pointer, pop value, store.
|
// and BX if str), push, eval pointer, pop value, store.
|
||||||
// We default to MOVQ (8B) since most fixtures use it; for
|
// We default to MOVQ (8B) since most fixtures use it; for
|
||||||
// `*bool` / `*u8` / `*i32` we narrow via the local's tnode.
|
// `*bool` / `*u8` / `*i32` we narrow via the local's tnode.
|
||||||
|
// Retained gap: an aggregate >8B rhs (ident, tuple-lit, call)
|
||||||
|
// truncates to one word here — task #31 A/E/G; struct-lit
|
||||||
|
// diverts at the placeslit gate, array-lit dies loud (#32).
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_UN) {
|
if (lhs.kind == nkind.N_UN) {
|
||||||
if (lhs.op == tkind.TK_STAR) {
|
if (lhs.op == tkind.TK_STAR) {
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN && !placeslit) {
|
||||||
let inner: *node = lhs.lhs;
|
let inner: *node = lhs.lhs;
|
||||||
let elemstr: bool = false;
|
let elemstr: bool = false;
|
||||||
let elemfloat: bool = false;
|
let elemfloat: bool = false;
|
||||||
@@ -27552,7 +27611,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
// Array/slice/ptr index store: `arr[i] = v;`. Element size
|
||||||
// from base.tnode picks MOVB vs MOVQ.
|
// from base.tnode picks MOVB vs MOVQ.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_INDEX) {
|
if (lhs.kind == nkind.N_INDEX && !placeslit) {
|
||||||
if (n.op == tkind.TK_ASSIGN) {
|
if (n.op == tkind.TK_ASSIGN) {
|
||||||
let base: *node = lhs.lhs;
|
let base: *node = lhs.lhs;
|
||||||
let idx: *node = lhs.rhs;
|
let idx: *node = lhs.rhs;
|
||||||
@@ -27771,8 +27830,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// address, then word-copy esz bytes: the WRITE-twin of
|
// address, then word-copy esz bytes: the WRITE-twin of
|
||||||
// the #268 let-init copy loop. Source shapes mirror that
|
// the #268 let-init copy loop. Source shapes mirror that
|
||||||
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
// loop (ident, N_DOT field via dotchainaddr, `*p`
|
||||||
// deref); a by-value call result is the deferred #271, so
|
// deref); struct-lit sources divert at the placeslit
|
||||||
// N_CALL/literal sources fall through unchanged. esz>8
|
// gate above (#20), array-lit dies loud (task #32), and
|
||||||
|
// a by-value call result still falls to the scalar tail
|
||||||
|
// — RAX-only store, task #31-G. esz>8
|
||||||
// non-str/non-slice IS a struct/array/tuple here (the
|
// non-str/non-slice IS a struct/array/tuple here (the
|
||||||
// tagged element already returned above; floats are ≤8).
|
// tagged element already returned above; floats are ≤8).
|
||||||
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
let aggsrc: bool = (n.rhs.kind == nkind.N_IDENT)
|
||||||
@@ -28196,7 +28257,7 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// field write).
|
// field write).
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
if (lhs.kind == nkind.N_DOT && lhs.lhs != nil
|
||||||
&& lhs.lhs.kind == nkind.N_INDEX) {
|
&& lhs.lhs.kind == nkind.N_INDEX && !placeslit) {
|
||||||
let idxbase: *node = lhs.lhs.lhs;
|
let idxbase: *node = lhs.lhs.lhs;
|
||||||
let idx: *node = lhs.lhs.rhs;
|
let idx: *node = lhs.lhs.rhs;
|
||||||
let fld2: str = lhs.str;
|
let fld2: str = lhs.str;
|
||||||
@@ -30569,8 +30630,13 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
// N_DOT shape the resolver can't address dies LOUD below: the
|
// N_DOT shape the resolver can't address dies LOUD below: the
|
||||||
// pre-C1 fall-off-the-function tail silently emitted NOTHING
|
// pre-C1 fall-off-the-function tail silently emitted NOTHING
|
||||||
// (rhs unevaluated). Mirror of the cstage cgen.c N_ASSIGN arm.
|
// (rhs unevaluated). Mirror of the cstage cgen.c N_ASSIGN arm.
|
||||||
|
// #20 (task): N_INDEX and N_UN(STAR) lvalues enroll too — only
|
||||||
|
// the struct-lit-rhs diversion above reaches here (every other
|
||||||
|
// indexed/deref shape returned from its legacy arm), and the
|
||||||
|
// C1.25 aggregate branch fills via @placescr.
|
||||||
if (lhs != nil) {
|
if (lhs != nil) {
|
||||||
if (lhs.kind == nkind.N_DOT) {
|
if (lhs.kind == nkind.N_DOT || lhs.kind == nkind.N_INDEX
|
||||||
|
|| (lhs.kind == nkind.N_UN && lhs.op == tkind.TK_STAR)) {
|
||||||
let ft: *tinfo = lhs.type_: *tinfo;
|
let ft: *tinfo = lhs.type_: *tinfo;
|
||||||
let fu: *tinfo = ft;
|
let fu: *tinfo = ft;
|
||||||
for (fu != nil && fu.kind == tykind.TY_NAMED) {
|
for (fu != nil && fu.kind == tykind.TY_NAMED) {
|
||||||
|
|||||||
549
test/wcc/809_idx_structlit_store.c
Normal file
549
test/wcc/809_idx_structlit_store.c
Normal file
@@ -0,0 +1,549 @@
|
|||||||
|
/*
|
||||||
|
* 809_idx_structlit_store — cstage and wwstage agree, byte-for-byte and
|
||||||
|
* at runtime, that a struct-LITERAL store into an INDEXED element place
|
||||||
|
* (`a[i] = pt{...}`, `cs[i] = capture{...}`, `(*ts)[i].caps[k] =
|
||||||
|
* capture{...}`) and into a DEREF place (`*p = pt{...}`) writes every
|
||||||
|
* field (task #20; ken's p13 array find + prober PG4 slice extension;
|
||||||
|
* gates regex fold-5a's run_thread groupstart capture store,
|
||||||
|
* regex.ha:643-651).
|
||||||
|
*
|
||||||
|
* Pre-fix BOTH stages compiled these byte-identically WRONG (gate-blind
|
||||||
|
* — only a runtime pin can hold this): the N_ASSIGN N_INDEX-lhs arm's
|
||||||
|
* aggregate branch (#270-1b) gated its rhs on ident/dot/deref shapes,
|
||||||
|
* so an N_STRUCTLIT rhs fell to the scalar store tail; cgexpr on a
|
||||||
|
* struct literal emits NOTHING (AX stays 0) and the tail stored ONE
|
||||||
|
* zero word at the element base. Net effect: every literal field
|
||||||
|
* silently dropped, the element's first 8 bytes zeroed — for a
|
||||||
|
* str-leading element (the regex capture shape) that nulls content.ptr
|
||||||
|
* and the next read of the str field SEGFAULTS. The N_UN(STAR) deref
|
||||||
|
* arm (`*p = pt{...}`) had the same fall-to-scalar tail — same class.
|
||||||
|
*
|
||||||
|
* The fix (BOTH stages, converged byte-identical): a struct-lit rhs
|
||||||
|
* aimed at an N_INDEX, N_UN(STAR), or indexed-base N_DOT place
|
||||||
|
* (`a[i].f = pt{...}`, reviewer-20 sibling — the a[i].f legacy arm's
|
||||||
|
* fldstoreop tail had the same silent drop) skips the legacy arms and
|
||||||
|
* routes through the F6 assign-resolver (cgplaceaddr) — the SAME C1.25
|
||||||
|
* aggregate arm that wires `(*ts)[i].field = capture{...}` N_DOT
|
||||||
|
* places: materialise the literal into a FRESH per-use @placescr slot
|
||||||
|
* (cg_structlit_fill_bp / cgstructlitfillbp: nested literals, str
|
||||||
|
* fields, TK_ELLIPSIS autofill), resolve the place address, word-copy
|
||||||
|
* esz bytes. Close-by-construction: every non-ident place kind (DOT /
|
||||||
|
* INDEX / UN-STAR) now funnels struct-lit stores through that single
|
||||||
|
* arm; ident places keep their enumerated fill paths. Class boundary
|
||||||
|
* for OTHER rhs kinds at these places: tuple-lit/str-lit into INDEX
|
||||||
|
* and DOT places work (pinned below); array-lit rhs at assignment is
|
||||||
|
* unwired for every place kind and now dies LOUD (task #32, build-fail
|
||||||
|
* rows below); tuple-lit through deref truncates (filed #31-E); CALL
|
||||||
|
* rhs into INDEX/UN-STAR places stores only RAX (filed #31-G).
|
||||||
|
*
|
||||||
|
* row | shape | want
|
||||||
|
* ------------------+-----------------------------------------+------
|
||||||
|
* arr_elem | a[1] = pt{3,4}; a[1].x*10+a[1].y | 34
|
||||||
|
* arr_neighbor | store a[1]; a[0]/a[2] field-set intact | 82
|
||||||
|
* arr_var_idx | a[geti()] = pt{...} (runtime index) | 34
|
||||||
|
* small_elem | 8B one-field elem (esz<=8 word-copy) | 52
|
||||||
|
* nested_lit | a[1] = outer{ in = inner{3,4}, t=5 } | 45
|
||||||
|
* partial_ellipsis | prefill 9,9; a[1] = pt{x=3, ...} → y=0 | 30
|
||||||
|
* alias_lit | type qt = pt; a[1] = qt{3,4} (chase | 34
|
||||||
|
* | peels the NAMED alias before TY_STRUCT) |
|
||||||
|
* all_default | prefill 9,9; a[1] = pt{} zeroes EVERY | 7
|
||||||
|
* | word (fill-loop floor) + 7 |
|
||||||
|
* tuple_elem | a[1] = (3,4); copy-out readback | 34
|
||||||
|
* dotidx_field | a[1].p = pt{3,4} (indexed-base DOT | 34
|
||||||
|
* | place, reviewer-20 sibling fix) |
|
||||||
|
* slice_elem_str | cs[1] = capture{...} 56B str-leading | 128
|
||||||
|
* fieldplace_slice | ts[0].caps[1] = capture{...} | 128
|
||||||
|
* derefspine_slice | (*tsp)[0].caps[1] = capture{...} | 128
|
||||||
|
* callee_capture | (*ts)[i].caps[k] = capture{...} in a | 197
|
||||||
|
* | callee w/ *[]thr param; str readback via |
|
||||||
|
* | strings.compare + len + both neighbors |
|
||||||
|
* | (the EXACT fold-5a consumer shape) |
|
||||||
|
* deref_lit | *p = pt{3,4} (N_UN place, same class) | 34
|
||||||
|
* global_arr | g[1] = pt{3,4}, g: [3]pt module let | 34
|
||||||
|
* arrlit_idx_loud | a[1] = [3,4] — LOUD build-fail (#32) | -1
|
||||||
|
* arrlit_deref_loud | *p = [3,4] — LOUD build-fail (#32) | -1
|
||||||
|
*
|
||||||
|
* global_arr is RUNTIME-ONLY (byteid=0): an uninitialised [N]struct
|
||||||
|
* value-global already diverges on master — wwstage emits a zero
|
||||||
|
* `DATAW main.g(SB)` record (under-sized: 24B for a 48B array),
|
||||||
|
* cstage emits none. Pre-existing, independent of this fix (task #11
|
||||||
|
* family: value-global data emission); the store/readback this row
|
||||||
|
* pins is symmetric and correct on both stages. The arrlit_* rows pin
|
||||||
|
* task #32's loud boundary (want -1 = driver build MUST fail; pre-#32
|
||||||
|
* both stages silently zeroed one word); flip them to runtime rows
|
||||||
|
* when #32 wires the fill.
|
||||||
|
*/
|
||||||
|
#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; int want; int byteid; };
|
||||||
|
|
||||||
|
/* The 56B regex capture shape: str header leading + 4 size fields.
|
||||||
|
* Shared preamble for the slice rows; only the store site differs. */
|
||||||
|
#define CAPTURE_DEFS \
|
||||||
|
"package main;\n" \
|
||||||
|
"type capture = struct {\n" \
|
||||||
|
"\tcontent: str,\n" \
|
||||||
|
"\tstart: size,\n" \
|
||||||
|
"\tstart_bytesize: size,\n" \
|
||||||
|
"\tend: size,\n" \
|
||||||
|
"\tend_bytesize: size,\n" \
|
||||||
|
"};\n"
|
||||||
|
|
||||||
|
#define CAPTURE_SEED \
|
||||||
|
"\tlet cs: []capture = [];\n" \
|
||||||
|
"\tappend(cs, capture { content = \"a\", start = 1: size, " \
|
||||||
|
"start_bytesize = 1: size, end = 1: size, " \
|
||||||
|
"end_bytesize = 1: size });\n" \
|
||||||
|
"\tappend(cs, capture { content = \"b\", start = 2: size, " \
|
||||||
|
"start_bytesize = 2: size, end = 2: size, " \
|
||||||
|
"end_bytesize = 2: size });\n"
|
||||||
|
|
||||||
|
#define CAPTURE_STORE(place) \
|
||||||
|
"\t" place " = capture { content = \"grp\", start = 3: size, " \
|
||||||
|
"start_bytesize = 4: size, end = 60: size, " \
|
||||||
|
"end_bytesize = 61: size };\n"
|
||||||
|
|
||||||
|
static const struct row rows[] = {
|
||||||
|
{ "arr_elem",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [3]pt;\n"
|
||||||
|
"\ta[1] = pt { x = 3u64, y = 4u64 };\n"
|
||||||
|
"\treturn (a[1].x * 10u64 + a[1].y): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
34, 1 },
|
||||||
|
|
||||||
|
{ "arr_neighbor",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [3]pt;\n"
|
||||||
|
"\ta[0].x = 7u64; a[2].y = 9u64;\n"
|
||||||
|
"\ta[1] = pt { x = 3u64, y = 4u64 };\n"
|
||||||
|
"\treturn (a[0].x * 10u64 + a[2].y + a[1].x): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
82, 1 },
|
||||||
|
|
||||||
|
{ "arr_var_idx",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"fn geti() size = { return 1: size; };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [3]pt;\n"
|
||||||
|
"\ta[geti()] = pt { x = 3u64, y = 4u64 };\n"
|
||||||
|
"\treturn (a[1].x * 10u64 + a[1].y): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
34, 1 },
|
||||||
|
|
||||||
|
{ "small_elem",
|
||||||
|
"package main;\n"
|
||||||
|
"type one = struct { v: u64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [2]one;\n"
|
||||||
|
"\ta[0].v = 2u64;\n"
|
||||||
|
"\ta[1] = one { v = 5u64 };\n"
|
||||||
|
"\treturn (a[1].v * 10u64 + a[0].v): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
52, 1 },
|
||||||
|
|
||||||
|
{ "nested_lit",
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = struct { a: u64, b: u64 };\n"
|
||||||
|
"type outer = struct { in: inner, t: u64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [2]outer;\n"
|
||||||
|
"\ta[1] = outer { in = inner { a = 3u64, b = 4u64 }, "
|
||||||
|
"t = 5u64 };\n"
|
||||||
|
"\treturn (a[1].in.b * 10u64 + a[1].t): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
45, 1 },
|
||||||
|
|
||||||
|
{ "partial_ellipsis",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [3]pt;\n"
|
||||||
|
"\ta[1].x = 9u64; a[1].y = 9u64;\n"
|
||||||
|
"\ta[1] = pt { x = 3u64, ... };\n"
|
||||||
|
"\treturn (a[1].x * 10u64 + a[1].y): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
30, 1 },
|
||||||
|
|
||||||
|
{ "alias_lit",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"type qt = pt;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [3]pt;\n"
|
||||||
|
"\ta[1] = qt { x = 3u64, y = 4u64 };\n"
|
||||||
|
"\treturn (a[1].x * 10u64 + a[1].y): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
34, 1 },
|
||||||
|
|
||||||
|
{ "all_default",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [3]pt;\n"
|
||||||
|
"\ta[1].x = 9u64; a[1].y = 9u64;\n"
|
||||||
|
"\ta[1] = pt {};\n"
|
||||||
|
"\treturn (a[1].x * 10u64 + a[1].y + 7u64): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
7, 1 },
|
||||||
|
|
||||||
|
{ "tuple_elem",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [2](u64, u64);\n"
|
||||||
|
"\ta[1] = (3u64, 4u64);\n"
|
||||||
|
"\tlet t: (u64, u64) = a[1];\n"
|
||||||
|
"\treturn (t.0 * 10u64 + t.1): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
34, 1 },
|
||||||
|
|
||||||
|
{ "dotidx_field",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"type box = struct { p: pt, t: u64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [2]box;\n"
|
||||||
|
"\ta[1].p.x = 9u64; a[1].p.y = 9u64;\n"
|
||||||
|
"\tif (a[1].p.x != 9u64 || a[1].p.y != 9u64) { return 250; };\n"
|
||||||
|
"\ta[1].p = pt { x = 3u64, y = 4u64 };\n"
|
||||||
|
"\treturn (a[1].p.x * 10u64 + a[1].p.y): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
34, 1 },
|
||||||
|
|
||||||
|
{ "slice_elem_str",
|
||||||
|
CAPTURE_DEFS
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
CAPTURE_SEED
|
||||||
|
CAPTURE_STORE("cs[1]")
|
||||||
|
"\tif (cs[0].start != 1) { return 250; };\n"
|
||||||
|
"\treturn (cs[1].start + cs[1].start_bytesize + cs[1].end + "
|
||||||
|
"cs[1].end_bytesize): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
128, 1 },
|
||||||
|
|
||||||
|
{ "fieldplace_slice",
|
||||||
|
CAPTURE_DEFS
|
||||||
|
"type thr = struct { pc: size, caps: []capture };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
CAPTURE_SEED
|
||||||
|
"\tlet ts: []thr = [];\n"
|
||||||
|
"\tappend(ts, thr { pc = 5: size, caps = cs });\n"
|
||||||
|
CAPTURE_STORE("ts[0].caps[1]")
|
||||||
|
"\tif (ts[0].caps[0].start != 1) { return 250; };\n"
|
||||||
|
"\treturn (ts[0].caps[1].start + ts[0].caps[1].start_bytesize + "
|
||||||
|
"ts[0].caps[1].end + ts[0].caps[1].end_bytesize): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
128, 1 },
|
||||||
|
|
||||||
|
{ "derefspine_slice",
|
||||||
|
CAPTURE_DEFS
|
||||||
|
"type thr = struct { pc: size, caps: []capture };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
CAPTURE_SEED
|
||||||
|
"\tlet ts: []thr = [];\n"
|
||||||
|
"\tappend(ts, thr { pc = 5: size, caps = cs });\n"
|
||||||
|
"\tlet tsp = &ts;\n"
|
||||||
|
CAPTURE_STORE("(*tsp)[0].caps[1]")
|
||||||
|
"\tif (ts[0].caps[0].start != 1) { return 250; };\n"
|
||||||
|
"\treturn (ts[0].caps[1].start + ts[0].caps[1].start_bytesize + "
|
||||||
|
"ts[0].caps[1].end + ts[0].caps[1].end_bytesize): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
128, 1 },
|
||||||
|
|
||||||
|
/* The fold-5a consumer pin: regex.ha:643-651 verbatim shape —
|
||||||
|
* callee takes *[]thr, double-indexes through the deref spine,
|
||||||
|
* stores the 56B capture literal. Readback covers the str field
|
||||||
|
* (strings.compare + len — pre-fix this SEGFAULTED on the nulled
|
||||||
|
* content.ptr), both neighbor elements (56B store must not
|
||||||
|
* smear), and all four size fields. 3+4+60+61+3*23 = 197. */
|
||||||
|
{ "callee_capture",
|
||||||
|
"package main;\n"
|
||||||
|
"\n"
|
||||||
|
"import strings;\n"
|
||||||
|
"\n"
|
||||||
|
"type capture = struct {\n"
|
||||||
|
"\tcontent: str,\n"
|
||||||
|
"\tstart: size,\n"
|
||||||
|
"\tstart_bytesize: size,\n"
|
||||||
|
"\tend: size,\n"
|
||||||
|
"\tend_bytesize: size,\n"
|
||||||
|
"};\n"
|
||||||
|
"type thr = struct { pc: size, caps: []capture };\n"
|
||||||
|
"fn store(ts: *[]thr, i: size, idx: size) void = {\n"
|
||||||
|
"\t(*ts)[i].caps[idx] = capture {\n"
|
||||||
|
"\t\tcontent = \"grp\",\n"
|
||||||
|
"\t\tstart = 3: size,\n"
|
||||||
|
"\t\tstart_bytesize = 4: size,\n"
|
||||||
|
"\t\tend = 60: size,\n"
|
||||||
|
"\t\tend_bytesize = 61: size,\n"
|
||||||
|
"\t};\n"
|
||||||
|
"};\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet cs: []capture = [];\n"
|
||||||
|
"\tappend(cs, capture { content = \"a\", start = 1: size, "
|
||||||
|
"start_bytesize = 1: size, end = 1: size, "
|
||||||
|
"end_bytesize = 1: size });\n"
|
||||||
|
"\tappend(cs, capture { content = \"b\", start = 2: size, "
|
||||||
|
"start_bytesize = 2: size, end = 2: size, "
|
||||||
|
"end_bytesize = 2: size });\n"
|
||||||
|
"\tappend(cs, capture { content = \"c\", start = 9: size, "
|
||||||
|
"start_bytesize = 9: size, end = 9: size, "
|
||||||
|
"end_bytesize = 9: size });\n"
|
||||||
|
"\tlet ts: []thr = [];\n"
|
||||||
|
"\tappend(ts, thr { pc = 5: size, caps = cs });\n"
|
||||||
|
"\tstore(&ts, 0: size, 1: size);\n"
|
||||||
|
"\tif (strings.compare(ts[0].caps[1].content, \"grp\") != 0) "
|
||||||
|
"{ return 250; };\n"
|
||||||
|
"\tif (len(ts[0].caps[1].content) != 3) { return 249; };\n"
|
||||||
|
"\tif (ts[0].caps[0].end != 1 || ts[0].caps[2].start != 9) "
|
||||||
|
"{ return 248; };\n"
|
||||||
|
"\tif (strings.compare(ts[0].caps[2].content, \"c\") != 0) "
|
||||||
|
"{ return 247; };\n"
|
||||||
|
"\tlet acc = ts[0].caps[1].start + ts[0].caps[1].start_bytesize\n"
|
||||||
|
"\t\t+ ts[0].caps[1].end + ts[0].caps[1].end_bytesize\n"
|
||||||
|
"\t\t+ ts[0].caps[1].start * 23;\n"
|
||||||
|
"\treturn acc: i32;\n"
|
||||||
|
"};\n",
|
||||||
|
197, 1 },
|
||||||
|
|
||||||
|
{ "deref_lit",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: pt = pt { x = 1u64, y = 2u64 };\n"
|
||||||
|
"\tlet p: *pt = &a;\n"
|
||||||
|
"\t*p = pt { x = 3u64, y = 4u64 };\n"
|
||||||
|
"\treturn (a.x * 10u64 + a.y): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
34, 1 },
|
||||||
|
|
||||||
|
{ "global_arr",
|
||||||
|
"package main;\n"
|
||||||
|
"type pt = struct { x: u64, y: u64 };\n"
|
||||||
|
"let g: [3]pt;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tg[1] = pt { x = 3u64, y = 4u64 };\n"
|
||||||
|
"\treturn (g[1].x * 10u64 + g[1].y): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
34, 0 },
|
||||||
|
|
||||||
|
{ "arrlit_idx_loud",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [2][2]u64;\n"
|
||||||
|
"\ta[1] = [3u64, 4u64];\n"
|
||||||
|
"\treturn (a[1][0] * 10u64 + a[1][1]): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
-1, 0 },
|
||||||
|
|
||||||
|
{ "arrlit_deref_loud",
|
||||||
|
"package main;\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet a: [2]u64;\n"
|
||||||
|
"\tlet p: *[2]u64 = &a;\n"
|
||||||
|
"\t*p = [3u64, 4u64];\n"
|
||||||
|
"\treturn (a[0] * 10u64 + a[1]): i32;\n"
|
||||||
|
"};\n",
|
||||||
|
-1, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
run_driver(const char *driver, const struct row *r, int i)
|
||||||
|
{
|
||||||
|
char src[64], tmpdir[64], cmd[1024];
|
||||||
|
snprintf(src, sizeof src, "/tmp/isls_%d_%d.ww", getpid(), i);
|
||||||
|
snprintf(tmpdir, sizeof tmpdir, "/tmp/isls_%d_d_%d", getpid(), i);
|
||||||
|
|
||||||
|
FILE *f = fopen(src, "wb");
|
||||||
|
if (!f) return -1;
|
||||||
|
fputs(r->src, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
mkdir(tmpdir, 0755);
|
||||||
|
snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null",
|
||||||
|
tmpdir, driver, src);
|
||||||
|
if (runwait(cmd) != 0) {
|
||||||
|
/* want == -1 rows pin a LOUD build refusal (task #32). */
|
||||||
|
if (r->want != -1)
|
||||||
|
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||||
|
r->label, driver);
|
||||||
|
unlink(src); rmdir(tmpdir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *base = strrchr(src, '/');
|
||||||
|
base = base ? base + 1 : src;
|
||||||
|
char outbin[128];
|
||||||
|
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||||
|
char *dot = strrchr(outbin, '.');
|
||||||
|
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||||
|
int got = runwait(outbin);
|
||||||
|
|
||||||
|
unlink(src); unlink(outbin); rmdir(tmpdir);
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* asm_byte_identical — generate .s via the driver, once with cstage's
|
||||||
|
* w6c and once with the wwstage compiler (WW_W6C override; w6a/w6l stay
|
||||||
|
* cstage — only the .s is compared), and diff. The driver route — not a
|
||||||
|
* bare `w6c src.ww` — so importing rows (callee_capture pulls strings)
|
||||||
|
* resolve; the driver leaves src.s NEXT TO the source. Pre-fix the asm
|
||||||
|
* was byte-identically WRONG (both stages dropped the fill), so these
|
||||||
|
* rows pin only that the converged fix stays symmetric; the runtime
|
||||||
|
* rows above carry correctness. */
|
||||||
|
static int
|
||||||
|
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||||
|
{
|
||||||
|
char src[64], srcs[64], comb[80], srco[64], tmpdir[64];
|
||||||
|
char cs[64], ws[64], cmd[1024];
|
||||||
|
snprintf(src, sizeof src, "/tmp/isls_asm_%d_%d.ww", getpid(), i);
|
||||||
|
snprintf(srcs, sizeof srcs, "/tmp/isls_asm_%d_%d.s", getpid(), i);
|
||||||
|
snprintf(srco, sizeof srco, "/tmp/isls_asm_%d_%d.o", getpid(), i);
|
||||||
|
snprintf(comb, sizeof comb, "/tmp/isls_asm_%d_%d.combined.ww",
|
||||||
|
getpid(), i);
|
||||||
|
snprintf(tmpdir, sizeof tmpdir, "/tmp/isls_asm_%d_d_%d", getpid(), i);
|
||||||
|
snprintf(cs, sizeof cs, "/tmp/isls_asm_%d_%d_c.s", getpid(), i);
|
||||||
|
snprintf(ws, sizeof ws, "/tmp/isls_asm_%d_%d_w.s", getpid(), i);
|
||||||
|
|
||||||
|
FILE *f = fopen(src, "wb");
|
||||||
|
if (!f) return -1;
|
||||||
|
fputs(r->src, f);
|
||||||
|
fclose(f);
|
||||||
|
mkdir(tmpdir, 0755);
|
||||||
|
|
||||||
|
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s 2>/dev/null",
|
||||||
|
tmpdir, bin, src);
|
||||||
|
if (runwait(cmd) != 0 || rename(srcs, cs) != 0) {
|
||||||
|
fprintf(stderr, "row[%s]: cstage driver .s failed\n",
|
||||||
|
r->label);
|
||||||
|
unlink(src); unlink(srco); unlink(comb); rmdir(tmpdir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"cd %s && WW_W6C=%s/w6c_ww %s/ww build %s 2>/dev/null",
|
||||||
|
tmpdir, bin, bin, src);
|
||||||
|
if (runwait(cmd) != 0 || rename(srcs, ws) != 0) {
|
||||||
|
fprintf(stderr, "row[%s]: wwstage driver .s failed\n",
|
||||||
|
r->label);
|
||||||
|
unlink(src); unlink(srco); unlink(comb); unlink(cs);
|
||||||
|
rmdir(tmpdir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fc = fopen(cs, "rb");
|
||||||
|
FILE *fw = fopen(ws, "rb");
|
||||||
|
int rc = 0;
|
||||||
|
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, "row[%s]: cstage vs wwstage asm differs\n",
|
||||||
|
r->label);
|
||||||
|
{
|
||||||
|
char outbin[128];
|
||||||
|
snprintf(outbin, sizeof outbin, "%s/isls_asm_%d_%d",
|
||||||
|
tmpdir, getpid(), i);
|
||||||
|
unlink(outbin);
|
||||||
|
}
|
||||||
|
unlink(src); unlink(srco); unlink(comb);
|
||||||
|
unlink(cs); unlink(ws);
|
||||||
|
rmdir(tmpdir);
|
||||||
|
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];
|
||||||
|
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||||
|
char wdrv[1024];
|
||||||
|
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||||
|
|
||||||
|
struct { const char *name; const char *path; int gated_on_existence; }
|
||||||
|
drivers[] = {
|
||||||
|
{ "cstage", cdrv, 0 },
|
||||||
|
{ "wwstage", wdrv, 1 },
|
||||||
|
{ NULL, NULL, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||||
|
int total = 0, fail = 0;
|
||||||
|
|
||||||
|
for (int d = 0; drivers[d].name; d++) {
|
||||||
|
if (drivers[d].gated_on_existence
|
||||||
|
&& access(drivers[d].path, X_OK) != 0) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"idx_structlit_store: skip %s (no %s)\n",
|
||||||
|
drivers[d].name, drivers[d].path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||||
|
total++;
|
||||||
|
if (got != rows[i].want) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"idx_structlit_store[%s][%s]: "
|
||||||
|
"exit=%d want=%d\n",
|
||||||
|
drivers[d].name, rows[i].label,
|
||||||
|
got, rows[i].want);
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (access(wdrv, X_OK) == 0) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
if (!rows[i].byteid) continue;
|
||||||
|
total++;
|
||||||
|
if (asm_byte_identical(bin, &rows[i], i) != 0)
|
||||||
|
fail++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fail) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"idx_structlit_store: %d/%d fixtures failed\n",
|
||||||
|
fail, total);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("idx_structlit_store: %d/%d ok\n", total, total);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user