cstage+selfhost+test: cgen N_ASSIGN whole-STRUCT (call+structlit, 5 sites)
Receive side of #4's cgreturn ABI (aee8149) for TY_STRUCT lvalues of size <=24B. Producer materialises rhs into AX=bytes[0..7], DX=[8..15], CX=[16..23], zero-padded to 24B; receive sites here read the regs and write only `declared sz` bytes — MOVQ for full 8B chunks plus a sized tail (MOVL/MOVW/MOVB) by the *declared* struct size. ASYMMETRY: do NOT mirror the sender's three uniform MOVQs, else trailing 1..7B chunks overrun the next local slot. Tail chunks in {3,5,6,7} are unreachable under WW struct align rules (size%align==0) and fall through. Five sites wired in each stage (cstage cgen.c, wwstage cgenexpr.ww + cgenstmt.ww), call-result + structlit rhs at each: - N_LET `let s: T = bar()` / `= T{...}` cgenstmt cglet - N_ASSIGN N_IDENT-lhs `s = bar()` / `= T{...}` cgenexpr cgassign - N_ASSIGN single-DOT local-base `o.f = ...` - N_ASSIGN single-DOT ptr-base auto-deref `p.f = ...` - N_ASSIGN single-DOT global-base `g.f = ...` - N_ASSIGN chained-DOT depth>=2 `o.m.in = ...` (The four dot-flavors share one shape pattern, hence "5 sites".) Where the dst addr needs scratch (ptr-base/global-base/via_cx), it is loaded into BX after the call so CX stays as the third value word; for structlit field-walks BX is reloaded before each store since cgexpr clobbers AX/BX between fields. wwstage needed a new `structnaturalsize(si)` helper (cgenutil.ww): si.totsize is mis-named — it's slot-padded to 8 by registerstruct for stack-slot use, while the receive ABI wants the type's natural size (max(foff+fsz)). Splitting si.totsize into naturalsize + slotsize is tracked as the wwstage struct sizing follow-up (task #15); until that lands, the helper recovers the natural size at receive sites. Test 701_cgassign_struct.c (18 rows, 3 checks each — cstage value, wwstage value, asm byte-identity), wired in Makefile after 698. The headline ASYMMETRY case is the 20B `{i32×5}` row: sender pads to 24B via three MOVQs, receiver writes MOVQ AX +0, MOVQ DX +8, MOVL CX +16. A regression to a MOVQ tail there overruns 4B past the slot and flips the exit-code check. smoke.combined.ww is the auto-regen ride-along of strings.freeall landing in714d089(worker-shlex). Pre-existing gaps surfaced and tracked separately (not fixed here, out of scope): - task #16: silent drop of `(*p).f = ...` explicit-deref dot lhs. - task #17: silent zero of nested STRUCTLIT field in N_LET / N_ASSIGN initializer — the field_chain and field_global test rows use explicit field writes (`o.m.t = 10i64;`) rather than nested literals as a fixture-level workaround. - task #9: module-name-mangle for fn labels avoided in the field_global_call fixture by `let g: outer;` (no init). make test: 59/59. 994_w6c_ww + 995_self_rebuild PASS — bootstrap byte-identity is the load-bearing proof for this commit's scope.
This commit is contained in:
@@ -1148,6 +1148,31 @@ fn nodeprimwidth(c: *cgen, n: *node) i32 = {
|
||||
|
||||
// ---- type-driven slot sizing ----------------------------------------
|
||||
|
||||
// structnaturalsize — type-natural size of `si`, i.e. max(foff +
|
||||
// fsz) across declared fields. Mirrors cstage's `lu->size` for a
|
||||
// TY_STRUCT (rounded only to the struct's maxalign).
|
||||
//
|
||||
// NOTE: si.totsize is mis-named — it's actually the *slot-padded*
|
||||
// size (rounded up to 8 for stack-slot use; see registerstruct's
|
||||
// tail `if ((off & 7) != 0) ...`). Frame allocation, [N]foo stride,
|
||||
// and similar consumers want that slot-padded number. The
|
||||
// receive-side ABI (#5) and any future "TYPE size, not slot size"
|
||||
// query wants the natural size. Until si.totsize is split into
|
||||
// si.naturalsize + si.slotsize (tracked as the wwstage-sizing
|
||||
// follow-up task), recover the type-natural size from the field
|
||||
// chain here.
|
||||
fn structnaturalsize(si: *structinfo) i32 = {
|
||||
if (si == nil) { return 0; };
|
||||
let n: i32 = 0;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let end: i32 = fi.foff + fi.fsz;
|
||||
if (end > n) { n = end; };
|
||||
fi = fi.finext;
|
||||
};
|
||||
return n;
|
||||
};
|
||||
|
||||
fn structlookup(c: *cgen, name: str) *structinfo = {
|
||||
// Exact match first: bare-from-source struct names and already-
|
||||
// leafed lookups hit here directly.
|
||||
|
||||
Reference in New Issue
Block a user