Commit Graph

29 Commits

Author SHA1 Message Date
47d75d9b59 w6c+selfhost: widen concrete variant to tagged-union call arg
Tagged-union widening already fired for `let r: (str|rune) = "...";`,
`r = "...";`, and `return "..."` from a tagged-returning fn — but not
at call sites, so `fn f(x: (str|rune))` couldn't be called with a bare
str or rune. The arg was pushed as its own static type (2 words for
str, 1 for rune) while the callee's slot expected 3 (tag + payload).

C cgen: at the call boundary, look up the callee's declared param
type per arg. When the param is TY_TAGGED and the arg is a concrete
variant, materialise (tag, value-words, padding) sized to the param's
tagged_arg_size — then the existing pop-into-arg-regs logic picks it
up. Nullable `(*T | void)` collapses to a single 8B push.

selfhost: fnret now carries the params head alongside rtype (amalloc
bumped to 48); pushargsrev takes the matching param node and runs the
same widening sequence per arg. The pop drain in cgcall already
handled extra slot words, so no change needed on that side.

Verified with a smoke covering str/rune literals, typed locals,
pre-existing tagged-local pass-through, and nullable widening from a
raw pointer. Selfhost emits byte-identical asm to C cgen on the test.
2026-05-13 04:44:03 +09:00
be8a662f15 lib/strconv: graduate to owned-str returns with Hare-shape base param
i64tos / u64tos / f64tos return a fresh owned str (caller frees via
os.free) instead of writing into a caller-supplied [N]u8. Adds typed
variants (i32tos / i16tos / i8tos and u32 / u16 / u8) and the missing
base parameter on stoi64 / stou64 + typed parse wrappers.

Base values are exported as plain-i32 `def`s (strconv.DEC,
strconv.HEX_UPPER, ...) rather than a `base` enum: cross-module
`strconv.base.DEC` chains miscompile in the cstage cgen — it emits a
memory load through `base(SB)` rather than inlining the constant.
The Sdef path resolves correctly, so callers say `strconv.DEC` and
both cgens lower to an immediate.

Also renames strings.byteindex / rbyteindex to strings.indexbyte /
rindexbyte, matching bytes.indexbyte and reserving the Hare name
`byteindex` for the future `(str | rune)`-needle shape.

fmt drops printint / printlnint / fprintint — those were stand-ins
for variadic `fmt::println(42)`; with the owned-str graduation the
substitute is one call: `fmt.println(strconv.i64tos(42, strconv.DEC))`.

strerror is sketched in a comment but not shipped — match arms over
the wider `error = !(invalid | overflow)` union still expose a
cstage-vs-wwstage spill divergence.
2026-05-13 03:55:16 +09:00
7c75dd218a selfhost: fix several wwstage cgen miscompilations
Surfaced via examples/lisp, which had to work around the following in
source. Each lowering now matches cstage on the same shape.

- cgassign / cgdot: two-level field through a non-pointer sub-struct.
  `(*L).cur.kind = k` (cur a struct-by-value field of L) silently
  dropped the store; the corresponding read fell into the SB-symbol
  fallback and the linker reported `undefined reference to kind`. The
  two new branches resolve outer-field offset + inner-field offset
  and emit a single direct store/load at the combined slot, both for
  T-by-value and *T-base shapes.

- cgdot: `xs[i].field` chains the trailing field load through the
  N_INDEX result for [N]T / []T / *T element-of-struct-ptr. The
  cgforrange loop variable now carries the elem tnode so the same
  fast path covers `for (let x .. xs) { x.field }`.

- cgindex / cgassign: top-level `[N]T` array and `*T` pointer used
  as an index base. cgindex now emits LEAQ name(SB) (array) or
  MOVQ name(SB) (pointer) with the correct element scaling; without
  this the fallback emitted neither base and walked off the saved
  BP slot. Adds letvartnode() helper, an N_TARRAY branch to
  letemitsize so the array shows up in c.lets, and an N_TARRAY
  initialiser path in emitletdataw that lays the literal bytes into
  DATAW.

- cglet / scanlocals: infer the local's tnode for an unannotated
  `let x = f()` / `let x = f()?`. inferletcalltype() reads the
  callee's declared return; `?` and `!` strip to the success variant
  so a tagged-union let allocates the full 24B slot and the
  struct-field dispatch in cgdot/cgassign sees the right type.
  letslotsize now defers to slotsize on the inferred type.

- slotsize: follow type aliases for tagged-union variants. With
  `type parserr = !str;`, the variant slot was 8B instead of the
  required 16B; the tagged let stomped on the next slot at the
  AX/DX/CX spill.

- cgreturn: tagged-union return forwarding. `return f();` where f
  also returns a tagged union now passes the (tag, payload1,
  payload2) triple through unchanged instead of re-wrapping it.

- cgreturn / cglet / taggedvariantindex: dispatch by variant name
  with module-qualified-vs-bare matching, and recognise N_STRUCTLIT
  as the variant tag for `return eof{};`. cgexpr default emits
  `MOVQ $0, AX` so the surrounding return shuffle isn't left with
  a stale AX.

- isstrtype / nodeisstr: resolve through `!T` aliases. `parserr =
  !str` was not propagating the str-shape to the rhs check and the
  MOVQ BX,CX shuffle was being dropped from str-typed local
  returns.

- exprfloatkind: recognise `p.field` as f64/f32 when the struct
  field is so declared, so `v.fval: i64` lowers to CVTTSD2SI on X0.

- cgassign: str field on a direct struct local writes both halves.
  `L.src = s;` previously dropped s.len.

- cgcall: pop into the int reg window only up to 6 (DI..R9); rest
  stays on the stack and the caller emits ADDQ to clean up.
  cgfnparams accepts >6-arg signatures by registering the overflow
  params at positive BP offsets (16+8*k(BP)), no spill instruction
  emitted.

All 26 harness tests pass; bootstrap reaches a byte-stable fixed
point at ww3 == ww4.
2026-05-13 03:06:46 +09:00
f67c07cbae selfhost: port switch — N_SWITCH parser + cgen + scratch slot 2026-05-12 15:10:00 +09:00
5155ba55f3 selfhost: port float lex + expression cgen — feature parity with C
Lexer: `lexnum` now parses the digit/exponent tail into an f64 via a
new `parsef64` (decimal-only, integer-arith driver + pow-10 multiply,
no strtod). The IEEE bits are also stashed in tok.uval via pointer
reinterpret so cgen consumers stay integer-only.

Parser: TK_FLOAT → N_FLOATLIT, carrying both fval and uval. Parser
state grows curfval to plumb the lexer's f64 through refill.

cgen:
  - cgfloatlit reads n.uval and materialises X0 via the standard
    MOVQ-PUSHQ-MOVSD-ADDQ trampoline.
  - cglet, cgident, cgassign learn float-typed branches: MOVSS/MOVSD
    for locals; LEAQ-indirect MOVSS/MOVSD for globals.
  - cgbin handles ADDSD/SUBSD/MULSD/DIVSD (+ SS variants) and
    UCOMISD/UCOMISS-based comparisons. cgun handles float negate
    via the `0 - X0` shape C cgen uses.
  - cgcast routes int↔float and f32↔f64 through CVTSI2SD/CVTTSD2SI/
    CVTSD2SS/CVTSS2SD and their SS twins.
  - cgcall + pushargsrev push float args via SUBQ+MOVSD and pop into
    the X0..X7 stream, tracked by a per-class counter alongside the
    int DI..R9 stream. cgfnparams loads float params from the same
    stream.
  - emitletdataw bakes FLOATLIT init bits into DATAW (4B for f32,
    8B for f64).

Tests: smoke programs (literal init, reassign, arithmetic, fn args/
returns, casts) produce byte-identical asm through `w6c` and
`wwdump_ww -c`, and the resulting binary exits with the same value
whether compiled by the C or wwstage toolchain. Full `make test` is
26/26 and `make bootstrap` still reaches its byte-identical
ww2==ww3==ww4 fixed point.
2026-05-12 14:21:50 +09:00
6f04713601 w6c+selfhost: float globals — DATAW + LEAQ-indirect MOVSS/MOVSD
f32 → 4B slot, f64 → 8B. C cgen bakes the FLOATLIT bit pattern into
DATAW directly; selfhost emits zero-init only (its parser doesn't
lex N_FLOATLIT yet). Read/write goes LEAQ name(SB),CX + MOVSS/MOVSD
since w6a has no D_EXTERN operand form for SSE moves.
2026-05-12 13:23:45 +09:00
1ac9980f7e selfhost: mirror writable .data + R_X86_64_64 across the wwstage
Bring the wwstage toolchain to parity with C-side DATAW / DATAR /
.data / .rela.data support. With this, w6c_ww + w6a_ww + w6l_ww can
compile, assemble and link `let g: str = "lit";` (and the scalar /
str / slice / struct globals that landed earlier) end-to-end, with
output that's byte-identical to the C-side pipeline.

w6a (types.ww / parse.ww / asm.ww / obj.ww):
  - A_DATAW + A_DATAR opcodes; parser learns `name+disp(SB)`;
    A_DATAR records an R_X86_64_64 reloc in .data via the new
    addrelocdata helper; areloc gains a `section` flag and asym
    an `isdata` flag; obj.ww splits relocs into .rela.text /
    .rela.data, emits .data PROGBITS + .rela.data conditionally,
    and shuffles section indices the same way cmd/w6a/obj.c does
    so byte output stays identical when no DATAW/DATAR are used.

w6l (sym.ww / obj.ww / pass.ww / out.ww / dynout.ww / main.ww):
  - lrel grows `section`; lsym grows `indata`; lobj tracks
    dataoff / datasize; lnk grows combined .data buffer;
  - obj.ww loads .data and .rela.data, registers data symbols
    with indata=1 and val shifted by the input's data_off, and
    the archive scanner includes both .text and .data globals;
  - pass.ww adds R_X86_64_64 (patch 8 bytes in .text or .data
    with sym_va + addend); relocate's signature becomes
    (textva, datava);
  - out.ww emits a second PT_LOAD (R+W) when datalen > 0, with
    .data at the page-aligned offset after .text;
  - dynout.ww refuses .data + -l/-L cleanly (matches the C-side
    error message);
  - main.ww computes text_va / data_va and passes both to
    relocate.

wcc cgen (cgen.ww / cgendecl.ww):
  - letpreintern walks top-level str-lets and interns the strlit
    BEFORE emitdatasection emits its DATA row, so emitletdataw
    can later look up the same label;
  - emitletdataw's 16B branch detects non-empty strlit init and
    emits the 8-zero + 8-LE-len DATAW plus a DATAR slot+0,strlit
    reloc, mirroring cmd/w6c/cgen.c.

Verified: `wwdump_ww -c` byte-matches `w6c` on a `let g: str =
"hello world\n";` fixture; `w6a_ww` and `w6l_ww` produce a
binary byte-identical to the C-side pipeline that runs and
prints "hello world". Bootstrap fixed-point holds (ww2 == ww3 ==
ww4), 26/26 tests green.
2026-05-12 13:03:03 +09:00
00d1120441 w6c+selfhost: struct globals — zero-init DATAW + LEAQ-based field access
Extend top-level mutable `let` to cover structs. Same approach as
str / slice: take the field-access base through &name(SB) instead
of off(BP).

  - emit_lets / emitletdataw: emit `sizeof(T)` zero bytes for any
    struct global without a baked-in initialiser. Struct-literal
    init is skipped → undefined symbol at link if used;
  - cgdot read path: when the IDENT base's local lookup misses and
    the name is a struct let, LEAQ name(SB), CX and load the field
    at fi.foff(CX) with the width-aware op (MOVQ / MOVL /
    MOVZBQ / MOVSXD; MOVQ pair for str fields);
  - cgassign write path: parallel handling for plain `=` (incl. str
    fields) and the compound ops (+=, -=) via load → push → eval
    rhs → combine → store with a re-LEAQ between cgexpr clobbers.

Tagged-union fields on struct globals are unsupported in v1 — the
local path's tagged branch isn't generalised yet. Whole-struct
by-value flow through expressions remains NYI (matches the local
status). 630_let_global gains 3 fixtures (read/write, compound +=,
narrow u8 field); selfhost mirror keeps test 990 / 994 / 995 byte
identical; bootstrap fixed point holds.
2026-05-12 12:26:21 +09:00
97eb1fe20d 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.
2026-05-12 12:16:52 +09:00
208bdd25df 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.
2026-05-12 12:10:23 +09:00
3c812faa08 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.
2026-05-12 11:56:51 +09:00
922877309b ww+wcc: Hare-strict enum types — back out the int↔enum relaxation
Cascades the four enum kinds through every signature and local that
holds one of their values, then removes the type_assignable /
unify_arith relaxation that previously let bare i32 mix with the
named enum types.

Signature updates:
  - kwlookup() now returns `tkind` (not i32); tokname() takes `tkind`
  - accepttok / expecttok / bprec / isassignop take `tkind`
  - parsearglist's closekind is `tkind`
  - newtype / prim take `tykind`; scopedefine takes `skind`
  - newnode / nkname take `nkind`

Struct fields:
  - tok.kind is `tkind`; parser.curkind is `tkind`
  - node.kind is `nkind`; node.op is `tkind`
  - tinfo.kind is `tykind`; sym.skind is `skind`

Locals holding kinds across lex/parse/check/cgen are now typed with
their enum, including sentinel patterns like `let lkind: nkind =
nkind.N_NONE; if (...) lkind = tn.kind;`.

The selfhost cgen had a load-width bug exposed by this: fieldsize()
fell back to 8 bytes for any TNAME that wasn't a struct or primitive.
For a tkind-typed field that gave `MOVQ (BX), AX` instead of `MOVL`,
diverging from the C cgen on tok.kind / parser.curkind / etc. Two
fixes:
  - fieldsize now consults the enum registry and returns the storage
    type's size (4 for `enum i32`)
  - collectenums runs before collectstructs in cgfile so the registry
    is populated when registerstruct asks for field sizes

All 22 tests stay green; 990/993/995 byte-identity probes pass with
the strict typing in place.
2026-05-12 05:04:33 +09:00
3affe01705 selfhost: graduate N_* defs to nkind enum 2026-05-12 04:54:23 +09:00
408ea2a322 ww+wcc: graduate selfhost TK_* defs to tkind enum
`type tkind = enum i32 { TK_NONE = 0, TK_EOF = 1, ... TK_LAST = 86 }`
replaces the 87-line `def TK_*: i32 = N` cluster in lib/ww/lex/tok.ww.
Numeric values explicit so 990_selfhost's byte-diff against the C-side
`Tkind` enum still passes.

All ~270 reference sites in lib/ww and selfhost/cmd/{wcc,wwdump}
sed-renamed `TK_X` → `tkind.TK_X`. Struct fields (`tok.kind`,
`parser.curkind`) intentionally kept as `i32` — making them `tkind`
shifted some byte-positions in the cgen output and broke 990/993/995
byte-identity probes without an obvious win.

To make the rename non-cascading on every signature, type_assignable
and unify_arith in cmd/wcc/check+type relax to allow enum ↔ int
mixing when storage matches (a `tkind` value flows into an `i32`
slot and vice versa, no explicit cast). This deviates from Hare's
strict enum semantics; doc'd as an explicit pragmatic relaxation
for the compiler's internal enum-shaped kinds. External user code
can still get the type-safety benefit if they declare their
parameters with the enum type.

combined.ww files regenerated by ww build.
2026-05-12 04:50:36 +09:00
5149d10618 wcc+selfhost: pkg-qualified enum access (os.whence.CUR)
Driver-side concatenation flattens module names, but enum member
lookup keyed off the exact lhs ident — so `whence.CUR` worked while
`os.whence.CUR` fell through to w6l with `undefined main.whence`.

C side: fold TY_ENUM members in the post-cexpr cascade too, not
just the early SK_TYPE shortcut. The recursive cexpr lands the
inner N_DOT(os, whence) on the named enum type; the outer access
then folds normally.

Selfhost: cgdot now treats `N_IDENT.MEMBER` and `N_DOT.MEMBER` the
same way, keying off the leaf name. enumlookup strips a trailing
`.`-prefix from the lookup key.

Adds e2e test 700: `use os; os.whence.CUR as i32 == 1`.
2026-05-12 04:30:20 +09:00
f597ce67f6 selfhost: cgen for enum (member fold + as pass-through)
w6c_ww now compiles enum end-to-end and emits byte-identical
asm to the C w6c on the new 994 corpus case (`type mode = enum u8
{ R, W, RW = R | W }; main() { return (mode.RW): i32 }`). Mechanism
mirrors the C side:

- collectenums walks every `type X = enum {...}` at file scope and
  pre-resolves each member's u64 value (auto-increment from prior,
  sibling-ref folding for `RDWR = READ | WRITE`).
- cgdot recognises `EnumName.MEMBER` before the local lookup and
  emits MOVQ $value, AX directly.
- cgtypeassert short-circuits when either side is enum: cgexpr on
  the LHS lands the value in AX with the right integer width; no
  tag/unwrap.

main.combined.ww (wwdump/ + w6c/) regenerated by ww build.
2026-05-12 04:25:28 +09:00
404705b6fd selfhost: mirror defer; e2e tests for LIFO ordering
C cgen has carried defer for a while (defers[] global + reverse
walk on every return). Selfhost cgen now mirrors:

- cgen struct: deferbuf (**node, LIFO stack) + defertop counter.
- cgstmt N_DEFER: push n.lhs.
- cgreturn: rundefers() at entry — same as the C cgen pattern.
- cgfn fall-through return: rundefers() before zero-AX+RET.

DEFER_MAX = 16 matches C cgen.

Two new e2e rows: defer with an explicit `return acc;` (321 mod 256
= 65), and defer firing on an implicit void-fn fall-through (87).
Both rows verified via the wwstage cgen too.

Defer's semantics: queued exprs fire LIFO before the return expr
is evaluated, so a return that reads memory mutated by a deferred
call sees the post-defer state. Matches C cgen and Hare.
2026-05-12 03:11:13 +09:00
f267f99a2b wcc: match-as-expression with yield
`match (e) { ... }` can now sit in expression position, with each
arm using `yield expr;` to produce the match's value:

    let v = match (r) {
    case let n: i32 => yield n + 1;
    case let s: str => yield s.len: i32 + 100;
    };

TK_YIELD keyword + N_YIELD AST node, both appended at the tail of
their enums to keep prior numeric values byte-stable for the
wwdump-diff gates.

Checker: cexpr for N_MATCH walks each arm's body looking for the
first N_YIELD; the match's type is the unified yield type (or
ty_void if no yield, preserving the statement-form semantics).
Mismatched arm yields are flagged.

Cgen: a yield-target stack (separate from the loop break stack)
holds each enclosing match's end label. N_YIELD evaluates its
expression into AX (and BX for str) and JMPs to the topmost entry.
cgmatch pushes its end label on entry and pops on exit.

Selfhost mirror: lib/ww/lex/tok.ww kwtab+name, lib/ww/ast.ww
N_YIELD def+print, lib/ww/parse/stmt.ww yield-stmt; selfhost cgen
adds a yieldbuf to the cgen struct and a cgyield helper. Verified
end-to-end: a yield-using program compiled via the wwstage cgen
matches the C-cgen build's exit code.
2026-05-12 03:08:00 +09:00
1ac1d985f6 lib: rename stdlib surface to Hare names; add endian/math
Sweeping rename so the lib/ surface mirrors Hare's stdlib spellings.
- ascii: rune-taking predicates; ishex -> isxdigit
- bufio: rinit -> init; take1/takeline -> readbyte/readline
- bytes: indexsub -> index
- encoding/utf8: runelen -> runesz
- errors: eEOF/eShortRead/... -> eof/underread/...
- fmt: errln -> errorln; println/fprintln return i64
- os: readfull/writefull -> readall/writeall; unlink -> remove
- path: isabs -> abs; drop lastindex (now strings.rbyteindex)
- strconv: u64toa/i64toa -> u64tos/i64tos; parse64/parseu64 -> stoi64/stou64
- strings: drop len/isempty; equal -> compare; indexbyte -> byteindex; +rbyteindex
- types: drop numeric helpers (moved to math)
- new lib/endian (htonu16/ntohu16), lib/math (absi32/absi64)
- net: drop htons (use endian.htonu16)

Callers in selfhost/, lib/ww/, cmd/w6c/cgen.c, and test/wcc/700_e2e.c
updated to match.
2026-05-12 00:45:18 +09:00
7cefedb634 wcc/cgen: handle def-str field access (.ptr/.len) on Sdef ident 2026-05-11 22:35:05 +09:00
97ca76d2bb selfhost: drop snake_case locals in dyn/dynout/obj + w6a + cgen + ww driver 2026-05-11 16:33:02 +09:00
1e7be36577 wcc/cgen: drop underscore from cgen_*.ww filenames 2026-05-11 16:19:46 +09:00
ebcc8f2d09 wcc/cgen: lift helpers→cgen_util, fn/file→cgen_decl 2026-05-11 15:39:39 +09:00
328bcd743c wcc/cgen: split cgexpr→cgen_expr.ww, cgstmt→cgen_stmt.ww (rob pike #5) 2026-05-11 15:35:16 +09:00
e301a198f4 wcc: c-side mangles private decls; main exempt as entry-point convention 2026-05-11 15:29:29 +09:00
895f221b6c wcc: mangle private decls as <module>.<name> via // MODULE: marker 2026-05-11 15:16:22 +09:00
7ed6b39744 selfhost/cmd/wcc/typ: drop ty_ prefix underscore on tctx primitives 2026-05-11 14:38:20 +09:00
6204c4cd27 selfhost/cmd/wcc: drop snake_case from 119 cross-cutting identifiers 2026-05-11 14:37:45 +09:00
2c33228b7e ww: rename toolchain to w-prefix + hare-style build/run/test driver
Plan 9-style w-prefix on the per-arch tools, disambiguating from the
real Plan 9 6c/6a/6l in ref/plan9front/:

    cmd/wwc/      → cmd/wcc/        libwwc.a → libwcc.a
    cmd/6{c,a,l}  → cmd/w6{c,a,l}   binary names too
    test/wwc/     → test/wcc/       6 test files w/ w6 prefix
    selfhost/cmd  mirror in lockstep
    bootstrap/amd64/{w6c,w6a,w6l}   snapshot binaries (gitignored)
    WW_6{C,A,L}   → WW_W6{C,A,L}    env-var overrides

Plan 9 source-tree refs ("Plan 9 6c shape", ref/plan9front/, etc.)
preserved. Hare-style driver, both C and ww sides:

    ww test [path]   discover *_test.ww in a directory module, run
                     each; single-file mode for `ww test foo.ww`
    Module-by-name   `ww build foo` resolves to foo.ww or foo/foo.ww
                     via search path (cwd : -I dirs : $WW_LIB)
    Default-to-cwd   `ww build` / `ww test` build the cwd module
    Run pass-through `ww run path arg1 arg2` reaches the program

lib/os: getcwd (79) and getdents64 (217) syscalls power `.` resolution
and directory enumeration on the ww side.

Makefile: wwstage tool deps now include lib/os/os.ww (+ lib/strconv
for wwdump_ww) so lib/* edits force their rebuild instead of leaving
stale binaries — surfaced when test 995 first failed against a stale
w6c_ww built before the lib/os additions.

Test 993 byte-identical parity gate (C-side ww vs ww-side ww_ww on a
build corpus) stays green; all 19 tests pass.
2026-05-11 13:49:27 +09:00