selfhost/cmd/wcc: index-base esz from n.type_.size + delete indexbaseesz (#72, A.6.3k)
The N_INDEX node carries the checker-stamped element tinfo (#60 arc); cgindex, cgun TK_AMP, and cgassign now read the element stride off that .type_.size instead of indexbaseesz's manual N_DOT-pseudo-field + structlookup walk, retiring the helper (0 callers, ~96 LOC). Aligns down to cstage's idx_eff(base->type)->sub->size (cmd/w6c/cgen.c:3517-18, natural element size) -- strictly more cstage-faithful than indexbaseesz's totsize/slotsize derivation (byte-id held only because firing shapes have totsize==natural; cstage reads natural and ww-old==cstage, so the flip is structural). Each site keeps its nil->default-8 fallback; cgindex stays esz-only (signed_elem unset for the N_DOT base, as before). Closes the A.6.3 cgenutil-collapse arc: every type/size/offset query in cgen now reads the checker-stamped tinfo, and the AST-walker / structinfo-walk helpers it replaced (dotfieldtnode, indexvaluetnode, rhstargetname, dotinnerstructptr, indexbaseesz) are retired. make test 134/134, byte-id 990-997 hold. Coverage: 713/741/755 + self-rebuild.
This commit is contained in:
@@ -8373,8 +8373,8 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
// cmd/wcc/check.c:329-345 which stores tuple positionals on
|
||||
// t->params (Tparam, no offset, consumer recomputes by walking
|
||||
// at cgen.c:5723-5750); the offset-stored shape lets Phase
|
||||
// 2/J/K consumers (dotchainresolve, indexbaseesz) read offsets
|
||||
// directly per the A.6 stamp-once-read-many arc. Direct analog
|
||||
// 2/J consumers (dotchainresolve) read offsets directly per
|
||||
// the A.6 stamp-once-read-many arc. Direct analog
|
||||
// 26724fe (#50 phase 1, A.6.3f-a) for the head/tail append
|
||||
// pattern. Offset matches cstage's raw-sum layout (no per-
|
||||
// element padding) — rule 10 aligns wwstage tuple layout down
|
||||
@@ -10549,7 +10549,7 @@ fn dynamicgrow(m: *state, need: i32) void = {
|
||||
// - pushargsrev: per-call arg pushing
|
||||
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
||||
// - field ops: fieldloadop, fieldstoreop
|
||||
// - index helpers: indexbaseesz, elemsizeof
|
||||
// - index helpers: elemsizeof, elemsizeofc
|
||||
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
||||
// registerstruct, collectstructs
|
||||
// - rhs helpers: taggedvariantindex
|
||||
@@ -11204,8 +11204,8 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
// N_INDEX through a struct field: e.g. cmd.argsptr[i]
|
||||
// where argsptr: *str. cgindex correctly loads the
|
||||
// (ptr, len) pair via indexbaseesz; without this arm
|
||||
// pushargsrev would only push AX and lose the .len.
|
||||
// (ptr, len) pair off the stamped element size; without
|
||||
// this arm pushargsrev would only push AX and lose .len.
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let fld: str = base.str;
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
@@ -11396,103 +11396,6 @@ export fn localloadop(c: *cgen, tnode: *node) str = {
|
||||
return loadopsz(sigd, sz);
|
||||
};
|
||||
|
||||
// indexbaseesz — element size for `arr[i]` where the base is a
|
||||
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
||||
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
||||
// the slice element type.
|
||||
fn indexbaseesz(c: *cgen, base: *node) i32 = {
|
||||
if (base == nil) { return 8; };
|
||||
if (base.kind != nkind.N_DOT) { return 8; };
|
||||
let fld: str = base.str;
|
||||
let inner: *node = base.lhs;
|
||||
if (inner == nil) { return 8; };
|
||||
if (inner.kind != nkind.N_IDENT) { return 8; };
|
||||
let nm: str = inner.str;
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc == nil) { return 8; };
|
||||
let tn: *node = lc.tnode;
|
||||
if (tn == nil) { return 8; };
|
||||
|
||||
// `.ptr` pseudo-field on str/slice → element of the str/slice.
|
||||
// Gated on inner kind, NOT on the field name alone: a struct with
|
||||
// a literal `ptr: *T` field (lib/memio.state, lib/bufio.state) must
|
||||
// route through the generic struct-field arm below so the stride
|
||||
// comes from primsize/structlookup, not the str/slice default. The
|
||||
// over-broad pre-#21 shortcut hard-coded esz=8 and silently
|
||||
// miscompiled `m.ptr[i]` for `*u8` callers (also widened the load
|
||||
// op MOVZBQ → MOVQ in cgindex). Mirrors cstage which routes every
|
||||
// base through `base->type->sub->size` (cmd/w6c/cgen.c idx_eff).
|
||||
if (streq(fld, "ptr")) {
|
||||
let innert: *node = tn;
|
||||
if (tn.kind == nkind.N_TPTR) { innert = tn.lhs; };
|
||||
if (innert != nil) {
|
||||
if (innert.kind == nkind.N_TNAME) {
|
||||
if (streq(innert.str, "str")) { return 1; };
|
||||
};
|
||||
// Slice element: resolve through elemsizeofc so a
|
||||
// slice of a named struct (e.g. *[]option) returns
|
||||
// the struct stride instead of falling through to
|
||||
// elemsizeof's default 8.
|
||||
if (innert.kind == nkind.N_TSLICE) {
|
||||
return elemsizeofc(c, innert);
|
||||
};
|
||||
};
|
||||
// Inner is a struct N_TNAME (or unresolved) — fall through
|
||||
// to the generic struct-field arm below.
|
||||
};
|
||||
|
||||
// Generic struct field: if it's *T, element size is T's size.
|
||||
let lkind: nkind = tn.kind;
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (lkind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let pinner: *node = tn.lhs;
|
||||
if (pinner != nil) {
|
||||
if (pinner.kind == nkind.N_TNAME) { sname = pinner.str; };
|
||||
};
|
||||
};
|
||||
if (sname.len == 0) { return 8; };
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si == nil) { return 8; };
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let fn_: str = fi.fname;
|
||||
if (streq(fn_, fld)) {
|
||||
let ft: *node = fi.tnode;
|
||||
if (ft == nil) { return 8; };
|
||||
if (ft.kind == nkind.N_TPTR) {
|
||||
let elem: *node = ft.lhs;
|
||||
if (elem != nil) {
|
||||
if (elem.kind == nkind.N_TNAME) {
|
||||
if (streq(elem.str, "str")) { return primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(elem.str);
|
||||
if (ps > 0) { return ps; };
|
||||
// Pointer to named struct: indexing
|
||||
// stride is the struct slot size.
|
||||
// Without this, &p.ptr[i] for p.ptr:
|
||||
// *S falls through to 8 and reads
|
||||
// the wrong element.
|
||||
let si: *structinfo = structlookup(c, elem.str);
|
||||
if (si != nil) { return si.totsize; };
|
||||
};
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
if (ft.kind == nkind.N_TSLICE) { return elemsizeof(ft); };
|
||||
// str-typed field: indexing yields one byte
|
||||
// (`n.s[i]` where .s is str — matches C cgen's
|
||||
// MOVZBQ for byte indexing).
|
||||
if (ft.kind == nkind.N_TNAME) {
|
||||
if (streq(ft.str, "str")) { return 1; };
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
|
||||
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
||||
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
||||
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
||||
@@ -14588,7 +14491,14 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
// `s.ptr[i]` / struct-field index: stride is the
|
||||
// checker-stamped element tinfo's natural size, the
|
||||
// same idiom as the N_INDEX-base arm below (#60/#72).
|
||||
// cstage idx_eff(base->type)->sub->size (cmd/w6c/
|
||||
// cgen.c:3517-18). esz-only — N_DOT-base signedness
|
||||
// stays unset, as before.
|
||||
let dt: *tinfo = n.type_: *tinfo;
|
||||
if (dt != nil) { esz = dt.size: i32; };
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
// #60: chained `names[i][k]` — n.type_ is the checker-
|
||||
// stamped outer element tinfo (indexresult over the inner
|
||||
@@ -16371,11 +16281,13 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
// `&p.ptr[i]` shape: stride is the element
|
||||
// of the slice/struct-pointer field, not
|
||||
// the default 8. Mirrors cgindex's N_DOT
|
||||
// arm so &p.ptr[i] and p.ptr[i] agree.
|
||||
esz = indexbaseesz(c, base);
|
||||
// `&p.ptr[i]`: stride is the checker-stamped
|
||||
// element tinfo's natural size, mirroring
|
||||
// cgindex's N_DOT arm so &p.ptr[i] and
|
||||
// p.ptr[i] agree. cstage idx_eff(base->type)
|
||||
// ->sub->size (cmd/w6c/cgen.c:3517-18). #72.
|
||||
let dt: *tinfo = opnd.type_: *tinfo;
|
||||
if (dt != nil) { esz = dt.size: i32; };
|
||||
};};
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
@@ -17612,18 +17524,14 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
// Tagged-element gate (below) keys on the store
|
||||
// target's element type. lhs.type_ is the
|
||||
// checker-stamped element tinfo of the N_INDEX,
|
||||
// so carry lhs and let the gate read it via
|
||||
// lhs.type_ is the checker-stamped element tinfo
|
||||
// of the N_INDEX: esz is its natural size and the
|
||||
// tagged-element gate (below) reads the same
|
||||
// .type_ — same idiom as cgindex's n.type_ read
|
||||
// (#60). Drops the indexvaluetnode walk for the
|
||||
// N_DOT base (#69/#61d, was #30). cstage reads
|
||||
// idx_eff(base->type)->sub (cmd/w6c/cgen.c:3517-
|
||||
// 3523).
|
||||
// (#60/#72). cstage idx_eff(base->type)->sub->size
|
||||
// (cmd/w6c/cgen.c:3517-18).
|
||||
let dt: *tinfo = lhs.type_: *tinfo;
|
||||
if (dt != nil) { elemtn = lhs; };
|
||||
if (dt != nil) { esz = dt.size: i32; elemtn = lhs; };
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
// Chained-write write-side parallel of the
|
||||
// cgindex N_INDEX-base arm (#24): `names[i][k]
|
||||
|
||||
@@ -720,7 +720,14 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
// `s.ptr[i]` / struct-field index: stride is the
|
||||
// checker-stamped element tinfo's natural size, the
|
||||
// same idiom as the N_INDEX-base arm below (#60/#72).
|
||||
// cstage idx_eff(base->type)->sub->size (cmd/w6c/
|
||||
// cgen.c:3517-18). esz-only — N_DOT-base signedness
|
||||
// stays unset, as before.
|
||||
let dt: *tinfo = n.type_: *tinfo;
|
||||
if (dt != nil) { esz = dt.size: i32; };
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
// #60: chained `names[i][k]` — n.type_ is the checker-
|
||||
// stamped outer element tinfo (indexresult over the inner
|
||||
@@ -2503,11 +2510,13 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
// `&p.ptr[i]` shape: stride is the element
|
||||
// of the slice/struct-pointer field, not
|
||||
// the default 8. Mirrors cgindex's N_DOT
|
||||
// arm so &p.ptr[i] and p.ptr[i] agree.
|
||||
esz = indexbaseesz(c, base);
|
||||
// `&p.ptr[i]`: stride is the checker-stamped
|
||||
// element tinfo's natural size, mirroring
|
||||
// cgindex's N_DOT arm so &p.ptr[i] and
|
||||
// p.ptr[i] agree. cstage idx_eff(base->type)
|
||||
// ->sub->size (cmd/w6c/cgen.c:3517-18). #72.
|
||||
let dt: *tinfo = opnd.type_: *tinfo;
|
||||
if (dt != nil) { esz = dt.size: i32; };
|
||||
};};
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
@@ -3744,18 +3753,14 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
// Tagged-element gate (below) keys on the store
|
||||
// target's element type. lhs.type_ is the
|
||||
// checker-stamped element tinfo of the N_INDEX,
|
||||
// so carry lhs and let the gate read it via
|
||||
// lhs.type_ is the checker-stamped element tinfo
|
||||
// of the N_INDEX: esz is its natural size and the
|
||||
// tagged-element gate (below) reads the same
|
||||
// .type_ — same idiom as cgindex's n.type_ read
|
||||
// (#60). Drops the indexvaluetnode walk for the
|
||||
// N_DOT base (#69/#61d, was #30). cstage reads
|
||||
// idx_eff(base->type)->sub (cmd/w6c/cgen.c:3517-
|
||||
// 3523).
|
||||
// (#60/#72). cstage idx_eff(base->type)->sub->size
|
||||
// (cmd/w6c/cgen.c:3517-18).
|
||||
let dt: *tinfo = lhs.type_: *tinfo;
|
||||
if (dt != nil) { elemtn = lhs; };
|
||||
if (dt != nil) { esz = dt.size: i32; elemtn = lhs; };
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
// Chained-write write-side parallel of the
|
||||
// cgindex N_INDEX-base arm (#24): `names[i][k]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// - pushargsrev: per-call arg pushing
|
||||
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
||||
// - field ops: fieldloadop, fieldstoreop
|
||||
// - index helpers: indexbaseesz, elemsizeof
|
||||
// - index helpers: elemsizeof, elemsizeofc
|
||||
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
||||
// registerstruct, collectstructs
|
||||
// - rhs helpers: taggedvariantindex
|
||||
@@ -659,8 +659,8 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
// N_INDEX through a struct field: e.g. cmd.argsptr[i]
|
||||
// where argsptr: *str. cgindex correctly loads the
|
||||
// (ptr, len) pair via indexbaseesz; without this arm
|
||||
// pushargsrev would only push AX and lose the .len.
|
||||
// (ptr, len) pair off the stamped element size; without
|
||||
// this arm pushargsrev would only push AX and lose .len.
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let fld: str = base.str;
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
@@ -851,103 +851,6 @@ export fn localloadop(c: *cgen, tnode: *node) str = {
|
||||
return loadopsz(sigd, sz);
|
||||
};
|
||||
|
||||
// indexbaseesz — element size for `arr[i]` where the base is a
|
||||
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
||||
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
||||
// the slice element type.
|
||||
fn indexbaseesz(c: *cgen, base: *node) i32 = {
|
||||
if (base == nil) { return 8; };
|
||||
if (base.kind != nkind.N_DOT) { return 8; };
|
||||
let fld: str = base.str;
|
||||
let inner: *node = base.lhs;
|
||||
if (inner == nil) { return 8; };
|
||||
if (inner.kind != nkind.N_IDENT) { return 8; };
|
||||
let nm: str = inner.str;
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc == nil) { return 8; };
|
||||
let tn: *node = lc.tnode;
|
||||
if (tn == nil) { return 8; };
|
||||
|
||||
// `.ptr` pseudo-field on str/slice → element of the str/slice.
|
||||
// Gated on inner kind, NOT on the field name alone: a struct with
|
||||
// a literal `ptr: *T` field (lib/memio.state, lib/bufio.state) must
|
||||
// route through the generic struct-field arm below so the stride
|
||||
// comes from primsize/structlookup, not the str/slice default. The
|
||||
// over-broad pre-#21 shortcut hard-coded esz=8 and silently
|
||||
// miscompiled `m.ptr[i]` for `*u8` callers (also widened the load
|
||||
// op MOVZBQ → MOVQ in cgindex). Mirrors cstage which routes every
|
||||
// base through `base->type->sub->size` (cmd/w6c/cgen.c idx_eff).
|
||||
if (streq(fld, "ptr")) {
|
||||
let innert: *node = tn;
|
||||
if (tn.kind == nkind.N_TPTR) { innert = tn.lhs; };
|
||||
if (innert != nil) {
|
||||
if (innert.kind == nkind.N_TNAME) {
|
||||
if (streq(innert.str, "str")) { return 1; };
|
||||
};
|
||||
// Slice element: resolve through elemsizeofc so a
|
||||
// slice of a named struct (e.g. *[]option) returns
|
||||
// the struct stride instead of falling through to
|
||||
// elemsizeof's default 8.
|
||||
if (innert.kind == nkind.N_TSLICE) {
|
||||
return elemsizeofc(c, innert);
|
||||
};
|
||||
};
|
||||
// Inner is a struct N_TNAME (or unresolved) — fall through
|
||||
// to the generic struct-field arm below.
|
||||
};
|
||||
|
||||
// Generic struct field: if it's *T, element size is T's size.
|
||||
let lkind: nkind = tn.kind;
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (lkind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let pinner: *node = tn.lhs;
|
||||
if (pinner != nil) {
|
||||
if (pinner.kind == nkind.N_TNAME) { sname = pinner.str; };
|
||||
};
|
||||
};
|
||||
if (sname.len == 0) { return 8; };
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si == nil) { return 8; };
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let fn_: str = fi.fname;
|
||||
if (streq(fn_, fld)) {
|
||||
let ft: *node = fi.tnode;
|
||||
if (ft == nil) { return 8; };
|
||||
if (ft.kind == nkind.N_TPTR) {
|
||||
let elem: *node = ft.lhs;
|
||||
if (elem != nil) {
|
||||
if (elem.kind == nkind.N_TNAME) {
|
||||
if (streq(elem.str, "str")) { return primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(elem.str);
|
||||
if (ps > 0) { return ps; };
|
||||
// Pointer to named struct: indexing
|
||||
// stride is the struct slot size.
|
||||
// Without this, &p.ptr[i] for p.ptr:
|
||||
// *S falls through to 8 and reads
|
||||
// the wrong element.
|
||||
let si: *structinfo = structlookup(c, elem.str);
|
||||
if (si != nil) { return si.totsize; };
|
||||
};
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
if (ft.kind == nkind.N_TSLICE) { return elemsizeof(ft); };
|
||||
// str-typed field: indexing yields one byte
|
||||
// (`n.s[i]` where .s is str — matches C cgen's
|
||||
// MOVZBQ for byte indexing).
|
||||
if (ft.kind == nkind.N_TNAME) {
|
||||
if (streq(ft.str, "str")) { return 1; };
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
|
||||
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
||||
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
||||
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
||||
|
||||
@@ -1276,8 +1276,8 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
// cmd/wcc/check.c:329-345 which stores tuple positionals on
|
||||
// t->params (Tparam, no offset, consumer recomputes by walking
|
||||
// at cgen.c:5723-5750); the offset-stored shape lets Phase
|
||||
// 2/J/K consumers (dotchainresolve, indexbaseesz) read offsets
|
||||
// directly per the A.6 stamp-once-read-many arc. Direct analog
|
||||
// 2/J consumers (dotchainresolve) read offsets directly per
|
||||
// the A.6 stamp-once-read-many arc. Direct analog
|
||||
// 26724fe (#50 phase 1, A.6.3f-a) for the head/tail append
|
||||
// pattern. Offset matches cstage's raw-sum layout (no per-
|
||||
// element padding) — rule 10 aligns wwstage tuple layout down
|
||||
|
||||
@@ -8373,8 +8373,8 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
|
||||
// cmd/wcc/check.c:329-345 which stores tuple positionals on
|
||||
// t->params (Tparam, no offset, consumer recomputes by walking
|
||||
// at cgen.c:5723-5750); the offset-stored shape lets Phase
|
||||
// 2/J/K consumers (dotchainresolve, indexbaseesz) read offsets
|
||||
// directly per the A.6 stamp-once-read-many arc. Direct analog
|
||||
// 2/J consumers (dotchainresolve) read offsets directly per
|
||||
// the A.6 stamp-once-read-many arc. Direct analog
|
||||
// 26724fe (#50 phase 1, A.6.3f-a) for the head/tail append
|
||||
// pattern. Offset matches cstage's raw-sum layout (no per-
|
||||
// element padding) — rule 10 aligns wwstage tuple layout down
|
||||
@@ -10549,7 +10549,7 @@ fn dynamicgrow(m: *state, need: i32) void = {
|
||||
// - pushargsrev: per-call arg pushing
|
||||
// - type predicates: isstr*/isslice*/istagged*/nodeis* families
|
||||
// - field ops: fieldloadop, fieldstoreop
|
||||
// - index helpers: indexbaseesz, elemsizeof
|
||||
// - index helpers: elemsizeof, elemsizeofc
|
||||
// - slot sizing: structlookup, primsize, slotsize, fieldsize,
|
||||
// registerstruct, collectstructs
|
||||
// - rhs helpers: taggedvariantindex
|
||||
@@ -11204,8 +11204,8 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
};
|
||||
// N_INDEX through a struct field: e.g. cmd.argsptr[i]
|
||||
// where argsptr: *str. cgindex correctly loads the
|
||||
// (ptr, len) pair via indexbaseesz; without this arm
|
||||
// pushargsrev would only push AX and lose the .len.
|
||||
// (ptr, len) pair off the stamped element size; without
|
||||
// this arm pushargsrev would only push AX and lose .len.
|
||||
if (base.kind == nkind.N_DOT) {
|
||||
let fld: str = base.str;
|
||||
if (streq(fld, "ptr")) { return false; };
|
||||
@@ -11396,103 +11396,6 @@ export fn localloadop(c: *cgen, tnode: *node) str = {
|
||||
return loadopsz(sigd, sz);
|
||||
};
|
||||
|
||||
// indexbaseesz — element size for `arr[i]` where the base is a
|
||||
// chained-dot pseudo-field `s.ptr` (s being str/*str/slice/*slice).
|
||||
// For str the element is one byte; for `[]T` / `*[]T` we drill into
|
||||
// the slice element type.
|
||||
fn indexbaseesz(c: *cgen, base: *node) i32 = {
|
||||
if (base == nil) { return 8; };
|
||||
if (base.kind != nkind.N_DOT) { return 8; };
|
||||
let fld: str = base.str;
|
||||
let inner: *node = base.lhs;
|
||||
if (inner == nil) { return 8; };
|
||||
if (inner.kind != nkind.N_IDENT) { return 8; };
|
||||
let nm: str = inner.str;
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc == nil) { return 8; };
|
||||
let tn: *node = lc.tnode;
|
||||
if (tn == nil) { return 8; };
|
||||
|
||||
// `.ptr` pseudo-field on str/slice → element of the str/slice.
|
||||
// Gated on inner kind, NOT on the field name alone: a struct with
|
||||
// a literal `ptr: *T` field (lib/memio.state, lib/bufio.state) must
|
||||
// route through the generic struct-field arm below so the stride
|
||||
// comes from primsize/structlookup, not the str/slice default. The
|
||||
// over-broad pre-#21 shortcut hard-coded esz=8 and silently
|
||||
// miscompiled `m.ptr[i]` for `*u8` callers (also widened the load
|
||||
// op MOVZBQ → MOVQ in cgindex). Mirrors cstage which routes every
|
||||
// base through `base->type->sub->size` (cmd/w6c/cgen.c idx_eff).
|
||||
if (streq(fld, "ptr")) {
|
||||
let innert: *node = tn;
|
||||
if (tn.kind == nkind.N_TPTR) { innert = tn.lhs; };
|
||||
if (innert != nil) {
|
||||
if (innert.kind == nkind.N_TNAME) {
|
||||
if (streq(innert.str, "str")) { return 1; };
|
||||
};
|
||||
// Slice element: resolve through elemsizeofc so a
|
||||
// slice of a named struct (e.g. *[]option) returns
|
||||
// the struct stride instead of falling through to
|
||||
// elemsizeof's default 8.
|
||||
if (innert.kind == nkind.N_TSLICE) {
|
||||
return elemsizeofc(c, innert);
|
||||
};
|
||||
};
|
||||
// Inner is a struct N_TNAME (or unresolved) — fall through
|
||||
// to the generic struct-field arm below.
|
||||
};
|
||||
|
||||
// Generic struct field: if it's *T, element size is T's size.
|
||||
let lkind: nkind = tn.kind;
|
||||
let sname: str;
|
||||
sname.ptr = nil; sname.len = 0;
|
||||
if (lkind == nkind.N_TNAME) { sname = tn.str; };
|
||||
if (lkind == nkind.N_TPTR) {
|
||||
let pinner: *node = tn.lhs;
|
||||
if (pinner != nil) {
|
||||
if (pinner.kind == nkind.N_TNAME) { sname = pinner.str; };
|
||||
};
|
||||
};
|
||||
if (sname.len == 0) { return 8; };
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si == nil) { return 8; };
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
let fn_: str = fi.fname;
|
||||
if (streq(fn_, fld)) {
|
||||
let ft: *node = fi.tnode;
|
||||
if (ft == nil) { return 8; };
|
||||
if (ft.kind == nkind.N_TPTR) {
|
||||
let elem: *node = ft.lhs;
|
||||
if (elem != nil) {
|
||||
if (elem.kind == nkind.N_TNAME) {
|
||||
if (streq(elem.str, "str")) { return primtypesize("str"): i32; };
|
||||
let ps: i32 = primsize(elem.str);
|
||||
if (ps > 0) { return ps; };
|
||||
// Pointer to named struct: indexing
|
||||
// stride is the struct slot size.
|
||||
// Without this, &p.ptr[i] for p.ptr:
|
||||
// *S falls through to 8 and reads
|
||||
// the wrong element.
|
||||
let si: *structinfo = structlookup(c, elem.str);
|
||||
if (si != nil) { return si.totsize; };
|
||||
};
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
if (ft.kind == nkind.N_TSLICE) { return elemsizeof(ft); };
|
||||
// str-typed field: indexing yields one byte
|
||||
// (`n.s[i]` where .s is str — matches C cgen's
|
||||
// MOVZBQ for byte indexing).
|
||||
if (ft.kind == nkind.N_TNAME) {
|
||||
if (streq(ft.str, "str")) { return 1; };
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
fi = fi.finext;
|
||||
};
|
||||
return 8;
|
||||
};
|
||||
|
||||
// elemsizeof — given the type node of an indexable (`*T`, `[]T`,
|
||||
// `[N]T`, `str`), return the byte size of one element (1 for u8/i8/
|
||||
// bool/str-byte, 8 otherwise — same shape as C cgen's esz fallback).
|
||||
@@ -14588,7 +14491,14 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
// `s.ptr[i]` / struct-field index: stride is the
|
||||
// checker-stamped element tinfo's natural size, the
|
||||
// same idiom as the N_INDEX-base arm below (#60/#72).
|
||||
// cstage idx_eff(base->type)->sub->size (cmd/w6c/
|
||||
// cgen.c:3517-18). esz-only — N_DOT-base signedness
|
||||
// stays unset, as before.
|
||||
let dt: *tinfo = n.type_: *tinfo;
|
||||
if (dt != nil) { esz = dt.size: i32; };
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
// #60: chained `names[i][k]` — n.type_ is the checker-
|
||||
// stamped outer element tinfo (indexresult over the inner
|
||||
@@ -16371,11 +16281,13 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
// `&p.ptr[i]` shape: stride is the element
|
||||
// of the slice/struct-pointer field, not
|
||||
// the default 8. Mirrors cgindex's N_DOT
|
||||
// arm so &p.ptr[i] and p.ptr[i] agree.
|
||||
esz = indexbaseesz(c, base);
|
||||
// `&p.ptr[i]`: stride is the checker-stamped
|
||||
// element tinfo's natural size, mirroring
|
||||
// cgindex's N_DOT arm so &p.ptr[i] and
|
||||
// p.ptr[i] agree. cstage idx_eff(base->type)
|
||||
// ->sub->size (cmd/w6c/cgen.c:3517-18). #72.
|
||||
let dt: *tinfo = opnd.type_: *tinfo;
|
||||
if (dt != nil) { esz = dt.size: i32; };
|
||||
};};
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
@@ -17612,18 +17524,14 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
} else { if (base.kind == nkind.N_DOT) {
|
||||
esz = indexbaseesz(c, base);
|
||||
// Tagged-element gate (below) keys on the store
|
||||
// target's element type. lhs.type_ is the
|
||||
// checker-stamped element tinfo of the N_INDEX,
|
||||
// so carry lhs and let the gate read it via
|
||||
// lhs.type_ is the checker-stamped element tinfo
|
||||
// of the N_INDEX: esz is its natural size and the
|
||||
// tagged-element gate (below) reads the same
|
||||
// .type_ — same idiom as cgindex's n.type_ read
|
||||
// (#60). Drops the indexvaluetnode walk for the
|
||||
// N_DOT base (#69/#61d, was #30). cstage reads
|
||||
// idx_eff(base->type)->sub (cmd/w6c/cgen.c:3517-
|
||||
// 3523).
|
||||
// (#60/#72). cstage idx_eff(base->type)->sub->size
|
||||
// (cmd/w6c/cgen.c:3517-18).
|
||||
let dt: *tinfo = lhs.type_: *tinfo;
|
||||
if (dt != nil) { elemtn = lhs; };
|
||||
if (dt != nil) { esz = dt.size: i32; elemtn = lhs; };
|
||||
} else { if (base.kind == nkind.N_INDEX) {
|
||||
// Chained-write write-side parallel of the
|
||||
// cgindex N_INDEX-base arm (#24): `names[i][k]
|
||||
|
||||
Reference in New Issue
Block a user