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:
2026-05-12 12:16:52 +09:00
parent 208bdd25df
commit 97eb1fe20d
6 changed files with 276 additions and 42 deletions

View File

@@ -113,6 +113,31 @@ static const struct fixture fixtures[] = {
"};\n",
7,
},
{
"slice-zeroinit",
/* Slice globals start as {nil, 0, 0}. .len and .cap both
* read zero; .ptr is nil but we don't dereference it. */
"let buf: []u8;\n"
"fn main() i32 = {\n"
"\treturn (buf.len + buf.cap): i32;\n"
"};\n",
0,
},
{
"slice-cap-via-raw-pointer",
/* Drive the slice header through a raw u64 pointer cast
* — the language has no slice-reassignment expression
* yet, so this is the only way to fill the header from
* ww source today. .cap reads back as the value we
* wrote. */
"let buf: []u8;\n"
"fn main() i32 = {\n"
"\tlet p: *u64 = (&buf): *u64;\n"
"\tp[2] = 99u64;\n"
"\treturn buf.cap: i32;\n"
"};\n",
99,
},
{ NULL, NULL, 0 }
};