cgen: convert the struct-store loop arms to a tinfo-native fill (#31 c2)
The W1/W2/W5/W4b store / structlit-fill arms still resolved struct field LAYOUT by bare-leaf name (structlookupchain / structlookup / letvarstructinfo) inside the per-field store loop -- the write-side twin of commit-1's read fix. Under a cross-module same-leaf collision (two modules each exporting a `pair`, 16B vs 24B) the field is stored at the wrong offset/width; an interior 2-byte field store silently corrupted neighbours. cstage walks the resolved Type* (never a name) and is correct; this aligns wwstage UP (ww-only change). Convert each store loop to a tinfo-native fill. Two new helpers in cgenutil.ww: sretretsizetn (a verbatim port of cstage cg_sret_retsize -- 4 kind-branches, no TY_BANG, dropping the #129 module-juggling that was pure name-keying compensation, redundant once the lookup walks resolved tinfo) and cgstructlitfilltn (the tinfo-native twin of cgstructlitfill, recursing on tichase(tf.type_).fields with NO name lookup at any nesting level). Supporting twins storeopsz / tfieldlookup / cgdotfieldhardstoptn. Because the fill never bottoms out on a name, the W4b "decisive wall" (a structlit that needed a (sname,smod)-keyed structinfo the stamped leaf-only tinfo couldn't supply) dissolves by construction, with no module-aware tinfo->structinfo bridge. This CLOSES the nine in-loop nested sub-arms (N_CALL / N_STRUCTLIT / N_IDENT field receive) by construction -- the tf walk has no name lookup. The non-reddenable global arms (W5/W4b) are converted-for-construction: a qualified global decl resolves its bare leaf correctly today, so byte-id (cs.s==ww.s) is their net, not a value pin. `!T` is kind-preserving in both stages (resolve_type / tinfofornode), so a `!struct` sret sizes as the inner struct, never TAGGED -- the #129 drop changes no size. Completes the 2-commit #31 arc (RULING R2 / Opt-2, .ai/ken-31-spec.md): the #224 name-keyed receiver-layout cluster is now closed by construction. The residual nested-field surface (the shared cgstructlitfill kept for its non-#31 callers + the cgenstmt/cgenutil let-receive/fill family) is tracked as #32. Pins: test/wcc/797 value-asserts ptr-WRITE (W1) and val-WRITE (W2), each reddening under independent per-arm revert; nestfill_box drives cgstructlitfilltn's nested recursion (bootstrap-uncovered -- the corpus has no struct-literal field store -- so the value pin is its only net).
This commit is contained in:
@@ -1695,6 +1695,19 @@ fn loadopsz(sigd: bool, sz: i32) str = {
|
||||
return "MOVQ";
|
||||
};
|
||||
|
||||
// storeopsz — store op when the size has already been resolved
|
||||
// upstream off a stamped tinfo (the #31 receiver-layout store choke-
|
||||
// point). Size-only twin of fieldstoreop's body {1->MOVB, 2->MOVW,
|
||||
// 4->MOVL, else MOVQ}; the store side carries no signedness. Preserves
|
||||
// the #15 sub-8 guard (a 2B tail field stores MOVW, not a slot-wide
|
||||
// MOVQ into saved BP).
|
||||
fn storeopsz(sz: i32) str = {
|
||||
if (sz == 1) { return "MOVB"; };
|
||||
if (sz == 2) { return "MOVW"; };
|
||||
if (sz == 4) { return "MOVL"; };
|
||||
return "MOVQ";
|
||||
};
|
||||
|
||||
// localloadop — read instruction for a scalar local/let load. Same
|
||||
// dispatch as fieldloadop, but keyed on the value's own tnode.type_.
|
||||
// Lets the caller emit MOVSXD/MOVSWQ/MOVSBQ on a signed-narrow slot
|
||||
@@ -2214,6 +2227,80 @@ export fn sretretsize(c: *cgen, t: *syntax.node) i32 = {
|
||||
return n;
|
||||
};
|
||||
|
||||
// tfieldlookup — the named field `fld` in struct tinfo `sti`'s stamped
|
||||
// field chain, else nil. The #31 receiver-layout store choke-point: the
|
||||
// tfield twin of the name-keyed `for (fi != nil)` fieldinfo walk. A
|
||||
// cross-module same-leaf collision that mis-resolved structlookupchain
|
||||
// cannot reach a stamped tinfo, so every field-OFFSET/width/kind keyed
|
||||
// off the returned tf is collision-immune.
|
||||
fn tfieldlookup(sti: *syntax.tinfo, fld: str) *syntax.tfield = {
|
||||
if (sti == nil) { return nil; };
|
||||
let tf: *syntax.tfield = sti.fields;
|
||||
for (tf != nil) {
|
||||
if (syntax.streq(tf.name, fld)) { return tf; };
|
||||
tf = tf.tnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
// sretretsizetn — verbatim port of cstage cg_sret_retsize (cmd/w6c/
|
||||
// cgen.c:380) keyed on the checker-STAMPED *tinfo. The tinfo twin of
|
||||
// sretretsize, the #31 store-loop choke-point (the sret hard-stop /
|
||||
// sret-receive field sub-arms). Walks resolved tinfo, so the #129
|
||||
// aliassamemod/module-juggling sretretsize carries — pure name-keying
|
||||
// compensation for structlookup's any-module fallback — is NOT needed
|
||||
// and absent: tichase(t) peels NAMED then falls through all four kind
|
||||
// tests to 0, exactly what #129 reconstructed by name (cstage sees a
|
||||
// resolved Type*, never a name). There is NO TY_BANG tinfo kind — `!T`
|
||||
// folds into TY_TAGGED's per-variant iserror (typ.ww:82-90), so there
|
||||
// is no error-peel branch; mirror cstage's four branches exactly. `c`
|
||||
// kept for call-site symmetry with sretretsize (unused: tinfo carries
|
||||
// the module-independent layout).
|
||||
fn sretretsizetn(c: *cgen, t: *syntax.tinfo) i32 = {
|
||||
let u: *syntax.tinfo = tichase(t);
|
||||
if (u == nil) { return 0; };
|
||||
if (u.kind == syntax.tykind.TY_STRUCT) {
|
||||
if (u.size: i32 <= 24) { return 0; };
|
||||
return u.size: i32;
|
||||
};
|
||||
// #38: a tagged union rides AX(tag)+DX/CX/R8 = TUPLE_GPCAP
|
||||
// eightbytes; nullable folds to one ptr word. Mirrors cstage
|
||||
// cg_sret_retsize TY_TAGGED arm.
|
||||
if (u.kind == syntax.tykind.TY_TAGGED) {
|
||||
if (u.nullable != 0) { return 0; };
|
||||
if (u.size: i32 <= TUPLE_GPCAP * 8) { return 0; };
|
||||
return u.size: i32;
|
||||
};
|
||||
// #267: arrays ride the struct-return ABI (≤24 reg / >24 sret).
|
||||
if (u.kind == syntax.tykind.TY_ARRAY) {
|
||||
if (u.size: i32 <= 24) { return 0; };
|
||||
return u.size: i32;
|
||||
};
|
||||
// #10: over-cap tuple → sret. Walk the positional element chain
|
||||
// over the SAME caps the SEND/receive use (a float = 1 SSE
|
||||
// eightbyte; str/slice/tagged ride their box-rounded GP stride via
|
||||
// tupeslot; a scalar 1 GP word). Mirrors cstage cg_sret_retsize
|
||||
// TY_TUPLE arm; tupleelems is the tinfo positional chain.
|
||||
if (u.kind == syntax.tykind.TY_TUPLE) {
|
||||
let gptotal: i32 = 0;
|
||||
let ssecount: i32 = 0;
|
||||
let te: *syntax.ttupleelem = u.tupleelems;
|
||||
for (te != nil) {
|
||||
if (syntax.typeisfloat(te.type_)) {
|
||||
ssecount = ssecount + 1;
|
||||
} else {
|
||||
gptotal = gptotal + tupeslot(te.type_) / 8;
|
||||
};
|
||||
te = te.tnext;
|
||||
};
|
||||
if (gptotal > TUPLE_GPCAP || ssecount > TUPLE_SSECAP) {
|
||||
return u.size: i32;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// callsretsize — if N_CALL `n`'s callee returns a plain TY_STRUCT
|
||||
// > 24B, return its natural size; else 0. Wraps sretretsize over the
|
||||
// callee's resolved return type, used by cglet / cgassign receive
|
||||
@@ -5738,3 +5825,419 @@ fn cgstructlitfillbp(c: *cgen, si: *structinfo, lit: *syntax.node, bpoff: i32) v
|
||||
if (si == nil) { return; };
|
||||
cgstructlitfill(c, si, lit, 0, 0, "", bpoff);
|
||||
};
|
||||
|
||||
// cgstructlitfilltn — tinfo-native twin of cgstructlitfill, the #31
|
||||
// receiver-layout fill choke-point. Walks the struct's checker-STAMPED
|
||||
// tfield chain (lu.fields) and keys every per-field decision off the
|
||||
// resolved tf.type_ (kind / size / signedness) — NO structlookup, NO
|
||||
// fi.tnode names. Nested struct fields recurse on tichase(tf.type_), so
|
||||
// a cross-module same-leaf collision (the #224 cluster) cannot reach a
|
||||
// name lookup at any nesting level. Faithful mirror of cstage
|
||||
// cg_structlit_fill, which recurses on type_chase_named(fl->type)
|
||||
// (cgen.c:3179) over resolved Type*. Emission (mode reloads, register
|
||||
// staging, store ordering) is byte-identical to cgstructlitfill; only
|
||||
// the LAYOUT source flips from si/fieldinfo to lu/tfield. tinfo.size
|
||||
// equals structabisize/fi.fsz for a struct field (check.ww:2468 ABI
|
||||
// formula; NAMED.size==under.size at :2234), so the size-keyed stores
|
||||
// match. Modes: 0=DST_BP, 1=DST_PTR_LOCAL(srcoff), 2=DST_GLOBAL(srcname),
|
||||
// 3=DST_PTR_SP. Routed by the W1/W2/W5 N_STRUCTLIT sub-arms + W4b.
|
||||
fn cgstructlitfilltn(c: *cgen, lu: *syntax.tinfo, lit: *syntax.node,
|
||||
mode: i32, srcoff: i32, srcname: str,
|
||||
disp: i32) void = {
|
||||
let s: *syntax.tinfo = tichase(lu);
|
||||
if (s == nil) { return; };
|
||||
if (s.kind != syntax.tykind.TY_STRUCT) { return; };
|
||||
let basereg: str = "BP";
|
||||
if (mode != 0) { basereg = "BX"; };
|
||||
let totsize: i32 = s.size: i32;
|
||||
if (lit.op == syntax.tkind.TK_ELLIPSIS) {
|
||||
// `..., ...` autofill — zero the entire slot first so
|
||||
// unmentioned fields read as 0. Sized stores: 8/4/1. For
|
||||
// non-BP modes, reload BX once before the loop.
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
let zi: i32 = 0;
|
||||
for (zi + 8 <= totsize) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 8;
|
||||
};
|
||||
for (zi + 4 <= totsize) {
|
||||
emitline("\tMOVL\tAX, ");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 4;
|
||||
};
|
||||
for (zi < totsize) {
|
||||
emitline("\tMOVB\tAX, ");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + zi): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + zi): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
zi += 1;
|
||||
};
|
||||
};
|
||||
let fieldnode: *syntax.node = lit.list;
|
||||
for (fieldnode != nil) {
|
||||
if (fieldnode.kind == syntax.nkind.N_FIELD) {
|
||||
let fname: str = fieldnode.str;
|
||||
let tf: *syntax.tfield = tfieldlookup(s, fname);
|
||||
if (tf != nil) {
|
||||
let foff: i32 = tf.offset: i32;
|
||||
let ftraw: *syntax.tinfo = tf.type_;
|
||||
let fu: *syntax.tinfo = tichase(ftraw);
|
||||
let fsz: i32 = 0;
|
||||
if (fu != nil) { fsz = fu.size: i32; };
|
||||
// Tagged-union field: delegate to the shared
|
||||
// widening writer (str/scalar/struct-lit/ident
|
||||
// payload + tagged-subset tag remap). For non-BP
|
||||
// modes, reload BX first.
|
||||
if (syntax.typeistagged(ftraw)) {
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
cgwidentaggedstore(c, ftraw,
|
||||
fieldnode.lhs, basereg,
|
||||
disp + foff, fsz);
|
||||
} else {
|
||||
// Nested struct-typed structlit value: recurse at
|
||||
// the field's offset off the resolved field tinfo
|
||||
// (NO structlookup). Pre-#17/#18 the cgexpr-then-
|
||||
// store tail would land AX = first qword and the
|
||||
// rest silently stayed zero.
|
||||
let nested: bool = false;
|
||||
if (fieldnode.lhs != nil) {
|
||||
if (fieldnode.lhs.kind == syntax.nkind.N_STRUCTLIT) {
|
||||
if (fu != nil) {
|
||||
if (fu.kind == syntax.tykind.TY_STRUCT) {
|
||||
cgstructlitfilltn(c, fu,
|
||||
fieldnode.lhs,
|
||||
mode, srcoff, srcname,
|
||||
disp + foff);
|
||||
nested = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// Nested struct-typed CALL value (#20): cgexpr
|
||||
// leaves AX/DX/CX per #4's cgreturn ABI; the
|
||||
// choke-point stores full 8B chunks + a sized tail.
|
||||
// Guard fu.size <= 24 (#14: every in-cap tail incl.
|
||||
// 3/5/6/7); >24B falls through (sret deferred).
|
||||
// INVARIANT: between cgexpr(N_CALL) and the AX/DX/CX
|
||||
// stores, only the BX reload may intervene.
|
||||
let callwhole: bool = false;
|
||||
if (!nested) {
|
||||
if (fieldnode.lhs != nil) {
|
||||
if (fieldnode.lhs.kind == syntax.nkind.N_CALL) {
|
||||
if (fu != nil) {
|
||||
if (fu.kind == syntax.tykind.TY_STRUCT) {
|
||||
let cfsz: i32 = fu.size: i32;
|
||||
if (cfsz <= 24) {
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
cgaggregstore(c, basereg, disp + foff, cfsz, false);
|
||||
callwhole = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (nested) {
|
||||
// recursed above
|
||||
} else if (callwhole) {
|
||||
// stored above
|
||||
} else if (syntax.typeisstr(ftraw) || syntax.typeisslice(ftraw)) {
|
||||
// str IS []u8: 3-word {ptr,len,cap}. cgexpr
|
||||
// leaves AX/BX/CX; for non-BP modes the dst
|
||||
// base goes in DX to dodge BX=len / CX=cap.
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
if (mode == 0) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((disp + foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((disp + foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((disp + foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), DX\n");
|
||||
} else { if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), DX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), DX\n");
|
||||
}; };
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg((disp + foff): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((disp + foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((disp + foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
};
|
||||
} else if (fu != nil && fu.kind == syntax.tykind.TY_ARRAY
|
||||
&& fieldnode.lhs != nil
|
||||
&& fieldnode.lhs.kind == syntax.nkind.N_ARRLIT) {
|
||||
// #249: array field from an N_ARRLIT. element-
|
||||
// wise store at disp+foff+i*esz off the array
|
||||
// element tinfo (fu.sub). int/float elements
|
||||
// only; str/slice/struct/tagged elements are the
|
||||
// N_LET multi-word gap — loud rule-7 (cstage
|
||||
// cg_structlit_fill array arm twin).
|
||||
let eti: *syntax.tinfo = tichase(fu.sub);
|
||||
let isstructel: bool = false;
|
||||
if (eti != nil) {
|
||||
if (eti.kind == syntax.tykind.TY_STRUCT) { isstructel = true; };
|
||||
};
|
||||
if (syntax.typeisstr(fu.sub) || syntax.typeisslice(fu.sub)
|
||||
|| isstructel || syntax.typeistagged(fu.sub)) {
|
||||
let e1: str = "ww: struct-literal array field '";
|
||||
os.write(2, e1.ptr, e1.len: u64);
|
||||
os.write(2, fname.ptr, fname.len: u64);
|
||||
let e2: str = "' has a str/slice/struct/tagged element — multi-word element store out of #249 scope (N_LET array-init gap)\n";
|
||||
os.write(2, e2.ptr, e2.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let esz: i32 = 8;
|
||||
if (eti != nil) { esz = eti.size: i32; };
|
||||
let mop: str = storeopsz(esz);
|
||||
let isfloatel: bool = syntax.typeisfloat(fu.sub);
|
||||
let fmov: str = "MOVSD";
|
||||
if (syntax.typeisf32(fu.sub)) { fmov = "MOVSS"; };
|
||||
let idx: i32 = 0;
|
||||
let repeat: bool = false;
|
||||
let e: *syntax.node = fieldnode.lhs.list;
|
||||
for (e != nil) {
|
||||
let isellip: bool = false;
|
||||
if (e.kind == syntax.nkind.N_FIELD) {
|
||||
if (syntax.streq(e.str, "...")) {
|
||||
repeat = true;
|
||||
isellip = true;
|
||||
};
|
||||
};
|
||||
if (isellip) {
|
||||
e = nil;
|
||||
} else {
|
||||
cgexpr(c, e);
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
let eoff: i32 = disp + foff + idx * esz;
|
||||
if (isfloatel) {
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(mop);
|
||||
emitline("\tAX, ");
|
||||
};
|
||||
if (mode == 0) {
|
||||
emitoff(eoff: i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg(eoff: i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
idx += 1;
|
||||
e = e.next;
|
||||
};
|
||||
};
|
||||
if (repeat) {
|
||||
let total: i32 = fu.alen: i32;
|
||||
for (idx < total) {
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
let eoff: i32 = disp + foff + idx * esz;
|
||||
if (isfloatel) {
|
||||
emitline("\t");
|
||||
emitline(fmov);
|
||||
emitline("\tX0, ");
|
||||
} else {
|
||||
emitline("\t");
|
||||
emitline(mop);
|
||||
emitline("\tAX, ");
|
||||
};
|
||||
if (mode == 0) {
|
||||
emitoff(eoff: i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg(eoff: i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
idx += 1;
|
||||
};
|
||||
};
|
||||
} else if (fu != nil
|
||||
&& (fu.kind == syntax.tykind.TY_STRUCT
|
||||
|| fu.kind == syntax.tykind.TY_ARRAY
|
||||
|| fu.kind == syntax.tykind.TY_TUPLE)) {
|
||||
// #49: aggregate (struct/array/tuple) field from
|
||||
// an ADDRESSABLE source expr — `outer{.., r = r}`.
|
||||
// Funnel: source addr via aggargsrcaddr (SI),
|
||||
// field addr via LEAQ/ADDQ (BX, AFTER the source
|
||||
// walk clobbers BX/AX), then aggcopy. Width = the
|
||||
// resolved field tinfo size. Non-addressable
|
||||
// sources die loud (rule 7). cstage #49 arm twin.
|
||||
if (!aggargsrcaddr(c, fieldnode.lhs, "SI")) {
|
||||
let m49g: str = "structlit fill: aggregate field '";
|
||||
os.write(2, m49g.ptr, m49g.len: u64);
|
||||
os.write(2, fname.ptr, fname.len: u64);
|
||||
let m49h: str = "' from a non-addressable source unwired (task #49/rule-7)\n";
|
||||
os.write(2, m49h.ptr, m49h.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (mode == 0) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff((disp + foff): i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else { if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
}; };
|
||||
if (disp + foff != 0) {
|
||||
emitline("\tADDQ\t$");
|
||||
emitint((disp + foff): i64);
|
||||
emitline(", BX\n");
|
||||
};
|
||||
};
|
||||
aggcopy(c, fsz);
|
||||
} else {
|
||||
cgexpr(c, fieldnode.lhs);
|
||||
// For non-BP modes, cgexpr just clobbered BX;
|
||||
// reload it before the store.
|
||||
if (mode == 1) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(srcoff: i64);
|
||||
emitline("(BP), BX\n");
|
||||
};
|
||||
if (mode == 2) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, srcname);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
if (mode == 3) {
|
||||
emitline("\tMOVQ\t(SP), BX\n");
|
||||
};
|
||||
if (syntax.typeisfloat(ftraw)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (syntax.typeisf32(ftraw)) { mov = "MOVSS"; };
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + foff): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
} else {
|
||||
// Full field-width store {1->MOVB, 2->MOVW,
|
||||
// 4->MOVL, else MOVQ}; the #15 sub-8 guard.
|
||||
let op: str = storeopsz(fsz);
|
||||
emitline("\t");
|
||||
emitline(op);
|
||||
emitline("\tAX, ");
|
||||
if (mode == 0) {
|
||||
emitoff((disp + foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
emitdispreg((disp + foff): i64, basereg);
|
||||
emitline("\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
fieldnode = fieldnode.next;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user