w6c+selfhost: str globals — 16B DATAW + (LEAQ, MOVQ, MOVQ) sequences
Extend top-level mutable `let` to cover str. The cgen now:
- emits a 16-byte zero DATAW for `let s: str;` (and the trivial
`nil` / `""` inits); a non-empty strlit init is skipped because
a compile-time .data → .text reloc isn't supported yet, so the
user gets a clean undefined-symbol error at link;
- loads `s` as `(LEAQ s(SB), CX; MOVQ (CX), AX; MOVQ 8(CX), BX)`
so the (AX=ptr, BX=len) pair convention is preserved;
- stores via the same `&s` indirection for `s = expr;` and routes
the `.ptr` / `.len` pseudo-field N_DOT branch through it; and
- tracks the declared type on each LetVar so cgident / cgdot /
cgassign pick the right load/store shape.
Selfhost cgen mirrors all four paths byte-for-byte; test 990
(cgen-match on err.ww) and tests 994/995 (self-rebuild) stay
green. 630_let_global gains two new fixtures (`let msg: str;` +
runtime assign, plus reassign from a helper).
Slice and struct globals still NYI — same scope deferred.
This commit is contained in:
@@ -401,11 +401,20 @@ fn cgident(c: *cgen, n: *node) void = {
|
||||
return;
|
||||
};
|
||||
// Top-level mutable `let` — RIP-relative load from its DATAW
|
||||
// slot. Mirrors C cgen's catch-all `MOVQ masym(s), AX`. Names
|
||||
// that aren't lets either (typos, never-defined) flow through
|
||||
// here too in C; the divergence today is bounded to scalar lets,
|
||||
// which is what `isletvar` gates on.
|
||||
// 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.
|
||||
if (isletvar(c, nm)) {
|
||||
if (letvarisstr(c, nm)) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t(CX), AX\n");
|
||||
emitline("\tMOVQ\t8(CX), BX\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
@@ -871,6 +880,30 @@ 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`.
|
||||
if (lhs != nil) {
|
||||
if (lhs.kind == nkind.N_IDENT) {
|
||||
if (isletvar(c, lhs.str)) {
|
||||
if (letvarisstr(c, lhs.str)) {
|
||||
let delta: i32 = -1;
|
||||
if (streq(fld, "ptr")) { delta = 0; };
|
||||
if (streq(fld, "len")) { delta = 8; };
|
||||
if (delta >= 0) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, lhs.str);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(delta: i64, "CX");
|
||||
emitline(", AX\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
// Module-qualified value reference: `mod.name` where `mod`
|
||||
// is nkind.N_IDENT bound as skind.SK_USE and the leaf isn't a local.
|
||||
// Treat as a SB symbol — `MOVQ leaf(SB), AX`. Same fallback
|
||||
@@ -1546,10 +1579,20 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (off == 0) {
|
||||
// Top-level let target: RIP-relative store
|
||||
// for `=`, or load→combine→store for the
|
||||
// compound forms.
|
||||
// compound forms. For a str global, take its
|
||||
// address into CX and store both halves; the
|
||||
// asm has no `name+8(SB)` operand form.
|
||||
if (!isletvar(c, nm)) { return; };
|
||||
cgexpr(c, n.rhs);
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
if (letvarisstr(c, nm)) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), CX\n");
|
||||
emitline("\tMOVQ\tAX, (CX)\n");
|
||||
emitline("\tMOVQ\tBX, 8(CX)\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
|
||||
Reference in New Issue
Block a user