Commit Graph

1357 Commits

Author SHA1 Message Date
607cc8d330 cgen: name-based slot reuse, fn-addr ffi_resolve, indirect call
Closes the byte-identity gap between selfhost/cmd/wwc/cgen.ww and
the C cgen, so a ww_ww-built binary matches the cstage-built binary
on the same input. The bootstrap fixed point was already green;
these are the bytes inside that fixed point that diverged from
what cmd/6c emits.

Slot allocation:

- local_add dedups by name (mirror cmd/6c/cgen.c:localoff). Two
  `let cp: pos;` in disjoint if-branches share one slot. The
  stored tnode is refreshed on each hit so a later `let m: *node`
  shadowing an earlier `let m: i32` sees its own type when
  emitting `m.next` — without this the N_DOT cgen fell into the
  SB-symbol fallback and the linker complained about undefined
  `next`.
- scan_locals dedups at frame-size time to keep the prologue SUBQ
  in sync. cgfn seeds c.locals with param-name stubs before the
  scan so a body's `let <param-name>` reuses the param slot, then
  resets c.locals before emission so real offsets get installed.
- local_alloc (no dedup) for N_MCASE bindings: C cgen handles a
  match as an expression with by-value `locals`, so two separate
  matches each get fresh slots for `v`/`e`.

Per-instruction matching:

- `return;` in a void fn zeros AX (C cgen falls through to
  cgexpr_int(c, 0)).
- N_INTLIT prints i64 (signed), not u64. FNV-1a's offset basis
  now prints as `$-3750763034362895579`, matching `$%lld`.
- *p = strexpr push order swapped to PUSH AX / PUSH BX → POP CX
  / POP AX (cgen.c:1033-1041).

Feature port from 635818e (the half that the wwdump corpus
actually exercises):

- N_IDENT used as a value with fn type now LEAQs through
  ffi_resolve, so `let fp = some_ffi_fn;` emits the C symbol.
- N_CALL on a bare ident checks local_find_node first; a local
  fn-pointer dispatches as `cgexpr(callee); CALL AX` instead of
  `CALL ident(SB)`.

Tests 990 probe 6 and 994 are byte-identical on mem/err/tok/smoke
+ the four selfhost main.combined.ww files. Bootstrap still
ww2 == ww3.
2026-05-11 11:19:42 +09:00
78ddc360aa examples/snake: ncurses snake demo
Small interactive demo wired through the new dynamic linker.
Uses libncurses and libc via @symbol bindings; the Makefile
runs the unmodified `ww build` pipeline with -l ncurses -l c
-L /usr/lib. Resulting binary has libncurses.so.6 + libc.so.6
in NEEDED.

Workaround: state struct keeps no array fields and threads the
xs/ys body buffers through main()'s frame, because the current
6c codegen segfaults on `&struct.field`. Once that's fixed the
arrays can move into state and place_food / step / render lose
their *i32 parameters.
2026-05-11 09:42:42 +09:00
ecf0a84127 6l: dynamic linking with symbol versioning
Teach the linker to consume ET_DYN shared objects and emit a
dynamically-linked ELF executable. Snake et al. can now link
against libncurses + libc through the system dynamic loader.

Pipeline additions:

- dyn.c: read ET_DYN, parse .dynsym + DT_SONAME, walk
  .gnu.version_d / .gnu.version to learn each export's default
  version (skip hidden entries).
- pass.c: when an undefined sym is provided by some Lso,
  promote it to dynamic, assign a PLT slot, record the
  matched version on the Lsym.
- dynout.c: emit PT_INTERP + PT_DYNAMIC, .dynsym/.dynstr/.hash,
  .plt + .got.plt + .rela.plt, .gnu.version + .gnu.version_r,
  and the full DT_* set with DT_BIND_NOW. Patch PC32/PLT32
  references against dyn syms to point at their PLT stubs.
- main.c: -L<dir> and -l<name> flag parsing; resolve <name>
  via .so / .so.<N> / .a in libdir order, skipping GNU ld
  linker scripts (libc.so on most distros).
- ww driver: collect -l/-L (joined and split forms) and pass
  through to 6l.

Design choices:

- DT_BIND_NOW so the loader resolves all PLT slots at startup;
  no PLT0 lazy resolver stub.
- SysV .hash, not .gnu.hash. One bucket; loader scans the
  chain. Slow at scale, fine for snake-class binaries.
- Non-PIE at fixed 0x400000.
- No section headers — loader uses program headers, but
  readelf -V/-S won't display anything.

Symbol versioning is the only correctness item beyond the
basic PLT/GOT machinery: glibc symbols default to versions
later than GLIBC_2.2.5 (e.g. clock_gettime → GLIBC_2.17 for
the vDSO impl), and the loader rejects unversioned references
to those without a matching Vernaux entry.

test/wwc/810_dyn covers four cases: bare libc dyn call,
multi-PLT, clock_gettime versioning, and fn-pointer to FFI
binding (which exercises the codegen fixes from the parent
commit alongside the new linker path).
2026-05-11 09:42:27 +09:00
635818eb13 6c: float compare, fn-address @symbol, indirect call
Three N_BIN/N_IDENT/N_CALL sites missed cases that bit ncurses
demos and dyn-linker exercises:

- Float compare emitted CMPQ + signed Jcc on f64 bits. Now goes
  through UCOMISD/UCOMISS + the unsigned Jcc family, mirroring
  Plan 9 6c (txt.c around AUCOMISD).
- LEAQ-of-fn-ident (taking the address of a function as a value)
  used n->str without ffi_resolve, so binding a *fn from an
  @symbol("name") fn declaration produced a reference to the
  ww ident rather than the C symbol.
- N_CALL on a bare ident with no localfind hit was a direct
  CALL ident(SB). Now checks localfind first and indirects
  through AX when the callee names a local fn-pointer slot.

Hare/QBE handles both shapes the same way (sort/search.ha:15
calls cmp(...) where cmp is a *cmpfunc parameter).
2026-05-11 09:42:02 +09:00
cb57cd795d build: split cstage/wwstage targets, document bootstrap
Phase 10 step 8 (delete the C trees) is deferred to v1.0 — until the
compiler stops churning we keep Cstage as the fresh-checkout entry
point. Split the Makefile so the two stages are named, and add
BOOTSTRAP.md describing the cstage → ww1 → ww2 → ww3 fixed-point
flow. PLAN.md gets a status note pointing at it.
2026-05-11 02:36:46 +09:00
1657bdeda3 ww: import toolchain — C bootstrap + ww-side self-host (phases 0-10)
C bootstrap (phases 0-9):
  cmd/wwc, cmd/6c, cmd/6a, cmd/6l, cmd/ww, rt, lib/*.

ww-side self-host (phase 10):
  selfhost/cmd/wwc — ww-cgen frontend; bootstrap fixed point.
  selfhost/cmd/6a  — assembler; byte-identical to C 6a (test 991).
  selfhost/cmd/6l  — linker w/ archive (.a) support; byte-identical
                     to C 6l (test 992).
  selfhost/cmd/ww  — driver (build/run/version); byte-identical to
                     C ww (test 993).

make test: 15/15. make bootstrap: ww2.s == ww3.s, ww2.o == ww3.o,
ww2 == ww3 byte-identical, with the full ww-tooled chain.
2026-05-11 02:17:47 +09:00
4c8fc59ca1 first commit 2026-05-10 01:24:58 +09:00