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:
118
test/wcc/630_let_global.c
Normal file
118
test/wcc/630_let_global.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 630_let_global — top-level mutable `let` end-to-end through the
|
||||
* full Cstage pipeline (w6c → w6a → w6l). Each fixture is a tiny
|
||||
* .ww program that exercises one path: literal-init read, plain
|
||||
* assignment, compound assignment, and address-of.
|
||||
*
|
||||
* Exit code = the value the runtime feeds to `exit(2)`. main's
|
||||
* return value flows into DI via rt/start.s and ends up as the
|
||||
* shell's $?.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
run_exit(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
build_and_run(const char *bin, const char *prog)
|
||||
{
|
||||
/* `ww run` builds with the driver (w6c → w6a → w6l + libwwrt.a)
|
||||
* and executes the result. The exit code propagates back as the
|
||||
* shell's $? so we just compare against the fixture's want. */
|
||||
char src[64], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/wwt_lg_%d.ww", getpid());
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(prog, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/ww run %s", bin, src);
|
||||
int rc = run_exit(cmd);
|
||||
unlink(src);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct fixture {
|
||||
const char *label;
|
||||
const char *prog;
|
||||
int want;
|
||||
};
|
||||
|
||||
static const struct fixture fixtures[] = {
|
||||
{
|
||||
"read",
|
||||
"let counter: i32 = 42;\n"
|
||||
"fn main() i32 = { return counter; };\n",
|
||||
42,
|
||||
},
|
||||
{
|
||||
"assign",
|
||||
"let counter: i32 = 0;\n"
|
||||
"fn main() i32 = { counter = 99; return counter; };\n",
|
||||
99,
|
||||
},
|
||||
{
|
||||
"compound",
|
||||
"let counter: i32 = 1;\n"
|
||||
"fn inc() void = { counter += 1; };\n"
|
||||
"fn main() i32 = { inc(); inc(); inc(); return counter; };\n",
|
||||
4,
|
||||
},
|
||||
{
|
||||
"zero-init",
|
||||
/* No initialiser → zero-filled DATAW slot. */
|
||||
"let counter: i32;\n"
|
||||
"fn main() i32 = { counter = 7; return counter; };\n",
|
||||
7,
|
||||
},
|
||||
{
|
||||
"addr-of",
|
||||
"let counter: i32 = 11;\n"
|
||||
"fn main() i32 = {\n"
|
||||
"\tlet p: *i32 = &counter;\n"
|
||||
"\t*p = 55;\n"
|
||||
"\treturn counter;\n"
|
||||
"};\n",
|
||||
55,
|
||||
},
|
||||
{
|
||||
"u64-read",
|
||||
"let val: u64 = 123u64;\n"
|
||||
"fn main() i32 = { return val: i32; };\n",
|
||||
123,
|
||||
},
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
|
||||
int fail = 0, ran = 0;
|
||||
for (int i = 0; fixtures[i].label; i++, ran++) {
|
||||
int got = build_and_run(bin, fixtures[i].prog);
|
||||
if (got != fixtures[i].want) {
|
||||
fprintf(stderr, "let_global[%s]: exit=%d, want %d\n",
|
||||
fixtures[i].label, got, fixtures[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr, "let_global: %d/%d fixtures failed\n", fail, ran);
|
||||
return 1;
|
||||
}
|
||||
printf("let_global: %d/%d ok\n", ran, ran);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user