wcc: str -> 24B {ptr,len,cap}, 3-reg ABI -- parity with []u8 (both stages)
A ww `str` becomes a 24-byte {ptr,len,cap} value, identical in layout to
[]u8 -- the enabling prerequisite for the Phase 2 `str == []u8` collapse.
Both stages, atomically:
- ty_str 16->24B; str value flows 3-reg AX/BX/CX (was 2-reg); str literals
emit cap (=len).
- str in a tagged union grows to a 32B slot, using the AX/DX/CX/R8 4th-word
path already used by 32B slice-variant unions -- str-variant is now
structurally identical.
- tuple (scalar,str) return: 4-reg AX/DX/CX/R8 + 32B receive, extending the
existing type-keyed return (no sret).
- str == []u8 for index and .ptr/.len/.cap, kind-gated where size-based
dispatch collided at 24B; cstage and wwstage mirror exactly.
- table-driven runtime coverage: test/wcc/928_str_abi_run.c.
Cannot be split (rule 10/11): a 24B str and a 16B str cannot coexist across
the two compiler stages without breaking byte-identity, so the size change
and every dependent ABI/codegen site land in one atomic commit, both stages.
Known follow-ups (zero corpus impact, tracked): str-literal global .cap
static-init; >16B struct by-value (pre-existing); tagged-union
match-scrutinee stage divergence (pre-existing).
This commit is contained in:
@@ -185,7 +185,11 @@ fn cgtryprop(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
if (succisstr) {
|
||||
// str IS []u8: success arrives DX=ptr, CX=len, R8=cap
|
||||
// (slot 32B). Move len out before cap overwrites CX
|
||||
// (#1/Phase 3).
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
emitline("\tMOVQ\tR8, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tDX, AX\n");
|
||||
return;
|
||||
@@ -241,7 +245,11 @@ fn cgtryunw(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
if (succisstr) {
|
||||
// str IS []u8: success arrives DX=ptr, CX=len, R8=cap
|
||||
// (slot 32B). Move len out before cap overwrites CX
|
||||
// (#1/Phase 3).
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
emitline("\tMOVQ\tR8, CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tDX, AX\n");
|
||||
return;
|
||||
@@ -514,8 +522,9 @@ fn cgcast(c: *cgen, n: *node) void = {
|
||||
};
|
||||
|
||||
fn cgstrlit(c: *cgen, n: *node) void = {
|
||||
// Result is the (ptr, len) pair: ptr in AX, len in BX. Call
|
||||
// sites that expect a str arg pick these up directly.
|
||||
// str IS []u8: the (ptr, len, cap) triple — ptr in AX, len in BX,
|
||||
// cap in CX. A static literal has no spare storage, so cap = len
|
||||
// (#1/Phase 3). Call sites that expect a str arg pick these up.
|
||||
let nstr: str = n.str;
|
||||
let lab: str = internstrlit(c, nstr);
|
||||
emitline("\tLEAQ\t");
|
||||
@@ -524,6 +533,9 @@ fn cgstrlit(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(nstr.len: i64);
|
||||
emitline(", BX\n");
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(nstr.len: i64);
|
||||
emitline(", CX\n");
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -559,9 +571,14 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
emitoff(off: i64);
|
||||
emitline("(BP), AX\n");
|
||||
if (isstr) {
|
||||
// str IS []u8: load (ptr,len,cap) into AX/BX/CX,
|
||||
// identical to the slice arm below (#1/Phase 3).
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP), BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP), CX\n");
|
||||
};
|
||||
if (issl) {
|
||||
emitline("\tMOVQ\t");
|
||||
@@ -591,6 +608,11 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(bytes.len: i64);
|
||||
emitline(", BX\n");
|
||||
// str IS []u8: cap = len for a static def literal
|
||||
// (#1/Phase 3).
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(bytes.len: i64);
|
||||
emitline(", CX\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
@@ -622,17 +644,16 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
let isstr: bool = letvarisstr(c, nm);
|
||||
let issl: bool = letvarisslice(c, nm);
|
||||
if (isstr || issl) {
|
||||
// str IS []u8: both str and slice carry a third 8B
|
||||
// (cap); load it unconditionally. The address holder CX
|
||||
// is overwritten by the cap as the last step, after
|
||||
// ptr/len are already loaded (#1/Phase 3).
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t(CX), AX\n");
|
||||
emitline("\tMOVQ\t8(CX), BX\n");
|
||||
if (issl) {
|
||||
// Overwrites the address holder with the
|
||||
// cap as the last step — CX is no longer
|
||||
// needed once both ptr/len are loaded.
|
||||
emitline("\tMOVQ\t16(CX), CX\n");
|
||||
};
|
||||
emitline("\tMOVQ\t16(CX), CX\n");
|
||||
return;
|
||||
};
|
||||
// Float global: same LEAQ-indirect shape, since MOVSS/
|
||||
@@ -686,6 +707,12 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
let idx: *node = n.rhs;
|
||||
let esz: i32 = 8;
|
||||
let signed_elem: bool = false;
|
||||
// #1/Phase 3: str=24B collides with slice=24B, so the str-element
|
||||
// branches below MUST gate on kind (mirroring cstage's elem_is_str),
|
||||
// not a bare `esz == primtypesize("str")` size check — otherwise a
|
||||
// []u8 element (also 24B) misfires into the str 2-word load and
|
||||
// diverges from cstage (#60 collision class; sentinel 754).
|
||||
let elemisstr: bool = false;
|
||||
let baselocal: *local = nil;
|
||||
// Global `[N]T` array or `*T` pointer used as an index base.
|
||||
// The local-ident lookup above misses it; we need LEAQ name(SB)
|
||||
@@ -727,7 +754,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
// 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; };
|
||||
if (dt != nil) { esz = dt.size: i32; elemisstr = typeisstr(dt); };
|
||||
} 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
|
||||
@@ -776,6 +803,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
esz = elem_slot_sz;
|
||||
};
|
||||
};
|
||||
elemisstr = isstrtype(c, etn);
|
||||
};
|
||||
};
|
||||
cgexpr(c, idx);
|
||||
@@ -811,7 +839,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// #43: str element-stride routes through primtypesize so the
|
||||
// 16-vs-24 dispatch tracks ty_str.size for #1.
|
||||
if (esz == primtypesize("str"): i32) {
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(BX), CX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
@@ -853,7 +881,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
// str element (16B today): load (ptr, len) into (AX, BX) so
|
||||
// the value flows through the str-rhs convention.
|
||||
// #43: route via primtypesize so the stride tracks #1.
|
||||
if (esz == primtypesize("str"): i32) {
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(BX), CX\n");
|
||||
emitline("\tMOVQ\t(BX), AX\n");
|
||||
emitline("\tMOVQ\tCX, BX\n");
|
||||
@@ -885,7 +913,7 @@ fn cgindex(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// #43: str element-stride routes through primtypesize so the
|
||||
// 16-vs-24 dispatch tracks ty_str.size for #1.
|
||||
if (esz == primtypesize("str"): i32) {
|
||||
if (elemisstr) {
|
||||
emitline("\tMOVQ\t8(AX), BX\n");
|
||||
emitline("\tMOVQ\t(AX), AX\n");
|
||||
return;
|
||||
@@ -1480,10 +1508,11 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Hare-style tuple positional access: `t.0`, `t.1`.
|
||||
// Walk the tuple element type list summing slotsize
|
||||
// (matches the (scalar, str) init layout which puts
|
||||
// the scalar in an 8B slot and the str in 16B). For
|
||||
// a str element, load both halves into (AX, BX) so
|
||||
// chains like `t.1.len` propagate correctly.
|
||||
// (matches the (scalar, str) init layout: scalar in an
|
||||
// 8B slot, str in 24B — str IS []u8, #1/Phase 3). For a
|
||||
// str element, load (ptr, len) into (AX, BX); the cap
|
||||
// stays in the slot (the 2-word str-field read, like
|
||||
// every other chained/dot str leaf read — task #14).
|
||||
if (lkind == nkind.N_TTUPLE) {
|
||||
let idx: i32 = fldnumidx(fld);
|
||||
if (idx >= 0) {
|
||||
@@ -1598,9 +1627,9 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
let delta: i32 = -1;
|
||||
if (streq(fld, "ptr")) { delta = 0; };
|
||||
if (streq(fld, "len")) { delta = 8; };
|
||||
if (issl) {
|
||||
if (streq(fld, "cap")) { delta = 16; };
|
||||
};
|
||||
// str IS []u8: .cap is valid on a str global too,
|
||||
// not slice-only — mirrors cstage (#1/Phase 3, #11).
|
||||
if (streq(fld, "cap")) { delta = 16; };
|
||||
if (delta >= 0) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, lhs.str);
|
||||
@@ -2456,7 +2485,9 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
let gdelta: i32 = -1;
|
||||
if (streq(fld, "ptr")) { gdelta = 0; };
|
||||
if (streq(fld, "len")) { gdelta = 8; };
|
||||
if (issl) { if (streq(fld, "cap")) { gdelta = 16; }; };
|
||||
// str IS []u8: &str.cap is valid too, not slice-only
|
||||
// — mirrors cstage (#1/Phase 3, #11).
|
||||
if (streq(fld, "cap")) { gdelta = 16; };
|
||||
if (gdelta >= 0) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, basenm);
|
||||
@@ -2828,16 +2859,19 @@ fn cgalloc(c: *cgen, n: *node) void = {
|
||||
emitline("\n");
|
||||
fi = nil;
|
||||
} else { if (isstrtype(c, fi.tnode)) {
|
||||
// alloc(T{ fval = s }) for str field: cgexpr
|
||||
// leaves (AX=ptr, BX=len). Use CX for the heap
|
||||
// base so BX=len survives both stores. Mirrors
|
||||
// cmd/w6c/cgen.c:4184-4190.
|
||||
emitline("\tMOVQ\t(SP), CX\n");
|
||||
// str IS []u8: cgexpr leaves (AX=ptr,
|
||||
// BX=len, CX=cap). Route the heap base
|
||||
// through DX so all three survive — CX
|
||||
// holds cap, BX holds len (#1/Phase 3).
|
||||
emitline("\tMOVQ\t(SP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "CX");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
fi = nil;
|
||||
} else {
|
||||
@@ -3297,7 +3331,8 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
popped += 1;
|
||||
} else {
|
||||
let extra: i32 = 0;
|
||||
if (nodeisstr(c, a)) { extra = 1; };
|
||||
// str IS []u8: 3-word arg, same as slice (#1/Phase 3).
|
||||
if (nodeisstr(c, a)) { extra = 2; };
|
||||
if (nodeisslice(c, a)) { extra = 2; };
|
||||
// #21: tagged-CALL arg was pushed AX/DX/CX/R8 high→low
|
||||
// by pushargsrev; size the per-arg pop to match so the
|
||||
@@ -3467,31 +3502,9 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
emitint((stackslots * 8): i64);
|
||||
emitline(", SP\n");
|
||||
};
|
||||
// SysV returns 16-byte aggregates in (AX, DX). Our str
|
||||
// convention is (AX, BX), so shuffle for str-returning calls.
|
||||
// Route through fnretlookupmod: for N_DOT cross-module callees,
|
||||
// the bare-leaf fnretlookup's same-module-first walk (#4e) would
|
||||
// pick the caller-module's same-leaf fn — a str-returning
|
||||
// caller-side `slice` over a []u8-returning `mod.slice` then
|
||||
// emits a phantom MOVQ DX, BX after the cross-module CALL (#34).
|
||||
if (calleename.len > 0) {
|
||||
let cmod: str;
|
||||
cmod.ptr = nil; cmod.len = 0;
|
||||
if (callee != nil) {
|
||||
if (callee.kind == nkind.N_IDENT) { cmod = c.curmod; };
|
||||
if (callee.kind == nkind.N_DOT) {
|
||||
if (callee.lhs != nil) {
|
||||
if (callee.lhs.kind == nkind.N_IDENT) {
|
||||
cmod = callee.lhs.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
let rtyp: *node = fnretlookupmod(c, calleename, cmod);
|
||||
if (isstrtype(c, rtyp)) {
|
||||
emitline("\tMOVQ\tDX, BX\n");
|
||||
};
|
||||
};
|
||||
// str IS []u8: a str-returning callee leaves AX=ptr, BX=len,
|
||||
// CX=cap — same as a slice, so there is no receive-side shuffle
|
||||
// (#1/Phase 3).
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -3590,17 +3603,21 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tX0, (BX)\n");
|
||||
return;
|
||||
};
|
||||
// Push order matches C cgen
|
||||
// (cmd/w6c/cgen.c:1033-1041): PUSHQ AX
|
||||
// (ptr) first, then PUSHQ BX (len) if
|
||||
// str, so the pop sequence is POP CX
|
||||
// (len) → POP AX (ptr) → MOVQ AX,
|
||||
// (BX) → MOVQ CX, 8(BX).
|
||||
// str IS []u8: PUSHQ AX (ptr) first, then
|
||||
// PUSHQ BX (len) + PUSHQ CX (cap) across the
|
||||
// pointer eval which clobbers BX/CX. Pop drains
|
||||
// cap (top) → 16(BX), then len, then ptr → 0(BX)
|
||||
// with len → 8(BX) (#1/Phase 3).
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
if (elemstr) { emitline("\tPUSHQ\tBX\n"); };
|
||||
if (elemstr) {
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
};
|
||||
cgexpr(c, inner);
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
if (elemstr) {
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tMOVQ\tCX, 16(BX)\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tMOVQ\tAX, (BX)\n");
|
||||
@@ -3854,10 +3871,19 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
cgexpr(c, n.rhs); // value → AX
|
||||
// #43: spill BX (str.len) before computing
|
||||
// the index so the post-index store can pop
|
||||
// it; the stride gate tracks ty_str.size.
|
||||
if (esz == primtypesize("str"): i32) { emitline("\tPUSHQ\tBX\n"); };
|
||||
// str IS []u8: spill cap (CX) + len (BX) before
|
||||
// computing the index so the post-index store can
|
||||
// pop all three. #1/Phase 3: str=24B collides with
|
||||
// slice=24B, so this MUST gate on kind (cstage's
|
||||
// elem_is_str, cmd/w6c/cgen.c:3576) — not a bare
|
||||
// `esz == primtypesize("str")` — or a []u8 element
|
||||
// (also 24B) misfires the str 3-word store and
|
||||
// diverges from cstage. Write-side mirror of the
|
||||
// cgindex read-path gate (#7/754).
|
||||
if (isstrtype(c, elemtn)) {
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
};
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
cgexpr(c, idx); // idx → AX
|
||||
if (esz > 1) {
|
||||
@@ -3895,13 +3921,15 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tPOPQ\tAX\n"); // scaled idx
|
||||
emitline("\tADDQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tAX\n"); // value
|
||||
// #43: str-element write — pop the saved
|
||||
// .len and store both halves. Stride gate
|
||||
// routes through primtypesize for #1.
|
||||
if (esz == primtypesize("str"): i32) {
|
||||
// str IS []u8: pop the saved len + cap and store
|
||||
// all three words. Kind-gate, not size — see the
|
||||
// spill site above (#1/Phase 3, #7/754).
|
||||
if (isstrtype(c, elemtn)) {
|
||||
emitline("\tMOVQ\tAX, (BX)\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tMOVQ\tCX, 8(BX)\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tMOVQ\tCX, 16(BX)\n");
|
||||
return;
|
||||
};
|
||||
let isop: str = tnodestoreop(c, elemtn, esz);
|
||||
@@ -4328,20 +4356,24 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, AX\n");
|
||||
};
|
||||
};
|
||||
// str field via *struct: rhs left
|
||||
// (AX=ptr, BX=len). Use CX as the
|
||||
// address scratch so we don't clobber
|
||||
// the len half before storing it.
|
||||
// str IS []u8: rhs left (AX=ptr,
|
||||
// BX=len, CX=cap). CX holds cap, so
|
||||
// stage the struct addr in DX and
|
||||
// store all three words — identical
|
||||
// to the slice arm below (#1/Phase 3).
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("(BP), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "CX");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
@@ -4532,11 +4564,11 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};};
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
// str field: cgexpr left (AX=ptr, BX=len);
|
||||
// store both halves at +0/+8. Without this,
|
||||
// `L.src = s` would only write the ptr and
|
||||
// `L.src.len` would carry whatever was on the
|
||||
// stack.
|
||||
// str IS []u8: cgexpr left (AX=ptr,
|
||||
// BX=len, CX=cap); store all three at
|
||||
// +0/+8/+16, identical to the slice
|
||||
// arm below. BP base, no scratch
|
||||
// reload needed (#1/Phase 3).
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((lc.off + fi.foff): i64);
|
||||
@@ -4544,6 +4576,9 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((lc.off + fi.foff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((lc.off + fi.foff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
};
|
||||
// slice field direct: cgexpr left
|
||||
@@ -4791,14 +4826,22 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
cgexpr(c, n.rhs);
|
||||
if (isstrtype(c, fi.tnode)) {
|
||||
// str IS []u8: cgexpr left
|
||||
// (AX=ptr, BX=len, CX=cap). CX
|
||||
// holds cap, so stage the base
|
||||
// addr in DX and store all three
|
||||
// words (#1/Phase 3).
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("(SB), DX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(fi.foff: i64, "CX");
|
||||
emitdispreg(fi.foff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((fi.foff + 8): i64, "CX");
|
||||
emitdispreg((fi.foff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((fi.foff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
@@ -5037,22 +5080,29 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
if (typeisstr(leaftype)) {
|
||||
// str IS []u8: store ptr/len/cap. cgexpr leaves
|
||||
// CX=cap, so the viacx base goes in DX (not CX) to
|
||||
// avoid clobbering it — same as the single-dot str
|
||||
// field store (#1/Phase 3).
|
||||
cgexpr(c, n.rhs);
|
||||
if (viacx) {
|
||||
if (ptrroot) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff(rootoff: i64);
|
||||
emitline("(BP), CX\n");
|
||||
emitline("(BP), DX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, rootname);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("(SB), DX\n");
|
||||
};
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(totaloff: i64, "CX");
|
||||
emitdispreg(totaloff: i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitdispreg((totaloff + 8): i64, "CX");
|
||||
emitdispreg((totaloff + 8): i64, "DX");
|
||||
emitline("\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitdispreg((totaloff + 16): i64, "DX");
|
||||
emitline("\n");
|
||||
} else {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
@@ -5061,6 +5111,9 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff((rootoff + totaloff + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((rootoff + totaloff + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
@@ -5548,11 +5601,17 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
cgexpr(c, n.rhs);
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (letvarisstr(c, nm)) {
|
||||
// str IS []u8: stash cap in DI before LEAQ
|
||||
// overwrites CX, then store ptr/len/cap —
|
||||
// identical to the slice arm below
|
||||
// (#1/Phase 3).
|
||||
emitline("\tMOVQ\tCX, DI\n");
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, (CX)\n");
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
emitline("\tMOVQ\tDI, 16(CX)\n");
|
||||
return;
|
||||
};
|
||||
if (letvarisslice(c, nm)) {
|
||||
@@ -5838,7 +5897,9 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
emitoff((off + 8): i64);
|
||||
emitline("(BP)\n");
|
||||
};
|
||||
if (lcsl) {
|
||||
// str IS []u8: store the cap word too, identical to
|
||||
// the slice store (#1/Phase 3).
|
||||
if (lcstr || lcsl) {
|
||||
emitline("\tMOVQ\tCX, ");
|
||||
emitoff((off + 16): i64);
|
||||
emitline("(BP)\n");
|
||||
|
||||
Reference in New Issue
Block a user