w6c+w6c_ww: >32B tagged INDEX/DOT reads go mem-based — box address in AX (#37)
A tagged box wider than the AX/DX/CX/R8 cursor (size > TUPLE_GPCAP*8)
read via N_INDEX or N_DOT now leaves its ADDRESS in AX — joining the
sret-call mem-based class the #38b gates already speak — and every
cursor consumer branches on one shared predicate (cg_tagged_memread /
taggedmemread) before reading AX as the tag. <=32B keeps the cursor
byte-for-byte (32B-at-cap asm proven identical to base on both the
t.N and INDEX routes).
Emitters: N_INDEX ident+fallback arms, N_DOT tuple-element (flips the
#22b loud bound), N_DOT struct-field + ptr-chained-field (were silent
clamps at 32B); wwstage twins gate cgloadtaggedfield at the helper
choke-point. Consumers: match spill x2 and the widen-store subset +
nested arms (the let/assign/return-widen/arg-widen/vararg choke-point)
copy the box from memory, then share the existing zero-pad + tag-remap
tail; `is` loads the tag through the address; `as` spills mem-based.
Rule-7 loud bounds replace silent cursor garbage for the non-mem-based
>32B kinds, `?`/`!`, and the 33-48B in-reg tagged arg push (mem-based
push stays the #35 family); the exact-type >32B return passthrough
from INDEX/DOT flips from its #38b loud into the widener route. The
pre-existing >48B memarg stack blit (cgplaceaddr) never used the
cursor and is pinned unchanged.
Reviewer-37 amendment: the non-mem-based >32B loud was ONE-SIDED on
two wwstage routes — cgwidentaggedstorebp had no fall-through guard
at all (`let w = *p` on a 56B box: cstage loud, wwstage silent word0
truncation), and cgmatch's guard keyed on matchscrutt-resolved
spillsz, which defaults under cap for kinds matchscrutt can't resolve
(N_UN deref et al), so `match (*p)` slipped it the same way. Both now
loud off the stamped src/scrut type_ (the kind-blind key cstage
already uses), restoring the rule-10 symmetry the body claims.
Emitters and consumers ship as ONE commit: they share the memread
contract, and splitting would open a transient window where a wired
emitter hands an address to an unwired consumer (silent garbage) —
the #61-precedent route-sharing fuse. The CX-global-tuple-base LEAQ
arm is TRIPWIRE wiring: a >32B tagged global-tuple element is
unreachable today (module-level tuple inits are int/str-literal-only;
tagged elements loud at the DATA emit), and the LEAQ keeps the same
base_reg generality as the cursor walk it replaces (ken note, task
record).
This was the last 5b compiler gate: `match insts[pc]` on the regex
inst union (inst_lit|inst_repeat, 56B) was silent-wrong gate-blind
byte-id on both stages (payload words 3+ dropped past the R8 clamp).
test 941 grows 165->200 checks: the #22b BUILDERR pin flips to a
runtime row, plus the 56B driver match, str+nested-tagged payload,
let/is/assign, indexed return, widening (identity and reversed-order
remap), 56B memarg, ken's X1 composition row, 32B-at-cap INDEX
boundary, the ptr-chained p.f match (BX-arm) and (*p)[i] fallback-arm
rows, and the two deref loud-symmetry BUILDERR pins. At base f272068
the 11 bug rows fail (2 BUILDERR flips + 7 silent-wrong + 2 missing-
loud pins, both drivers); the anchors pass. Oracle: ken PASS at
bf21964b pre-amendment; re-bind on the amended tree pending (source
bytes changed: cgenutil.ww/cgenexpr.ww louds + combined.ww regen).
This commit is contained in:
@@ -16910,6 +16910,15 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
if (arg.kind == nkind.N_INDEX || arg.kind == nkind.N_DOT) {
|
||||
if (istaggedtype(c, arg)) {
|
||||
let isz: i32 = slotsize(c, arg);
|
||||
// #37 (rule 7): a 33-48B box from an INDEX/DOT read
|
||||
// is mem-based (AX = addr, no cursor to push) — was
|
||||
// silent cursor truncation pre-#37; the mem-based
|
||||
// push is the #35 family. Mirrors cstage.
|
||||
if (taggedmemread(c, arg)) {
|
||||
let m37g: str = "#37: >32B tagged arg from a mem-based read unwired (#35/#40-family follow-up)\n";
|
||||
os.write(2, m37g.ptr, m37g.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (isz > 24) { emitline("\tPUSHQ\tR8\n"); };
|
||||
if (isz > 16) { emitline("\tPUSHQ\tCX\n"); };
|
||||
if (isz > 8) { emitline("\tPUSHQ\tDX\n"); };
|
||||
@@ -19033,6 +19042,23 @@ fn rhstaggedabicall(c: *cgen, src: *node) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// taggedmemread — #37: does cgexpr leave this tagged expr's box in
|
||||
// MEMORY (AX = box address) instead of the AX/DX/CX/R8 cursor? True
|
||||
// for an N_INDEX/N_DOT read whose box exceeds the 4-reg cursor — the
|
||||
// same mem-based class as an sret-classified call (which the #38b
|
||||
// gates key separately on callsretsize). Every cursor-spill consumer
|
||||
// must branch on this before reading AX as the tag. Mirrors cstage
|
||||
// cg_tagged_memread.
|
||||
fn taggedmemread(c: *cgen, e: *node) bool = {
|
||||
if (e == nil) { return false; };
|
||||
if (e.kind != nkind.N_INDEX && e.kind != nkind.N_DOT) { return false; };
|
||||
let u: *tinfo = e.type_: *tinfo;
|
||||
for (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; };
|
||||
if (u == nil) { return false; };
|
||||
if (u.kind != tykind.TY_TAGGED) { return false; };
|
||||
return u.size: i32 > TUPLE_GPCAP * 8;
|
||||
};
|
||||
|
||||
// cgloadtaggedfield — load a tagged-union slot at `basereg`+foff
|
||||
// into the tagged-return ABI registers (AX=tag, DX=word0, CX=word1,
|
||||
// R8=word2). Slot sizes: 16B = (tag, word0), 24B = + word1, 32B
|
||||
@@ -19048,6 +19074,16 @@ fn rhstaggedabicall(c: *cgen, src: *node) bool = {
|
||||
// only register loaded into that is NOT a target is BX, so AX-
|
||||
// or DX-rooted callers must spill first.
|
||||
fn cgloadtaggedfield(c: *cgen, basereg: str, foff: i32, slot_sz: i32) void = {
|
||||
// #37: >32B box — leave its ADDRESS in AX (taggedmemread, the
|
||||
// sret-receive convention); the 4-reg cursor walk below would
|
||||
// truncate past payload word 2. Mirrors cstage's N_DOT
|
||||
// TY_STRUCT/TY_PTR tagged arms.
|
||||
if (slot_sz > TUPLE_GPCAP * 8) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitdispreg(foff: i64, basereg);
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
// tag → AX
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(foff: i64, basereg);
|
||||
@@ -19291,6 +19327,29 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
if (taggedmemread(c, src)) {
|
||||
// #37: >32B box read — ADDRESS in AX;
|
||||
// copy the inner box from memory into
|
||||
// the payload area. Mirrors cstage.
|
||||
cgexpr(c, src);
|
||||
let mk: i32 = 0;
|
||||
for (mk < ssz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(mk: i64, "AX");
|
||||
emitline(", DX\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((slot_off + 8 + mk): i64);
|
||||
emitline("(BP)\n");
|
||||
mk += 8;
|
||||
};
|
||||
} else {
|
||||
// #37 (rule 7): >32B from a non-mem-based
|
||||
// kind would spill an unfilled cursor.
|
||||
if (ssz > TUPLE_GPCAP * 8) {
|
||||
let m37a: str = "#37: >32B tagged payload from a non-mem-based source unwired (rule 7)\n";
|
||||
os.write(2, m37a.ptr, m37a.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + 8): i64);
|
||||
@@ -19310,6 +19369,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
emitoff((slot_off + 32): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(nested: i64);
|
||||
@@ -19363,6 +19423,40 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
// #37: >32B box read (insts[pc], t.N, s.f) — cgexpr left
|
||||
// its ADDRESS in AX; copy the whole box from memory, pad,
|
||||
// tag-remap — the mem-based twin of the ident arm above.
|
||||
// Mirrors cstage cg_widen_tagged_store's memread arm.
|
||||
if (taggedmemread(c, src)) {
|
||||
let su37: *tinfo = src.type_: *tinfo;
|
||||
for (su37 != nil && su37.kind == tykind.TY_NAMED) {
|
||||
su37 = su37.under;
|
||||
};
|
||||
let ssz37: i32 = su37.size: i32;
|
||||
cgexpr(c, src);
|
||||
let mk37: i32 = 0;
|
||||
for (mk37 < ssz37) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(mk37: i64, "AX");
|
||||
emitline(", DX\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((slot_off + mk37): i64);
|
||||
emitline("(BP)\n");
|
||||
mk37 += 8;
|
||||
};
|
||||
if (ssz37 < slot_sz) {
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let pp37: i32 = ssz37;
|
||||
for (pp37 < slot_sz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((slot_off + pp37): i64);
|
||||
emitline("(BP)\n");
|
||||
pp37 += 8;
|
||||
};
|
||||
};
|
||||
cgwidentagremap(c, dt, src.type_: *tinfo, slot_off);
|
||||
return;
|
||||
};
|
||||
cgexpr(c, src);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(slot_off: i64);
|
||||
@@ -19384,6 +19478,23 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
};
|
||||
return;
|
||||
};
|
||||
// #37 (rule 7): a >32B TAGGED source of a kind the resolver arms
|
||||
// above don't carry (deref/cast/unwrap/...) would fall to the
|
||||
// scalar word0 arm below and silently truncate — keyed on the
|
||||
// stamped src.type_ (kind-blind), the twin of cstage
|
||||
// cg_widen_tagged_store's generic-else bound. Surfaced by
|
||||
// reviewer-37's `let w = *p` probe on a 56B box: cstage loud,
|
||||
// wwstage silent (rule-10 break).
|
||||
let sf37: *tinfo = src.type_: *tinfo;
|
||||
for (sf37 != nil && sf37.kind == tykind.TY_NAMED) {
|
||||
sf37 = sf37.under;
|
||||
};
|
||||
if (sf37 != nil && sf37.kind == tykind.TY_TAGGED
|
||||
&& sf37.size: i32 > TUPLE_GPCAP * 8) {
|
||||
let m37f: str = "#37: >32B tagged source of a non-mem-based kind unwired (rule 7)\n";
|
||||
os.write(2, m37f.ptr, m37f.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
// #242: tuple payload. Each element rides ONE register-ABI
|
||||
// eightbyte — scalar/float a single 8B word, a slice/str its 3-word
|
||||
// {ptr,len,cap} header (24B) — matching the tagged-return load
|
||||
@@ -20668,6 +20779,13 @@ fn cgtryprop(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #37 (rule 7): a >32B box read leaves AX = address, not the
|
||||
// tag the unwrap below compares. Mirrors cstage.
|
||||
if (taggedmemread(c, n.lhs)) {
|
||||
let m37p: str = "#37: `?` on a >32B mem-based tagged read unwired (#40-family follow-up)\n";
|
||||
os.write(2, m37p.ptr, m37p.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (sretretsize(c, c.fnret) > 0) {
|
||||
let m38q: str = "#38b: `?` propagation into a >32B tagged return unwired (sret error-propagate is a #40-family follow-up)\n";
|
||||
os.write(2, m38q.ptr, m38q.len: u64);
|
||||
@@ -20801,6 +20919,12 @@ fn cgtryunw(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #37 (rule 7): see the cgtryprop twin.
|
||||
if (taggedmemread(c, n.lhs)) {
|
||||
let m37u: str = "#37: `!` on a >32B mem-based tagged read unwired (#40-family follow-up)\n";
|
||||
os.write(2, m37u.ptr, m37u.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
cgexpr(c, n.lhs);
|
||||
let cl: str = mklabel(c, "tryunw_ok");
|
||||
emitline("\tCMPQ\t$0, AX\n");
|
||||
@@ -20905,6 +21029,12 @@ fn cgtypetest(c: *cgen, n: *node) void = {
|
||||
// tag read landed on (BP) — the saved-BP word.
|
||||
nonident = true;
|
||||
cgexpr(c, lhs);
|
||||
// #37: a >32B box read leaves its ADDRESS in AX —
|
||||
// load the tag word from memory before the compare.
|
||||
// Mirrors cstage N_TYPETEST.
|
||||
if (taggedmemread(c, lhs)) {
|
||||
emitline("\tMOVQ\t(AX), AX\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = 0;
|
||||
@@ -21053,6 +21183,28 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
|
||||
scrutt = matchscrutt(c, lhs);
|
||||
let spillsz: i32 = matchspillsz(c, scrutt);
|
||||
scrutoff = localalloc(c, "@asrt_spill", spillsz, nil);
|
||||
if (taggedmemread(c, lhs)) {
|
||||
// #37: >32B box read — ADDRESS in AX; copy the
|
||||
// whole box from memory. Mirrors cstage.
|
||||
cgexpr(c, lhs);
|
||||
let ak37: i32 = 0;
|
||||
for (ak37 < spillsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(ak37: i64, "AX");
|
||||
emitline(", DX\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((scrutoff + ak37): i64);
|
||||
emitline("(BP)\n");
|
||||
ak37 += 8;
|
||||
};
|
||||
} else {
|
||||
// #37 (rule 7): >32B from a non-mem-based kind would
|
||||
// spill an unfilled cursor. Mirrors cstage.
|
||||
if (!isnullabletype(scrutt) && spillsz > TUPLE_GPCAP * 8) {
|
||||
let m37s: str = "#37: `as` on a >32B tagged value from a non-mem-based source unwired (rule 7)\n";
|
||||
os.write(2, m37s.ptr, m37s.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
cgexpr(c, lhs);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(scrutoff: i64);
|
||||
@@ -21074,6 +21226,7 @@ fn cgtypeassert(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
let want: i32 = cgtagvariantidx(c, scrutt, n.rhs);
|
||||
@@ -22150,6 +22303,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
if (elem_tagged) {
|
||||
// #37: >32B box — ADDRESS in AX (the taggedmemread
|
||||
// convention); the 4-reg cursor walk below would
|
||||
// truncate past payload word 2. Mirrors cstage.
|
||||
if (elem_slot_sz > TUPLE_GPCAP * 8) {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
return;
|
||||
};
|
||||
if (elem_slot_sz > 24) {
|
||||
emitline("\tMOVQ\t24(BX), R8\n");
|
||||
};
|
||||
@@ -22209,6 +22369,13 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
if (elem_tagged) {
|
||||
// #37: >32B box — ADDRESS in AX (the taggedmemread
|
||||
// convention); the 4-reg cursor walk below would
|
||||
// truncate past payload word 2. Mirrors cstage.
|
||||
if (elem_slot_sz > TUPLE_GPCAP * 8) {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
return;
|
||||
};
|
||||
if (elem_slot_sz > 24) {
|
||||
emitline("\tMOVQ\t24(BX), R8\n");
|
||||
};
|
||||
@@ -22264,6 +22431,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
// arms have it) — a >24B-slot element via a non-ident base
|
||||
// under-read the cursor and the match spill stored stale R8.
|
||||
// Mirrors cstage cgen.c:9106-9117.
|
||||
// #37: >32B box — AX already holds the element address;
|
||||
// leave it (taggedmemread). Mirrors cstage.
|
||||
if (elem_slot_sz > TUPLE_GPCAP * 8) {
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
if (elem_slot_sz > 24) {
|
||||
emitline("\tMOVQ\t24(BX), R8\n");
|
||||
@@ -22653,7 +22825,47 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
c.sretdestoff = scrutoff;
|
||||
cgexpr(c, scrut);
|
||||
c.sretdestoff = 0;
|
||||
} else { if (taggedmemread(c, scrut)) {
|
||||
// #37: >32B box read (insts[pc], t.N) — cgexpr left
|
||||
// its ADDRESS in AX; copy the whole box from memory.
|
||||
// Mirrors cstage cgmatch.
|
||||
cgexpr(c, scrut);
|
||||
let mk37: i32 = 0;
|
||||
for (mk37 < spillsz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(mk37: i64, "AX");
|
||||
emitline(", DX\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((scrutoff + mk37): i64);
|
||||
emitline("(BP)\n");
|
||||
mk37 += 8;
|
||||
};
|
||||
} else {
|
||||
// #37 (rule 7): a >32B box from a kind with no mem-read
|
||||
// convention would spill the cursor it never filled —
|
||||
// loud, not garbage. Mirrors cstage.
|
||||
if (!isnullabletype(scrutt) && spillsz > TUPLE_GPCAP * 8) {
|
||||
let m37m: str = "#37: >32B tagged match scrutinee from a non-mem-based source unwired (rule 7)\n";
|
||||
os.write(2, m37m.ptr, m37m.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
// #37 (rule 7) stamped twin: matchscrutt returns nil
|
||||
// for kinds it can't resolve (deref/cast/...), so
|
||||
// spillsz defaults under cap and the guard above is
|
||||
// blind there. cstage sizes the spill from the
|
||||
// stamped s->type, so it louds — key on scrut.type_
|
||||
// to match. Surfaced by reviewer-37's `match (*p)`
|
||||
// probe on a 56B box.
|
||||
let ms37: *tinfo = scrut.type_: *tinfo;
|
||||
for (ms37 != nil && ms37.kind == tykind.TY_NAMED) {
|
||||
ms37 = ms37.under;
|
||||
};
|
||||
if (ms37 != nil && ms37.kind == tykind.TY_TAGGED
|
||||
&& ms37.size: i32 > TUPLE_GPCAP * 8) {
|
||||
let m37n: str = "#37: >32B tagged match scrutinee from a non-mem-based source unwired (rule 7)\n";
|
||||
os.write(2, m37n.ptr, m37n.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
cgexpr(c, scrut);
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff(scrutoff: i64);
|
||||
@@ -22678,7 +22890,7 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
};
|
||||
};
|
||||
}; };
|
||||
};
|
||||
};
|
||||
let endl: str = mklabel(c, "match_end");
|
||||
@@ -23170,19 +23382,18 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
// tagged arm.
|
||||
if (istaggedtype(c, tpt)) {
|
||||
let eslot: i32 = tupeslotn(tpt);
|
||||
// #22b (rule 7): a >32B box
|
||||
// overruns the 4-reg cursor —
|
||||
// pre-bound tupreg clamped k>=3
|
||||
// to R8 (silent payload drop)
|
||||
// while cstage emitted invalid
|
||||
// asm. Reachable only since the
|
||||
// over-cap sret send unwired;
|
||||
// the mem-based box read is the
|
||||
// #37 family. Mirrors cstage.
|
||||
// #37: a >32B box overruns the
|
||||
// 4-reg cursor — leave its
|
||||
// ADDRESS in AX (taggedmemread,
|
||||
// the sret-receive convention);
|
||||
// consumers copy from memory.
|
||||
// Replaces the #22b loud bound.
|
||||
// Mirrors cstage.
|
||||
if (eslot > TUPLE_GPCAP * 8) {
|
||||
let m37: str = "tagged tuple element read exceeds the AX/DX/CX/R8 box cursor (mem-based read is the #37 family; rule 7)\n";
|
||||
os.write(2, m37.ptr, m37.len: u64);
|
||||
os.exit(1);
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff((lc.off + foff): i64);
|
||||
emitline("(BP), AX\n");
|
||||
return;
|
||||
};
|
||||
let k: i32 = 0;
|
||||
for (k < eslot / 8) {
|
||||
@@ -31860,16 +32071,11 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
// cgwidentaggedstore's non-BP base.
|
||||
if (sretretsize(c, c.fnret) > 0) {
|
||||
let sa38v: i32 = localfind(c, "@sretarg");
|
||||
if (forwardtagged) {
|
||||
// exact type, but only an N_CALL source
|
||||
// sret's into outer's dest; a cursor
|
||||
// source (N_INDEX/N_DOT) can't carry
|
||||
// >32B (rule 7, #38b residual).
|
||||
if (rhs.kind != nkind.N_CALL) {
|
||||
let m38d: str = "#38b: >32B tagged return from a cursor source (N_INDEX/N_DOT) unsupported\n";
|
||||
os.write(2, m38d.ptr, m38d.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
if (forwardtagged && rhs.kind == nkind.N_CALL) {
|
||||
// exact-type N_CALL forward: inner sret's
|
||||
// into outer's dest; an N_INDEX/N_DOT
|
||||
// source routes through the widener's
|
||||
// #37 mem-read arm below instead.
|
||||
c.sretforward = 1;
|
||||
cgexpr(c, rhs);
|
||||
emitline("\tMOVQ\t");
|
||||
@@ -31886,9 +32092,13 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
||||
ru38 = ru38.under;
|
||||
};
|
||||
if (ru38 != nil) {
|
||||
// #37 wired the N_INDEX/N_DOT mem-read into
|
||||
// the widener; the remaining >32B kinds stay
|
||||
// loud.
|
||||
if (ru38.kind == tykind.TY_TAGGED
|
||||
&& rhs.kind != nkind.N_IDENT
|
||||
&& ru38.size: i32 > TUPLE_GPCAP * 8) {
|
||||
&& ru38.size: i32 > TUPLE_GPCAP * 8
|
||||
&& !taggedmemread(c, rhs)) {
|
||||
let m38e: str = "#40: widening tagged return-forward of a >32B source needs mem-to-mem tag-remap (unwired)\n";
|
||||
os.write(2, m38e.ptr, m38e.len: u64);
|
||||
os.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user