w6c+selfhost: codegen for top-level mutable let
Third step toward writable globals. The C cgen and its selfhost
mirror now:
- emit DATAW <name>(SB),"<8 LE bytes>" for every top-level `let`
whose type lands in the scalar set (i8..i64/u8..u64/bool/rune/
int/uint/uintptr/ptr; floats and multi-word types deferred);
- drop the "no writable .data" silent-drop guard at the N_IDENT
store path, replacing it with a RIP-relative MOVQ for `=` and
a load→combine→store sequence for the compound ops; and
- route `&name` through LEAQ name(SB) instead of dropping it.
Type aliases resolve via aliaslookup so `type counter = i32; let c:
counter = 0;` still emits a DATAW slot. Non-literal initialisers
silently skip, which surfaces as a clean undefined-symbol error if
the binding is ever referenced.
The selfhost mirror lands in the same commit because test 990
diffs the C cgen against wwdump_ww -c on err.ww (which has
top-level `let nerrors: i32 = 0; ... nerrors += 1;`). Any drift
between the two cgens makes 990 fail. Bootstrap stays at a fixed
point: ww2 == ww3 == ww4 byte-identical.
This commit is contained in:
@@ -950,6 +950,13 @@ fn cgun(c: *cgen, n: *node) void = {
|
||||
emitline("(BP), AX\n");
|
||||
return;
|
||||
};
|
||||
// Top-level mutable let — RIP-relative LEAQ.
|
||||
if (isletvar(c, nm)) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), AX\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
};
|
||||
return;
|
||||
@@ -1525,7 +1532,52 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (lhs.kind == nkind.N_IDENT) {
|
||||
let nm: str = lhs.str;
|
||||
let off: i32 = localfind(c, nm);
|
||||
if (off == 0) { return; };
|
||||
if (off == 0) {
|
||||
// Top-level let target: RIP-relative store
|
||||
// for `=`, or load→combine→store for the
|
||||
// compound forms.
|
||||
if (!isletvar(c, nm)) { return; };
|
||||
cgexpr(c, n.rhs);
|
||||
if (n.op == tkind.TK_ASSIGN) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
return;
|
||||
};
|
||||
emitline("\tMOVQ\t");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB), BX\n");
|
||||
let didcompound: bool = true;
|
||||
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tAX, BX\n"); }
|
||||
else { if (n.op == tkind.TK_MINUSEQ) { emitline("\tSUBQ\tAX, BX\n"); }
|
||||
else { if (n.op == tkind.TK_STAREQ) { emitline("\tIMULQ\tAX, BX\n"); }
|
||||
else { if (n.op == tkind.TK_AMPEQ) { emitline("\tANDQ\tAX, BX\n"); }
|
||||
else { if (n.op == tkind.TK_PIPEEQ) { emitline("\tORQ\tAX, BX\n"); }
|
||||
else { if (n.op == tkind.TK_CARETEQ) { emitline("\tXORQ\tAX, BX\n"); }
|
||||
else { if (n.op == tkind.TK_LSHIFTEQ) {
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tSHLQ\tCX, BX\n");
|
||||
}
|
||||
else { if (n.op == tkind.TK_RSHIFTEQ) {
|
||||
emitline("\tMOVQ\tAX, CX\n");
|
||||
emitline("\tSHRQ\tCX, BX\n");
|
||||
}
|
||||
else {
|
||||
// Unsupported compound: store rhs
|
||||
// directly. Mirrors the local path's
|
||||
// fallback for TK_SLASHEQ etc.
|
||||
didcompound = false;
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
};};};};};};};};
|
||||
if (didcompound) {
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitsymname(c, nm);
|
||||
emitline("(SB)\n");
|
||||
};
|
||||
return;
|
||||
};
|
||||
// Detect str-typed local — assignment must store both
|
||||
// halves (AX=ptr at +0, BX=len at +8).
|
||||
let lcstr: bool = false;
|
||||
|
||||
Reference in New Issue
Block a user