toolchain: banner purge + WHY-only comment sweep (rule 8)

selfhost/, cmd/, internal/ join the tree-wide sweep: every section
banner dies (91 selfhost + the cmd C-style dividers -> 0); narration
and stale contracts deleted (pre-#22 bundler notes, retired
single-PT_LOAD and no-archive claims, superseded ABI tables); every
ref/harec/qbe cite, task cite, encoding/ELF contract, and rule-10
twin pointer kept; lost lifetime/rationale lines restored where the
sweep over-cut (elf_globals ownership, kwtab linear-scan). Comment-
only proven: all five wwstage tool binaries byte-identical across
the sweep; test-commit, test-byteid (161+1399, 0 pinned-divergent),
and test-bootstrap (fixed point + 991-995 byte-id) all exit 0.
The read-through banked 66 latent-bug leads (checkpoint).
This commit is contained in:
2026-08-08 23:14:03 +09:00
parent 83f5956df2
commit 62b9d20383
60 changed files with 232 additions and 1045 deletions

View File

@@ -24,9 +24,8 @@ import strings;
// the cgen #127 mod-mangle attribution bug consumer per rule-7.
def CMD_MAX: u64 = 8192u64;
// cerr — bare stderr fragment writer for the driver's piecewise
// diagnostics. Tool-local (NOT a lib wrapper): messages are built from
// many fragments and we route through os.write to avoid libc stdio.
// Tool-local (NOT a lib wrapper): messages are built from many
// fragments and we route through os.write to avoid libc stdio.
// .len replaces the error-prone hand-counted byte literals these sites
// carried. Lives here (the selfhost ww-driver build is a single main.ww;
// err.c's bare-message path is not ported into this tree).
@@ -34,17 +33,14 @@ fn cerr(m: str) void = {
os.write(2, m.ptr, m.len: u64);
};
// ---- C-string helpers --------------------------------------------------
fn cstrlen(p: *u8) u64 = {
let n: u64 = 0u64;
for (p[n] != 0u8) { n += 1u64; };
return n;
};
// pathstr — view a NUL-terminated *u8 as a str. Bridges the
// driver's argv-style *u8 paths to lib/os entrypoints (str
// post-task-#23).
// Bridges the driver's argv-style *u8 paths to lib/os entrypoints
// (str post-task-#23).
fn pathstr(p: *u8) str = {
let r: str;
r.ptr = p;
@@ -56,12 +52,10 @@ fn cstreq(a: *u8, b: *u8) bool = {
return strings.compare(pathstr(a), pathstr(b)) == 0;
};
// cstreqlit — compare a NUL-terminated *u8 to a ww string literal.
fn cstreqlit(a: *u8, lit: str) bool = {
return strings.compare(pathstr(a), lit) == 0;
};
// memcpy
fn bytecpy(dst: *u8, src: *u8, n: u64) void = {
let i: u64 = 0u64;
for (i < n) {
@@ -70,8 +64,6 @@ fn bytecpy(dst: *u8, src: *u8, n: u64) void = {
};
};
// Copy a NUL-terminated *u8 into dst starting at off; return the new
// offset (without writing a NUL).
fn cstrinto(dst: *u8, off: u64, src: *u8) u64 = {
let i: u64 = 0u64;
for (src[i] != 0u8) {
@@ -81,7 +73,6 @@ fn cstrinto(dst: *u8, off: u64, src: *u8) u64 = {
return off + i;
};
// Same, but for a ww `str` (no NUL on the source side; we copy len bytes).
fn strinto(dst: *u8, off: u64, src: str) u64 = {
let n: i32 = src.len;
let i: i32 = 0;
@@ -94,22 +85,15 @@ fn strinto(dst: *u8, off: u64, src: str) u64 = {
return off + nu;
};
// Write a single byte, return new offset.
fn byteinto(dst: *u8, off: u64, c: u8) u64 = {
dst[off] = c;
return off + 1u64;
};
// NUL-terminate at off and return the same off (handy when passing the
// buffer to a syscall that expects a C-string).
fn cstrseal(dst: *u8, off: u64) void = {
dst[off] = 0u8;
};
// ---- Tool-path resolution ---------------------------------------------
// dirname-equivalent: copy argv[0] up to (but not including) the last
// '/' into dst, NUL-terminated. If no slash, write ".".
fn selfdirinto(dst: *u8, dstsz: u64, argv0: *u8) void = {
let n: u64 = cstrlen(argv0);
let cut: u64 = 0u64;
@@ -128,7 +112,6 @@ fn selfdirinto(dst: *u8, dstsz: u64, argv0: *u8) void = {
dst[cut] = 0u8;
};
// Build "$dir/$name" (NUL-terminated) into a fresh page-sized buffer.
fn joinpath(dir: *u8, name: *u8) *u8 = {
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
buf.len = os.PATH_MAX;
@@ -139,7 +122,6 @@ fn joinpath(dir: *u8, name: *u8) *u8 = {
return buf.ptr;
};
// Same, but the second component is a ww `str` literal.
fn joinpathlit(dir: *u8, name: str) *u8 = {
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
buf.len = os.PATH_MAX;
@@ -208,8 +190,6 @@ fn execpackagetests(selfdir: *u8, argv: **u8, argc: i32, start: i32,
return 1;
};
// ---- import resolution + visited-set -----------------------------------
//
// The separate-compilation producer scans each unit's top-of-file
// `import IDENT;` lines and resolves them via the colon-separated `dirs`
// search path. A per-scan visited set (linear; typical builds visit a
@@ -409,10 +389,9 @@ fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64) i32 = {
return 1;
};
// Byte-wise memcmp returning < 0, 0, > 0. Rule-10 byte-id requires
// cstage and wwstage sort the same way; memcmp is the
// locale-independent total order (mirrors ref/hare/sort/cmp/cmp.ha
// strs).
// Rule-10 byte-id requires cstage and wwstage sort the same way;
// memcmp is the locale-independent total order (mirrors
// ref/hare/sort/cmp/cmp.ha strs).
fn bytecmp(a: *u8, alen: u64, b: *u8, blen: u64) i32 = {
let n: u64 = alen;
if (blen < n) { n = blen; };
@@ -526,8 +505,6 @@ fn enumeratedir(dirpath: *u8) (**u8, i32) = {
return exact.ptr, n;
};
// ---- file slurp -------------------------------------------------------
fn slurp(pathcs: *u8) (*u8, u64) = {
let fd: i32 = os.open(pathstr(pathcs), os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
@@ -566,7 +543,6 @@ fn isidentbyte(c: u8) bool = {
// caller passes a slice of the source: src points at the line start.
fn scanuse(src: *u8, len: u64) (*u8, u64) = {
let i: u64 = 0u64;
// skip leading whitespace
for (i < len) {
if (src[i] != 32u8) { if (src[i] != 9u8) { break; }; };
i += 1u64;
@@ -595,11 +571,6 @@ fn scanuse(src: *u8, len: u64) (*u8, u64) = {
return src + idstart, idlen;
};
// ---- Build pipeline ---------------------------------------------------
// Strip the trailing ".ww" off `src` (a NUL-terminated path) into
// `stem`, NUL-terminated. If there's no .ww, the stem is the whole
// path.
fn makestem(stem: *u8, src: *u8) void = {
let n: u64 = cstrlen(src);
let stop: u64 = n;
@@ -617,7 +588,6 @@ fn makestem(stem: *u8, src: *u8) void = {
stem[stop] = 0u8;
};
// Append a literal suffix to `stem` (which already lives in a buffer).
fn appendlit(stem: *u8, suffix: str) *u8 = {
let buf: []u8 = alloc([], (os.PATH_MAX: u64))!;
buf.len = os.PATH_MAX;
@@ -638,8 +608,6 @@ type lflags = struct {
nlibs: i32,
};
// ---- separate-compilation driver -------------------------------------
//
// Port of cmd/ww/main.c build_one_sep (task #46/c3). The build path
// materializes each imported package's `.wwi` interface and compiles
// every package on its own (`w6c -c`), then flat-links the `.o` set.
@@ -677,7 +645,6 @@ type sepgraph = struct {
n: i32,
};
// Find a package by dotted path, or add it. Returns index, -1 if full.
fn sepfindoradd(g: *sepgraph, path: *u8, entry: *u8, isdir: i32) i32 = {
let i: i32 = 0;
for (i < g.n) {
@@ -1060,8 +1027,6 @@ fn seploadpkg(g: *sepgraph, pi: i32, searchpath: *u8) i32 = {
return 0;
};
// Print one cycle-chain node: a package path, or "(root)" for the
// empty root path.
fn sepcyclenode(p: *u8) void = {
if (p[0] == 0u8) { cerr("(root)"); } else { cerr(pathstr(p)); };
};
@@ -1103,7 +1068,6 @@ fn septopovisit(g: *sepgraph, pi: i32, order: []i32, no: *i32,
return 0;
};
// Mark pi's transitive deps (excluding pi) in inset[].
fn sepmarkdeps(g: *sepgraph, pi: i32, inset: []u8) void = {
let k: i32 = 0;
for (k < g.pkg[pi].ndeps) {
@@ -1306,7 +1270,7 @@ fn archiveo(objpath: *u8, apath: *u8) i32 = {
// reverse-topo `w6l` of the root `.o` + dependency `.a` set + libwwrt.a.
// Side files land in a cold `<stem>.sepwork` scratch dir. Twin of cstage
// build_one_sep.
// ---- -w workdir freshness ----------------------------------------------
// A `-w DIR` workdir is a caller-owned persistent package-artifact tree
// that replaces the fresh `.sepwork` scratch. Staleness is pure content
// identity, never mtime: a package is reused only when its freshly
@@ -1478,7 +1442,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
let a6: *u8 = joinpathlit(selfdir, "w6a_ww");
let l6: *u8 = joinpathlit(selfdir, "w6l_ww");
// Default lib search path: <selfdir>/../../lib
let dotdotlib: []u8 = alloc([], (os.PATH_MAX: u64))!;
dotdotlib.len = os.PATH_MAX;
{
@@ -1487,7 +1450,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
cstrseal(dotdotlib.ptr, off);
};
// Source directory.
let srcd: []u8 = alloc([], (os.PATH_MAX: u64))!;
srcd.len = os.PATH_MAX;
if (entryisdir != 0) {
@@ -1532,7 +1494,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
cstrseal(searchpath.ptr, off);
};
// Stem for the scratch dir.
let stem: []u8 = alloc([], (os.PATH_MAX: u64))!;
stem.len = os.PATH_MAX;
if (entryisdir != 0) {
@@ -1606,7 +1567,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
};
};
// libwwrt.a path: <selfdir>/../lib/libwwrt.a
let libwwrt: []u8 = alloc([], (os.PATH_MAX: u64))!;
libwwrt.len = os.PATH_MAX;
{
@@ -1615,7 +1575,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
cstrseal(libwwrt.ptr, off);
};
// Discover.
let pkgslot: []seppkg = alloc([], SEP_MAXPKG: u64)!;
pkgslot.len = SEP_MAXPKG;
let g: *sepgraph = alloc(sepgraph{pkg = pkgslot, n = 0})!;
@@ -1649,7 +1608,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
};
if (seploadpkg(g, root, searchpath.ptr) < 0) { return 1; };
// Reset colors, reverse-topo.
let ci: i32 = 0;
for (ci < g.n) { g.pkg[ci].color = 0; ci += 1; };
let order: []i32 = alloc([], g.n: u64)!;
@@ -1659,7 +1617,6 @@ fn buildonesepimpl(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
let norder: i32 = 0;
if (septopovisit(g, root, order, &norder, stack, 0) < 0) { return 1; };
// Producer loop — dep-first, one `w6c -c -I` per package.
let oi: i32 = 0;
for (oi < norder) {
let pi: i32 = order[oi];
@@ -1961,22 +1918,10 @@ fn buildonesep(selfdir: *u8, src: *u8, entryisdir: i32, out: *u8,
return r;
};
// ---- Module-by-name resolution ----------------------------------------
//
// Mirrors cmd/ww/main.c:resolvemodule. Maps a name like "foo", "lib/foo",
// "foo.ww", or "." to a concrete .ww file path:
// 1. literal <name>.ww that exists → use as-is
// 2. "." → <cwd>/<basename(cwd)>.ww → that, if it exists
// 3. <name>/<basename(name)>.ww → that, if it exists
// 4. walk search path (cwd:incs:<selfdir>/../../lib):
// <dir>/<name>.ww or <dir>/<name>/<name>.ww
fn cstrendswithlit(p: *u8, lit: str) bool = {
return strings.hassuffix(pathstr(p), lit);
};
// basenameoff — return the offset of the last path segment within `p`
// (i.e. one past the final '/'). Returns 0 if there's no slash.
fn basenameoff(p: *u8, plen: u64) u64 = {
let start: u64 = 0u64;
let i: u64 = 0u64;
@@ -1987,8 +1932,6 @@ fn basenameoff(p: *u8, plen: u64) u64 = {
return start;
};
// arenadupcstr — copy `plen` bytes from `src` into a fresh NUL-sealed
// heap buffer.
fn arenadupcstr(src: *u8, plen: u64) *u8 = {
let buf: []u8 = alloc([], plen + 1u64)!;
let i: u64 = 0u64;
@@ -1997,8 +1940,7 @@ fn arenadupcstr(src: *u8, plen: u64) *u8 = {
return buf.ptr;
};
// buildsearchpath — compose the colon-separated lookup path used by
// resolvemodule's case (4). Order: "." : <incs> : <selfdir>/../../lib
// resolvemodule search-path order: "." : <incs> : <selfdir>/../../lib
fn buildsearchpath(selfdir: *u8, incs: *u8) *u8 = {
let buf: []u8 = alloc([], (os.PATH_MAX: u64) * 2u64)!;
let off: u64 = 0u64;
@@ -2016,13 +1958,11 @@ fn buildsearchpath(selfdir: *u8, incs: *u8) *u8 = {
return buf.ptr;
};
// resolvemodule — map a name like "foo", "lib/foo", "foo.ww", or
// "." to a concrete entry path. Sets *isdir when the entry is a
// Mirrors cmd/ww/main.c:resolvemodule. Sets *isdir when the entry is a
// module directory (caller will dir-enumerate).
fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
let nlen: u64 = cstrlen(name);
// (1) Literal file that exists → use as-is.
if (cstrendswithlit(name, ".ww")) {
if (os.access(pathstr(name), 0i32) == 0) {
*isdir = 0;
@@ -2030,7 +1970,6 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
};
};
// (2) Existing path → use as-is, dir vs file via stat.
let fi: os.filestat;
let sr: (void | os.oserror) = os.stat(&fi, pathstr(name));
let found: bool = false;
@@ -2048,13 +1987,10 @@ fn resolvemodule(selfdir: *u8, name: *u8, incs: *u8, isdir: *i32) *u8 = {
return arenadupcstr(name, nlen);
};
// (3) Search-path lookup with dot-to-slash path translation.
let search: *u8 = buildsearchpath(selfdir, incs);
return locateimport(search, name, nlen, isdir);
};
// ---- Subcommand handlers ----------------------------------------------
fn writeusage(fd: i32) void = {
let s: str = "usage: ww [-V] <subcommand> [args...]\n -V print version and exit\n build [-S] [-w DIR] [-o FILE] [path] compile module; -S stops after package asm\n run [path] ... build then exec, passing extra args to the program\n test [-S -o STEM] [-w DIR] [options] [path] build/run tests; -S emits package asm\n version print version and exit\n\n path forms:\n foo.ww literal file\n foo search cwd, -I dirs, then $WW_LIB-equiv for foo.ww or foo/foo.ww\n lib/foo directory: build lib/foo/foo.ww\n lib/... every package under lib, recursively (test only)\n . build the cwd's <basename>.ww\n";
os.write(fd, s.ptr, s.len: u64);
@@ -2065,8 +2001,6 @@ fn doversion() i32 = {
return 0;
};
// Compute the basename of src (without trailing ".ww") into a fresh
// buffer. Used as the default output path for `ww build`.
fn defaultoutpath(src: *u8) *u8 = {
let n: u64 = cstrlen(src);
let start: u64 = 0u64;
@@ -2084,7 +2018,6 @@ fn defaultoutpath(src: *u8) *u8 = {
off += 1u64;
j += 1u64;
};
// Strip ".ww" if present.
if (off >= 3u64) {
if (out[off - 3u64] == 46u8) {
if (out[off - 2u64] == 119u8) {
@@ -2209,7 +2142,6 @@ fn dobuild(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
};
if (src == nil) {
// default to cwd module
let dot: [2]u8 = ['.': u8, 0u8];
src = &dot[0];
};
@@ -2264,7 +2196,6 @@ fn makedrivertmp(buf: *u8, prefix: str) void = {
pk += 1;
};
let pid: i32 = os.getpid();
// itoa for non-negative pid
let dig: [16]u8;
let n: i32 = 0;
if (pid <= 0) {
@@ -2383,7 +2314,7 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
src = p;
i += 1;
} else {
passstart = i; // remaining args go to the program
passstart = i;
};
};
};
@@ -2426,8 +2357,8 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
return 1;
};
// Execute with [tmp, argv[passstart..argc)). The standard process
// facility inherits stdio and waits only for this user program.
// The standard process facility inherits stdio and waits only for
// this user program.
let nextra: i32 = 0;
if (passstart >= 0) { nextra = argc - passstart; };
let execargv: []str = alloc([], (nextra + 1): u64)!;
@@ -2464,8 +2395,6 @@ fn dorun(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
return rc;
};
// ---- ww test ----------------------------------------------------------
//
// Mirrors cmd/ww/main.c:dotest. Explicit regular files retain the bootstrap
// compatibility route; directory/default requests delegate to wwtest.
@@ -2762,15 +2691,12 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
replacement, targetindex < 0);
};
// ---- Entry -------------------------------------------------------------
export fn main(argc: i32, argv: **u8) i32 = {
if (argc < 1) {
writeusage(2);
return 2;
};
// selfdir = dirname(argv[0])
let selfdir: []u8 = alloc([], (os.PATH_MAX: u64))!;
selfdir.len = os.PATH_MAX;
selfdirinto(selfdir.ptr, (os.PATH_MAX: u64), argv[0]);