w6c+selfhost: tagged-arr element ABI + full selfhost mirror
Closes the remaining tagged-union gaps after the prior two commits:
1. Tagged element in an array/slice (cstage). N_INDEX load now reads
slot words into AX/DX/CX, matching the tagged-return ABI so match
/ call-arg / let-init paths consume `arr[i]` uniformly. N_INDEX
store routes through a scratch slot + cg_widen_tagged_store +
byte-copy to &arr[i], so the full widening machinery (scalar /
str / struct payload / tagged subset / nullable fold) lights up
for element writes too.
2. Selfhost mirror — the cgen widen helpers (struct payload,
tagged-subset, spread-flatten) C cgen has had for two commits
finally land in selfhost:
cgwidentaggedstore — single writer for nullable / tagged ident /
tagged via AX:DX:CX / struct (lit + ident) /
str / scalar source shapes.
cgwidentagremap — CMPQ-chain tag remap for variant-subset.
rhsstructpayload — struct-name predicate; filters `!void` /
`!i32` aliases that share N_STRUCTLIT shape
but aren't structs.
rhstaggedident,
rhstaggedabicall — source-shape predicates.
flatvariantidx — spread-aware variant index lookup. Walks
`(...inner | T)` entries by resolving the
alias and inlining the inner's variants so
wwstage's tag order matches the check.c
flattening cstage does at type resolution.
cglet tagged init, cgassign tagged-ident reassign, cgreturn struct
/ subset payload, pushargsrev struct payload, cgindex tagged
element load, cgassign N_INDEX tagged element store all delegate
to these. cgmatch picks up scrutt from N_INDEX bases (element
type) and uses flatvariantidx for case dispatch.
3. Selfhost frame accounting: scanlocals reserves a 24B @tagscr slot
when the body contains a tagged-arr store, a struct-payload
tagged return, or a struct-payload call arg — dedup'd via
scanseenmark so multiple sites share one slot. N_LET stubs now
carry tnode so walk-time type checks see the array element type.
slotsize TARRAY learned to size tagged / struct / ptr / aliased
elements (was 8B-default for anything not N_TNAME-primitive,
undersizing tagged-element arrays).
Scalar / str call-arg widening keeps its direct-push fast path
(no scratch), so wwstage's asm on selfhost source remains
byte-identical to cstage's — 993/995 still pass.
700_e2e: 9 new rows — scalar/str/struct/subset/nullable variants in
arrays and slices, plus pass-arg / let-init / return / match shapes.
This commit is contained in:
@@ -72,6 +72,34 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
return rest + 1;
|
||||
};
|
||||
if (widensz > 0) {
|
||||
// Struct-payload widening into a tagged-union param uses
|
||||
// @tagscr (zero + cgwidentaggedstore writes fields + tag,
|
||||
// then push slot words high → low). Scalar / str go via
|
||||
// the direct push fast path below — keeps wwstage's asm
|
||||
// byte-identical to cstage for selfhost source.
|
||||
let pname: str = rhsstructpayload(c, arg);
|
||||
if (pname.len > 0) {
|
||||
let ptype: *node = param.lhs;
|
||||
let scroff: i32 = localadd(c, "@tagscr", 24, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zz: i32 = 0;
|
||||
for (zz < widensz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((scroff + zz): i64);
|
||||
emitline("(BP)\n");
|
||||
zz += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, ptype, arg, scroff, widensz);
|
||||
let pp: i32 = widensz - 8;
|
||||
for (pp >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scroff + pp): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
pp -= 8;
|
||||
};
|
||||
return rest + widensz / 8;
|
||||
};
|
||||
cgexpr(c, arg);
|
||||
if (nodeisstr(c, arg)) {
|
||||
// slot 24: [+0]=tag,[+8]=ptr,[+16]=len. Push high→low
|
||||
@@ -955,8 +983,23 @@ fn slotsize(c: *cgen, typn: *node) i32 = {
|
||||
if (elemn.kind == nkind.N_TNAME) {
|
||||
let en: str = elemn.str;
|
||||
let ps: i32 = primsize(en);
|
||||
if (ps > 0) { esz = ps; };
|
||||
};
|
||||
if (ps > 0) { esz = ps; }
|
||||
else {
|
||||
// Named struct / aliased type: size off
|
||||
// the structinfo if present.
|
||||
let si: *structinfo = structlookup(c, en);
|
||||
if (si != nil) { esz = si.totsize; };
|
||||
};
|
||||
} else { if (elemn.kind == nkind.N_TTAGGED) {
|
||||
// Tagged-union element: full slot (8 tag +
|
||||
// padded max payload). Matches C cgen's
|
||||
// resolve_type for `[N]TAGGED`.
|
||||
esz = slotsize(c, elemn);
|
||||
} else { if (elemn.kind == nkind.N_TPTR) {
|
||||
esz = 8;
|
||||
} else { if (elemn.kind == nkind.N_TSTRUCT) {
|
||||
esz = slotsize(c, elemn);
|
||||
}; }; }; };
|
||||
};
|
||||
return (esz: i64 * elen): i32;
|
||||
};
|
||||
@@ -1424,21 +1467,40 @@ fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
|
||||
if (rhs == nil) { return -1; };
|
||||
let wantname: str = rhstargetname(c, rhs);
|
||||
if (wantname.len > 0) {
|
||||
let v: *node = tagged.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
if (v.kind == nkind.N_TNAME) {
|
||||
if (variantnamematch(v.str, wantname)) { return idx; };
|
||||
};
|
||||
v = v.next;
|
||||
idx += 1;
|
||||
};
|
||||
let r: i32 = flatvariantidx(c, tagged, wantname);
|
||||
if (r >= 0) { return r; };
|
||||
};
|
||||
// Fallback: by str-shape (resolves aliases).
|
||||
// Fallback: by str-shape (resolves aliases). Walks the
|
||||
// spread-flattened variant list so a `(...inner | str)` outer
|
||||
// agrees with the (i32 | str) inner's str position.
|
||||
let wantstr: bool = nodeisstr(c, rhs);
|
||||
let v: *node = tagged.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
let isspread: bool = (v.op == tkind.TK_ELLIPSIS);
|
||||
if (isspread) {
|
||||
let inner: *node = v;
|
||||
if (inner.kind == nkind.N_TNAME) {
|
||||
let a: *node = aliaslookup(c, inner.str);
|
||||
if (a != nil) { inner = a; };
|
||||
};
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_TTAGGED) {
|
||||
let iv: *node = inner.list;
|
||||
for (iv != nil) {
|
||||
let ivisstr: bool = false;
|
||||
if (iv.kind == nkind.N_TNAME) {
|
||||
if (isstrtype(c, iv)) { ivisstr = true; };
|
||||
};
|
||||
if (ivisstr == wantstr) { return idx; };
|
||||
iv = iv.next;
|
||||
idx += 1;
|
||||
};
|
||||
v = v.next;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
};
|
||||
let visstr: bool = false;
|
||||
if (v.kind == nkind.N_TNAME) {
|
||||
if (isstrtype(c, v)) { visstr = true; };
|
||||
@@ -1449,3 +1511,404 @@ fn taggedvariantindex(c: *cgen, tagged: *node, rhs: *node) i32 = {
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
// flatvariantidx — walk `tagged`'s variant list (with spread `...inner`
|
||||
// expansion) and return the flat 0-based index where `want` matches.
|
||||
// Mirrors check.c's spread flatten at type resolution: an outer
|
||||
// `(...inner | T)` has the inner's variants inlined in declaration
|
||||
// order, so the tag indices stay in sync between cstage (which
|
||||
// resolves types upfront) and wwstage (which doesn't). Returns -1 if
|
||||
// no variant matches.
|
||||
fn flatvariantidx(c: *cgen, tagged: *node, want: str) i32 = {
|
||||
if (tagged == nil) { return -1; };
|
||||
if (tagged.kind != nkind.N_TTAGGED) { return -1; };
|
||||
if (want.len == 0) { return -1; };
|
||||
let v: *node = tagged.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
let isspread: bool = (v.op == tkind.TK_ELLIPSIS);
|
||||
if (isspread) {
|
||||
let inner: *node = v;
|
||||
if (inner.kind == nkind.N_TNAME) {
|
||||
let a: *node = aliaslookup(c, inner.str);
|
||||
if (a != nil) { inner = a; };
|
||||
};
|
||||
if (inner != nil) {
|
||||
if (inner.kind == nkind.N_TTAGGED) {
|
||||
let iv: *node = inner.list;
|
||||
for (iv != nil) {
|
||||
if (iv.kind == nkind.N_TNAME) {
|
||||
if (variantnamematch(iv.str, want)) {
|
||||
return idx;
|
||||
};
|
||||
};
|
||||
iv = iv.next;
|
||||
idx += 1;
|
||||
};
|
||||
v = v.next;
|
||||
continue;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (v.kind == nkind.N_TNAME) {
|
||||
if (variantnamematch(v.str, want)) { return idx; };
|
||||
};
|
||||
v = v.next;
|
||||
idx += 1;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
// cgwidentagremap — when widening from one tagged union to a wider one,
|
||||
// rewrite the source's variant tag at slot_off+0 to use the destination's
|
||||
// variant indices. No-op when src and dst index orders coincide.
|
||||
// Mirrors cg_widen_tag_remap in cmd/w6c/cgen.c.
|
||||
fn cgwidentagremap(c: *cgen, dst: *node, src: *node, slot_off: i32) void = {
|
||||
if (dst == nil) { return; };
|
||||
if (src == nil) { return; };
|
||||
if (dst.kind != nkind.N_TTAGGED) { return; };
|
||||
if (src.kind != nkind.N_TTAGGED) { return; };
|
||||
let identity: bool = true;
|
||||
let v: *node = src.list;
|
||||
let idx: i32 = 0;
|
||||
for (v != nil) {
|
||||
let di: i32 = cgtagvariantidx(c, dst, v);
|
||||
if (di < 0) { di = 0; };
|
||||
if (di != idx) { identity = false; v = nil; }
|
||||
else { v = v.next; idx += 1; };
|
||||
};
|
||||
if (identity) { return; };
|
||||
let done: str = mklabel(c, "remap_done");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
v = src.list;
|
||||
idx = 0;
|
||||
for (v != nil) {
|
||||
let next: str = mklabel(c, "remap_next");
|
||||
let di: i32 = cgtagvariantidx(c, dst, v);
|
||||
if (di < 0) { di = 0; };
|
||||
emitline("\tCMPQ\t$");
|
||||
emitint(idx: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tJNE\t");
|
||||
emitline(next);
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(di: i64);
|
||||
emitline(", AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tJMP\t");
|
||||
emitline(done);
|
||||
emitline("\n");
|
||||
emitlabel(next);
|
||||
v = v.next;
|
||||
idx += 1;
|
||||
};
|
||||
emitlabel(done);
|
||||
return;
|
||||
};
|
||||
|
||||
// rhsisstructpayload — is `src` a struct value (literal or local ident
|
||||
// of a struct type)? Returns the struct name, or empty str. Only true
|
||||
// when the name is registered in c.structs — `!void` / `!i32` aliases
|
||||
// share the N_STRUCTLIT / N_TNAME shape but aren't structs, and must
|
||||
// fall through to the scalar/str/tagged-source paths instead.
|
||||
fn rhsstructpayload(c: *cgen, src: *node) str = {
|
||||
let empty: str;
|
||||
empty.ptr = nil; empty.len = 0;
|
||||
if (src == nil) { return empty; };
|
||||
if (src.kind == nkind.N_STRUCTLIT) {
|
||||
let trefn: *node = src.lhs;
|
||||
if (trefn != nil) {
|
||||
let nm: str;
|
||||
nm.ptr = nil; nm.len = 0;
|
||||
if (trefn.kind == nkind.N_IDENT) { nm = trefn.str; };
|
||||
if (trefn.kind == nkind.N_TNAME) { nm = trefn.str; };
|
||||
if (nm.len > 0) {
|
||||
if (structlookup(c, nm) != nil) { return nm; };
|
||||
};
|
||||
};
|
||||
return empty;
|
||||
};
|
||||
if (src.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
if (lc != nil) {
|
||||
let tn: *node = lc.tnode;
|
||||
if (tn != nil) {
|
||||
if (tn.kind == nkind.N_TNAME) {
|
||||
if (structlookup(c, tn.str) != nil) {
|
||||
return tn.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return empty;
|
||||
};
|
||||
|
||||
// rhstaggedsource — return the tagged-type node for `src` when src is a
|
||||
// tagged-typed local ident; nil otherwise. The slot-copy path uses this
|
||||
// to walk variants for tag remap.
|
||||
fn rhstaggedident(c: *cgen, src: *node) *node = {
|
||||
if (src == nil) { return nil; };
|
||||
if (src.kind != nkind.N_IDENT) { return nil; };
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
if (lc == nil) { return nil; };
|
||||
let tn: *node = lc.tnode;
|
||||
if (!istaggedtype(c, tn)) { return nil; };
|
||||
return resolvetagged(c, tn);
|
||||
};
|
||||
|
||||
// rhstaggedabicall — does `src` produce a tagged value via the AX/DX/CX
|
||||
// return ABI? True for N_CALL of a tagged-returning fn and N_INDEX of a
|
||||
// tagged-element base. Used to decide whether cgexpr/spill works for the
|
||||
// tagged-source branch of cgwidentaggedstore.
|
||||
fn rhstaggedabicall(c: *cgen, src: *node) bool = {
|
||||
if (src == nil) { return false; };
|
||||
if (src.kind == nkind.N_CALL) {
|
||||
let callee: *node = src.lhs;
|
||||
if (callee != nil) {
|
||||
let calleename: str;
|
||||
calleename.ptr = nil; calleename.len = 0;
|
||||
if (callee.kind == nkind.N_IDENT) { calleename = callee.str; };
|
||||
if (callee.kind == nkind.N_DOT) { calleename = callee.str; };
|
||||
if (calleename.len > 0) {
|
||||
let rt: *node = fnretlookup(c, calleename);
|
||||
if (rt != nil) {
|
||||
if (istaggedtype(c, rt)) { return true; };
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (src.kind == nkind.N_INDEX) {
|
||||
let base: *node = src.lhs;
|
||||
if (base != nil) {
|
||||
if (base.kind == nkind.N_IDENT) {
|
||||
let bl: *local = localfindnode(c, base.str);
|
||||
if (bl != nil) {
|
||||
let btn: *node = bl.tnode;
|
||||
if (btn != nil) {
|
||||
let bk: nkind = btn.kind;
|
||||
let elemt: *node = nil;
|
||||
if (bk == nkind.N_TARRAY) { elemt = btn.lhs; };
|
||||
if (bk == nkind.N_TSLICE) { elemt = btn.lhs; };
|
||||
if (bk == nkind.N_TPTR) { elemt = btn.lhs; };
|
||||
if (elemt != nil) {
|
||||
if (istaggedtype(c, elemt)) {
|
||||
return true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgwidentaggedstore — write tagged-union slot bytes for `src` into the
|
||||
// slot at BP+slot_off, sized to slot_sz. Mirrors cg_widen_tagged_store
|
||||
// in cmd/w6c/cgen.c. Branches by source shape:
|
||||
// - nullable dst (8B slot): cgexpr → AX → slot+0.
|
||||
// - tagged src ident: copy slot words, zero-pad, tag-remap.
|
||||
// - tagged src via AX/DX/CX ABI (call / tagged-arr index): cgexpr,
|
||||
// spill words; no remap (callee already speaks dst tag order — or
|
||||
// it doesn't, in which case the source is the wider one and remap
|
||||
// would need a reversed direction we don't currently emit).
|
||||
// - struct src (literal or ident): zero slot, write fields at +8+foff,
|
||||
// tag last.
|
||||
// - str src: tag@+0, ptr@+8, len@+16.
|
||||
// - scalar src: tag@+0, value@+8.
|
||||
fn cgwidentaggedstore(c: *cgen, dst: *node, src: *node, slot_off: i32, slot_sz: i32) void = {
|
||||
let dt: *node = resolvetagged(c, dst);
|
||||
if (dt == nil) { return; };
|
||||
// Nullable fold: one 8B word holding the pointer (or 0 for void).
|
||||
if (isnullabletype(dst)) {
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// Tagged source ident: byte-copy slot words then tag-remap.
|
||||
let st: *node = rhstaggedident(c, src);
|
||||
if (st != nil) {
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
let ssz: i32 = slotsize(c, lc.tnode);
|
||||
let soff: i32 = lc.off;
|
||||
let k: i32 = 0;
|
||||
for (k < ssz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((soff + k): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + k): i64);
|
||||
emitline("(BP)\n");
|
||||
k += 8;
|
||||
};
|
||||
if (ssz < slot_sz) {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let p: i32 = ssz;
|
||||
for (p < slot_sz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + p): i64);
|
||||
emitline("(BP)\n");
|
||||
p += 8;
|
||||
};
|
||||
};
|
||||
cgwidentagremap(c, dt, st, slot_off);
|
||||
return;
|
||||
};
|
||||
// Tagged source via AX/DX/CX register ABI (N_CALL, N_INDEX of
|
||||
// tagged element).
|
||||
if (rhstaggedabicall(c, src)) {
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
if (slot_sz > 8) {
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((slot_off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
if (slot_sz > 16) {
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((slot_off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Struct payload (literal or ident).
|
||||
let sname: str = rhsstructpayload(c, src);
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
if (si != nil) {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zoff: i32 = 0;
|
||||
for (zoff < slot_sz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + zoff): i64);
|
||||
emitline("(BP)\n");
|
||||
zoff += 8;
|
||||
};
|
||||
let tag: i32 = taggedvariantindex(c, dt, src);
|
||||
if (tag < 0) { tag = 0; };
|
||||
if (src.kind == nkind.N_STRUCTLIT) {
|
||||
let fnode: *node = src.list;
|
||||
for (fnode != nil) {
|
||||
if (fnode.kind == nkind.N_FIELD) {
|
||||
let fname: str = fnode.str;
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
if (streq(fi.fname, fname)) {
|
||||
cgexpr(c, fnode.lhs);
|
||||
if (isfloattype(c, fi.tnode)) {
|
||||
let mov: str = "MOVSD";
|
||||
if (isf32type(c, fi.tnode)) {
|
||||
mov = "MOVSS";
|
||||
};
|
||||
emitline("\t");
|
||||
emitline(mov);
|
||||
emitline("\tX0, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
} else { if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((slot_off + 8 + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
} else {
|
||||
let sop: str = fieldstoreop(fi);
|
||||
emitline("\t");
|
||||
emitline(sop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((slot_off + 8 + fi.foff): i64);
|
||||
emitline("(BP)\n");
|
||||
}; };
|
||||
fi = nil;
|
||||
} else {
|
||||
fi = fi.finext;
|
||||
};
|
||||
};
|
||||
};
|
||||
fnode = fnode.next;
|
||||
};
|
||||
} else {
|
||||
// Struct ident source: byte-copy struct words to slot+8+k.
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
let soff: i32 = 0;
|
||||
if (lc != nil) { soff = lc.off; };
|
||||
let stotal: i32 = si.totsize;
|
||||
let ki: i32 = 0;
|
||||
for (ki + 8 <= stotal) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + 8 + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
ki += 8;
|
||||
};
|
||||
if (ki < stotal) {
|
||||
let tail: i32 = stotal - ki;
|
||||
let lop: str = "MOVQ";
|
||||
if (tail == 4) { lop = "MOVL"; }
|
||||
else { if (tail == 1) { lop = "MOVB"; }; };
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\t");
|
||||
emitoff((soff + ki): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\t");
|
||||
emitline(lop);
|
||||
emitline("\tAX, ");
|
||||
emitoff((slot_off + 8 + ki): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(tag: i64);
|
||||
emitline(", ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
// Str payload.
|
||||
if (nodeisstr(c, src)) {
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((slot_off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
let tag: i32 = taggedvariantindex(c, dt, src);
|
||||
if (tag < 0) { tag = 0; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(tag: i64);
|
||||
emitline(", ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// Scalar payload.
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
let tag: i32 = taggedvariantindex(c, dt, src);
|
||||
if (tag < 0) { tag = 0; };
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(tag: i64);
|
||||
emitline(", ");
|
||||
emitoff(slot_off: i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user