w6c+selfhost: slice globals — 24B DATAW + (AX,BX,CX) load
Extend top-level mutable `let` to cover slices. Same shape as the
str work, with one more 8-byte field and the address holder CX
overwritten by the cap as the last load step:
- emit_lets / emitletdataw: 24-byte zero DATAW for `let v: []u8;`
(and the trivial `nil` init); no slice-literal syntax exists
so the no-init path is the only supported shape;
- cgident: LEAQ name(SB), CX → MOVQ (CX), AX → MOVQ 8(CX), BX →
MOVQ 16(CX), CX, so the slice ABI triple lands in (AX, BX, CX);
- cgdot: .cap delta 16 wired alongside .ptr / .len through the
same &name(SB) base.
Slice reassignment (`v = some_slice;`) is still unsupported — slice
values don't yet flow as a full (AX, BX, CX) triple through general
expressions even for locals — so reads/`&` are the supported surface
today. Manual fill through `(&v): *u64` continues to work.
Tests 630 (10/10), 990, 994, 995 stay green; bootstrap fixed point
holds.
This commit is contained in:
@@ -402,17 +402,25 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Top-level mutable `let` — RIP-relative load from its DATAW
|
||||
// slot. Mirrors C cgen's catch-all `MOVQ masym(s), AX` for
|
||||
// scalar lets, plus the (LEAQ, MOVQ, MOVQ) sequence for str
|
||||
// globals so both ptr and len land in (AX, BX). Names that
|
||||
// aren't lets either (typos, never-defined) drop through to
|
||||
// the silent return.
|
||||
// scalar lets, plus the (LEAQ, MOVQ, MOVQ[, MOVQ]) sequence
|
||||
// for str / slice globals so the ABI pair / triple lands in
|
||||
// (AX, BX[, CX]). Names that aren't lets either (typos,
|
||||
// never-defined) drop through to the silent return.
|
||||
if (isletvar(c, nm)) {
|
||||
if (letvarisstr(c, nm)) {
|
||||
let isstr: bool = letvarisstr(c, nm);
|
||||
let issl: bool = letvarisslice(c, nm);
|
||||
if (isstr || issl) {
|
||||
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");
|
||||
};
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
@@ -880,17 +888,22 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// Top-level str global field access — load .ptr / .len via
|
||||
// &name(SB) into CX, then MOVQ delta(CX), AX. Without this
|
||||
// the module-qualified fallback below would mis-emit
|
||||
// `MOVQ <field>(SB), AX`.
|
||||
// Top-level str/slice global field access — load .ptr / .len
|
||||
// (and .cap for slices) via &name(SB) into CX, then MOVQ
|
||||
// delta(CX), AX. Without this the module-qualified fallback
|
||||
// below would mis-emit `MOVQ <field>(SB), AX`.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_IDENT) {
|
||||
if (isletvar(c, lhs.str)) {
|
||||
if (letvarisstr(c, lhs.str)) {
|
||||
let isstr: bool = letvarisstr(c, lhs.str);
|
||||
let issl: bool = letvarisslice(c, lhs.str);
|
||||
if (isstr || issl) {
|
||||
let delta: i32 = -1;
|
||||
if (streq(fld, "ptr")) { delta = 0; };
|
||||
if (streq(fld, "len")) { delta = 8; };
|
||||
if (issl) {
|
||||
if (streq(fld, "cap")) { delta = 16; };
|
||||
};
|
||||
if (delta >= 0) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, lhs.str);
|
||||
|
||||
Reference in New Issue
Block a user