diff --git a/.gitignore b/.gitignore index 0e1ce59a..762b2dd4 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,5 @@ examples/snake/snake # committing them is the v1.0 lock per PLAN.md (the new trust # surface). `make bootstrap-snapshot` populates the dir from the # current wwstage; `make nocc` verifies they self-reproduce. -# To ship, `git add -f bootstrap/amd64/{ww,6c,6a,6l}` explicitly. +# To ship, `git add -f bootstrap/amd64/{ww,w6c,w6a,w6l}` explicitly. bootstrap/*/* diff --git a/BOOTSTRAP.md b/BOOTSTRAP.md index 1ebdc4bb..36ffc43e 100644 --- a/BOOTSTRAP.md +++ b/BOOTSTRAP.md @@ -8,11 +8,11 @@ under `selfhost/cmd/` and reach a byte-identical fixed point under come from somewhere. Today, that "somewhere" is a small C bootstrap toolchain — the **Cstage**: - cmd/wwc/ frontend library (lex, parse, check) → libwwc.a - cmd/6c/ amd64 compiler .ww → .s - cmd/6a/ amd64 assembler .s → .o - cmd/6l/ amd64 linker .o → static ELF - cmd/ww/ user-facing driver (orchestrates 6c → 6a → 6l) + cmd/wcc/ frontend library (lex, parse, check) → libwcc.a + cmd/w6c/ amd64 compiler .ww → .s + cmd/w6a/ amd64 assembler .s → .o + cmd/w6l/ amd64 linker .o → static ELF + cmd/ww/ user-facing driver (orchestrates w6c → w6a → w6l) Cstage is built with `cc`. Once it exists, it compiles the ww-rewritten tools (the **wwstage**) under `selfhost/cmd/`, which then @@ -37,7 +37,7 @@ involved. ## Make targets make builds cstage + wwstage + libs - make cstage Cstage only (cc → ww, 6c, 6a, 6l, wwdump, libs) + make cstage Cstage only (cc → ww, w6c, w6a, w6l, wwdump, libs) make wwstage wwstage only (assumes cstage) make bootstrap three-stage build; gates on cmp ww2 == ww3 make test runs all tests, including 990–995 (ww-side @@ -57,12 +57,12 @@ Without cc: ## Status Phase 10 of `PLAN.md` is at its first exit criterion: the bootstrap is -green and `selfhost/cmd/{wwc,6c,6a,6l,ww}` are byte-identical to their +green and `selfhost/cmd/{wcc,w6c,w6a,w6l,ww}` are byte-identical to their Cstage counterparts. Test 995 pins the stronger property — the -wwstage rebuilds every one of its own tools through `ww_ww + 6c_ww + -6a_ww + 6l_ww`, byte-for-byte. Test 996 pins the equivalent for the -dynamic linker: `6l_ww` with `-L`/`-l` produces a byte-identical -PT_INTERP+PT_DYNAMIC binary to C-side `6l` on snake. +wwstage rebuilds every one of its own tools through `ww_ww + w6c_ww + +w6a_ww + w6l_ww`, byte-for-byte. Test 996 pins the equivalent for the +dynamic linker: `w6l_ww` with `-L`/`-l` produces a byte-identical +PT_INTERP+PT_DYNAMIC binary to C-side `w6l` on snake. `make nocc` is wired and verifies stage-0 self-reproduction locally; the stage-0 binaries under `bootstrap/$(ARCH)/` are gitignored until @@ -79,12 +79,12 @@ The pieces are already wired; only the binary commit is held back. When the compiler stabilises: 1. On each supported arch, run `make wwstage && make bootstrap-snapshot` - to populate `bootstrap//{ww,6c,6a,6l}` from the current + to populate `bootstrap//{ww,w6c,w6a,w6l}` from the current wwstage. Verify with `make nocc`. -2. `git add -f bootstrap//{ww,6c,6a,6l}` — explicit because +2. `git add -f bootstrap//{ww,w6c,w6a,w6l}` — explicit because the dir is gitignored. The binary SHAs become the new trust surface. -3. Delete `cmd/wwc/`, `cmd/6c/`, `cmd/6a/`, `cmd/6l/`, `cmd/ww/`. +3. Delete `cmd/wcc/`, `cmd/w6c/`, `cmd/w6a/`, `cmd/w6l/`, `cmd/ww/`. Drop `CC`/`AR` C-tool dependencies from the top-level Makefile. 4. Move `selfhost/cmd/*` to `cmd/*` and `make nocc` becomes the default `make`. diff --git a/CLAUDE.md b/CLAUDE.md index 256a0593..19dbfa3e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,17 +13,17 @@ This file is the contract for the work. Read PLAN.md for the schedule. - Toolchain: Plan 9-organized. One library + one binary per role per target architecture. Plan 9 uses a single digit per arch (8=386, 6=amd64, 5=arm, 7=arm64, 9=power); we adopt that. - - `cmd/wwc/` — frontend library (lex, parse, check). Builds - `libwwc.a`. Not a binary. - - `cmd/6c/` — amd64 compiler. Reads `.ww`, writes `.s` + - `cmd/wcc/` — frontend library (lex, parse, check). Builds + `libwcc.a`. Not a binary. + - `cmd/w6c/` — amd64 compiler. Reads `.ww`, writes `.s` (Plan 9 amd64 asm). - - `cmd/6a/` — amd64 assembler. Reads `.s`, writes `.o` + - `cmd/w6a/` — amd64 assembler. Reads `.s`, writes `.o` (ELF, for C interop). - - `cmd/6l/` — amd64 linker. Reads `.o` and `.a`, writes a + - `cmd/w6l/` — amd64 linker. Reads `.o` and `.a`, writes a static ELF binary. - `cmd/ww/` — user-facing driver (Hare's `hare(1)` / - Plan 9's `cc(1)` analogue). Orchestrates `6c → 6a → 6l`. - Adding a new target later means a new triple (e.g. `7c`/`7a`/`7l` + Plan 9's `cc(1)` analogue). Orchestrates `w6c → w6a → w6l`. + Adding a new target later means a new triple (e.g. `w7c`/`w7a`/`w7l` for arm64). The frontend library `wwc` is shared. ## Hard rules (do not violate) @@ -48,12 +48,12 @@ This file is the contract for the work. Read PLAN.md for the schedule. `extern "c"` marker is needed. 6. **Plan 9 toolchain. No LLVM, no QBE, no external IR.** We do not invent a portable SSA IR. We follow Plan 9: the per-target - compiler (`6c` for amd64) reads `.ww` and writes Plan 9-style - target assembly (`.s`); the per-target assembler (`6a`) writes - ELF objects; the per-target linker (`6l`) produces a static + compiler (`w6c` for amd64) reads `.ww` and writes Plan 9-style + target assembly (`.s`); the per-target assembler (`w6a`) writes + ELF objects; the per-target linker (`w6l`) produces a static binary. Each tool uses Plan 9 cc's in-memory `Prog`/`Adr` - shapes — read `ref/plan9front/sys/src/cmd/cc/`, `cmd/6c/`, - `cmd/6a/`, `cmd/6l/` before writing your own. + shapes — read `ref/plan9front/sys/src/cmd/cc/`, `cmd/w6c/`, + `cmd/w6a/`, `cmd/w6l/` before writing your own. 7. **No generics, no interfaces, no closures, no lambdas.** Four forms of bloat we refuse. Polymorphism, when genuinely needed, is a struct of function pointers plus a `ctx: *void` (Plan 9 `Bio`, @@ -74,7 +74,7 @@ This file is the contract for the work. Read PLAN.md for the schedule. 8. **Tests run after every change.** `make test` is the truth. A change without a green `make test` is not a change. 9. **Prototype in C, then self-host.** The C bootstrap toolchain - (`libwwc`, `6c`, `6a`, `6l`, `ww`) is throwaway scaffolding. + (`libwcc`, `w6c`, `w6a`, `w6l`, `ww`) is throwaway scaffolding. Its job is to compile enough of `ww` to compile the ww reimplementations of itself. Do not over-engineer the C side. @@ -300,7 +300,7 @@ ergonomics live in a sibling pure-`ww` package. POSIX `make`, no autotools, no cmake. ``` -make # builds libwwc, 6c, 6a, 6l, ww, stdlib +make # builds libwcc, w6c, w6a, w6l, ww, stdlib make test # runs all tests (toolchain + stdlib) make install # installs to $PREFIX (default /usr/local) make clean @@ -312,11 +312,11 @@ Target layout under `out/`: out/ bin/ ww user-facing driver - 6c amd64 compiler - 6a amd64 assembler - 6l amd64 linker + w6c amd64 compiler + w6a amd64 assembler + w6l amd64 linker lib/ - libwwc.a frontend library (linked into 6c) + libwcc.a frontend library (linked into w6c) libwwrt.a runtime archive (linked into final binaries) .a precompiled stdlib modules obj/... intermediate .s, .o per package @@ -329,7 +329,7 @@ ELF. Dynamic is `ww build -shared` or per-library `-l`. Three tiers, all driven by `make test`: -1. **Compiler unit tests** (`test/wwc/`): C, table-driven. Lex, parse, +1. **Compiler unit tests** (`test/wcc/`): C, table-driven. Lex, parse, typecheck, IR-gen, codegen each have their own table. 2. **Language tests** (`test/lang/*.ww`): each file is a single ww program with a comment header declaring expected exit code and @@ -355,7 +355,7 @@ Every change must: Applied to this project: -- `6c` is a single-pass-ish recursive-descent parser into a typed +- `w6c` is a single-pass-ish recursive-descent parser into a typed AST, walked into a `Prog` list, then printed as text. No parser generator, no LLVM, no SSA pass pipeline. - Optimizer is intentionally absent at first. Constant fold + dead @@ -370,7 +370,7 @@ Applied to this project: - `ref/hare/` — the Hare distribution. Read for syntax, FFI (`@symbol`), stdlib layout, type names, error idioms. - `ref/plan9front/` — 9front. Read `sys/src/cmd/cc/` (the shared - frontend library), `sys/src/cmd/8c/` and `sys/src/cmd/6c/` (per- + frontend library), `sys/src/cmd/8c/` and `sys/src/cmd/w6c/` (per- target compilers), `sys/src/cmd/8a/` (assembler), `sys/src/cmd/8l/` (linker) for toolchain organization, `Prog`/`Adr` data shapes, mkfile style, and naming. @@ -404,7 +404,7 @@ When making changes: writing code. - Keep diffs small. One concern per change. - Commits: Plan 9 style. Subject is one short lowercase line, - prefixed with the affected area: `6c: fix const fold for i64`, + prefixed with the affected area: `w6c: fix const fold for i64`, `lib/fmt: handle %v for slices`, `cc: typo`. Body only when the why is not obvious from the diff. No `Co-Authored-By` trailer, no "Generated with" footer, no emoji. diff --git a/Makefile b/Makefile index e870e9ce..2c1044e6 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # ww — Plan 9-organised toolchain. POSIX make. # -# Build a small static frontend library libwwc.a and the user-facing -# driver `ww`. Per-target tools (6c, 6a, 6l) and the runtime are added +# Build a small static frontend library libwcc.a and the user-facing +# driver `ww`. Per-target tools (w6c, w6a, w6l) and the runtime are added # in their own phases. Everything lands under out/. PREFIX ?= /usr/local @@ -9,17 +9,17 @@ CC ?= cc AR ?= ar CFLAGS ?= -O0 -g -Wall -Wextra -Wpedantic -Wno-unused-parameter CFLAGS += -std=c99 -fno-strict-aliasing -D_POSIX_C_SOURCE=200809L -INCS = -Icmd/wwc +INCS = -Icmd/wcc OUT = out BIN = $(OUT)/bin LIB = $(OUT)/lib OBJ = $(OUT)/obj -WWC_SRC = cmd/wwc/mem.c cmd/wwc/err.c cmd/wwc/tok.c cmd/wwc/lex.c \ - cmd/wwc/ast.c cmd/wwc/parse.c cmd/wwc/sym.c cmd/wwc/type.c \ - cmd/wwc/check.c -WWC_OBJ = $(WWC_SRC:cmd/wwc/%.c=$(OBJ)/wwc/%.o) +WCC_SRC = cmd/wcc/mem.c cmd/wcc/err.c cmd/wcc/tok.c cmd/wcc/lex.c \ + cmd/wcc/ast.c cmd/wcc/parse.c cmd/wcc/sym.c cmd/wcc/type.c \ + cmd/wcc/check.c +WCC_OBJ = $(WCC_SRC:cmd/wcc/%.c=$(OBJ)/wcc/%.o) WW_SRC = cmd/ww/main.c WW_OBJ = $(WW_SRC:cmd/ww/%.c=$(OBJ)/ww/%.o) @@ -27,234 +27,238 @@ WW_OBJ = $(WW_SRC:cmd/ww/%.c=$(OBJ)/ww/%.o) WD_SRC = cmd/wwdump/main.c WD_OBJ = $(WD_SRC:cmd/wwdump/%.c=$(OBJ)/wwdump/%.o) -C6_SRC = cmd/6c/main.c cmd/6c/cgen.c cmd/6c/txt.c cmd/6c/swt.c \ - cmd/6c/peep.c cmd/6c/reg.c -C6_OBJ = $(C6_SRC:cmd/6c/%.c=$(OBJ)/6c/%.o) +W6C_SRC = cmd/w6c/main.c cmd/w6c/cgen.c cmd/w6c/txt.c cmd/w6c/swt.c \ + cmd/w6c/peep.c cmd/w6c/reg.c +W6C_OBJ = $(W6C_SRC:cmd/w6c/%.c=$(OBJ)/w6c/%.o) -A6_SRC = cmd/6a/main.c cmd/6a/lex.c cmd/6a/parse.c cmd/6a/asm.c cmd/6a/obj.c -A6_OBJ = $(A6_SRC:cmd/6a/%.c=$(OBJ)/6a/%.o) +W6A_SRC = cmd/w6a/main.c cmd/w6a/lex.c cmd/w6a/parse.c cmd/w6a/asm.c cmd/w6a/obj.c +W6A_OBJ = $(W6A_SRC:cmd/w6a/%.c=$(OBJ)/w6a/%.o) -L6_SRC = cmd/6l/main.c cmd/6l/obj.c cmd/6l/sym.c cmd/6l/pass.c cmd/6l/out.c \ - cmd/6l/dyn.c cmd/6l/dynout.c -L6_OBJ = $(L6_SRC:cmd/6l/%.c=$(OBJ)/6l/%.o) +W6L_SRC = cmd/w6l/main.c cmd/w6l/obj.c cmd/w6l/sym.c cmd/w6l/pass.c cmd/w6l/out.c \ + cmd/w6l/dyn.c cmd/w6l/dynout.c +W6L_OBJ = $(W6L_SRC:cmd/w6l/%.c=$(OBJ)/w6l/%.o) RT_S = rt/start.s rt/syscall.s rt/alloc.s rt/streq.s rt/abort.s RT_OBJ = $(RT_S:rt/%.s=$(OBJ)/rt/%.o) # Cstage: one-time C bootstrap (see BOOTSTRAP.md). Built by `cc`. # Goes away at v1.0, replaced by a checked-in stage-0 binary. -CSTAGE_BINS = $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l $(BIN)/wwdump +CSTAGE_BINS = $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l $(BIN)/wwdump # Wwstage: ww-rewritten toolchain. Built by Cstage today; will be the # only toolchain after Phase 10 closeout. Byte-identical to Cstage # (tests 990-994). -WWSTAGE_BINS = $(BIN)/wwdump_ww $(BIN)/6c_ww $(BIN)/6a_ww $(BIN)/6l_ww $(BIN)/ww_ww +WWSTAGE_BINS = $(BIN)/wwdump_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww $(BIN)/ww_ww BINS = $(CSTAGE_BINS) $(WWSTAGE_BINS) -LIBS = $(LIB)/libwwc.a $(LIB)/libwwrt.a +LIBS = $(LIB)/libwcc.a $(LIB)/libwwrt.a all: cstage wwstage cstage: $(CSTAGE_BINS) $(LIBS) wwstage: $(WWSTAGE_BINS) -# ---- libwwc.a ---------------------------------------------------------- -$(LIB)/libwwc.a: $(WWC_OBJ) | $(LIB) - $(AR) rcs $@ $(WWC_OBJ) +# ---- libwcc.a ---------------------------------------------------------- +$(LIB)/libwcc.a: $(WCC_OBJ) | $(LIB) + $(AR) rcs $@ $(WCC_OBJ) -$(OBJ)/wwc/%.o: cmd/wwc/%.c cmd/wwc/ww.h | $(OBJ)/wwc +$(OBJ)/wcc/%.o: cmd/wcc/%.c cmd/wcc/ww.h | $(OBJ)/wcc $(CC) $(CFLAGS) $(INCS) -c -o $@ $< # ---- ww driver --------------------------------------------------------- -$(BIN)/ww: $(WW_OBJ) $(LIB)/libwwc.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $(WW_OBJ) -L$(LIB) -lwwc +$(BIN)/ww: $(WW_OBJ) $(LIB)/libwcc.a | $(BIN) + $(CC) $(CFLAGS) -o $@ $(WW_OBJ) -L$(LIB) -lwcc -$(OBJ)/ww/%.o: cmd/ww/%.c cmd/wwc/ww.h | $(OBJ)/ww +$(OBJ)/ww/%.o: cmd/ww/%.c cmd/wcc/ww.h | $(OBJ)/ww $(CC) $(CFLAGS) $(INCS) -c -o $@ $< # ---- wwdump (lex/AST diff anchor) -------------------------------------- -$(BIN)/wwdump: $(WD_OBJ) $(LIB)/libwwc.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $(WD_OBJ) -L$(LIB) -lwwc +$(BIN)/wwdump: $(WD_OBJ) $(LIB)/libwcc.a | $(BIN) + $(CC) $(CFLAGS) -o $@ $(WD_OBJ) -L$(LIB) -lwcc -$(OBJ)/wwdump/%.o: cmd/wwdump/%.c cmd/wwc/ww.h | $(OBJ)/wwdump +$(OBJ)/wwdump/%.o: cmd/wwdump/%.c cmd/wcc/ww.h | $(OBJ)/wwdump $(CC) $(CFLAGS) $(INCS) -c -o $@ $< -# ---- 6c amd64 compiler ------------------------------------------------- -$(BIN)/6c: $(C6_OBJ) $(LIB)/libwwc.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $(C6_OBJ) -L$(LIB) -lwwc +# ---- w6c amd64 compiler ------------------------------------------------ +$(BIN)/w6c: $(W6C_OBJ) $(LIB)/libwcc.a | $(BIN) + $(CC) $(CFLAGS) -o $@ $(W6C_OBJ) -L$(LIB) -lwcc -$(OBJ)/6c/%.o: cmd/6c/%.c cmd/6c/gc.h cmd/6c/6.out.h cmd/wwc/ww.h | $(OBJ)/6c - $(CC) $(CFLAGS) $(INCS) -Icmd/6c -c -o $@ $< +$(OBJ)/w6c/%.o: cmd/w6c/%.c cmd/w6c/gc.h cmd/w6c/6.out.h cmd/wcc/ww.h | $(OBJ)/w6c + $(CC) $(CFLAGS) $(INCS) -Icmd/w6c -c -o $@ $< -# ---- 6a amd64 assembler ------------------------------------------------ -$(BIN)/6a: $(A6_OBJ) | $(BIN) - $(CC) $(CFLAGS) -o $@ $(A6_OBJ) +# ---- w6a amd64 assembler ----------------------------------------------- +$(BIN)/w6a: $(W6A_OBJ) | $(BIN) + $(CC) $(CFLAGS) -o $@ $(W6A_OBJ) -$(OBJ)/6a/%.o: cmd/6a/%.c cmd/6a/a.h cmd/6c/6.out.h | $(OBJ)/6a - $(CC) $(CFLAGS) -Icmd/6a -Icmd/6c -c -o $@ $< +$(OBJ)/w6a/%.o: cmd/w6a/%.c cmd/w6a/a.h cmd/w6c/6.out.h | $(OBJ)/w6a + $(CC) $(CFLAGS) -Icmd/w6a -Icmd/w6c -c -o $@ $< -# ---- 6l amd64 linker --------------------------------------------------- -$(BIN)/6l: $(L6_OBJ) | $(BIN) - $(CC) $(CFLAGS) -o $@ $(L6_OBJ) +# ---- w6l amd64 linker -------------------------------------------------- +$(BIN)/w6l: $(W6L_OBJ) | $(BIN) + $(CC) $(CFLAGS) -o $@ $(W6L_OBJ) -$(OBJ)/6l/%.o: cmd/6l/%.c cmd/6l/l.h | $(OBJ)/6l - $(CC) $(CFLAGS) -Icmd/6l -c -o $@ $< +$(OBJ)/w6l/%.o: cmd/w6l/%.c cmd/w6l/l.h | $(OBJ)/w6l + $(CC) $(CFLAGS) -Icmd/w6l -c -o $@ $< # ---- ww-side wwdump (the lexer port, exercised by 990_selfhost) -------- -# Built via the user-facing ww driver, with -I selfhost/cmd/wwc so it +# Built via the user-facing ww driver, with -I selfhost/cmd/wcc so it # can find the lex/tok/mem ports. Output named wwdump_ww to avoid # colliding with the C-side wwdump in $(BIN). $(BIN)/wwdump_ww: selfhost/cmd/wwdump/main.ww \ - selfhost/cmd/wwc/lex.ww selfhost/cmd/wwc/tok.ww \ - selfhost/cmd/wwc/mem.ww selfhost/cmd/wwc/ast.ww \ - selfhost/cmd/wwc/parse.ww selfhost/cmd/wwc/typ.ww \ - selfhost/cmd/wwc/sym.ww selfhost/cmd/wwc/check.ww \ - selfhost/cmd/wwc/cgen.ww \ - $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l \ + selfhost/cmd/wcc/lex.ww selfhost/cmd/wcc/tok.ww \ + selfhost/cmd/wcc/mem.ww selfhost/cmd/wcc/ast.ww \ + selfhost/cmd/wcc/parse.ww selfhost/cmd/wcc/typ.ww \ + selfhost/cmd/wcc/sym.ww selfhost/cmd/wcc/check.ww \ + selfhost/cmd/wcc/cgen.ww \ + lib/os/os.ww lib/strconv/strconv.ww \ + $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) - cd $(BIN) && ./ww build -I $$PWD/../../selfhost/cmd/wwc \ + cd $(BIN) && ./ww build -I $$PWD/../../selfhost/cmd/wcc \ $$PWD/../../selfhost/cmd/wwdump/main.ww mv $(BIN)/main $@ -# ---- ww-side 6c (compiler port, exercised by 994_6c_ww) ---------------- -# Thin driver: parse + cgen, both already living in selfhost/cmd/wwc. +# ---- ww-side w6c (compiler port, exercised by 994_w6c_ww) ------------- +# Thin driver: parse + cgen, both already living in selfhost/cmd/wcc. # Build through `ww build` like the other wwstage tools. -$(BIN)/6c_ww: selfhost/cmd/6c/main.ww \ - selfhost/cmd/wwc/lex.ww selfhost/cmd/wwc/tok.ww \ - selfhost/cmd/wwc/mem.ww selfhost/cmd/wwc/ast.ww \ - selfhost/cmd/wwc/parse.ww selfhost/cmd/wwc/typ.ww \ - selfhost/cmd/wwc/sym.ww selfhost/cmd/wwc/check.ww \ - selfhost/cmd/wwc/cgen.ww \ - $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l \ +$(BIN)/w6c_ww: selfhost/cmd/w6c/main.ww \ + selfhost/cmd/wcc/lex.ww selfhost/cmd/wcc/tok.ww \ + selfhost/cmd/wcc/mem.ww selfhost/cmd/wcc/ast.ww \ + selfhost/cmd/wcc/parse.ww selfhost/cmd/wcc/typ.ww \ + selfhost/cmd/wcc/sym.ww selfhost/cmd/wcc/check.ww \ + selfhost/cmd/wcc/cgen.ww \ + lib/os/os.ww \ + $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) - cd $(BIN) && ./ww build -I $$PWD/../../selfhost/cmd/wwc \ - $$PWD/../../selfhost/cmd/6c/main.ww + cd $(BIN) && ./ww build -I $$PWD/../../selfhost/cmd/wcc \ + $$PWD/../../selfhost/cmd/w6c/main.ww mv $(BIN)/main $@ -# ---- ww-side 6a (assembler port, exercised by 991_6a_ww) --------------- -# Built like wwdump_ww. Needs -I selfhost/cmd/6a for the local types/lex/ -# parse/asm/obj modules and -I selfhost/cmd/wwc to find `mem`. -$(BIN)/6a_ww: selfhost/cmd/6a/main.ww selfhost/cmd/6a/types.ww \ - selfhost/cmd/6a/lex.ww selfhost/cmd/6a/parse.ww \ - selfhost/cmd/6a/asm.ww selfhost/cmd/6a/obj.ww \ - selfhost/cmd/wwc/mem.ww \ - $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l \ +# ---- ww-side w6a (assembler port, exercised by 991_w6a_ww) ------------ +# Built like wwdump_ww. Needs -I selfhost/cmd/w6a for the local types/lex/ +# parse/asm/obj modules and -I selfhost/cmd/wcc to find `mem`. +$(BIN)/w6a_ww: selfhost/cmd/w6a/main.ww selfhost/cmd/w6a/types.ww \ + selfhost/cmd/w6a/lex.ww selfhost/cmd/w6a/parse.ww \ + selfhost/cmd/w6a/asm.ww selfhost/cmd/w6a/obj.ww \ + selfhost/cmd/wcc/mem.ww \ + lib/os/os.ww \ + $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) cd $(BIN) && ./ww build \ - -I $$PWD/../../selfhost/cmd/6a \ - -I $$PWD/../../selfhost/cmd/wwc \ - $$PWD/../../selfhost/cmd/6a/main.ww + -I $$PWD/../../selfhost/cmd/w6a \ + -I $$PWD/../../selfhost/cmd/wcc \ + $$PWD/../../selfhost/cmd/w6a/main.ww mv $(BIN)/main $@ -# ---- ww-side 6l (linker port, exercised by 992_6l_ww) ------------------ -# Built like 6a_ww. Needs -I selfhost/cmd/6l for the local sym/obj/pass/ -# out modules and -I selfhost/cmd/wwc to find `mem`. -$(BIN)/6l_ww: selfhost/cmd/6l/main.ww selfhost/cmd/6l/sym.ww \ - selfhost/cmd/6l/obj.ww selfhost/cmd/6l/dyn.ww \ - selfhost/cmd/6l/pass.ww selfhost/cmd/6l/dynout.ww \ - selfhost/cmd/6l/out.ww selfhost/cmd/wwc/mem.ww \ - $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l \ +# ---- ww-side w6l (linker port, exercised by 992_w6l_ww) --------------- +# Built like 6a_ww. Needs -I selfhost/cmd/w6l for the local sym/obj/pass/ +# out modules and -I selfhost/cmd/wcc to find `mem`. +$(BIN)/w6l_ww: selfhost/cmd/w6l/main.ww selfhost/cmd/w6l/sym.ww \ + selfhost/cmd/w6l/obj.ww selfhost/cmd/w6l/dyn.ww \ + selfhost/cmd/w6l/pass.ww selfhost/cmd/w6l/dynout.ww \ + selfhost/cmd/w6l/out.ww selfhost/cmd/wcc/mem.ww \ + lib/os/os.ww \ + $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) cd $(BIN) && ./ww build \ - -I $$PWD/../../selfhost/cmd/6l \ - -I $$PWD/../../selfhost/cmd/wwc \ - $$PWD/../../selfhost/cmd/6l/main.ww + -I $$PWD/../../selfhost/cmd/w6l \ + -I $$PWD/../../selfhost/cmd/wcc \ + $$PWD/../../selfhost/cmd/w6l/main.ww mv $(BIN)/main $@ # ---- ww-side ww driver (exercised by 993_ww_ww) ------------------------ -# The driver pulls in lib/os (default search path) and selfhost/cmd/wwc -# (for the bump arena). It then orchestrates 6c/6a/6l like the C driver. -$(BIN)/ww_ww: selfhost/cmd/ww/main.ww selfhost/cmd/wwc/mem.ww lib/os/os.ww \ - $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l \ +# The driver pulls in lib/os (default search path) and selfhost/cmd/wcc +# (for the bump arena). It then orchestrates w6c/w6a/w6l like the C driver. +$(BIN)/ww_ww: selfhost/cmd/ww/main.ww selfhost/cmd/wcc/mem.ww lib/os/os.ww \ + $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) cd $(BIN) && ./ww build \ - -I $$PWD/../../selfhost/cmd/wwc \ + -I $$PWD/../../selfhost/cmd/wcc \ $$PWD/../../selfhost/cmd/ww/main.ww mv $(BIN)/main $@ -# ---- runtime (libwwrt.a, assembled by our own 6a) ---------------------- -$(OBJ)/rt/%.o: rt/%.s $(BIN)/6a | $(OBJ)/rt - $(BIN)/6a -o $@ $< +# ---- runtime (libwwrt.a, assembled by our own w6a) --------------------- +$(OBJ)/rt/%.o: rt/%.s $(BIN)/w6a | $(OBJ)/rt + $(BIN)/w6a -o $@ $< $(LIB)/libwwrt.a: $(RT_OBJ) | $(LIB) $(AR) rcs $@ $(RT_OBJ) # ---- directories ------------------------------------------------------- -$(BIN) $(LIB) $(OBJ)/wwc $(OBJ)/ww $(OBJ)/wwdump $(OBJ)/6c $(OBJ)/6a $(OBJ)/6l $(OBJ)/rt: +$(BIN) $(LIB) $(OBJ)/wcc $(OBJ)/ww $(OBJ)/wwdump $(OBJ)/w6c $(OBJ)/w6a $(OBJ)/w6l $(OBJ)/rt: mkdir -p $@ # ---- tests ------------------------------------------------------------- # Each phase adds a $(BIN)/test_ target; the runner walks them. TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ - $(BIN)/test_6c $(BIN)/test_6a $(BIN)/test_6l $(BIN)/test_arch \ + $(BIN)/test_w6c $(BIN)/test_w6a $(BIN)/test_w6l $(BIN)/test_arch \ $(BIN)/test_e2e $(BIN)/test_ffi $(BIN)/test_dyn $(BIN)/test_stdlib \ - $(BIN)/test_selfhost $(BIN)/test_6a_ww $(BIN)/test_6l_ww \ - $(BIN)/test_6c_ww $(BIN)/test_ww_ww $(BIN)/test_self_rebuild \ + $(BIN)/test_selfhost $(BIN)/test_w6a_ww $(BIN)/test_w6l_ww \ + $(BIN)/test_w6c_ww $(BIN)/test_ww_ww $(BIN)/test_self_rebuild \ $(BIN)/test_dyn_ww -$(BIN)/test_smoke: test/wwc/000_smoke.c $(LIB)/libwwc.a | $(BIN) - $(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwwc +$(BIN)/test_smoke: test/wcc/000_smoke.c $(LIB)/libwcc.a | $(BIN) + $(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwcc -$(BIN)/test_lex: test/wwc/100_lex.c $(LIB)/libwwc.a | $(BIN) - $(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwwc +$(BIN)/test_lex: test/wcc/100_lex.c $(LIB)/libwcc.a | $(BIN) + $(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwcc -$(BIN)/test_parse: test/wwc/200_parse.c $(LIB)/libwwc.a | $(BIN) - $(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwwc +$(BIN)/test_parse: test/wcc/200_parse.c $(LIB)/libwcc.a | $(BIN) + $(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwcc -$(BIN)/test_check: test/wwc/300_check.c $(LIB)/libwwc.a | $(BIN) - $(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwwc +$(BIN)/test_check: test/wcc/300_check.c $(LIB)/libwcc.a | $(BIN) + $(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwcc -$(BIN)/test_6c: test/wwc/400_6c.c $(BIN)/6c | $(BIN) +$(BIN)/test_w6c: test/wcc/400_w6c.c $(BIN)/w6c | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_6a: test/wwc/500_6a.c $(BIN)/6c $(BIN)/6a | $(BIN) +$(BIN)/test_w6a: test/wcc/500_w6a.c $(BIN)/w6c $(BIN)/w6a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_6l: test/wwc/600_6l.c $(BIN)/6c $(BIN)/6a $(BIN)/6l | $(BIN) +$(BIN)/test_w6l: test/wcc/600_w6l.c $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_arch: test/wwc/610_arch.c $(BIN)/6c $(BIN)/6a $(BIN)/6l \ +$(BIN)/test_arch: test/wcc/610_arch.c $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(OBJ)/rt/start.o | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_e2e: test/wwc/700_e2e.c $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l \ +$(BIN)/test_e2e: test/wcc/700_e2e.c $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a $(OBJ)/rt/start.o | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_ffi: test/wwc/800_ffi.c $(BIN)/6c | $(BIN) +$(BIN)/test_ffi: test/wcc/800_ffi.c $(BIN)/w6c | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_dyn: test/wwc/810_dyn.c $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l \ +$(BIN)/test_dyn: test/wcc/810_dyn.c $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_stdlib: test/wwc/900_stdlib.c $(BIN)/6c | $(BIN) +$(BIN)/test_stdlib: test/wcc/900_stdlib.c $(BIN)/w6c | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_selfhost: test/wwc/990_selfhost.c $(BIN)/6c $(BIN)/6a $(BIN)/6l \ +$(BIN)/test_selfhost: test/wcc/990_selfhost.c $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww $(BIN)/wwdump $(BIN)/wwdump_ww $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_6a_ww: test/wwc/991_6a_ww.c $(BIN)/6a $(BIN)/6a_ww | $(BIN) +$(BIN)/test_w6a_ww: test/wcc/991_w6a_ww.c $(BIN)/w6a $(BIN)/w6a_ww | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_6l_ww: test/wwc/992_6l_ww.c $(BIN)/6l $(BIN)/6l_ww \ +$(BIN)/test_w6l_ww: test/wcc/992_w6l_ww.c $(BIN)/w6l $(BIN)/w6l_ww \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_6c_ww: test/wwc/994_6c_ww.c $(BIN)/6c_ww $(BIN)/wwdump_ww | $(BIN) +$(BIN)/test_w6c_ww: test/wcc/994_w6c_ww.c $(BIN)/w6c_ww $(BIN)/wwdump_ww | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_ww_ww: test/wwc/993_ww_ww.c $(BIN)/ww $(BIN)/ww_ww \ - $(BIN)/6c $(BIN)/6a $(BIN)/6l $(LIB)/libwwrt.a | $(BIN) +$(BIN)/test_ww_ww: test/wcc/993_ww_ww.c $(BIN)/ww $(BIN)/ww_ww \ + $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_self_rebuild: test/wwc/995_self_rebuild.c $(BIN)/ww_ww \ - $(BIN)/6c_ww $(BIN)/6a_ww $(BIN)/6l_ww $(BIN)/wwdump_ww \ +$(BIN)/test_self_rebuild: test/wcc/995_self_rebuild.c $(BIN)/ww_ww \ + $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww $(BIN)/wwdump_ww \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_dyn_ww: test/wwc/996_dyn_ww.c $(BIN)/ww $(BIN)/6l $(BIN)/6l_ww \ +$(BIN)/test_dyn_ww: test/wcc/996_dyn_ww.c $(BIN)/ww $(BIN)/w6l $(BIN)/w6l_ww \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< @@ -264,7 +268,7 @@ test: all $(TESTS) install: all mkdir -p $(PREFIX)/bin $(PREFIX)/lib cp $(BIN)/ww $(PREFIX)/bin/ - cp $(LIB)/libwwc.a $(PREFIX)/lib/ + cp $(LIB)/libwcc.a $(PREFIX)/lib/ clean: rm -rf $(OUT) @@ -272,8 +276,8 @@ clean: # ---- bootstrap (phase 10) ---------------------------------------------- # Four-stage self-host: Cstage (these C tools) → ww1 → ww2 → ww3 → ww4 # with cmp ww2 == ww3 and cmp ww3 == ww4. Stage 1 is wwdump_ww (the -# ww-built frontend); stages 2/3/4 assemble with 6a_ww and link with -# 6l_ww — every tool below the driver is now in ww. The C `ww` driver +# ww-built frontend); stages 2/3/4 assemble with w6a_ww and link with +# w6l_ww — every tool below the driver is now in ww. The C `ww` driver # still orchestrates the pipeline (porting it is the remaining phase-10 # work). # @@ -284,28 +288,28 @@ clean: BS = $(OUT)/bootstrap bootstrap: all @echo "=== stage 0 (Cstage): C-built tools ===" - @echo " $(BIN)/ww $(BIN)/6c $(BIN)/6a $(BIN)/6l" + @echo " $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l" @echo @echo "=== stage 1: ww-built tools, used to drive stages 2/3/4 ===" - @ls -l $(BIN)/wwdump_ww $(BIN)/6a_ww $(BIN)/6l_ww $(BIN)/ww_ww + @ls -l $(BIN)/wwdump_ww $(BIN)/w6a_ww $(BIN)/w6l_ww $(BIN)/ww_ww @rm -rf $(BS) && mkdir -p $(BS) @echo @echo "=== stage 2: ww2 = ww1 self-compiles main.combined.ww ===" @$(BIN)/wwdump_ww -c selfhost/cmd/wwdump/main.combined.ww > $(BS)/ww2.s - @$(BIN)/6a_ww -o $(BS)/ww2.o $(BS)/ww2.s - @$(BIN)/6l_ww -o $(BS)/ww2 $(BS)/ww2.o $(LIB)/libwwrt.a + @$(BIN)/w6a_ww -o $(BS)/ww2.o $(BS)/ww2.s + @$(BIN)/w6l_ww -o $(BS)/ww2 $(BS)/ww2.o $(LIB)/libwwrt.a @ls -l $(BS)/ww2 @echo @echo "=== stage 3: ww3 = ww2 self-compiles main.combined.ww ===" @$(BS)/ww2 -c selfhost/cmd/wwdump/main.combined.ww > $(BS)/ww3.s - @$(BIN)/6a_ww -o $(BS)/ww3.o $(BS)/ww3.s - @$(BIN)/6l_ww -o $(BS)/ww3 $(BS)/ww3.o $(LIB)/libwwrt.a + @$(BIN)/w6a_ww -o $(BS)/ww3.o $(BS)/ww3.s + @$(BIN)/w6l_ww -o $(BS)/ww3 $(BS)/ww3.o $(LIB)/libwwrt.a @ls -l $(BS)/ww3 @echo @echo "=== stage 4: ww4 = ww3 self-compiles main.combined.ww ===" @$(BS)/ww3 -c selfhost/cmd/wwdump/main.combined.ww > $(BS)/ww4.s - @$(BIN)/6a_ww -o $(BS)/ww4.o $(BS)/ww4.s - @$(BIN)/6l_ww -o $(BS)/ww4 $(BS)/ww4.o $(LIB)/libwwrt.a + @$(BIN)/w6a_ww -o $(BS)/ww4.o $(BS)/ww4.s + @$(BIN)/w6l_ww -o $(BS)/ww4 $(BS)/ww4.o $(LIB)/libwwrt.a @ls -l $(BS)/ww4 @echo @echo "=== fixed-point gates ===" @@ -316,21 +320,21 @@ bootstrap: all @cmp $(BS)/ww3.o $(BS)/ww4.o && echo " ww3.o == ww4.o ✓ (byte-identical .o)" @cmp $(BS)/ww3 $(BS)/ww4 && echo " ww3 == ww4 ✓ (byte-identical exe, ww3 is byte-stable as a compiler)" @echo - @echo "BOOTSTRAP OK: ww-cgen + 6a_ww + 6l_ww reach a fixed point that holds across two iterations." + @echo "BOOTSTRAP OK: ww-cgen + w6a_ww + w6l_ww reach a fixed point that holds across two iterations." @echo @echo "Phase 10 remaining work:" - @echo " - delete cmd/wwc/ cmd/6c/ cmd/6a/ cmd/6l/ cmd/ww/ in the final commit" + @echo " - delete cmd/wcc/ cmd/w6c/ cmd/w6a/ cmd/w6l/ cmd/ww/ in the final commit" @echo @echo "Already in selfhost/:" - @echo " - cmd/wwc/ : ww-cgen (lex/parse/check/cgen) — bootstrap fixed point" - @echo " - cmd/6c/ : ww-6c (lex/parse/cgen front-end) — matches wwdump_ww -c (test 994)" - @echo " - cmd/6a/ : ww-6a (lex/parse/asm/obj) — byte-identical to C 6a (test 991)" - @echo " - cmd/6l/ : ww-6l (linker w/ archive support) — byte-identical to C 6l (test 992)" + @echo " - cmd/wcc/ : ww-cgen (lex/parse/check/cgen) — bootstrap fixed point" + @echo " - cmd/w6c/ : ww-w6c (lex/parse/cgen front-end) — matches wwdump_ww -c (test 994)" + @echo " - cmd/w6a/ : ww-w6a (lex/parse/asm/obj) — byte-identical to C w6a (test 991)" + @echo " - cmd/w6l/ : ww-w6l (linker w/ archive support) — byte-identical to C w6l (test 992)" @echo " - cmd/ww/ : ww-driver (build/run/version) — byte-identical to C ww (test 993)" # ---- nocc: bootstrap from a checked-in stage-0 binary, no cc ----------- # A fresh-checkout flow that never invokes cc. Mirrors `make bootstrap`'s -# fixed-point check but uses `bootstrap/$(ARCH)/{ww,6c,6a,6l}` as the +# fixed-point check but uses `bootstrap/$(ARCH)/{ww,w6c,w6a,w6l}` as the # starting point and lands in $(OUT)/nocc/ so the cstage build under # $(OUT)/ stays untouched. See bootstrap/README.md and BOOTSTRAP.md. # @@ -347,14 +351,14 @@ NOCC_OBJ = $(NOCC_OUT)/obj bootstrap-snapshot: wwstage @mkdir -p $(STAGE0) @cp $(BIN)/ww_ww $(STAGE0)/ww - @cp $(BIN)/6c_ww $(STAGE0)/6c - @cp $(BIN)/6a_ww $(STAGE0)/6a - @cp $(BIN)/6l_ww $(STAGE0)/6l - @echo "snapshot: $(STAGE0)/{ww,6c,6a,6l} populated from $(BIN)/*_ww" + @cp $(BIN)/w6c_ww $(STAGE0)/w6c + @cp $(BIN)/w6a_ww $(STAGE0)/w6a + @cp $(BIN)/w6l_ww $(STAGE0)/w6l + @echo "snapshot: $(STAGE0)/{ww,w6c,w6a,w6l} populated from $(BIN)/*_ww" @echo " (gitignored — 'git add -f bootstrap/$(ARCH)/*' to ship)" nocc: - @for f in ww 6c 6a 6l; do \ + @for f in ww w6c w6a w6l; do \ test -x $(STAGE0)/$$f || { \ echo "$@: missing $(STAGE0)/$$f"; \ echo "$@: run 'make bootstrap-snapshot' first, or read bootstrap/README.md"; \ @@ -366,38 +370,38 @@ nocc: @mkdir -p $(NOCC_BIN) $(NOCC_LIB) $(NOCC_OBJ) @# Stage 0: drop bootstrap binaries under both natural names and @# the _ww-suffixed names the driver expects when shelling out. - @for f in ww 6c 6a 6l; do \ + @for f in ww w6c w6a w6l; do \ cp $(STAGE0)/$$f $(NOCC_BIN)/$$f; \ cp $(STAGE0)/$$f $(NOCC_BIN)/$${f}_ww; \ done - @# Assemble libwwrt.a using stage-0 6a — the linker needs it for + @# Assemble libwwrt.a using stage-0 w6a — the linker needs it for @# every wwstage-built binary in the next stage. Member order @# follows $(RT_S) so the embedded symbol table matches the @# cstage-built libwwrt.a (cstage uses ar rcs $(RT_OBJ), same @# order). @objs=""; for s in $(RT_S); do \ b=`basename $$s .s`; \ - $(NOCC_BIN)/6a -o $(NOCC_OBJ)/$$b.o $$s; \ + $(NOCC_BIN)/w6a -o $(NOCC_OBJ)/$$b.o $$s; \ objs="$$objs $(NOCC_OBJ)/$$b.o"; \ done; $(AR) rcs $(NOCC_LIB)/libwwrt.a $$objs @echo "=== stage 1: stage-0 rebuilds the wwstage tools from source ===" @# The driver's default lib path is $self_dir/../../lib — under @# $(NOCC_BIN) that resolves to $(NOCC_OUT)/lib (libwwrt only). @# Pass -I $(CURDIR)/lib so `use os` etc. resolve to source. - @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/wwc \ - -I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/6c/main.ww && mv main 6c_ww1 - @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/6a \ - -I $(CURDIR)/selfhost/cmd/wwc -I $(CURDIR)/lib \ - $(CURDIR)/selfhost/cmd/6a/main.ww && mv main 6a_ww1 - @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/6l \ - -I $(CURDIR)/selfhost/cmd/wwc -I $(CURDIR)/lib \ - $(CURDIR)/selfhost/cmd/6l/main.ww && mv main 6l_ww1 - @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/wwc \ + @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/wcc \ + -I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/w6c/main.ww && mv main w6c_ww1 + @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/w6a \ + -I $(CURDIR)/selfhost/cmd/wcc -I $(CURDIR)/lib \ + $(CURDIR)/selfhost/cmd/w6a/main.ww && mv main w6a_ww1 + @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/w6l \ + -I $(CURDIR)/selfhost/cmd/wcc -I $(CURDIR)/lib \ + $(CURDIR)/selfhost/cmd/w6l/main.ww && mv main w6l_ww1 + @cd $(NOCC_BIN) && ./ww build -I $(CURDIR)/selfhost/cmd/wcc \ -I $(CURDIR)/lib $(CURDIR)/selfhost/cmd/ww/main.ww && mv main ww_ww1 @echo "=== fixed-point gates: rebuilt tools == stage-0 binaries? ===" - @cmp $(NOCC_BIN)/6c_ww1 $(STAGE0)/6c && echo " 6c ✓" - @cmp $(NOCC_BIN)/6a_ww1 $(STAGE0)/6a && echo " 6a ✓" - @cmp $(NOCC_BIN)/6l_ww1 $(STAGE0)/6l && echo " 6l ✓" + @cmp $(NOCC_BIN)/w6c_ww1 $(STAGE0)/w6c && echo " w6c ✓" + @cmp $(NOCC_BIN)/w6a_ww1 $(STAGE0)/w6a && echo " w6a ✓" + @cmp $(NOCC_BIN)/w6l_ww1 $(STAGE0)/w6l && echo " w6l ✓" @cmp $(NOCC_BIN)/ww_ww1 $(STAGE0)/ww && echo " ww ✓" @echo @echo "NOCC OK: $(STAGE0)/* reproduces itself from source. cc not invoked." diff --git a/PLAN.md b/PLAN.md index 2f6beca7..6c8abb4a 100644 --- a/PLAN.md +++ b/PLAN.md @@ -13,31 +13,31 @@ that prints its version. No compilation yet. Deliverables: - `Makefile` (POSIX) with `all`, `test`, `clean`, `install` targets. -- `cmd/wwc/` C library skeleton (builds `libwwc.a`): +- `cmd/wcc/` C library skeleton (builds `libwcc.a`): - `ww.h` — central typedefs (`Node`, `Sym`, `Type`, `Lex`) - `mem.c` — bump arena allocator (no `malloc` in hot paths) - `err.c` — `errorf`, `fatal`, `warn` - (lex/parse/check stubs added in later phases) - `cmd/ww/` driver skeleton: argv parsing, version print. - `test/run` — shell test harness. -- `test/wwc/000_smoke.c` — smoke test that asserts `ww -V` prints +- `test/wcc/000_smoke.c` — smoke test that asserts `ww -V` prints the version. -Per-target binaries (`6c`, `6a`, `6l`) are NOT created here. They +Per-target binaries (`w6c`, `w6a`, `w6l`) are NOT created here. They ship in their own phases (4, 5, 6). Exit criteria: -- `make` produces `out/bin/ww` and `out/lib/libwwc.a`. +- `make` produces `out/bin/ww` and `out/lib/libwcc.a`. - `make test` runs the smoke test and passes. -## Phase 1 — Lexer (in `libwwc.a`) +## Phase 1 — Lexer (in `libwcc.a`) Goal: tokenize ww source into a stream of `Tok` structs. Deliverables: -- `cmd/wwc/lex.c` — hand-rolled DFA. Handles: +- `cmd/wcc/lex.c` — hand-rolled DFA. Handles: - identifiers, keywords (`fn`, `let`, `def`, `if`, `else`, `for`, `switch`, `case`, `return`, `use`, `type`, `struct`, `defer`, `break`, `continue`, `export`, `proc`, `chan`, `nil`, `true`, @@ -49,10 +49,10 @@ Deliverables: (`@symbol("name")`, etc.) - explicit `;` terminators — no automatic insertion (Hare rule) - `//` and `/* */` comments -- `cmd/wwc/tok.c` — token names, debug printer. -- `test/wwc/100_lex.c` — table-driven: input → token sequence. +- `cmd/wcc/tok.c` — token names, debug printer. +- `test/wcc/100_lex.c` — table-driven: input → token sequence. - `test/lang/lex/*.ww` — small files exercised via a tiny test - harness that links against `libwwc.a`. + harness that links against `libwcc.a`. Exit criteria: @@ -60,15 +60,15 @@ Exit criteria: stream deterministically. - All lex tests green. -## Phase 2 — Parser & AST (in `libwwc.a`) +## Phase 2 — Parser & AST (in `libwcc.a`) Goal: parse the full grammar into an AST. No types yet. Deliverables: -- `cmd/wwc/parse.c` — recursive descent. Pratt expression parser for +- `cmd/wcc/parse.c` — recursive descent. Pratt expression parser for precedence. No yacc. -- `cmd/wwc/ast.c` — `Node` constructor helpers; printer. +- `cmd/wcc/ast.c` — `Node` constructor helpers; printer. - Grammar coverage: - `use` imports (paths use `.` not `::`) - `type` declarations (struct, alias, fn type) with trailing `=` @@ -81,7 +81,7 @@ Deliverables: - `defer` - body-less `fn` declarations and `@symbol("name")` attribute for FFI imports -- `test/wwc/200_parse.c` — table-driven AST shape tests. +- `test/wcc/200_parse.c` — table-driven AST shape tests. - `test/lang/parse/*.ww` — programs that should parse but not yet typecheck; harness only checks parser exits 0. @@ -91,22 +91,22 @@ Exit criteria: `ref/hare/cmd/hare/main.ha`. - AST printer output is deterministic. -## Phase 3 — Type checker (in `libwwc.a`) +## Phase 3 — Type checker (in `libwcc.a`) Goal: resolve names, infer/check types, produce a typed AST. Deliverables: -- `cmd/wwc/sym.c` — symbol table. Lexical scopes. Plan 9 style hash. -- `cmd/wwc/type.c` — type representation, equality, conversion rules. -- `cmd/wwc/check.c` — type checking pass over the AST. Errors carry +- `cmd/wcc/sym.c` — symbol table. Lexical scopes. Plan 9 style hash. +- `cmd/wcc/type.c` — type representation, equality, conversion rules. +- `cmd/wcc/check.c` — type checking pass over the AST. Errors carry source locations. - Built-in types: all integer widths, `bool`, `f32`, `f64`, `rune`, `str`, `void`, pointers, slices `[]T`, fixed arrays `[N]T`, function types. - Slice semantics: `{ ptr, len, cap }`, indexing bounds-checked at runtime (debug builds; release may elide via flag). -- `test/wwc/300_check.c` — table-driven: src → expected error or OK. +- `test/wcc/300_check.c` — table-driven: src → expected error or OK. - `test/lang/check/*.ww` — both passing and failing cases. Exit criteria: @@ -114,17 +114,17 @@ Exit criteria: - All built-in types correctly checked. - Negative tests produce stable, useful diagnostics. -## Phase 4 — `6c` amd64 compiler +## Phase 4 — `w6c` amd64 compiler Goal: turn typed AST into Plan 9-style amd64 assembly text. The -binary `6c` is a Plan 9 cc analogue for amd64. There is no separate +binary `w6c` is a Plan 9 cc analogue for amd64. There is no separate SSA IR file; the only on-disk intermediate is `.s`. Deliverables: -- `cmd/6c/` (links against `libwwc.a`): +- `cmd/w6c/` (links against `libwcc.a`): - `6.out.h` — amd64 opcode enum, register names, addressing - modes. Mirror `ref/plan9front/sys/src/cmd/6c/` shape. + modes. Mirror `ref/plan9front/sys/src/cmd/w6c/` shape. - `gc.h` — `Prog`, `Adr`, scratch regs, stack layout. - `cgen.c` — typed AST → `Prog` list (walking, not SSA). - `txt.c` — `Prog` list → textual Plan 9 amd64 asm. @@ -133,22 +133,22 @@ Deliverables: first; tighten later). - `mkfile` — Plan 9 mkfile next to the Makefile entries. - Output suffix `.s`. Plan 9 amd64 asm syntax (not GAS). -- `test/lang/6c/*.ww` — golden tests: src → expected `.s` text. +- `test/lang/w6c/*.ww` — golden tests: src → expected `.s` text. Exit criteria: -- `6c hello.ww` produces `hello.s`. +- `w6c hello.ww` produces `hello.s`. - 20+ programs round-trip identically across runs. -- The output is consumable by phase 5's `6a`. +- The output is consumable by phase 5's `w6a`. -## Phase 5 — `6a` amd64 assembler +## Phase 5 — `w6a` amd64 assembler Goal: assemble Plan 9 amd64 asm into ELF object files. ELF (not Plan 9 a.out) so we can interop with C archives in phase 8. Deliverables: -- `cmd/6a/`: +- `cmd/w6a/`: - `lex.c` — tokenize Plan 9 asm. - `parse.c` — hand-rolled grammar (Plan 9 uses yacc; we go hand-rolled to keep rule #6 honest). @@ -157,21 +157,21 @@ Deliverables: - `mkfile` - Pseudoregisters supported: `SP`, `FP`, `SB` (static base) per Plan 9 convention, on top of `AX`, `BX`, ..., `R8`-`R15`. -- `test/wwc/500_asm.c` — round-trip known asm to known bytes. +- `test/wcc/500_asm.c` — round-trip known asm to known bytes. Exit criteria: -- `6a hello.s -o hello.o` produces a valid ELF amd64 object. +- `w6a hello.s -o hello.o` produces a valid ELF amd64 object. - `objdump -d hello.o` matches expectations across a 20-program corpus. -## Phase 6 — `6l` amd64 linker +## Phase 6 — `w6l` amd64 linker Goal: link ELF objects into a static ELF executable. Deliverables: -- `cmd/6l/`: +- `cmd/w6l/`: - `obj.c` — load ELF `.o` files and `.a` archives. - `sym.c` — global symbol table; resolution. - `pass.c` — section layout, relocation application. @@ -182,14 +182,14 @@ Deliverables: Exit criteria: -- `6l -o hello hello.o libwwrt.a` produces a static ELF binary. +- `w6l -o hello hello.o libwwrt.a` produces a static ELF binary. - `ldd hello` reports "not a dynamic executable". - The binary exits 0 with the program's intended semantics. ## Phase 7 — Runtime + driver wiring Goal: a tiny static runtime, plus the `ww` driver wired to call -`6c → 6a → 6l` end to end. +`w6c → w6a → w6l` end to end. Deliverables: @@ -201,8 +201,8 @@ Deliverables: - `rt/slice.c` — slice helpers used by codegen (`bounds_check`, `slice_grow` for explicit user calls). - Build artifact: `out/lib/libwwrt.a`. -- `cmd/ww/` driver: reads `ww build foo.ww`, runs `6c` then `6a` - then `6l`, links `libwwrt.a` into the output. +- `cmd/ww/` driver: reads `ww build foo.ww`, runs `w6c` then `w6a` + then `w6l`, links `libwwrt.a` into the output. Exit criteria: @@ -217,8 +217,8 @@ Deliverables: - Body-less `fn` declarations + `@symbol("name")` attribute parser and checker rules already in phases 2/3 — finalize codegen. -- amd64 SysV ABI: formalize struct-by-value and varargs in `6c`. -- `6l` learns to slurp static archives from a search path. +- amd64 SysV ABI: formalize struct-by-value and varargs in `w6c`. +- `w6l` learns to slurp static archives from a search path. - `lib/c/libc/` — minimal libc bindings (`malloc`, `free`, `printf`, `read`, `write`, `open`, `close`). - `lib/c/tls/` — bindings to libtls or BearSSL (decide; BearSSL is @@ -226,7 +226,7 @@ Deliverables: - `lib/c/crypto/` — bindings to libcrypto subset (sha256, aes, hmac). - `lib/c/curses/` — ncurses bindings (initscr, getch, mvprintw, ...). - `ww` driver: `ww build -lcrypto -ltls -lncurses` resolves these - via a `pkg-config`-style probe, hands `.a` paths to `6l`. + via a `pkg-config`-style probe, hands `.a` paths to `w6l`. Exit criteria: @@ -268,7 +268,7 @@ Exit criteria per module: ## Phase 10 — Self-host -Goal: rewrite `libwwc`, `6c`, `6a`, `6l`, and `ww` in ww. Drop the +Goal: rewrite `libwcc`, `w6c`, `w6a`, `w6l`, and `ww` in ww. Drop the C bootstrap. Steps: @@ -278,9 +278,9 @@ Steps: 2. Port `lex.c` → `lex.ww`. Diff token streams against C output. 3. Port `parse.c` → `parse.ww`. Diff ASTs. 4. Port `check.c`. Diff typed ASTs. -5. Port `6c` (cgen, txt, peep, reg, swt). Diff `.s` output byte +5. Port `w6c` (cgen, txt, peep, reg, swt). Diff `.s` output byte for byte. -6. Port `6a` and `6l`. Diff resulting `.o` and final binaries. +6. Port `w6a` and `w6l`. Diff resulting `.o` and final binaries. 7. Three-stage bootstrap: `Cstage (gcc-built tools) → ww1 → ww2 → ww3`, with `cmp ww2 ww3` OK for each tool. @@ -288,7 +288,7 @@ Steps: Exit criteria: - `make bootstrap` runs the three-stage build green. -- The C `cmd/wwc/`, `cmd/6c/`, `cmd/6a/`, `cmd/6l/` C trees are +- The C `cmd/wcc/`, `cmd/w6c/`, `cmd/w6a/`, `cmd/w6l/` C trees are deleted in this phase's final commit. ### Status (2026-05) @@ -296,10 +296,10 @@ Exit criteria: Steps 1–6 done. The five ww-side tools live in `selfhost/cmd/`: - `wwc/` — ww-native lex/parse/check/cgen (the frontend library) -- `6c/` — ww-native compiler binary; thin driver over wwc's cgen -- `6a/` — ww-native amd64 assembler -- `6l/` — ww-native amd64 linker -- `ww/` — ww-native driver, shells to `6c_ww/6a_ww/6l_ww` +- `w6c/` — ww-native compiler binary; thin driver over wwc's cgen +- `w6a/` — ww-native amd64 assembler +- `w6l/` — ww-native amd64 linker +- `ww/` — ww-native driver, shells to `w6c_ww/w6a_ww/w6l_ww` Step 7 reaches its fixed point: `make bootstrap` produces ww1 → ww2 → ww3 with `cmp ww2 ww3` byte-identical. Tests 990–994 confirm each @@ -312,20 +312,20 @@ toolchain underneath). the wwstage tools end-to-end. Cstage is still required at first- checkout time (no checked-in stage-0 binary yet). ET_DYN dynamic linking is also fully ported to the ww side (test 996 confirms -`6l_ww -L ... -l ...` is byte-identical to `6l` on snake); the one +`w6l_ww -L ... -l ...` is byte-identical to `w6l` on snake); the one remaining gap is that `ww_ww build`'s argv parser does not yet -forward `-L`/`-l` to the linker — `6l_ww` accepts them when invoked +forward `-L`/`-l` to the linker — `w6l_ww` accepts them when invoked directly, just not through the ww-side driver. Snake's Makefile -still drives the C `ww` for that reason; switching it to `6l_ww` + +still drives the C `ww` for that reason; switching it to `w6l_ww` + explicit args works today. Exit criterion 1 (bootstrap green) is met. Exit criterion 2 (delete -the C trees) is **deferred to v1.0**: removing `cmd/wwc/`, `cmd/6c/`, -`cmd/6a/`, `cmd/6l/`, `cmd/ww/` is a one-way door. Until the compiler +the C trees) is **deferred to v1.0**: removing `cmd/wcc/`, `cmd/w6c/`, +`cmd/w6a/`, `cmd/w6l/`, `cmd/ww/` is a one-way door. Until the compiler stops churning we keep Cstage as the canonical fresh-checkout entry point and treat `selfhost/cmd/*` as the path forward. At v1.0, the plan is to ship a checked-in stage-0 binary archive -(`bootstrap//{ww,6c,6a,6l}`), delete the C trees, and promote +(`bootstrap//{ww,w6c,w6a,w6l}`), delete the C trees, and promote `selfhost/cmd/*` to `cmd/*`. See `BOOTSTRAP.md` for the build flow and `make cstage`/`make wwstage` @@ -365,7 +365,7 @@ Exit criteria: ## Test cadence -Every phase has its own test directory (`test/wwc/_*.c`, +Every phase has its own test directory (`test/wcc/_*.c`, `test/lang//*.ww`). `make test` runs them all in order of phase. CI is just `make test` in a clean tree. @@ -387,7 +387,7 @@ Tag the tree at each phase boundary: you've copied. - **Writing a linker is hard.** ELF is documented, static linking is the small subset. Read `ref/plan9front/sys/src/cmd/8l/` and - the ELF spec before writing `6l`. Start with hello-world and grow. + the ELF spec before writing `w6l`. Start with hello-world and grow. - **Static linking against C libs.** OpenSSL is a pain to build static; BearSSL or LibreTLS are more pleasant. Pick early. - **Self-host bootstrap divergence.** Diff outputs at every stage, diff --git a/bootstrap/README.md b/bootstrap/README.md index 7478bb12..33cb61f0 100644 --- a/bootstrap/README.md +++ b/bootstrap/README.md @@ -6,15 +6,15 @@ executables that match the wwstage layout: bootstrap/amd64/ ww driver, equivalent to out/bin/ww_ww - 6c compiler, out/bin/6c_ww - 6a assembler, out/bin/6a_ww - 6l linker, out/bin/6l_ww + w6c compiler, out/bin/w6c_ww + w6a assembler, out/bin/w6a_ww + w6l linker, out/bin/w6l_ww The binaries are intentionally **gitignored** until the compiler stops churning enough to make the trust surface stable — see `PLAN.md` and the v1.0 plan in `BOOTSTRAP.md`. To put them under your local working copy, run `make bootstrap-snapshot` after a green `make wwstage`; to -ship them, `git add -f bootstrap/amd64/{ww,6c,6a,6l}` explicitly. +ship them, `git add -f bootstrap/amd64/{ww,w6c,w6a,w6l}` explicitly. ## Workflow @@ -30,6 +30,6 @@ build under `out/` is undisturbed. ## When to refresh the snapshot The stage-0 binaries are frozen at the cgen state they were built -with. A `make nocc` failure usually means the live `selfhost/cmd/wwc` +with. A `make nocc` failure usually means the live `selfhost/cmd/wcc` cgen has drifted from the snapshot — regenerate with `make bootstrap-snapshot` and re-verify. diff --git a/cmd/6a/a.h b/cmd/w6a/a.h similarity index 91% rename from cmd/6a/a.h rename to cmd/w6a/a.h index 5e66d053..0bee42d2 100644 --- a/cmd/6a/a.h +++ b/cmd/w6a/a.h @@ -1,8 +1,8 @@ /* - * a.h — 6a-private header. Modelled on Plan 9 cmd/6a/a.h, trimmed - * to the instruction subset that 6c emits. + * a.h — w6a-private header. Modelled on Plan 9 cmd/6a/a.h, trimmed + * to the instruction subset that w6c emits. * - * 6a is line-oriented and has no preprocessor: each non-blank, non- + * w6a is line-oriented and has no preprocessor: each non-blank, non- * label line is one instruction. We read the whole file into a list * of `Aprog`s, then encode and emit ELF64. */ diff --git a/cmd/6a/asm.c b/cmd/w6a/asm.c similarity index 97% rename from cmd/6a/asm.c rename to cmd/w6a/asm.c index f88c1521..c5a3271b 100644 --- a/cmd/6a/asm.c +++ b/cmd/w6a/asm.c @@ -387,7 +387,7 @@ a_encode(Asm *a) Asym *s = a_intern(a, p->to.sym); a_addreloc(a, reloff, 2, s, -4); } else { - fprintf(stderr, "6a: line %d: unsupported MOVQ shape\n", p->line); + fprintf(stderr, "w6a: line %d: unsupported MOVQ shape\n", p->line); a->errs++; } break; @@ -407,7 +407,7 @@ a_encode(Asm *a) emit_modrm_mem(a, rcode(p->to.type), p->from.reg, p->from.offset); } else { - fprintf(stderr, "6a: line %d: unsupported MOVB shape\n", p->line); + fprintf(stderr, "w6a: line %d: unsupported MOVB shape\n", p->line); a->errs++; } break; @@ -421,7 +421,7 @@ a_encode(Asm *a) emit_modrm_mem(a, rcode(p->to.type), p->from.reg, p->from.offset); } else { - fprintf(stderr, "6a: line %d: unsupported MOVZBQ shape\n", p->line); + fprintf(stderr, "w6a: line %d: unsupported MOVZBQ shape\n", p->line); a->errs++; } break; @@ -449,7 +449,7 @@ a_encode(Asm *a) a_emit_byte(a, modrm(3, rcode(p->from.type), rcode(p->to.type))); } else { - fprintf(stderr, "6a: line %d: unsupported MOVL shape\n", p->line); + fprintf(stderr, "w6a: line %d: unsupported MOVL shape\n", p->line); a->errs++; } break; @@ -462,7 +462,7 @@ a_encode(Asm *a) emit_modrm_mem(a, rcode(p->to.type), p->from.reg, p->from.offset); } else { - fprintf(stderr, "6a: line %d: unsupported MOVSXD shape\n", p->line); + fprintf(stderr, "w6a: line %d: unsupported MOVSXD shape\n", p->line); a->errs++; } break; @@ -479,7 +479,7 @@ a_encode(Asm *a) sse_mr_load(a, 0xF2, 0x11, p->from.type, p->to.reg, p->to.offset); } else { - fprintf(stderr, "6a: line %d: unsupported MOVSD shape\n", p->line); + fprintf(stderr, "w6a: line %d: unsupported MOVSD shape\n", p->line); a->errs++; } break; @@ -516,7 +516,7 @@ a_encode(Asm *a) sse_mr_load(a, 0xF3, 0x11, p->from.type, p->to.reg, p->to.offset); } else { - fprintf(stderr, "6a: line %d: unsupported MOVSS shape\n", p->line); + fprintf(stderr, "w6a: line %d: unsupported MOVSS shape\n", p->line); a->errs++; } break; @@ -665,7 +665,7 @@ a_encode(Asm *a) break; } default: - fprintf(stderr, "6a: unsupported opcode %d on line %d\n", p->as, p->line); + fprintf(stderr, "w6a: unsupported opcode %d on line %d\n", p->as, p->line); a->errs++; } } @@ -673,7 +673,7 @@ a_encode(Asm *a) /* second pass: patch fixups */ for (Fixup *f = fixups; f; f = f->next) { if (!label_defined(a, f->label)) { - fprintf(stderr, "6a: undefined label '%s'\n", f->label); + fprintf(stderr, "w6a: undefined label '%s'\n", f->label); a->errs++; continue; } diff --git a/cmd/6a/lex.c b/cmd/w6a/lex.c similarity index 91% rename from cmd/6a/lex.c rename to cmd/w6a/lex.c index ec9f7fdf..8b4c1619 100644 --- a/cmd/6a/lex.c +++ b/cmd/w6a/lex.c @@ -1,5 +1,5 @@ /* - * lex.c — character-level helpers for 6a's line-oriented parser. + * lex.c — character-level helpers for w6a's line-oriented parser. * The parser itself lives in parse.c; here we keep the tokenisers * for identifiers and numbers so parse.c stays focused on syntax. */ diff --git a/cmd/6a/main.c b/cmd/w6a/main.c similarity index 73% rename from cmd/6a/main.c rename to cmd/w6a/main.c index 34bdcbb1..55343226 100644 --- a/cmd/6a/main.c +++ b/cmd/w6a/main.c @@ -1,5 +1,5 @@ /* - * 6a — amd64 assembler driver. Read .s, parse, encode, emit ELF .o. + * w6a — amd64 assembler driver. Read .s, parse, encode, emit ELF .o. */ #include "a.h" #include @@ -32,18 +32,18 @@ main(int argc, char **argv) for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) out = argv[++i]; else if (argv[i][0] == '-') { - fprintf(stderr, "6a: unknown flag %s\n", argv[i]); return 2; + fprintf(stderr, "w6a: unknown flag %s\n", argv[i]); return 2; } else if (src == NULL) src = argv[i]; - else { fprintf(stderr, "6a: only one input\n"); return 2; } + else { fprintf(stderr, "w6a: only one input\n"); return 2; } } if (src == NULL || out == NULL) { - fputs("usage: 6a -o file.o file.s\n", stderr); + fputs("usage: w6a -o file.o file.s\n", stderr); return 2; } char *buf; u64 len; if (slurp(src, &buf, &len) < 0) { - fprintf(stderr, "6a: cannot read %s\n", src); + fprintf(stderr, "w6a: cannot read %s\n", src); return 1; } Asm a; @@ -51,7 +51,7 @@ main(int argc, char **argv) if (a_parse(&a) != 0) return 1; if (a_encode(&a) != 0) return 1; FILE *f = fopen(out, "wb"); - if (f == NULL) { fprintf(stderr, "6a: cannot open %s\n", out); return 1; } + if (f == NULL) { fprintf(stderr, "w6a: cannot open %s\n", out); return 1; } int rc = a_emit_elf(&a, f); fclose(f); free(buf); diff --git a/cmd/6a/obj.c b/cmd/w6a/obj.c similarity index 100% rename from cmd/6a/obj.c rename to cmd/w6a/obj.c diff --git a/cmd/6a/parse.c b/cmd/w6a/parse.c similarity index 99% rename from cmd/6a/parse.c rename to cmd/w6a/parse.c index f3786e75..2cbb6901 100644 --- a/cmd/6a/parse.c +++ b/cmd/w6a/parse.c @@ -1,5 +1,5 @@ /* - * parse.c — line-oriented parser for the asm subset emitted by 6c. + * parse.c — line-oriented parser for the asm subset emitted by w6c. * * Grammar: * line := blank | comment | label | text | instr @@ -36,7 +36,7 @@ a_init(Asm *a, const char *file, const char *src, u64 len) static void err(Asm *a, const char *msg) { - fprintf(stderr, "6a: %s:%d: %s\n", a->file, a->line, msg); + fprintf(stderr, "w6a: %s:%d: %s\n", a->file, a->line, msg); a->errs++; } diff --git a/cmd/6c/6.out.h b/cmd/w6c/6.out.h similarity index 94% rename from cmd/6c/6.out.h rename to cmd/w6c/6.out.h index dac3f68a..23729b1b 100644 --- a/cmd/6c/6.out.h +++ b/cmd/w6c/6.out.h @@ -1,8 +1,8 @@ /* * 6.out.h — amd64 instruction enum + register names. Mirrors the * Plan 9 6c shape (cmd/6c/6.out.h) but trimmed to the subset that - * 6c emits and 6a consumes in this bootstrap. Each new opcode added - * here must also gain encoding support in cmd/6a/asm.c. + * w6c emits and w6a consumes in this bootstrap. Each new opcode added + * here must also gain encoding support in cmd/w6a/asm.c. */ #ifndef SIX_OUT_H #define SIX_OUT_H diff --git a/cmd/6c/cgen.c b/cmd/w6c/cgen.c similarity index 99% rename from cmd/6c/cgen.c rename to cmd/w6c/cgen.c index c3fb51f0..0f225be4 100644 --- a/cmd/6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -10,7 +10,7 @@ * (SysV amd64 ABI). We don't yet handle struct-by-value or * floats; floats and slices are deferred. * - * Calling our own functions: emit CALL (SB), let 6a/6l resolve. + * Calling our own functions: emit CALL (SB), let w6a/w6l resolve. * Calling C externs: same — extern symbols are just unresolved CALLs. */ #include "gc.h" @@ -605,7 +605,7 @@ cgexpr(Cg *c, Node *n, Local *locals) * inspect n->lhs. UCOMISD/UCOMISS sets ZF/CF as if an * unsigned compare, so the JA family is the right Jcc set * regardless of how the operand types are signed. Plan 9's - * own 6c picks the same pattern (txt.c around AUCOMISD). + * own w6c picks the same pattern (txt.c around AUCOMISD). * NaN handling: UCOMI sets PF on unordered; we ignore it, * which means NaN compares behave like Hare's default. */ if (n->lhs && node_isfloat(n->lhs) && @@ -1837,7 +1837,7 @@ cgexpr(Cg *c, Node *n, Local *locals) * with a single MOVQ. Without this, returning `o.p.val` * silently leaves AX = o.p (the pointer) and the outer * cast/use sees the pointer instead of the dereferenced - * field. (Surfaced building ww-6l.) */ + * field. (Surfaced building ww-w6l.) */ if (n->lhs->kind == N_DOT) { Type *lt = n->lhs->type; Type *lu = (lt && lt->kind == TY_NAMED) ? lt->under : lt; @@ -1927,7 +1927,7 @@ cgexpr(Cg *c, Node *n, Local *locals) * size — `b.data[i]` on a *u8 must read 1 byte, not 8. * * Scale the index in a register before pushing, because - * IMULQ on a memory operand isn't currently encoded by 6a + * IMULQ on a memory operand isn't currently encoded by w6a * (modrm bits use mod=3 register form). */ cgexpr(c, n->rhs, locals); if (esz > 1) { @@ -2614,7 +2614,7 @@ cgfn(Cg *c, FILE *out, Node *fn) } /* Emit DATA directives for top-level `def` constants whose value is - * an integer/rune literal. The 6a side stores the bytes inside .text + * an integer/rune literal. The w6a side stores the bytes inside .text * and accesses are RIP-relative. */ static void diff --git a/cmd/6c/gc.h b/cmd/w6c/gc.h similarity index 94% rename from cmd/6c/gc.h rename to cmd/w6c/gc.h index 62eb9ced..7f61fe85 100644 --- a/cmd/6c/gc.h +++ b/cmd/w6c/gc.h @@ -1,5 +1,5 @@ /* - * gc.h — 6c-private header: Prog/Adr structs, scratch register set, + * gc.h — w6c-private header: Prog/Adr structs, scratch register set, * stack-frame state. Plan 9 cmd/6c/gc.h shape, trimmed. */ #ifndef SIX_GC_H diff --git a/cmd/6c/main.c b/cmd/w6c/main.c similarity index 79% rename from cmd/6c/main.c rename to cmd/w6c/main.c index edaccb30..bc227806 100644 --- a/cmd/6c/main.c +++ b/cmd/w6c/main.c @@ -1,6 +1,6 @@ /* - * 6c — amd64 compiler driver. Reads a .ww source file, runs the - * libwwc frontend (lex → parse → check), then walks the typed AST + * w6c — amd64 compiler driver. Reads a .ww source file, runs the + * libwcc frontend (lex → parse → check), then walks the typed AST * via cgen.c and writes Plan 9-flavoured amd64 asm to stdout (or * the file given by -o). */ @@ -38,24 +38,24 @@ main(int argc, char **argv) if (strcmp(a, "-o") == 0 && i + 1 < argc) { out = argv[++i]; } else if (a[0] == '-') { - fprintf(stderr, "6c: unknown flag %s\n", a); + fprintf(stderr, "w6c: unknown flag %s\n", a); return 2; } else if (src == NULL) { src = a; } else { - fprintf(stderr, "6c: only one input supported\n"); + fprintf(stderr, "w6c: only one input supported\n"); return 2; } } if (src == NULL) { - fputs("usage: 6c [-o out.s] file.ww\n", stderr); + fputs("usage: w6c [-o out.s] file.ww\n", stderr); return 2; } char *buf; u64 len; if (slurp(src, &buf, &len) < 0) { - fprintf(stderr, "6c: %s: cannot read\n", src); + fprintf(stderr, "w6c: %s: cannot read\n", src); return 1; } @@ -78,7 +78,7 @@ main(int argc, char **argv) if (out) { of = fopen(out, "wb"); if (of == NULL) { - fprintf(stderr, "6c: cannot open %s\n", out); + fprintf(stderr, "w6c: cannot open %s\n", out); return 1; } } diff --git a/cmd/6c/peep.c b/cmd/w6c/peep.c similarity index 100% rename from cmd/6c/peep.c rename to cmd/w6c/peep.c diff --git a/cmd/6c/reg.c b/cmd/w6c/reg.c similarity index 100% rename from cmd/6c/reg.c rename to cmd/w6c/reg.c diff --git a/cmd/6c/swt.c b/cmd/w6c/swt.c similarity index 100% rename from cmd/6c/swt.c rename to cmd/w6c/swt.c diff --git a/cmd/6c/txt.c b/cmd/w6c/txt.c similarity index 99% rename from cmd/6c/txt.c rename to cmd/w6c/txt.c index 471ad045..9ea9cd41 100644 --- a/cmd/6c/txt.c +++ b/cmd/w6c/txt.c @@ -1,7 +1,7 @@ /* * txt.c — print a Prog list as Plan 9-flavoured amd64 asm text. * - * Format we emit (and that 6a expects): + * Format we emit (and that w6a expects): * TEXT name * MOVQ $1, AX * MOVQ AX, name(SB) ; extern symbol diff --git a/cmd/6l/dyn.c b/cmd/w6l/dyn.c similarity index 96% rename from cmd/6l/dyn.c rename to cmd/w6l/dyn.c index 39335c30..ce33620a 100644 --- a/cmd/6l/dyn.c +++ b/cmd/w6l/dyn.c @@ -86,11 +86,11 @@ l_load_so(Lnk *l, const char *path) u8 *buf; u64 len; if (l_read_all(path, &buf, &len) < 0) { - fprintf(stderr, "6l: %s: cannot read\n", path); + fprintf(stderr, "w6l: %s: cannot read\n", path); return -1; } if (len < sizeof(Ehdr)) { - fprintf(stderr, "6l: %s: short ELF\n", path); + fprintf(stderr, "w6l: %s: short ELF\n", path); free(buf); return -1; } @@ -99,12 +99,12 @@ l_load_so(Lnk *l, const char *path) || eh->e_ident[4] != 2 || eh->e_machine != 62 /* EM_X86_64 */ || eh->e_type != ET_DYN) { - fprintf(stderr, "6l: %s: not an amd64 ET_DYN\n", path); + fprintf(stderr, "w6l: %s: not an amd64 ET_DYN\n", path); free(buf); return -1; } if (eh->e_shoff == 0 || eh->e_shnum == 0) { - fprintf(stderr, "6l: %s: stripped .so unsupported\n", path); + fprintf(stderr, "w6l: %s: stripped .so unsupported\n", path); free(buf); return -1; } @@ -119,7 +119,7 @@ l_load_so(Lnk *l, const char *path) if (sh[i].sh_type == SHT_GNU_VERDEF) idx_verdef = i; } if (idx_dynsym < 0) { - fprintf(stderr, "6l: %s: no .dynsym\n", path); + fprintf(stderr, "w6l: %s: no .dynsym\n", path); free(buf); return -1; } diff --git a/cmd/6l/dynout.c b/cmd/w6l/dynout.c similarity index 98% rename from cmd/6l/dynout.c rename to cmd/w6l/dynout.c index c4538645..4ae161a2 100644 --- a/cmd/6l/dynout.c +++ b/cmd/w6l/dynout.c @@ -157,7 +157,7 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry) } for (int i = 0; i < N; i++) { if (dynsyms[i] == NULL) { - fprintf(stderr, "6l: dynout: no sym for plt_idx %d\n", i); + fprintf(stderr, "w6l: dynout: no sym for plt_idx %d\n", i); free(dynsyms); return 1; } @@ -288,7 +288,7 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry) } } if (!matched) { - fprintf(stderr, "6l: dynout: unmatched version %s for %s\n", + fprintf(stderr, "w6l: dynout: unmatched version %s for %s\n", vname, dynsyms[j]->name); versym_for[j] = VER_NDX_GLOBAL; } @@ -529,7 +529,7 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry) } dynamic[k].d_tag = DT_NULL; dynamic[k].d_val = 0; k++; if ((u64)k != ndyn) { - fprintf(stderr, "6l: dynamic entry count mismatch\n"); + fprintf(stderr, "w6l: dynamic entry count mismatch\n"); return 1; } } @@ -540,7 +540,7 @@ l_emit_dyn_elf(Lnk *l, FILE *f, u64 base, u64 entry) for (Lrel *r = l->rels; r; r = r->next) { if (r->sym == NULL || !r->sym->is_dyn) continue; if (r->kind != R_X86_64_PC32 && r->kind != R_X86_64_PLT32) { - fprintf(stderr, "6l: dynamic reloc kind %d unsupported\n", + fprintf(stderr, "w6l: dynamic reloc kind %d unsupported\n", r->kind); free(dynsyms); free(sos_used); free(soname_str); free(symname_str); free(dynstr); free(dynsym); diff --git a/cmd/6l/l.h b/cmd/w6l/l.h similarity index 95% rename from cmd/6l/l.h rename to cmd/w6l/l.h index c992efda..3945cb47 100644 --- a/cmd/6l/l.h +++ b/cmd/w6l/l.h @@ -1,6 +1,6 @@ /* - * l.h — 6l-private header. Loads relocatable ELF64 .o files (the - * format produced by 6a) and links them into a static executable. + * l.h — w6l-private header. Loads relocatable ELF64 .o files (the + * format produced by w6a) and links them into a static executable. * * No archives yet (phase 8). No dynamic linking ever. */ diff --git a/cmd/6l/main.c b/cmd/w6l/main.c similarity index 90% rename from cmd/6l/main.c rename to cmd/w6l/main.c index f81c9c45..7b46ac6d 100644 --- a/cmd/6l/main.c +++ b/cmd/w6l/main.c @@ -1,10 +1,10 @@ /* - * 6l — amd64 linker. Reads relocatable ELF .o files (from 6a) plus + * w6l — amd64 linker. Reads relocatable ELF .o files (from w6a) plus * .a archives, resolves, relocates, writes a static ELF executable. * Dynamic linking against .so files is the next increment; the -L/-l * flag plumbing here is its first step. * - * 6l -o out [-L...] [-l...] file1.o file2.o ... + * w6l -o out [-L...] [-l...] file1.o file2.o ... * * The first symbol named "_start" defined among the inputs becomes * the entry point. If none is found, fall back to "main". @@ -80,14 +80,14 @@ main(int argc, char **argv) } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) { lflags[n_lflags++] = argv[++i]; } else if (argv[i][0] == '-') { - fprintf(stderr, "6l: unknown flag %s\n", argv[i]); + fprintf(stderr, "w6l: unknown flag %s\n", argv[i]); return 2; } else { inputs[ninputs++] = argv[i]; } } if (out == NULL || ninputs == 0) { - fputs("usage: 6l -o exe [-L...] [-l...] " + fputs("usage: w6l -o exe [-L...] [-l...] " "file1.o [file2.o...]\n", stderr); return 2; } @@ -98,7 +98,7 @@ main(int argc, char **argv) for (int i = 0; i < n_lflags; i++) { const char *p = resolve_lib(lflags[i], libdirs, n_libdirs); if (p == NULL) { - fprintf(stderr, "6l: cannot find -l%s\n", lflags[i]); + fprintf(stderr, "w6l: cannot find -l%s\n", lflags[i]); return 1; } inputs[ninputs++] = p; @@ -120,13 +120,13 @@ main(int argc, char **argv) Lsym *entry = l_lookup(&l, "_start"); if (entry == NULL || !entry->defined) entry = l_lookup(&l, "main"); if (entry == NULL || !entry->defined) { - fprintf(stderr, "6l: no _start or main symbol defined\n"); + fprintf(stderr, "w6l: no _start or main symbol defined\n"); return 1; } FILE *f = fopen(out, "wb"); if (f == NULL) { - fprintf(stderr, "6l: cannot open %s\n", out); + fprintf(stderr, "w6l: cannot open %s\n", out); return 1; } int rc = l_emit_elf(&l, f, base, base + 0x1000 + entry->val); diff --git a/cmd/6l/obj.c b/cmd/w6l/obj.c similarity index 97% rename from cmd/6l/obj.c rename to cmd/w6l/obj.c index 6e111e75..08a97e84 100644 --- a/cmd/6l/obj.c +++ b/cmd/w6l/obj.c @@ -1,5 +1,5 @@ /* - * obj.c — load an ELF64 relocatable object emitted by 6a, append its + * obj.c — load an ELF64 relocatable object emitted by w6a, append its * .text bytes to the combined image, and pull its symbols and * relocations into the global tables (with offsets adjusted to the * combined section). @@ -251,7 +251,7 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len) Ehdr *eh = (Ehdr *)buf; if (memcmp(eh->e_ident, "\x7f""ELF", 4) != 0 || eh->e_ident[4] != 2 || eh->e_machine != EM_X86_64 || eh->e_type != ET_REL) { - fprintf(stderr, "6l: %s: not an amd64 ELF64 relocatable\n", path); + fprintf(stderr, "w6l: %s: not an amd64 ELF64 relocatable\n", path); free(buf); return -1; } @@ -271,7 +271,7 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len) idx_rela = i; } if (idx_text < 0 || idx_symtab < 0) { - fprintf(stderr, "6l: %s: missing .text or .symtab\n", path); + fprintf(stderr, "w6l: %s: missing .text or .symtab\n", path); free(buf); return -1; } @@ -303,7 +303,7 @@ load_image(Lnk *l, const char *path, u8 *buf, u64 len) if (symtab[i].st_shndx != 0 /* SHN_UNDEF */ && symtab[i].st_shndx == idx_text) { if (gs->defined) { - fprintf(stderr, "6l: %s: duplicate symbol %s\n", + fprintf(stderr, "w6l: %s: duplicate symbol %s\n", path, nm); l->errs++; } else { diff --git a/cmd/6l/out.c b/cmd/w6l/out.c similarity index 100% rename from cmd/6l/out.c rename to cmd/w6l/out.c diff --git a/cmd/6l/pass.c b/cmd/w6l/pass.c similarity index 95% rename from cmd/6l/pass.c rename to cmd/w6l/pass.c index 75620eaf..ebc0cd63 100644 --- a/cmd/6l/pass.c +++ b/cmd/w6l/pass.c @@ -50,7 +50,7 @@ l_resolve(Lnk *l) for (Lrel *r = l->rels; r; r = r->next) { if (r->sym == NULL) continue; if (!r->sym->defined && !r->sym->is_dyn) { - fprintf(stderr, "6l: undefined reference to '%s'\n", + fprintf(stderr, "w6l: undefined reference to '%s'\n", r->sym->name); l->errs++; } @@ -86,7 +86,7 @@ l_relocate(Lnk *l, u64 base) break; } default: - fprintf(stderr, "6l: unsupported reloc kind %d\n", + fprintf(stderr, "w6l: unsupported reloc kind %d\n", r->kind); l->errs++; } diff --git a/cmd/6l/sym.c b/cmd/w6l/sym.c similarity index 100% rename from cmd/6l/sym.c rename to cmd/w6l/sym.c diff --git a/cmd/wwc/ast.c b/cmd/wcc/ast.c similarity index 100% rename from cmd/wwc/ast.c rename to cmd/wcc/ast.c diff --git a/cmd/wwc/check.c b/cmd/wcc/check.c similarity index 99% rename from cmd/wwc/check.c rename to cmd/wcc/check.c index c0e31515..353549f7 100644 --- a/cmd/wwc/check.c +++ b/cmd/wcc/check.c @@ -460,7 +460,7 @@ cexpr(Checker *c, Node *n) if (ft == ty_err) { /* Walk args anyway so cgen sees real types. The * common case is a module-qualified call whose leaf - * isn't in this scope (raw 6c on a single file with + * isn't in this scope (raw w6c on a single file with * `use mod;` but no driver concatenation). */ for (Node *a = n->list; a; a = a->next) (void)cexpr(c, a); diff --git a/cmd/wwc/err.c b/cmd/wcc/err.c similarity index 100% rename from cmd/wwc/err.c rename to cmd/wcc/err.c diff --git a/cmd/wwc/lex.c b/cmd/wcc/lex.c similarity index 100% rename from cmd/wwc/lex.c rename to cmd/wcc/lex.c diff --git a/cmd/wwc/mem.c b/cmd/wcc/mem.c similarity index 100% rename from cmd/wwc/mem.c rename to cmd/wcc/mem.c diff --git a/cmd/wwc/parse.c b/cmd/wcc/parse.c similarity index 100% rename from cmd/wwc/parse.c rename to cmd/wcc/parse.c diff --git a/cmd/wwc/sym.c b/cmd/wcc/sym.c similarity index 100% rename from cmd/wwc/sym.c rename to cmd/wcc/sym.c diff --git a/cmd/wwc/tok.c b/cmd/wcc/tok.c similarity index 100% rename from cmd/wwc/tok.c rename to cmd/wcc/tok.c diff --git a/cmd/wwc/type.c b/cmd/wcc/type.c similarity index 100% rename from cmd/wwc/type.c rename to cmd/wcc/type.c diff --git a/cmd/wwc/ww.h b/cmd/wcc/ww.h similarity index 99% rename from cmd/wwc/ww.h rename to cmd/wcc/ww.h index 4b91b60c..2b298b9a 100644 --- a/cmd/wwc/ww.h +++ b/cmd/wcc/ww.h @@ -1,5 +1,5 @@ /* - * ww.h — central header for libwwc.a (the ww frontend library). + * ww.h — central header for libwcc.a (the ww frontend library). * * Plan 9 in spirit. This file mirrors cc/cc.h's role: one shared * header that declares everything every translation unit in the diff --git a/cmd/ww/main.c b/cmd/ww/main.c index 3140b8e8..9752052a 100644 --- a/cmd/ww/main.c +++ b/cmd/ww/main.c @@ -2,28 +2,36 @@ * ww — the user-facing driver. Plan 9 cc(1) / Hare hare(1) analogue. * * Pipeline: - * ww build foo.ww → 6c foo.ww > foo.s ; 6a foo.s > foo.o ; - * 6l -o foo foo.o + * ww build foo.ww → w6c foo.ww > foo.s ; w6a foo.s > foo.o ; + * w6l -o foo foo.o * ww run foo.ww → build then exec ./foo * * Tool paths default to siblings of $0 (so a fresh build runs out of - * out/bin/), and can be overridden with WW_6C / WW_6A / WW_6L. + * out/bin/), and can be overridden with WW_W6C / WW_W6A / WW_W6L. */ #include "ww.h" #include #include #include #include +#include +#include #include static const char *usage = "usage: ww [-V] [args...]\n" -" -V print version and exit\n" -" build compile module to a static binary\n" -" run build then exec\n" -" test build and run module tests\n" -" fmt reformat ww source\n" -" version print version and exit\n"; +" -V print version and exit\n" +" build [path] compile module to a static binary (path defaults to cwd)\n" +" run [path] ... build then exec, passing extra args to the program\n" +" test [path] build and run *_test.ww in the module (path defaults to cwd)\n" +" fmt reformat ww source\n" +" version print version and exit\n" +"\n" +" path forms:\n" +" foo.ww literal file\n" +" foo search cwd, -I dirs, then $WW_LIB for foo.ww or foo/foo.ww\n" +" lib/foo directory: build lib/foo/foo.ww\n" +" . build the cwd's .ww\n"; static char *self_dir; /* directory containing this binary */ @@ -159,9 +167,9 @@ static int build_one(const char *src, const char *out, const char *extra_includes, const char *extra_libs, const char *extra_libdirs) { - const char *c6 = toolpath("WW_6C", "6c"); - const char *a6 = toolpath("WW_6A", "6a"); - const char *l6 = toolpath("WW_6L", "6l"); + const char *c6 = toolpath("WW_W6C", "w6c"); + const char *a6 = toolpath("WW_W6A", "w6a"); + const char *l6 = toolpath("WW_W6L", "w6l"); const char *libdir = getenv("WW_LIB"); if (libdir == NULL || libdir[0] == 0) { static char libbuf[1024]; @@ -216,12 +224,12 @@ build_one(const char *src, const char *out, const char *extra_includes, char cmd[4096]; snprintf(cmd, sizeof cmd, "%s -o %s %s", c6, asmf, combined); if (run(cmd) != 0) { - fprintf(stderr, "ww: 6c failed\n"); + fprintf(stderr, "ww: w6c failed\n"); return 1; } snprintf(cmd, sizeof cmd, "%s -o %s %s", a6, obj, asmf); if (run(cmd) != 0) { - fprintf(stderr, "ww: 6a failed\n"); + fprintf(stderr, "ww: w6a failed\n"); return 1; } /* Link runtime: prefer libwwrt.a (selective archive pull) but @@ -238,7 +246,7 @@ build_one(const char *src, const char *out, const char *extra_includes, snprintf(a2, sizeof a2, "%s/../obj/rt/syscall.o", self_dir); snprintf(rtargs, sizeof rtargs, "%s %s", a1, a2); } - /* -L goes before -l so 6l can resolve the latter. */ + /* -L goes before -l so w6l can resolve the latter. */ const char *libargs = (extra_libs && extra_libs[0]) ? extra_libs : ""; const char *libdirset = (extra_libdirs && extra_libdirs[0]) ? extra_libdirs : ""; snprintf(cmd, sizeof cmd, "%s -o %s %s %s%s%s%s%s", @@ -246,7 +254,7 @@ build_one(const char *src, const char *out, const char *extra_includes, libdirset[0] ? " " : "", libdirset, libargs[0] ? " " : "", libargs); if (run(cmd) != 0) { - fprintf(stderr, "ww: 6l failed\n"); + fprintf(stderr, "ww: w6l failed\n"); return 1; } return 0; @@ -259,70 +267,292 @@ do_version(void) return 0; } +/* Compose the standard module search path: cwd : : $WW_LIB + * source dir. The `extra` string is colon-separated -I dirs from argv. */ +static const char * +search_path(const char *extra, char *buf, size_t bufsz) +{ + const char *libdir = getenv("WW_SRCLIB"); + static char libbuf[1024]; + if (libdir == NULL || libdir[0] == 0) { + libdir = getenv("WW_LIB"); + } + if (libdir == NULL || libdir[0] == 0) { + snprintf(libbuf, sizeof libbuf, "%s/../../lib", self_dir); + if (access(libbuf, 0) == 0) libdir = libbuf; + else if (access("lib", 0) == 0) libdir = "lib"; + else { + snprintf(libbuf, sizeof libbuf, "%s/../lib", self_dir); + libdir = libbuf; + } + } + if (extra && extra[0]) + snprintf(buf, bufsz, ".:%s:%s", extra, libdir); + else + snprintf(buf, bufsz, ".:%s", libdir); + return buf; +} + +/* basename_no_ext: last path segment with any trailing ".ww" stripped. */ +static void +basename_no_ext(const char *path, char *out, size_t outsz) +{ + const char *base = strrchr(path, '/'); + base = base ? base + 1 : path; + snprintf(out, outsz, "%s", base); + char *dot = strrchr(out, '.'); + if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; +} + +/* resolve_module: turn a name into a concrete .ww file path. + * foo.ww → use as-is if it exists + * /.ww (Hare module convention) + * . → .ww in the cwd + * foo (bare) → walk cwd:incs:WW_LIB for foo.ww or foo/foo.ww + * Returns 1 on success and writes the path to `out`, 0 on failure. */ +static int +resolve_module(const char *name, const char *incs, char *out, size_t outsz) +{ + struct stat st; + if (stat(name, &st) == 0) { + if (S_ISREG(st.st_mode)) { + snprintf(out, outsz, "%s", name); + return 1; + } + if (S_ISDIR(st.st_mode)) { + char buf[1024]; + const char *base; + if (strcmp(name, ".") == 0) { + if (getcwd(buf, sizeof buf) == NULL) return 0; + } else { + snprintf(buf, sizeof buf, "%s", name); + size_t bl = strlen(buf); + while (bl > 1 && buf[bl-1] == '/') buf[--bl] = '\0'; + } + const char *b = strrchr(buf, '/'); + base = b ? b + 1 : buf; + snprintf(out, outsz, "%s/%s.ww", name, base); + if (access(out, 0) == 0) return 1; + return 0; + } + } + char sp[4096]; + search_path(incs, sp, sizeof sp); + if (locate_import(sp, name, out, outsz)) return 1; + return 0; +} + +/* Append `path` to a heap string-array. Caller frees each entry + the array. */ +static void +strarr_push(char ***arr, int *n, int *cap, const char *path) +{ + if (*n + 1 > *cap) { + *cap = *cap ? *cap * 2 : 8; + *arr = realloc(*arr, *cap * sizeof **arr); + } + (*arr)[(*n)++] = strdup(path); +} + +/* collect_tests: enumerate _test.ww files in (no recursion). + * Returns 0 on success and writes the file list + count, -1 on failure. */ +static int +collect_tests(const char *dir, char ***files, int *n) +{ + DIR *d = opendir(dir); + if (!d) return -1; + *files = NULL; + *n = 0; + int cap = 0; + struct dirent *ent; + while ((ent = readdir(d)) != NULL) { + const char *nm = ent->d_name; + size_t nl = strlen(nm); + const char *suf = "_test.ww"; + size_t sl = strlen(suf); + if (nl <= sl) continue; + if (strcmp(nm + nl - sl, suf) != 0) continue; + char path[1024]; + snprintf(path, sizeof path, "%s/%s", dir, nm); + strarr_push(files, n, &cap, path); + } + closedir(d); + return 0; +} + +/* Parse the standard -I/-L/-l flags into incs/libdirs/libs. The first + * non-flag positional becomes *src_out. Returns the index past the last + * arg consumed for positionals (so callers can pick up trailing args). */ +static int +parse_build_flags(int argc, char **argv, + char *incs, size_t incsz, + char *libdirs, size_t libdirsz, + char *libs, size_t libsz, + const char **src_out) +{ + *src_out = NULL; + int i = 0; + for (; i < argc; i++) { + if (strncmp(argv[i], "-l", 2) == 0 && argv[i][2]) { + size_t n = strlen(libs); + snprintf(libs + n, libsz - n, + "%s%s", n ? " " : "", argv[i]); + } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) { + size_t n = strlen(libs); + snprintf(libs + n, libsz - n, + "%s-l%s", n ? " " : "", argv[++i]); + } else if (strcmp(argv[i], "-L") == 0 && i + 1 < argc) { + size_t n = strlen(libdirs); + snprintf(libdirs + n, libdirsz - n, + "%s-L%s", n ? " " : "", argv[++i]); + } else if (strncmp(argv[i], "-L", 2) == 0 && argv[i][2]) { + size_t n = strlen(libdirs); + snprintf(libdirs + n, libdirsz - n, + "%s%s", n ? " " : "", argv[i]); + } else if (strcmp(argv[i], "-I") == 0 && i + 1 < argc) { + size_t n = strlen(incs); + snprintf(incs + n, incsz - n, + "%s%s", n ? ":" : "", argv[++i]); + } else if (strncmp(argv[i], "-I", 2) == 0 && argv[i][2]) { + size_t n = strlen(incs); + snprintf(incs + n, incsz - n, + "%s%s", n ? ":" : "", argv[i] + 2); + } else if (*src_out == NULL) { + *src_out = argv[i]; + } else { + break; /* leave remaining argv to caller (run-args) */ + } + } + return i; +} + static int do_build(int argc, char **argv) { const char *src = NULL; - char libs[2048] = {0}; /* -l entries, space-separated */ - char libdirs[2048] = {0}; /* -L entries, space-separated */ + char libs[2048] = {0}; + char libdirs[2048] = {0}; char incs[2048] = {0}; - for (int i = 0; i < argc; i++) { - if (strncmp(argv[i], "-l", 2) == 0 && argv[i][2]) { - size_t n = strlen(libs); - snprintf(libs + n, sizeof libs - n, - "%s%s", n ? " " : "", argv[i]); - } else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) { - size_t n = strlen(libs); - snprintf(libs + n, sizeof libs - n, - "%s-l%s", n ? " " : "", argv[++i]); - } else if (strcmp(argv[i], "-L") == 0 && i + 1 < argc) { - size_t n = strlen(libdirs); - snprintf(libdirs + n, sizeof libdirs - n, - "%s-L%s", n ? " " : "", argv[++i]); - } else if (strncmp(argv[i], "-L", 2) == 0 && argv[i][2]) { - size_t n = strlen(libdirs); - snprintf(libdirs + n, sizeof libdirs - n, - "%s%s", n ? " " : "", argv[i]); - } else if (strcmp(argv[i], "-I") == 0 && i + 1 < argc) { - size_t n = strlen(incs); - snprintf(incs + n, sizeof incs - n, - "%s%s", n ? ":" : "", argv[++i]); - } else if (strncmp(argv[i], "-I", 2) == 0 && argv[i][2]) { - size_t n = strlen(incs); - snprintf(incs + n, sizeof incs - n, - "%s%s", n ? ":" : "", argv[i] + 2); - } else if (src == NULL) { - src = argv[i]; - } + parse_build_flags(argc, argv, incs, sizeof incs, + libdirs, sizeof libdirs, libs, sizeof libs, &src); + if (src == NULL) src = "."; /* default: build cwd */ + char resolved[1024]; + if (!resolve_module(src, incs, resolved, sizeof resolved)) { + fprintf(stderr, "ww build: cannot find module %s\n", src); + return 1; } - if (src == NULL) { fputs("ww build: missing source\n", stderr); return 2; } char out[1024]; - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - snprintf(out, sizeof out, "%s", base); - char *dot = strrchr(out, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - return build_one(src, out, incs, libs, libdirs); + basename_no_ext(resolved, out, sizeof out); + return build_one(resolved, out, incs, libs, libdirs); } static int do_run(int argc, char **argv) { - if (argc < 1) { fputs("ww run: missing source\n", stderr); return 2; } + const char *src = NULL; + char libs[2048] = {0}; + char libdirs[2048] = {0}; + char incs[2048] = {0}; + int next = parse_build_flags(argc, argv, incs, sizeof incs, + libdirs, sizeof libdirs, libs, sizeof libs, &src); + if (src == NULL) src = "."; + char resolved[1024]; + if (!resolve_module(src, incs, resolved, sizeof resolved)) { + fprintf(stderr, "ww run: cannot find module %s\n", src); + return 1; + } char tmp[1024]; snprintf(tmp, sizeof tmp, "/tmp/ww_run_%d", getpid()); - if (build_one(argv[0], tmp, "", "", "") != 0) return 1; - int rc = run(tmp); + if (build_one(resolved, tmp, incs, libs, libdirs) != 0) return 1; + /* exec the built binary with any trailing argv as its argv. */ + pid_t pid = fork(); + if (pid < 0) { perror("ww: fork"); unlink(tmp); return 1; } + if (pid == 0) { + int n_extra = argc - next; + char **xargv = calloc((size_t)n_extra + 2, sizeof *xargv); + xargv[0] = tmp; + for (int i = 0; i < n_extra; i++) xargv[i+1] = argv[next + i]; + xargv[n_extra+1] = NULL; + execv(tmp, xargv); + perror("ww: exec"); + _exit(127); + } + int status = 0; + waitpid(pid, &status, 0); unlink(tmp); - return rc; + if (WIFEXITED(status)) return WEXITSTATUS(status); + return 1; } static int do_test(int argc, char **argv) { - (void)argc; (void)argv; - fputs("ww: test: not implemented in this phase\n", stderr); - return 1; + const char *target = (argc > 0) ? argv[0] : "."; + struct stat st; + if (stat(target, &st) != 0) { + /* not a literal path — try module resolution and run as + * a single test program. */ + char resolved[1024]; + if (!resolve_module(target, "", resolved, sizeof resolved)) { + fprintf(stderr, "ww test: cannot find %s\n", target); + return 1; + } + char tmp[1024]; + snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d", getpid()); + if (build_one(resolved, tmp, "", "", "") != 0) return 1; + int rc = run(tmp); + unlink(tmp); + return rc; + } + if (S_ISREG(st.st_mode)) { + /* single .ww file — build+run it. */ + char tmp[1024]; + snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d", getpid()); + if (build_one(target, tmp, "", "", "") != 0) return 1; + int rc = run(tmp); + unlink(tmp); + return rc; + } + if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "ww test: %s is neither file nor directory\n", target); + return 1; + } + /* directory — run every *_test.ww inside. */ + char **files = NULL; + int n = 0; + if (collect_tests(target, &files, &n) < 0) { + fprintf(stderr, "ww test: cannot read directory %s\n", target); + return 1; + } + if (n == 0) { + fprintf(stderr, "ww test: no *_test.ww files in %s\n", target); + return 1; + } + int pass = 0, fail = 0; + for (int i = 0; i < n; i++) { + char tmp[1024]; + snprintf(tmp, sizeof tmp, "/tmp/ww_test_%d_%d", getpid(), i); + const char *label = strrchr(files[i], '/'); + label = label ? label + 1 : files[i]; + int rc = build_one(files[i], tmp, target, "", ""); + if (rc != 0) { + fprintf(stderr, "FAIL %s (build)\n", label); + fail++; + } else { + int xrc = run(tmp); + if (xrc == 0) { + printf("ok %s\n", label); + pass++; + } else { + printf("FAIL %s (rc=%d)\n", label, xrc); + fail++; + } + } + unlink(tmp); + free(files[i]); + } + free(files); + printf("ww test: %d pass, %d fail\n", pass, fail); + return fail == 0 ? 0 : 1; } static int diff --git a/cmd/wwdump/main.c b/cmd/wwdump/main.c index 2c699f41..7320042f 100644 --- a/cmd/wwdump/main.c +++ b/cmd/wwdump/main.c @@ -3,7 +3,7 @@ * * Reads a .ww file and writes either a token stream or an AST * s-expression to stdout, byte-for-byte stable across runs. It is the - * diff anchor for self-host: the C-side libwwc and the future ww-side + * diff anchor for self-host: the C-side libwcc and the future ww-side * frontend must produce the same dump for the same input. * * wwdump -t file.ww tokens, one per line: ":: [val]" diff --git a/examples/snake/Makefile b/examples/snake/Makefile index 1fed3119..bc431fdb 100644 --- a/examples/snake/Makefile +++ b/examples/snake/Makefile @@ -1,7 +1,7 @@ -# examples/snake — built with the ww toolchain only (6c → 6a → 6l). +# examples/snake — built with the ww toolchain only (w6c → w6a → w6l). # # `ww build` does the whole pipeline. -l ncurses -l c routes through -# 6l's dynamic linker (PT_INTERP + PT_DYNAMIC + PLT/GOT), so the +# w6l's dynamic linker (PT_INTERP + PT_DYNAMIC + PLT/GOT), so the # resulting binary maps libncurses.so.6 + libc.so.6 at runtime via # the system loader. No cc. diff --git a/examples/snake/snake.ww b/examples/snake/snake.ww index e5044892..a3215bc4 100644 --- a/examples/snake/snake.ww +++ b/examples/snake/snake.ww @@ -3,11 +3,11 @@ // What this proves: // 1. ww the language can express an interactive program. // 2. The @symbol FFI mechanism binds to libncurses and libc. -// 3. 6l's new dynamic linker (PT_INTERP + PT_DYNAMIC + PLT/GOT) +// 3. w6l's new dynamic linker (PT_INTERP + PT_DYNAMIC + PLT/GOT) // reaches a real .so at runtime — `ldd snake` shows // libncurses.so.6 and libc.so.6 as NEEDED entries. // -// Build: see ./Makefile. Pipeline is the ww toolchain (6c → 6a → 6l) +// Build: see ./Makefile. Pipeline is the ww toolchain (w6c → w6a → w6l) // with -l ncurses -l c. No cc. // // Controls: w/a/s/d to steer, q to quit. diff --git a/lib/os/os.ww b/lib/os/os.ww index 559e414e..46874c5e 100644 --- a/lib/os/os.ww +++ b/lib/os/os.ww @@ -18,19 +18,21 @@ export fn assert(cond: bool, msg: str) void = { if (!cond) { abort(msg); }; }; -def SYS_READ: i64 = 0; -def SYS_WRITE: i64 = 1; -def SYS_OPEN: i64 = 2; -def SYS_CLOSE: i64 = 3; -def SYS_LSEEK: i64 = 8; -def SYS_ACCESS: i64 = 21; -def SYS_DUP2: i64 = 33; -def SYS_GETPID: i64 = 39; -def SYS_FORK: i64 = 57; -def SYS_EXECVE: i64 = 59; -def SYS_EXIT: i64 = 60; -def SYS_WAIT4: i64 = 61; -def SYS_UNLINK: i64 = 87; +def SYS_READ: i64 = 0; +def SYS_WRITE: i64 = 1; +def SYS_OPEN: i64 = 2; +def SYS_CLOSE: i64 = 3; +def SYS_LSEEK: i64 = 8; +def SYS_ACCESS: i64 = 21; +def SYS_DUP2: i64 = 33; +def SYS_GETPID: i64 = 39; +def SYS_FORK: i64 = 57; +def SYS_EXECVE: i64 = 59; +def SYS_EXIT: i64 = 60; +def SYS_WAIT4: i64 = 61; +def SYS_UNLINK: i64 = 87; +def SYS_GETCWD: i64 = 79; +def SYS_GETDENTS64: i64 = 217; // open(2) flags. Linux values, matching . def O_RDONLY: i32 = 0; @@ -65,7 +67,7 @@ export fn close(fd: i32) i32 = { // dup2(2): make `newfd` refer to the same description as `oldfd`, // closing `newfd` first if open. Returns `newfd` on success or a -// negative errno. Used by 6c_ww to redirect stdout into an output +// negative errno. Used by w6c_ww to redirect stdout into an output // file without changing the cgen emit path. export fn dup2(oldfd: i32, newfd: i32) i32 = { return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; @@ -178,3 +180,29 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { return syscall4(SYS_WAIT4, pid: i64, status_out: i64, options: i64, rusage: i64): i32; }; + +// getcwd(2) — Linux flavour. Writes the NUL-terminated cwd into `buf` +// and returns the number of bytes written (including the NUL), or a +// negative errno. The driver uses it to expand `.` to the cwd's +// basename for `ww build` / `ww test`. +export fn getcwd(buf: *u8, n: u64) i64 = { + return syscall2(SYS_GETCWD, buf: i64, n: i64); +}; + +// getdents64(2) — Linux directory enumeration. The fd must be opened +// with O_RDONLY on a directory. `buf` receives a packed sequence of +// linux_dirent64 records: +// +// struct linux_dirent64 { +// u64 d_ino; // 0..7 +// i64 d_off; // 8..15 +// u16 d_reclen; // 16..17 — total bytes for this record +// u8 d_type; // 18 — DT_REG/DT_DIR/... +// u8 d_name[]; // 19.. — NUL-terminated name + padding +// }; +// +// Returns bytes written into `buf` (advance by d_reclen to walk), +// 0 at end-of-directory, or a negative errno. +export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { + return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); +}; diff --git a/selfhost/cmd/6a/asm.ww b/selfhost/cmd/w6a/asm.ww similarity index 97% rename from selfhost/cmd/6a/asm.ww rename to selfhost/cmd/w6a/asm.ww index fba81cc9..66ab98c0 100644 --- a/selfhost/cmd/6a/asm.ww +++ b/selfhost/cmd/w6a/asm.ww @@ -1,10 +1,10 @@ -// selfhost/cmd/6a/asm.ww — port of cmd/6a/asm.c. +// selfhost/cmd/w6a/asm.ww — port of cmd/w6a/asm.c. // // Encode the parsed aprog list into amd64 machine bytes, appending to // asm_.text. Relocations for CALL/branch targets that resolve to // externals are queued in asm_.relocs. // -// Encoding subset matches what 6c emits — see cmd/6a/asm.c for the +// Encoding subset matches what w6c emits — see cmd/w6a/asm.c for the // authoritative list. Helpers (rcode/rhi/modrm/emit_rex etc.) are // fully ported; a_encode itself is still a stub pending the full // switch over A_*. @@ -354,7 +354,7 @@ export fn a_encode(a: *asm_) i32 = { a_addreloc(a, reloff, 2, s, -4i64); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVQ shape\n".ptr, 27u64); + os.write(2, "w6a: unsupported MOVQ shape\n".ptr, 27u64); a.errs += 1; p = p.link; continue; }; @@ -374,7 +374,7 @@ export fn a_encode(a: *asm_) i32 = { emit_modrm_mem(a, rcode(tt), p.from.reg, p.from.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVB shape\n".ptr, 27u64); + os.write(2, "w6a: unsupported MOVB shape\n".ptr, 27u64); a.errs += 1; p = p.link; continue; }; @@ -389,7 +389,7 @@ export fn a_encode(a: *asm_) i32 = { emit_modrm_mem(a, rcode(tt), p.from.reg, p.from.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVZBQ shape\n".ptr, 29u64); + os.write(2, "w6a: unsupported MOVZBQ shape\n".ptr, 29u64); a.errs += 1; p = p.link; continue; }; @@ -415,7 +415,7 @@ export fn a_encode(a: *asm_) i32 = { a_emit_byte(a, modrm_byte(3, rcode(ft), rcode(tt))); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVL shape\n".ptr, 27u64); + os.write(2, "w6a: unsupported MOVL shape\n".ptr, 27u64); a.errs += 1; p = p.link; continue; }; @@ -429,7 +429,7 @@ export fn a_encode(a: *asm_) i32 = { emit_modrm_mem(a, rcode(tt), p.from.reg, p.from.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVSXD shape\n".ptr, 29u64); + os.write(2, "w6a: unsupported MOVSXD shape\n".ptr, 29u64); a.errs += 1; p = p.link; continue; }; @@ -449,7 +449,7 @@ export fn a_encode(a: *asm_) i32 = { sse_mr_load(a, 242u8, 17u8, ft, p.to.reg, p.to.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVSD shape\n".ptr, 28u64); + os.write(2, "w6a: unsupported MOVSD shape\n".ptr, 28u64); a.errs += 1; p = p.link; continue; }; @@ -476,7 +476,7 @@ export fn a_encode(a: *asm_) i32 = { sse_mr_load(a, 243u8, 17u8, ft, p.to.reg, p.to.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVSS shape\n".ptr, 28u64); + os.write(2, "w6a: unsupported MOVSS shape\n".ptr, 28u64); a.errs += 1; p = p.link; continue; }; @@ -651,7 +651,7 @@ export fn a_encode(a: *asm_) i32 = { p = p.link; continue; }; - os.write(2, "6a: unsupported opcode\n".ptr, 23u64); + os.write(2, "w6a: unsupported opcode\n".ptr, 23u64); a.errs += 1; p = p.link; }; @@ -660,7 +660,7 @@ export fn a_encode(a: *asm_) i32 = { let f: *afixup = a.fixups; for (f != nil) { if (!label_defined(a, f.label)) { - os.write(2, "6a: undefined label '".ptr, 21u64); + os.write(2, "w6a: undefined label '".ptr, 21u64); let lbl: str = f.label; os.write(2, lbl.ptr, lbl.len: u64); os.write(2, "'\n".ptr, 2u64); diff --git a/selfhost/cmd/6a/lex.ww b/selfhost/cmd/w6a/lex.ww similarity index 93% rename from selfhost/cmd/6a/lex.ww rename to selfhost/cmd/w6a/lex.ww index 793aa71e..c3ed9418 100644 --- a/selfhost/cmd/6a/lex.ww +++ b/selfhost/cmd/w6a/lex.ww @@ -1,6 +1,6 @@ -// selfhost/cmd/6a/lex.ww — port of cmd/6a/lex.c. +// selfhost/cmd/w6a/lex.ww — port of cmd/w6a/lex.c. // -// Character-level helpers for 6a's line-oriented parser. The parser +// Character-level helpers for w6a's line-oriented parser. The parser // itself is in parse.ww; here we keep tokenisers for identifiers and // numbers so parse.ww stays focused on syntax. diff --git a/selfhost/cmd/6a/main.combined.ww b/selfhost/cmd/w6a/main.combined.ww similarity index 94% rename from selfhost/cmd/6a/main.combined.ww rename to selfhost/cmd/w6a/main.combined.ww index a300d860..08755cbf 100644 --- a/selfhost/cmd/6a/main.combined.ww +++ b/selfhost/cmd/w6a/main.combined.ww @@ -18,19 +18,21 @@ export fn assert(cond: bool, msg: str) void = { if (!cond) { abort(msg); }; }; -def SYS_READ: i64 = 0; -def SYS_WRITE: i64 = 1; -def SYS_OPEN: i64 = 2; -def SYS_CLOSE: i64 = 3; -def SYS_LSEEK: i64 = 8; -def SYS_ACCESS: i64 = 21; -def SYS_DUP2: i64 = 33; -def SYS_GETPID: i64 = 39; -def SYS_FORK: i64 = 57; -def SYS_EXECVE: i64 = 59; -def SYS_EXIT: i64 = 60; -def SYS_WAIT4: i64 = 61; -def SYS_UNLINK: i64 = 87; +def SYS_READ: i64 = 0; +def SYS_WRITE: i64 = 1; +def SYS_OPEN: i64 = 2; +def SYS_CLOSE: i64 = 3; +def SYS_LSEEK: i64 = 8; +def SYS_ACCESS: i64 = 21; +def SYS_DUP2: i64 = 33; +def SYS_GETPID: i64 = 39; +def SYS_FORK: i64 = 57; +def SYS_EXECVE: i64 = 59; +def SYS_EXIT: i64 = 60; +def SYS_WAIT4: i64 = 61; +def SYS_UNLINK: i64 = 87; +def SYS_GETCWD: i64 = 79; +def SYS_GETDENTS64: i64 = 217; // open(2) flags. Linux values, matching . def O_RDONLY: i32 = 0; @@ -65,7 +67,7 @@ export fn close(fd: i32) i32 = { // dup2(2): make `newfd` refer to the same description as `oldfd`, // closing `newfd` first if open. Returns `newfd` on success or a -// negative errno. Used by 6c_ww to redirect stdout into an output +// negative errno. Used by w6c_ww to redirect stdout into an output // file without changing the cgen emit path. export fn dup2(oldfd: i32, newfd: i32) i32 = { return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; @@ -179,7 +181,33 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { options: i64, rusage: i64): i32; }; -// selfhost/cmd/wwc/mem.ww — port of cmd/wwc/mem.c. +// getcwd(2) — Linux flavour. Writes the NUL-terminated cwd into `buf` +// and returns the number of bytes written (including the NUL), or a +// negative errno. The driver uses it to expand `.` to the cwd's +// basename for `ww build` / `ww test`. +export fn getcwd(buf: *u8, n: u64) i64 = { + return syscall2(SYS_GETCWD, buf: i64, n: i64); +}; + +// getdents64(2) — Linux directory enumeration. The fd must be opened +// with O_RDONLY on a directory. `buf` receives a packed sequence of +// linux_dirent64 records: +// +// struct linux_dirent64 { +// u64 d_ino; // 0..7 +// i64 d_off; // 8..15 +// u16 d_reclen; // 16..17 — total bytes for this record +// u8 d_type; // 18 — DT_REG/DT_DIR/... +// u8 d_name[]; // 19.. — NUL-terminated name + padding +// }; +// +// Returns bytes written into `buf` (advance by d_reclen to walk), +// 0 at end-of-directory, or a negative errno. +export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { + return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); +}; + +// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c. // // Bump arena allocator. Backed by the runtime page allocator // (rt_alloc / rt_free), no libc. Each chunk is mmap'd; when the @@ -187,7 +215,7 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { // unmaps the chain. // // Memory handed out is 16-byte aligned. The C version under -// cmd/wwc/ is retained until the three-stage bootstrap diffs clean. +// cmd/wcc/ is retained until the three-stage bootstrap diffs clean. use os; @@ -286,15 +314,15 @@ export fn freearena(a: *arena) void = { }; }; -// selfhost/cmd/6a/types.ww — types + constants shared across the -// 6a port. Mirrors cmd/6a/a.h and cmd/6c/6.out.h. +// selfhost/cmd/w6a/types.ww — types + constants shared across the +// w6a port. Mirrors cmd/w6a/a.h and cmd/w6c/6.out.h. use mem; // ---- registers + operand kinds (from 6.out.h) ------------------------- // These must stay numerically aligned with the C enum so that ww-cgen // output (which reads them via `D_AX(SB)` etc.) lands on the same -// integers when read by ww-6a. +// integers when read by ww-w6a. def D_NONE: i32 = 0; def D_AX: i32 = 1; @@ -409,7 +437,7 @@ def A_JNZ: i32 = 58; def A_SYSCALL: i32 = 59; -// ---- structs (mirror cmd/6a/a.h) -------------------------------------- +// ---- structs (mirror cmd/w6a/a.h) -------------------------------------- type aoperand = struct { atype: i32, // D_NONE / D_AX..D_R15 / D_CONST / D_INDIR / D_EXTERN / D_BRANCH @@ -480,9 +508,9 @@ type asm_ = struct { errs: i32, }; -// selfhost/cmd/6a/lex.ww — port of cmd/6a/lex.c. +// selfhost/cmd/w6a/lex.ww — port of cmd/w6a/lex.c. // -// Character-level helpers for 6a's line-oriented parser. The parser +// Character-level helpers for w6a's line-oriented parser. The parser // itself is in parse.ww; here we keep tokenisers for identifiers and // numbers so parse.ww stays focused on syntax. @@ -547,9 +575,9 @@ export fn a_parsenum(p: *u8, n: u64) (i64, u64) = { return v, i; }; -// selfhost/cmd/6a/parse.ww — port of cmd/6a/parse.c. +// selfhost/cmd/w6a/parse.ww — port of cmd/w6a/parse.c. // -// Line-oriented parser for the asm subset emitted by 6c. +// Line-oriented parser for the asm subset emitted by w6c. // Grammar: // line := blank | comment | label | text | instr // blank := /^\s*$/ @@ -719,7 +747,7 @@ export fn a_intern(a: *asm_, name: str) *asym = { }; fn perr(a: *asm_, msg: str) void = { - os.write(2, "6a: ".ptr, 4u64); + os.write(2, "w6a: ".ptr, 4u64); let f: str = a.file; os.write(2, f.ptr, f.len: u64); os.write(2, ": ".ptr, 2u64); @@ -1131,13 +1159,13 @@ export fn a_parse(a: *asm_) i32 = { return a.errs; }; -// selfhost/cmd/6a/asm.ww — port of cmd/6a/asm.c. +// selfhost/cmd/w6a/asm.ww — port of cmd/w6a/asm.c. // // Encode the parsed aprog list into amd64 machine bytes, appending to // asm_.text. Relocations for CALL/branch targets that resolve to // externals are queued in asm_.relocs. // -// Encoding subset matches what 6c emits — see cmd/6a/asm.c for the +// Encoding subset matches what w6c emits — see cmd/w6a/asm.c for the // authoritative list. Helpers (rcode/rhi/modrm/emit_rex etc.) are // fully ported; a_encode itself is still a stub pending the full // switch over A_*. @@ -1487,7 +1515,7 @@ export fn a_encode(a: *asm_) i32 = { a_addreloc(a, reloff, 2, s, -4i64); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVQ shape\n".ptr, 27u64); + os.write(2, "w6a: unsupported MOVQ shape\n".ptr, 27u64); a.errs += 1; p = p.link; continue; }; @@ -1507,7 +1535,7 @@ export fn a_encode(a: *asm_) i32 = { emit_modrm_mem(a, rcode(tt), p.from.reg, p.from.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVB shape\n".ptr, 27u64); + os.write(2, "w6a: unsupported MOVB shape\n".ptr, 27u64); a.errs += 1; p = p.link; continue; }; @@ -1522,7 +1550,7 @@ export fn a_encode(a: *asm_) i32 = { emit_modrm_mem(a, rcode(tt), p.from.reg, p.from.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVZBQ shape\n".ptr, 29u64); + os.write(2, "w6a: unsupported MOVZBQ shape\n".ptr, 29u64); a.errs += 1; p = p.link; continue; }; @@ -1548,7 +1576,7 @@ export fn a_encode(a: *asm_) i32 = { a_emit_byte(a, modrm_byte(3, rcode(ft), rcode(tt))); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVL shape\n".ptr, 27u64); + os.write(2, "w6a: unsupported MOVL shape\n".ptr, 27u64); a.errs += 1; p = p.link; continue; }; @@ -1562,7 +1590,7 @@ export fn a_encode(a: *asm_) i32 = { emit_modrm_mem(a, rcode(tt), p.from.reg, p.from.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVSXD shape\n".ptr, 29u64); + os.write(2, "w6a: unsupported MOVSXD shape\n".ptr, 29u64); a.errs += 1; p = p.link; continue; }; @@ -1582,7 +1610,7 @@ export fn a_encode(a: *asm_) i32 = { sse_mr_load(a, 242u8, 17u8, ft, p.to.reg, p.to.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVSD shape\n".ptr, 28u64); + os.write(2, "w6a: unsupported MOVSD shape\n".ptr, 28u64); a.errs += 1; p = p.link; continue; }; @@ -1609,7 +1637,7 @@ export fn a_encode(a: *asm_) i32 = { sse_mr_load(a, 243u8, 17u8, ft, p.to.reg, p.to.offset); p = p.link; continue; };}; - os.write(2, "6a: unsupported MOVSS shape\n".ptr, 28u64); + os.write(2, "w6a: unsupported MOVSS shape\n".ptr, 28u64); a.errs += 1; p = p.link; continue; }; @@ -1784,7 +1812,7 @@ export fn a_encode(a: *asm_) i32 = { p = p.link; continue; }; - os.write(2, "6a: unsupported opcode\n".ptr, 23u64); + os.write(2, "w6a: unsupported opcode\n".ptr, 23u64); a.errs += 1; p = p.link; }; @@ -1793,7 +1821,7 @@ export fn a_encode(a: *asm_) i32 = { let f: *afixup = a.fixups; for (f != nil) { if (!label_defined(a, f.label)) { - os.write(2, "6a: undefined label '".ptr, 21u64); + os.write(2, "w6a: undefined label '".ptr, 21u64); let lbl: str = f.label; os.write(2, lbl.ptr, lbl.len: u64); os.write(2, "'\n".ptr, 2u64); @@ -1813,7 +1841,7 @@ export fn a_encode(a: *asm_) i32 = { return a.errs; }; -// selfhost/cmd/6a/obj.ww — port of cmd/6a/obj.c. +// selfhost/cmd/w6a/obj.ww — port of cmd/w6a/obj.c. // // Emit a tiny ELF64 relocatable object. Layout (in file order): // [0] ELF header @@ -2107,11 +2135,11 @@ export fn a_emit_elf(a: *asm_, fd: i32) i32 = { return 0; }; -// selfhost/cmd/6a/main.ww — port of cmd/6a/main.c. +// selfhost/cmd/w6a/main.ww — port of cmd/w6a/main.c. // -// 6a = amd64 assembler. Read .s, parse, encode, emit ELF .o. +// w6a = amd64 assembler. Read .s, parse, encode, emit ELF .o. // -// 6a_ww -o file.o file.s +// w6a_ww -o file.o file.s use os; use mem; @@ -2164,16 +2192,16 @@ export fn main(argc: i32, argv: **u8) i32 = { if (streq_cs(a, "-o")) { i += 1; if (i >= argc) { - os.write(2, "6a: -o requires arg\n".ptr, 20u64); + os.write(2, "w6a: -o requires arg\n".ptr, 20u64); return 2; }; out_cs = argv[i]; } else { if (a[0u64] == 45u8) { - os.write(2, "6a: unknown flag\n".ptr, 17u64); + os.write(2, "w6a: unknown flag\n".ptr, 17u64); return 2; } else { if (src_cs != nil) { - os.write(2, "6a: only one input\n".ptr, 19u64); + os.write(2, "w6a: only one input\n".ptr, 19u64); return 2; }; src_cs = a; @@ -2182,11 +2210,11 @@ export fn main(argc: i32, argv: **u8) i32 = { }; if (src_cs == nil) { - os.write(2, "usage: 6a_ww -o file.o file.s\n".ptr, 30u64); + os.write(2, "usage: w6a_ww -o file.o file.s\n".ptr, 30u64); return 2; }; if (out_cs == nil) { - os.write(2, "6a: missing -o\n".ptr, 15u64); + os.write(2, "w6a: missing -o\n".ptr, 15u64); return 2; }; @@ -2194,7 +2222,7 @@ export fn main(argc: i32, argv: **u8) i32 = { let blen: u64; buf, blen = slurp(src_cs); if (buf == nil) { - os.write(2, "6a: cannot read input\n".ptr, 22u64); + os.write(2, "w6a: cannot read input\n".ptr, 22u64); return 1; }; @@ -2210,7 +2238,7 @@ export fn main(argc: i32, argv: **u8) i32 = { // Open output for write. let fd: i32 = os.open(out_cs, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644 if (fd < 0) { - os.write(2, "6a: cannot open output\n".ptr, 23u64); + os.write(2, "w6a: cannot open output\n".ptr, 23u64); return 1; }; let rc: i32 = a_emit_elf(&asm, fd); diff --git a/selfhost/cmd/6a/main.ww b/selfhost/cmd/w6a/main.ww similarity index 78% rename from selfhost/cmd/6a/main.ww rename to selfhost/cmd/w6a/main.ww index cd7a9d1e..7999bdd7 100644 --- a/selfhost/cmd/6a/main.ww +++ b/selfhost/cmd/w6a/main.ww @@ -1,8 +1,8 @@ -// selfhost/cmd/6a/main.ww — port of cmd/6a/main.c. +// selfhost/cmd/w6a/main.ww — port of cmd/w6a/main.c. // -// 6a = amd64 assembler. Read .s, parse, encode, emit ELF .o. +// w6a = amd64 assembler. Read .s, parse, encode, emit ELF .o. // -// 6a_ww -o file.o file.s +// w6a_ww -o file.o file.s use os; use mem; @@ -55,16 +55,16 @@ export fn main(argc: i32, argv: **u8) i32 = { if (streq_cs(a, "-o")) { i += 1; if (i >= argc) { - os.write(2, "6a: -o requires arg\n".ptr, 20u64); + os.write(2, "w6a: -o requires arg\n".ptr, 20u64); return 2; }; out_cs = argv[i]; } else { if (a[0u64] == 45u8) { - os.write(2, "6a: unknown flag\n".ptr, 17u64); + os.write(2, "w6a: unknown flag\n".ptr, 17u64); return 2; } else { if (src_cs != nil) { - os.write(2, "6a: only one input\n".ptr, 19u64); + os.write(2, "w6a: only one input\n".ptr, 19u64); return 2; }; src_cs = a; @@ -73,11 +73,11 @@ export fn main(argc: i32, argv: **u8) i32 = { }; if (src_cs == nil) { - os.write(2, "usage: 6a_ww -o file.o file.s\n".ptr, 30u64); + os.write(2, "usage: w6a_ww -o file.o file.s\n".ptr, 30u64); return 2; }; if (out_cs == nil) { - os.write(2, "6a: missing -o\n".ptr, 15u64); + os.write(2, "w6a: missing -o\n".ptr, 15u64); return 2; }; @@ -85,7 +85,7 @@ export fn main(argc: i32, argv: **u8) i32 = { let blen: u64; buf, blen = slurp(src_cs); if (buf == nil) { - os.write(2, "6a: cannot read input\n".ptr, 22u64); + os.write(2, "w6a: cannot read input\n".ptr, 22u64); return 1; }; @@ -101,7 +101,7 @@ export fn main(argc: i32, argv: **u8) i32 = { // Open output for write. let fd: i32 = os.open(out_cs, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644 if (fd < 0) { - os.write(2, "6a: cannot open output\n".ptr, 23u64); + os.write(2, "w6a: cannot open output\n".ptr, 23u64); return 1; }; let rc: i32 = a_emit_elf(&asm, fd); diff --git a/selfhost/cmd/6a/obj.ww b/selfhost/cmd/w6a/obj.ww similarity index 99% rename from selfhost/cmd/6a/obj.ww rename to selfhost/cmd/w6a/obj.ww index dbd740f7..bdfd399e 100644 --- a/selfhost/cmd/6a/obj.ww +++ b/selfhost/cmd/w6a/obj.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/6a/obj.ww — port of cmd/6a/obj.c. +// selfhost/cmd/w6a/obj.ww — port of cmd/w6a/obj.c. // // Emit a tiny ELF64 relocatable object. Layout (in file order): // [0] ELF header diff --git a/selfhost/cmd/6a/parse.ww b/selfhost/cmd/w6a/parse.ww similarity index 99% rename from selfhost/cmd/6a/parse.ww rename to selfhost/cmd/w6a/parse.ww index 3a551fb0..dc5fa789 100644 --- a/selfhost/cmd/6a/parse.ww +++ b/selfhost/cmd/w6a/parse.ww @@ -1,6 +1,6 @@ -// selfhost/cmd/6a/parse.ww — port of cmd/6a/parse.c. +// selfhost/cmd/w6a/parse.ww — port of cmd/w6a/parse.c. // -// Line-oriented parser for the asm subset emitted by 6c. +// Line-oriented parser for the asm subset emitted by w6c. // Grammar: // line := blank | comment | label | text | instr // blank := /^\s*$/ @@ -170,7 +170,7 @@ export fn a_intern(a: *asm_, name: str) *asym = { }; fn perr(a: *asm_, msg: str) void = { - os.write(2, "6a: ".ptr, 4u64); + os.write(2, "w6a: ".ptr, 4u64); let f: str = a.file; os.write(2, f.ptr, f.len: u64); os.write(2, ": ".ptr, 2u64); diff --git a/selfhost/cmd/6a/types.ww b/selfhost/cmd/w6a/types.ww similarity index 94% rename from selfhost/cmd/6a/types.ww rename to selfhost/cmd/w6a/types.ww index 72797a58..1db5a82c 100644 --- a/selfhost/cmd/6a/types.ww +++ b/selfhost/cmd/w6a/types.ww @@ -1,12 +1,12 @@ -// selfhost/cmd/6a/types.ww — types + constants shared across the -// 6a port. Mirrors cmd/6a/a.h and cmd/6c/6.out.h. +// selfhost/cmd/w6a/types.ww — types + constants shared across the +// w6a port. Mirrors cmd/w6a/a.h and cmd/w6c/6.out.h. use mem; // ---- registers + operand kinds (from 6.out.h) ------------------------- // These must stay numerically aligned with the C enum so that ww-cgen // output (which reads them via `D_AX(SB)` etc.) lands on the same -// integers when read by ww-6a. +// integers when read by ww-w6a. def D_NONE: i32 = 0; def D_AX: i32 = 1; @@ -121,7 +121,7 @@ def A_JNZ: i32 = 58; def A_SYSCALL: i32 = 59; -// ---- structs (mirror cmd/6a/a.h) -------------------------------------- +// ---- structs (mirror cmd/w6a/a.h) -------------------------------------- type aoperand = struct { atype: i32, // D_NONE / D_AX..D_R15 / D_CONST / D_INDIR / D_EXTERN / D_BRANCH diff --git a/selfhost/cmd/6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww similarity index 97% rename from selfhost/cmd/6c/main.combined.ww rename to selfhost/cmd/w6c/main.combined.ww index c15942f5..a4f36648 100644 --- a/selfhost/cmd/6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -18,19 +18,21 @@ export fn assert(cond: bool, msg: str) void = { if (!cond) { abort(msg); }; }; -def SYS_READ: i64 = 0; -def SYS_WRITE: i64 = 1; -def SYS_OPEN: i64 = 2; -def SYS_CLOSE: i64 = 3; -def SYS_LSEEK: i64 = 8; -def SYS_ACCESS: i64 = 21; -def SYS_DUP2: i64 = 33; -def SYS_GETPID: i64 = 39; -def SYS_FORK: i64 = 57; -def SYS_EXECVE: i64 = 59; -def SYS_EXIT: i64 = 60; -def SYS_WAIT4: i64 = 61; -def SYS_UNLINK: i64 = 87; +def SYS_READ: i64 = 0; +def SYS_WRITE: i64 = 1; +def SYS_OPEN: i64 = 2; +def SYS_CLOSE: i64 = 3; +def SYS_LSEEK: i64 = 8; +def SYS_ACCESS: i64 = 21; +def SYS_DUP2: i64 = 33; +def SYS_GETPID: i64 = 39; +def SYS_FORK: i64 = 57; +def SYS_EXECVE: i64 = 59; +def SYS_EXIT: i64 = 60; +def SYS_WAIT4: i64 = 61; +def SYS_UNLINK: i64 = 87; +def SYS_GETCWD: i64 = 79; +def SYS_GETDENTS64: i64 = 217; // open(2) flags. Linux values, matching . def O_RDONLY: i32 = 0; @@ -65,7 +67,7 @@ export fn close(fd: i32) i32 = { // dup2(2): make `newfd` refer to the same description as `oldfd`, // closing `newfd` first if open. Returns `newfd` on success or a -// negative errno. Used by 6c_ww to redirect stdout into an output +// negative errno. Used by w6c_ww to redirect stdout into an output // file without changing the cgen emit path. export fn dup2(oldfd: i32, newfd: i32) i32 = { return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; @@ -179,7 +181,33 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { options: i64, rusage: i64): i32; }; -// selfhost/cmd/wwc/mem.ww — port of cmd/wwc/mem.c. +// getcwd(2) — Linux flavour. Writes the NUL-terminated cwd into `buf` +// and returns the number of bytes written (including the NUL), or a +// negative errno. The driver uses it to expand `.` to the cwd's +// basename for `ww build` / `ww test`. +export fn getcwd(buf: *u8, n: u64) i64 = { + return syscall2(SYS_GETCWD, buf: i64, n: i64); +}; + +// getdents64(2) — Linux directory enumeration. The fd must be opened +// with O_RDONLY on a directory. `buf` receives a packed sequence of +// linux_dirent64 records: +// +// struct linux_dirent64 { +// u64 d_ino; // 0..7 +// i64 d_off; // 8..15 +// u16 d_reclen; // 16..17 — total bytes for this record +// u8 d_type; // 18 — DT_REG/DT_DIR/... +// u8 d_name[]; // 19.. — NUL-terminated name + padding +// }; +// +// Returns bytes written into `buf` (advance by d_reclen to walk), +// 0 at end-of-directory, or a negative errno. +export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { + return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); +}; + +// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c. // // Bump arena allocator. Backed by the runtime page allocator // (rt_alloc / rt_free), no libc. Each chunk is mmap'd; when the @@ -187,7 +215,7 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { // unmaps the chain. // // Memory handed out is 16-byte aligned. The C version under -// cmd/wwc/ is retained until the three-stage bootstrap diffs clean. +// cmd/wcc/ is retained until the three-stage bootstrap diffs clean. use os; @@ -406,8 +434,8 @@ export fn parseu64(s: str) (u64 | str) = { return v; }; -// selfhost/cmd/wwc/tok.ww — port of cmd/wwc/tok.c plus the Tkind / -// Tok / Pos shapes from cmd/wwc/ww.h. +// selfhost/cmd/wcc/tok.ww — port of cmd/wcc/tok.c plus the Tkind / +// Tok / Pos shapes from cmd/wcc/ww.h. // // Token kind values must stay numerically equal to the C side: the // 990_selfhost test diffs ww-side wwdump output against C-side @@ -415,13 +443,13 @@ export fn parseu64(s: str) (u64 | str) = { // integers and breaks the diff. // // Bottom of file: tokprint, which emits one token per line in a -// format identical to cmd/wwc/tok.c:tokprint(). +// format identical to cmd/wcc/tok.c:tokprint(). use os; use strconv; // ---- Tkind ------------------------------------------------------------ -// Mirror of the C enum in cmd/wwc/ww.h. Don't reorder. +// Mirror of the C enum in cmd/wcc/ww.h. Don't reorder. def TK_NONE: i32 = 0; def TK_EOF: i32 = 1; @@ -516,7 +544,7 @@ def TK_LAST: i32 = 80; // ---- Pos / Tok -------------------------------------------------------- // // `pos` is used at error-reporting boundaries; we always pass it via -// *pos so the value never gets struct-copied (6c can't yet copy a +// *pos so the value never gets struct-copied (w6c can't yet copy a // 24-byte struct). // // `tok` is flat — file/line/col live directly on the token rather than @@ -554,7 +582,7 @@ fn streq_n(a: *u8, b: str, n: i32) bool = { // kwlookup — returns the matching TK_* keyword kind for a byte run, // or TK_NONE if it's an ordinary identifier. Linear search over a -// small alphabetised list, matching cmd/wwc/tok.c. +// small alphabetised list, matching cmd/wcc/tok.c. export fn kwlookup(p: *u8, n: i32) i32 = { if (streq_n(p, "as", n)) { return TK_AS; }; if (streq_n(p, "break", n)) { return TK_BREAK; }; @@ -683,7 +711,7 @@ export fn tokname(k: i32) str = { // ---- writer for tokprint ---------------------------------------------- // -// fputq mirrors cmd/wwc/tok.c:fputq — quote the string with C-style +// fputq mirrors cmd/wcc/tok.c:fputq — quote the string with C-style // escapes for \, ", \n, \t, \r and \xNN for other non-printables. fn fputc_byte(fd: i32, b: u8) void = { @@ -750,14 +778,14 @@ fn fputq(fd: i32, p: *u8, n: i32) void = { }; // tokprint — write one token line to fd. Format must match -// cmd/wwc/tok.c:tokprint() byte-for-byte: that's the diff anchor. +// cmd/wcc/tok.c:tokprint() byte-for-byte: that's the diff anchor. // ":: [ ]\n" // -// Takes `t` by pointer because 6c can't yet pass a >16-byte struct +// Takes `t` by pointer because w6c can't yet pass a >16-byte struct // by value; the C version takes Tok by value. export fn tokprint(fd: i32, t: *tok) void = { // Chained-dot field reads (`t.x.y`) on str sub-fields aren't yet - // reduced by 6c — `t.x.y` returns the whole str. Lift the str + // reduced by w6c — `t.x.y` returns the whole str. Lift the str // fields into locals so we can use the str pseudo-field path. let tfile: str = t.file; let ttext: str = t.text; @@ -894,14 +922,14 @@ export fn toupper(c: u8) u8 = { return c; }; -// selfhost/cmd/wwc/lex.ww — port of cmd/wwc/lex.c. +// selfhost/cmd/wcc/lex.ww — port of cmd/wcc/lex.c. // // The DFA, the helpers, and the order of decisions all mirror the C // version exactly. The 990_selfhost test diffs the resulting token // stream against the C-side wwdump byte-for-byte; any divergence is // a port bug. // -// Calling-convention note: 6c can't yet pass or return structs >16 +// Calling-convention note: w6c can't yet pass or return structs >16 // bytes by value, so `tok` and `pos` are passed by pointer (out // params). The C version passes `Tok` by value; we differ here only // in shape, not in observable behaviour. Token kind values stay @@ -1551,7 +1579,7 @@ export fn lexnext(l: *lex, out: *tok) void = { out.text = astrndup(l.a, one.ptr, 1u64); }; -// selfhost/cmd/wwc/ast.ww — port of cmd/wwc/ast.c (Node defs + printer). +// selfhost/cmd/wcc/ast.ww — port of cmd/wcc/ast.c (Node defs + printer). // // Status: AST printer is fully ported. Constructor `newnode` is here. // The parser (parse.ww) is currently minimal — see its file header. @@ -1567,7 +1595,7 @@ use tok; // ---- Nkind ------------------------------------------------------------ // -// Mirror of cmd/wwc/ww.h Nkind. Values must stay numerically equal so +// Mirror of cmd/wcc/ww.h Nkind. Values must stay numerically equal so // the AST diff probe in 990_selfhost works. def N_NONE: i32 = 0; @@ -1887,7 +1915,7 @@ export fn astprint(fd: i32, n: *node) void = { pr(fd, n, 0); }; -// selfhost/cmd/wwc/parse.ww — port of cmd/wwc/parse.c. +// selfhost/cmd/wcc/parse.ww — port of cmd/wcc/parse.c. // // Status: GROWING stub. Currently handles top-level `use IDENT;`, // `def NAME: TYPE = LIT;`, `type NAME = TYPE;`, and `fn NAME(params) @@ -1900,7 +1928,7 @@ export fn astprint(fd: i32, n: *node) void = { // surface form lands here gradually so the AST diff in 990_selfhost // grows toward whole-language coverage one increment at a time. // -// Calling-convention shim: 6c can't yet pass a sub-struct field +// Calling-convention shim: w6c can't yet pass a sub-struct field // (e.g. p.cur.line where p.cur is a `tok` of size 76). The parser // stores the current token as flat primitive fields rather than a // nested `tok` struct; `refill` copies a freshly lexed token in. @@ -2635,7 +2663,7 @@ fn parsestmt(p: *parser) *node = { }; // expression statement, or tuple-destructure multi-assign: // a, b = expr; - // Mirrors cmd/wwc/parse.c:1015-1031. We parse the first lvalue + // Mirrors cmd/wcc/parse.c:1015-1031. We parse the first lvalue // with parseexpr (matches the C side); subsequent lvalues go // through parsebin(parseunary, 1) so the `=` stays for us to // consume — parseexpr would absorb it. @@ -2870,7 +2898,7 @@ export fn parsefile(p: *parser) *node = { return f; }; -// selfhost/cmd/wwc/type.ww — port of cmd/wwc/type.c. +// selfhost/cmd/wcc/type.ww — port of cmd/wcc/type.c. // // Status: full structural port. The C version uses module-globals for // the primitive types (ty_void, ty_i32, …); ww doesn't have writable @@ -2882,7 +2910,7 @@ use os; use mem; // ---- TypeKind --------------------------------------------------------- -// Numeric values must stay aligned with cmd/wwc/ww.h TypeKind so the +// Numeric values must stay aligned with cmd/wcc/ww.h TypeKind so the // next diff signal (typed-AST printer / cgen) can compare across the // two implementations. @@ -3200,7 +3228,7 @@ export fn type_eq(a: *tinfo, b: *tinfo) bool = { return true; // primitives match by kind alone }; -// selfhost/cmd/wwc/sym.ww — port of cmd/wwc/sym.c. +// selfhost/cmd/wcc/sym.ww — port of cmd/wcc/sym.c. // // Per-scope hashtable, chained to the parent. Lookup walks up. // Plan 9 / Hare flavoured. Duplicate definitions in the same scope @@ -3210,7 +3238,7 @@ use mem; use typ; use ast; -// Symbol kinds — must stay numerically aligned with cmd/wwc/ww.h Skind. +// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind. def SK_NONE: i32 = 0; def SK_VAR: i32 = 1; def SK_PARAM: i32 = 2; @@ -3314,11 +3342,11 @@ export fn scope_define(s: *scope, name: str, k: i32, t: *tinfo, decl: *node) *sy return sy; }; -// selfhost/cmd/wwc/check.ww — minimal port of cmd/wwc/check.c. +// selfhost/cmd/wcc/check.ww — minimal port of cmd/wcc/check.c. // // Status: name-resolution + primitive-type seeding only. Full type // inference, conversion rules, tagged-union dispatch typing, return- -// type checking, etc. all live in cmd/wwc/check.c (937 lines) and +// type checking, etc. all live in cmd/wcc/check.c (937 lines) and // will land here in subsequent commits. // // What this version does: @@ -3562,10 +3590,10 @@ export fn check_file(c: *checker, file: *node) void = { }; }; -// selfhost/cmd/wwc/cgen.ww — port of cmd/6c/cgen.c. +// selfhost/cmd/wcc/cgen.ww — port of cmd/w6c/cgen.c. // // Status: GROWING. Each subsystem we add is verified by `wwdump_ww -c` -// producing byte-identical output to C-side `6c` for the same source, +// producing byte-identical output to C-side `w6c` for the same source, // then by assembling + linking + running the result. // // Current coverage: @@ -3760,7 +3788,7 @@ fn local_alloc(c: *cgen, name: str, sz: i32, tnode: *node) i32 = { fn local_add(c: *cgen, name: str, sz: i32, tnode: *node) i32 = { // Name-based slot reuse for N_LETs and params: if `name` is // already declared in this function, return its existing - // offset. Mirrors C cgen (cmd/6c/cgen.c:localoff). Two + // offset. Mirrors C cgen (cmd/w6c/cgen.c:localoff). Two // disjoint scopes that declare the same name share one slot — // so `escape` in wwdump (three `let cp: pos;` across separate // branches) reserves one slot, not three. scan_locals does @@ -4097,7 +4125,7 @@ fn fnret_lookup(c: *cgen, name: str) *node = { // ---- def-constant registry ------------------------------------------ // -// `def NAME: T = LIT;` becomes a DATA symbol the C-side 6c emits; an +// `def NAME: T = LIT;` becomes a DATA symbol the C-side w6c emits; an // ident reference loads it via `MOVQ NAME(SB), AX`. We collect them at // file load and consult on N_IDENT lookup. @@ -4725,7 +4753,7 @@ fn node_isunsigned(c: *cgen, n: *node) bool = { // Walks the base local's declared type and pulls the element // out — *u8 → u8, [N]u32 → u32, []u64 → u64. Without this the // compare-codegen for `p[i] >= 48u8` falls back to signed JGE - // instead of JAE, diverging from C 6c on byte indexing. + // instead of JAE, diverging from C w6c on byte indexing. if (k == N_INDEX) { let base: *node = n.lhs; if (base != nil) { @@ -5198,7 +5226,7 @@ fn cgexpr(c: *cgen, n: *node) void = { // path the cgen falls through and AX retains whatever the // inner expression left there — typically the *struct pointer // itself, so reads silently get the pointer value instead of - // the field. (Showed up porting 6l/pass.ww.) + // the field. (Showed up porting w6l/pass.ww.) if (lhs != nil) { if (lhs.kind == N_DOT) { let inner_t: *node = dot_inner_struct_ptr(c, lhs); @@ -5487,7 +5515,7 @@ fn cgexpr(c: *cgen, n: *node) void = { }; cgexpr(c, n.rhs); // Push order matches C cgen - // (cmd/6c/cgen.c:1033-1041): PUSHQ AX + // (cmd/w6c/cgen.c:1033-1041): PUSHQ AX // (ptr) first, then PUSHQ BX (len) if // str, so the pop sequence is POP CX // (len) → POP AX (ptr) → MOVQ AX, @@ -6259,7 +6287,7 @@ fn cgstmt(c: *cgen, n: *node) void = { }; } else { // Bare `let x: T;` with no initializer. C cgen - // (cmd/6c/cgen.c:2181-2183) zero-inits only when + // (cmd/w6c/cgen.c:2181-2183) zero-inits only when // the underlying type's natural size is 8 — pointers, // i64/u64, function pointers, ints. Structs/arrays/ // slices/strings/tagged/tuples are left for per-field @@ -6330,7 +6358,7 @@ fn cgstmt(c: *cgen, n: *node) void = { // Tuple-destructure assign: `a, b = call();`. The call's tuple // return lands in (AX, DX); push DX to free it, store AX into // the first lvalue, then pop DX into the second. Mirrors - // cmd/6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same + // cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same // as C — no fixture uses >2 today). if (k == N_MASSIGN) { if (n.rhs != nil) { cgexpr(c, n.rhs); }; @@ -6645,14 +6673,14 @@ export fn cg_file(c: *cgen, file: *node) void = { emit_def_constants(c, file); }; -// selfhost/cmd/6c/main.ww — port of cmd/6c/main.c. +// selfhost/cmd/w6c/main.ww — port of cmd/w6c/main.c. // -// 6c = amd64 compiler. Read .ww, parse, codegen, emit Plan 9 amd64 +// w6c = amd64 compiler. Read .ww, parse, codegen, emit Plan 9 amd64 // asm to stdout (or the file given by -o). // -// 6c_ww -o file.s file.ww +// w6c_ww -o file.s file.ww // -// The cgen routines in selfhost/cmd/wwc/cgen.ww write directly to +// The cgen routines in selfhost/cmd/wcc/cgen.ww write directly to // fd 1 via os.write(1, ...). For -o, we open the output file and // dup2 it onto fd 1 before invoking cg_file. This is the same trick // the bootstrap uses with shell redirection, just in-process. @@ -6710,16 +6738,16 @@ export fn main(argc: i32, argv: **u8) i32 = { if (streq_cs(a, "-o")) { i += 1; if (i >= argc) { - os.write(2, "6c: -o requires arg\n".ptr, 20u64); + os.write(2, "w6c: -o requires arg\n".ptr, 20u64); return 2; }; out_cs = argv[i]; } else { if (a[0u64] == 45u8) { - os.write(2, "6c: unknown flag\n".ptr, 17u64); + os.write(2, "w6c: unknown flag\n".ptr, 17u64); return 2; } else { if (src_cs != nil) { - os.write(2, "6c: only one input\n".ptr, 19u64); + os.write(2, "w6c: only one input\n".ptr, 19u64); return 2; }; src_cs = a; @@ -6728,7 +6756,7 @@ export fn main(argc: i32, argv: **u8) i32 = { }; if (src_cs == nil) { - os.write(2, "usage: 6c_ww [-o out.s] file.ww\n".ptr, 32u64); + os.write(2, "usage: w6c_ww [-o out.s] file.ww\n".ptr, 32u64); return 2; }; @@ -6736,7 +6764,7 @@ export fn main(argc: i32, argv: **u8) i32 = { let blen: u64; buf, blen = slurp(src_cs); if (buf == nil) { - os.write(2, "6c: cannot read input\n".ptr, 22u64); + os.write(2, "w6c: cannot read input\n".ptr, 22u64); return 1; }; @@ -6747,11 +6775,11 @@ export fn main(argc: i32, argv: **u8) i32 = { let ofd: i32 = os.open(out_cs, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644 if (ofd < 0) { - os.write(2, "6c: cannot open output\n".ptr, 23u64); + os.write(2, "w6c: cannot open output\n".ptr, 23u64); return 1; }; if (os.dup2(ofd, 1i32) < 0) { - os.write(2, "6c: dup2 failed\n".ptr, 16u64); + os.write(2, "w6c: dup2 failed\n".ptr, 16u64); os.close(ofd); return 1; }; diff --git a/selfhost/cmd/6c/main.ww b/selfhost/cmd/w6c/main.ww similarity index 79% rename from selfhost/cmd/6c/main.ww rename to selfhost/cmd/w6c/main.ww index 1aea7044..0766f0cf 100644 --- a/selfhost/cmd/6c/main.ww +++ b/selfhost/cmd/w6c/main.ww @@ -1,11 +1,11 @@ -// selfhost/cmd/6c/main.ww — port of cmd/6c/main.c. +// selfhost/cmd/w6c/main.ww — port of cmd/w6c/main.c. // -// 6c = amd64 compiler. Read .ww, parse, codegen, emit Plan 9 amd64 +// w6c = amd64 compiler. Read .ww, parse, codegen, emit Plan 9 amd64 // asm to stdout (or the file given by -o). // -// 6c_ww -o file.s file.ww +// w6c_ww -o file.s file.ww // -// The cgen routines in selfhost/cmd/wwc/cgen.ww write directly to +// The cgen routines in selfhost/cmd/wcc/cgen.ww write directly to // fd 1 via os.write(1, ...). For -o, we open the output file and // dup2 it onto fd 1 before invoking cg_file. This is the same trick // the bootstrap uses with shell redirection, just in-process. @@ -63,16 +63,16 @@ export fn main(argc: i32, argv: **u8) i32 = { if (streq_cs(a, "-o")) { i += 1; if (i >= argc) { - os.write(2, "6c: -o requires arg\n".ptr, 20u64); + os.write(2, "w6c: -o requires arg\n".ptr, 20u64); return 2; }; out_cs = argv[i]; } else { if (a[0u64] == 45u8) { - os.write(2, "6c: unknown flag\n".ptr, 17u64); + os.write(2, "w6c: unknown flag\n".ptr, 17u64); return 2; } else { if (src_cs != nil) { - os.write(2, "6c: only one input\n".ptr, 19u64); + os.write(2, "w6c: only one input\n".ptr, 19u64); return 2; }; src_cs = a; @@ -81,7 +81,7 @@ export fn main(argc: i32, argv: **u8) i32 = { }; if (src_cs == nil) { - os.write(2, "usage: 6c_ww [-o out.s] file.ww\n".ptr, 32u64); + os.write(2, "usage: w6c_ww [-o out.s] file.ww\n".ptr, 32u64); return 2; }; @@ -89,7 +89,7 @@ export fn main(argc: i32, argv: **u8) i32 = { let blen: u64; buf, blen = slurp(src_cs); if (buf == nil) { - os.write(2, "6c: cannot read input\n".ptr, 22u64); + os.write(2, "w6c: cannot read input\n".ptr, 22u64); return 1; }; @@ -100,11 +100,11 @@ export fn main(argc: i32, argv: **u8) i32 = { let ofd: i32 = os.open(out_cs, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644 if (ofd < 0) { - os.write(2, "6c: cannot open output\n".ptr, 23u64); + os.write(2, "w6c: cannot open output\n".ptr, 23u64); return 1; }; if (os.dup2(ofd, 1i32) < 0) { - os.write(2, "6c: dup2 failed\n".ptr, 16u64); + os.write(2, "w6c: dup2 failed\n".ptr, 16u64); os.close(ofd); return 1; }; diff --git a/selfhost/cmd/6l/dyn.ww b/selfhost/cmd/w6l/dyn.ww similarity index 98% rename from selfhost/cmd/6l/dyn.ww rename to selfhost/cmd/w6l/dyn.ww index 7d8629eb..34ebf2ef 100644 --- a/selfhost/cmd/6l/dyn.ww +++ b/selfhost/cmd/w6l/dyn.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/6l/dyn.ww — port of cmd/6l/dyn.c. +// selfhost/cmd/w6l/dyn.ww — port of cmd/w6l/dyn.c. // // Load a shared object (ET_DYN) so the linker knows which symbols it // exports and which DT_NEEDED entry to record. We do not pull bytes @@ -167,11 +167,11 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = { let blen: u64; buf, blen = read_all_so(path_cs); if (buf == nil) { - os.write(2, "6l: cannot read .so\n".ptr, 20u64); + os.write(2, "w6l: cannot read .so\n".ptr, 20u64); return -1; }; if (blen < 64u64) { - os.write(2, "6l: short ELF\n".ptr, 14u64); + os.write(2, "w6l: short ELF\n".ptr, 14u64); return -1; }; if (buf[0u64] != 127u8) { return so_err("not ELF"); }; @@ -359,7 +359,7 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = { }; fn so_err(msg: str) i32 = { - os.write(2, "6l: ".ptr, 4u64); + os.write(2, "w6l: ".ptr, 4u64); os.write(2, msg.ptr, msg.len: u64); os.write(2, "\n".ptr, 1u64); return -1; diff --git a/selfhost/cmd/6l/dynout.ww b/selfhost/cmd/w6l/dynout.ww similarity index 98% rename from selfhost/cmd/6l/dynout.ww rename to selfhost/cmd/w6l/dynout.ww index 204bf44a..a952f706 100644 --- a/selfhost/cmd/6l/dynout.ww +++ b/selfhost/cmd/w6l/dynout.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/6l/dynout.ww — port of cmd/6l/dynout.c. +// selfhost/cmd/w6l/dynout.ww — port of cmd/w6l/dynout.c. // // Emit a dynamic-linked ELF executable. The shape is the simplest // valid one: PT_INTERP + PT_DYNAMIC + DT_BIND_NOW so the loader @@ -196,7 +196,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { let i: i32 = 0; for (i < n) { if (dynsyms[i] == nil) { - os.write(2, "6l: dynout: no sym for plt_idx\n".ptr, 31u64); + os.write(2, "w6l: dynout: no sym for plt_idx\n".ptr, 31u64); return 1; }; i += 1; @@ -621,7 +621,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { }; d_wri64(dynamic_buf, dk * 16u64, DT_NULL); d_wr64(dynamic_buf, dk * 16u64 + 8u64, 0u64); dk += 1u64; if (dk != ndyn) { - os.write(2, "6l: dynamic entry count mismatch\n".ptr, 33u64); + os.write(2, "w6l: dynamic entry count mismatch\n".ptr, 33u64); return 1; }; @@ -633,7 +633,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { if (rsym.is_dyn != 0) { if (r.kind != R_X86_64_PC32_D) { if (r.kind != R_X86_64_PLT32_D) { - os.write(2, "6l: dynamic reloc kind unsupported\n".ptr, 35u64); + os.write(2, "w6l: dynamic reloc kind unsupported\n".ptr, 35u64); return 1; }; }; @@ -649,7 +649,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { // ---- assemble file buffer ---- let file_buf: *u8 = os.alloc(file_end): *u8; if (file_buf == nil) { - os.write(2, "6l: out of memory\n".ptr, 18u64); + os.write(2, "w6l: out of memory\n".ptr, 18u64); return 1; }; diff --git a/selfhost/cmd/6l/main.combined.ww b/selfhost/cmd/w6l/main.combined.ww similarity index 95% rename from selfhost/cmd/6l/main.combined.ww rename to selfhost/cmd/w6l/main.combined.ww index 2125c4e2..f1acb258 100644 --- a/selfhost/cmd/6l/main.combined.ww +++ b/selfhost/cmd/w6l/main.combined.ww @@ -18,19 +18,21 @@ export fn assert(cond: bool, msg: str) void = { if (!cond) { abort(msg); }; }; -def SYS_READ: i64 = 0; -def SYS_WRITE: i64 = 1; -def SYS_OPEN: i64 = 2; -def SYS_CLOSE: i64 = 3; -def SYS_LSEEK: i64 = 8; -def SYS_ACCESS: i64 = 21; -def SYS_DUP2: i64 = 33; -def SYS_GETPID: i64 = 39; -def SYS_FORK: i64 = 57; -def SYS_EXECVE: i64 = 59; -def SYS_EXIT: i64 = 60; -def SYS_WAIT4: i64 = 61; -def SYS_UNLINK: i64 = 87; +def SYS_READ: i64 = 0; +def SYS_WRITE: i64 = 1; +def SYS_OPEN: i64 = 2; +def SYS_CLOSE: i64 = 3; +def SYS_LSEEK: i64 = 8; +def SYS_ACCESS: i64 = 21; +def SYS_DUP2: i64 = 33; +def SYS_GETPID: i64 = 39; +def SYS_FORK: i64 = 57; +def SYS_EXECVE: i64 = 59; +def SYS_EXIT: i64 = 60; +def SYS_WAIT4: i64 = 61; +def SYS_UNLINK: i64 = 87; +def SYS_GETCWD: i64 = 79; +def SYS_GETDENTS64: i64 = 217; // open(2) flags. Linux values, matching . def O_RDONLY: i32 = 0; @@ -65,7 +67,7 @@ export fn close(fd: i32) i32 = { // dup2(2): make `newfd` refer to the same description as `oldfd`, // closing `newfd` first if open. Returns `newfd` on success or a -// negative errno. Used by 6c_ww to redirect stdout into an output +// negative errno. Used by w6c_ww to redirect stdout into an output // file without changing the cgen emit path. export fn dup2(oldfd: i32, newfd: i32) i32 = { return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; @@ -179,7 +181,33 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { options: i64, rusage: i64): i32; }; -// selfhost/cmd/wwc/mem.ww — port of cmd/wwc/mem.c. +// getcwd(2) — Linux flavour. Writes the NUL-terminated cwd into `buf` +// and returns the number of bytes written (including the NUL), or a +// negative errno. The driver uses it to expand `.` to the cwd's +// basename for `ww build` / `ww test`. +export fn getcwd(buf: *u8, n: u64) i64 = { + return syscall2(SYS_GETCWD, buf: i64, n: i64); +}; + +// getdents64(2) — Linux directory enumeration. The fd must be opened +// with O_RDONLY on a directory. `buf` receives a packed sequence of +// linux_dirent64 records: +// +// struct linux_dirent64 { +// u64 d_ino; // 0..7 +// i64 d_off; // 8..15 +// u16 d_reclen; // 16..17 — total bytes for this record +// u8 d_type; // 18 — DT_REG/DT_DIR/... +// u8 d_name[]; // 19.. — NUL-terminated name + padding +// }; +// +// Returns bytes written into `buf` (advance by d_reclen to walk), +// 0 at end-of-directory, or a negative errno. +export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { + return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); +}; + +// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c. // // Bump arena allocator. Backed by the runtime page allocator // (rt_alloc / rt_free), no libc. Each chunk is mmap'd; when the @@ -187,7 +215,7 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { // unmaps the chain. // // Memory handed out is 16-byte aligned. The C version under -// cmd/wwc/ is retained until the three-stage bootstrap diffs clean. +// cmd/wcc/ is retained until the three-stage bootstrap diffs clean. use os; @@ -286,7 +314,7 @@ export fn freearena(a: *arena) void = { }; }; -// selfhost/cmd/6l/sym.ww — port of cmd/6l/sym.c. +// selfhost/cmd/w6l/sym.ww — port of cmd/w6l/sym.c. // // Linker symbol table. Singly-linked list, usually a few hundred // entries; hashing isn't worth it yet. @@ -388,9 +416,9 @@ export fn l_lookup(l: *lnk, name: str) *lsym = { return nil; }; -// selfhost/cmd/6l/obj.ww — port of cmd/6l/obj.c. +// selfhost/cmd/w6l/obj.ww — port of cmd/w6l/obj.c. // -// Loads relocatable ELF64 .o files emitted by 6a, appends .text to +// Loads relocatable ELF64 .o files emitted by w6a, appends .text to // the combined image, and pulls in symbols + relocations with // offsets adjusted to the combined section. // @@ -410,7 +438,7 @@ def SHT_STRTAB: i32 = 3; def SHT_RELA: i32 = 4; // ---- little-endian byte readers ---------------------------------------- -// 6a/6l use straight LE on amd64. Reading via byte offsets keeps us off +// w6a/w6l use straight LE on amd64. Reading via byte offsets keeps us off // the cgen's u16 field-load story for now (MOVZBQ exists; MOVZWQ doesn't). fn rd_u16(p: *u8, off: u64) u16 = { @@ -663,7 +691,7 @@ fn member_defines_undef(l: *lnk, m: *armember) bool = { return false; }; -// load_archive — port of cmd/6l/obj.c:load_archive. +// load_archive — port of cmd/w6l/obj.c:load_archive. // // Pass 1 indexes every regular member. Pass 2 iteratively pulls in any // member that supplies a currently-undefined symbol; each pull may @@ -726,7 +754,7 @@ export fn l_load(l: *lnk, path_cs: *u8) i32 = { let buflen: u64; bufp, buflen = read_all(path_cs); if (bufp == nil) { - os.write(2, "6l: cannot read object\n".ptr, 23u64); + os.write(2, "w6l: cannot read object\n".ptr, 23u64); return -1; }; if (is_archive(bufp, buflen)) { @@ -773,11 +801,11 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = { i += 1u32; }; if (idx_text < 0) { - os.write(2, "6l: missing .text\n".ptr, 18u64); + os.write(2, "w6l: missing .text\n".ptr, 18u64); return -1; }; if (idx_symtab < 0) { - os.write(2, "6l: missing .symtab\n".ptr, 20u64); + os.write(2, "w6l: missing .symtab\n".ptr, 20u64); return -1; }; @@ -824,7 +852,7 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = { if (st_shndx != 0u16) { if ((st_shndx: i32) == idx_text) { if (gs.defined != 0) { - os.write(2, "6l: duplicate symbol\n".ptr, 21u64); + os.write(2, "w6l: duplicate symbol\n".ptr, 21u64); l.errs += 1; } else { gs.defined = 1; @@ -875,7 +903,7 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = { return 0; }; -// selfhost/cmd/6l/dyn.ww — port of cmd/6l/dyn.c. +// selfhost/cmd/w6l/dyn.ww — port of cmd/w6l/dyn.c. // // Load a shared object (ET_DYN) so the linker knows which symbols it // exports and which DT_NEEDED entry to record. We do not pull bytes @@ -1044,11 +1072,11 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = { let blen: u64; buf, blen = read_all_so(path_cs); if (buf == nil) { - os.write(2, "6l: cannot read .so\n".ptr, 20u64); + os.write(2, "w6l: cannot read .so\n".ptr, 20u64); return -1; }; if (blen < 64u64) { - os.write(2, "6l: short ELF\n".ptr, 14u64); + os.write(2, "w6l: short ELF\n".ptr, 14u64); return -1; }; if (buf[0u64] != 127u8) { return so_err("not ELF"); }; @@ -1236,7 +1264,7 @@ export fn l_load_so(l: *lnk, path_cs: *u8) i32 = { }; fn so_err(msg: str) i32 = { - os.write(2, "6l: ".ptr, 4u64); + os.write(2, "w6l: ".ptr, 4u64); os.write(2, msg.ptr, msg.len: u64); os.write(2, "\n".ptr, 1u64); return -1; @@ -1282,7 +1310,7 @@ fn s_eq(a: str, b: str) bool = { return true; }; -// selfhost/cmd/6l/pass.ww — port of cmd/6l/pass.c. +// selfhost/cmd/w6l/pass.ww — port of cmd/w6l/pass.c. // // Resolution + relocation. l_resolve flags every undefined symbol // referenced by a relocation, and promotes those provided by some @@ -1345,7 +1373,7 @@ export fn l_resolve(l: *lnk) i32 = { if (r2.sym != nil) { if (r2.sym.defined == 0) { if (r2.sym.is_dyn == 0) { - os.write(2, "6l: undefined reference to '".ptr, 28u64); + os.write(2, "w6l: undefined reference to '".ptr, 28u64); let nm: str = r2.sym.name; os.write(2, nm.ptr, nm.len: u64); os.write(2, "'\n".ptr, 2u64); @@ -1388,7 +1416,7 @@ export fn l_relocate(l: *lnk, base: u64) i32 = { let rel: i64 = (target - site: i64) + r.addend; patch_u32(l.text + r.off, rel: u32); } else { - os.write(2, "6l: unsupported reloc kind\n".ptr, 27u64); + os.write(2, "w6l: unsupported reloc kind\n".ptr, 27u64); l.errs += 1; };}; }; @@ -1398,7 +1426,7 @@ export fn l_relocate(l: *lnk, base: u64) i32 = { return l.errs; }; -// selfhost/cmd/6l/dynout.ww — port of cmd/6l/dynout.c. +// selfhost/cmd/w6l/dynout.ww — port of cmd/w6l/dynout.c. // // Emit a dynamic-linked ELF executable. The shape is the simplest // valid one: PT_INTERP + PT_DYNAMIC + DT_BIND_NOW so the loader @@ -1596,7 +1624,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { let i: i32 = 0; for (i < n) { if (dynsyms[i] == nil) { - os.write(2, "6l: dynout: no sym for plt_idx\n".ptr, 31u64); + os.write(2, "w6l: dynout: no sym for plt_idx\n".ptr, 31u64); return 1; }; i += 1; @@ -2021,7 +2049,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { }; d_wri64(dynamic_buf, dk * 16u64, DT_NULL); d_wr64(dynamic_buf, dk * 16u64 + 8u64, 0u64); dk += 1u64; if (dk != ndyn) { - os.write(2, "6l: dynamic entry count mismatch\n".ptr, 33u64); + os.write(2, "w6l: dynamic entry count mismatch\n".ptr, 33u64); return 1; }; @@ -2033,7 +2061,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { if (rsym.is_dyn != 0) { if (r.kind != R_X86_64_PC32_D) { if (r.kind != R_X86_64_PLT32_D) { - os.write(2, "6l: dynamic reloc kind unsupported\n".ptr, 35u64); + os.write(2, "w6l: dynamic reloc kind unsupported\n".ptr, 35u64); return 1; }; }; @@ -2049,7 +2077,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { // ---- assemble file buffer ---- let file_buf: *u8 = os.alloc(file_end): *u8; if (file_buf == nil) { - os.write(2, "6l: out of memory\n".ptr, 18u64); + os.write(2, "w6l: out of memory\n".ptr, 18u64); return 1; }; @@ -2142,7 +2170,7 @@ export fn l_emit_dyn_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { return 0; }; -// selfhost/cmd/6l/out.ww — port of cmd/6l/out.c. +// selfhost/cmd/w6l/out.ww — port of cmd/w6l/out.c. // // Emit a static ELF64 executable. File layout (per the C original): // [0..64) Ehdr @@ -2243,13 +2271,13 @@ export fn l_emit_elf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { return 0; }; -// selfhost/cmd/6l/main.ww — port of cmd/6l/main.c. +// selfhost/cmd/w6l/main.ww — port of cmd/w6l/main.c. // -// 6l = amd64 linker. Reads relocatable ELF .o files, SysV `ar` +// w6l = amd64 linker. Reads relocatable ELF .o files, SysV `ar` // archives, and shared objects (ET_DYN). Resolves symbols, applies // relocations, writes a static or dynamic-linked ELF executable. // -// 6l_ww -o out [-L...] [-l...] file1.o file2.o ... +// w6l_ww -o out [-L...] [-l...] file1.o file2.o ... use os; use mem; @@ -2471,14 +2499,14 @@ export fn main(argc: i32, argv: **u8) i32 = { if (streq_cs(a, "-o")) { i += 1; if (i >= argc) { - os.write(2, "6l: -o requires argument\n".ptr, 25u64); + os.write(2, "w6l: -o requires argument\n".ptr, 25u64); return 2; }; out_path = argv[i]; } else { if (streq_cs(a, "-L")) { i += 1; if (i >= argc) { - os.write(2, "6l: -L requires argument\n".ptr, 25u64); + os.write(2, "w6l: -L requires argument\n".ptr, 25u64); return 2; }; libdirs[n_libdirs] = argv[i]; @@ -2486,7 +2514,7 @@ export fn main(argc: i32, argv: **u8) i32 = { } else { if (streq_cs(a, "-l")) { i += 1; if (i >= argc) { - os.write(2, "6l: -l requires argument\n".ptr, 25u64); + os.write(2, "w6l: -l requires argument\n".ptr, 25u64); return 2; }; lflags[n_lflags] = argv[i]; @@ -2498,7 +2526,7 @@ export fn main(argc: i32, argv: **u8) i32 = { libdirs[n_libdirs] = a + 2u64; n_libdirs += 1; } else { - os.write(2, "6l: bare -L\n".ptr, 12u64); + os.write(2, "w6l: bare -L\n".ptr, 12u64); return 2; }; } else { if (a[1u64] == 108u8) { @@ -2506,16 +2534,16 @@ export fn main(argc: i32, argv: **u8) i32 = { lflags[n_lflags] = a + 2u64; n_lflags += 1; } else { - os.write(2, "6l: bare -l\n".ptr, 12u64); + os.write(2, "w6l: bare -l\n".ptr, 12u64); return 2; }; } else { - os.write(2, "6l: unknown flag\n".ptr, 17u64); + os.write(2, "w6l: unknown flag\n".ptr, 17u64); return 2; };}; } else { if (ninputs >= max_inputs) { - os.write(2, "6l: too many inputs\n".ptr, 20u64); + os.write(2, "w6l: too many inputs\n".ptr, 20u64); return 2; }; inputs[ninputs] = a; @@ -2525,11 +2553,11 @@ export fn main(argc: i32, argv: **u8) i32 = { }; if (out_path == nil) { - os.write(2, "usage: 6l_ww -o exe [-L...] [-l...] file1.o [file2.o...]\n".ptr, 68u64); + os.write(2, "usage: w6l_ww -o exe [-L...] [-l...] file1.o [file2.o...]\n".ptr, 68u64); return 2; }; if (ninputs == 0) { - os.write(2, "6l: no inputs\n".ptr, 14u64); + os.write(2, "w6l: no inputs\n".ptr, 14u64); return 2; }; @@ -2554,7 +2582,7 @@ export fn main(argc: i32, argv: **u8) i32 = { for (lf < n_lflags) { let p: *u8 = resolve_lib(a, lflags[lf], libdirs, n_libdirs); if (p == nil) { - os.write(2, "6l: cannot find -l".ptr, 18u64); + os.write(2, "w6l: cannot find -l".ptr, 18u64); let nm: *u8 = lflags[lf]; os.write(2, nm, cstrlen_l(nm)); os.write(2, "\n".ptr, 1u64); @@ -2575,18 +2603,18 @@ export fn main(argc: i32, argv: **u8) i32 = { if (entry_sym == nil) { entry_sym = l_lookup(l, "main"); } else { if (entry_sym.defined == 0) { entry_sym = l_lookup(l, "main"); }; }; if (entry_sym == nil) { - os.write(2, "6l: no _start or main symbol\n".ptr, 29u64); + os.write(2, "w6l: no _start or main symbol\n".ptr, 29u64); return 1; }; if (entry_sym.defined == 0) { - os.write(2, "6l: no _start or main symbol\n".ptr, 29u64); + os.write(2, "w6l: no _start or main symbol\n".ptr, 29u64); return 1; }; let flags: i32 = os.O_WRONLY | os.O_CREAT | os.O_TRUNC; let fd: i32 = os.open(out_path, flags, 493i32); // 0o755 if (fd < 0) { - os.write(2, "6l: cannot open output\n".ptr, 23u64); + os.write(2, "w6l: cannot open output\n".ptr, 23u64); return 1; }; diff --git a/selfhost/cmd/6l/main.ww b/selfhost/cmd/w6l/main.ww similarity index 89% rename from selfhost/cmd/6l/main.ww rename to selfhost/cmd/w6l/main.ww index ca056edc..da5450a0 100644 --- a/selfhost/cmd/6l/main.ww +++ b/selfhost/cmd/w6l/main.ww @@ -1,10 +1,10 @@ -// selfhost/cmd/6l/main.ww — port of cmd/6l/main.c. +// selfhost/cmd/w6l/main.ww — port of cmd/w6l/main.c. // -// 6l = amd64 linker. Reads relocatable ELF .o files, SysV `ar` +// w6l = amd64 linker. Reads relocatable ELF .o files, SysV `ar` // archives, and shared objects (ET_DYN). Resolves symbols, applies // relocations, writes a static or dynamic-linked ELF executable. // -// 6l_ww -o out [-L...] [-l...] file1.o file2.o ... +// w6l_ww -o out [-L...] [-l...] file1.o file2.o ... use os; use mem; @@ -226,14 +226,14 @@ export fn main(argc: i32, argv: **u8) i32 = { if (streq_cs(a, "-o")) { i += 1; if (i >= argc) { - os.write(2, "6l: -o requires argument\n".ptr, 25u64); + os.write(2, "w6l: -o requires argument\n".ptr, 25u64); return 2; }; out_path = argv[i]; } else { if (streq_cs(a, "-L")) { i += 1; if (i >= argc) { - os.write(2, "6l: -L requires argument\n".ptr, 25u64); + os.write(2, "w6l: -L requires argument\n".ptr, 25u64); return 2; }; libdirs[n_libdirs] = argv[i]; @@ -241,7 +241,7 @@ export fn main(argc: i32, argv: **u8) i32 = { } else { if (streq_cs(a, "-l")) { i += 1; if (i >= argc) { - os.write(2, "6l: -l requires argument\n".ptr, 25u64); + os.write(2, "w6l: -l requires argument\n".ptr, 25u64); return 2; }; lflags[n_lflags] = argv[i]; @@ -253,7 +253,7 @@ export fn main(argc: i32, argv: **u8) i32 = { libdirs[n_libdirs] = a + 2u64; n_libdirs += 1; } else { - os.write(2, "6l: bare -L\n".ptr, 12u64); + os.write(2, "w6l: bare -L\n".ptr, 12u64); return 2; }; } else { if (a[1u64] == 108u8) { @@ -261,16 +261,16 @@ export fn main(argc: i32, argv: **u8) i32 = { lflags[n_lflags] = a + 2u64; n_lflags += 1; } else { - os.write(2, "6l: bare -l\n".ptr, 12u64); + os.write(2, "w6l: bare -l\n".ptr, 12u64); return 2; }; } else { - os.write(2, "6l: unknown flag\n".ptr, 17u64); + os.write(2, "w6l: unknown flag\n".ptr, 17u64); return 2; };}; } else { if (ninputs >= max_inputs) { - os.write(2, "6l: too many inputs\n".ptr, 20u64); + os.write(2, "w6l: too many inputs\n".ptr, 20u64); return 2; }; inputs[ninputs] = a; @@ -280,11 +280,11 @@ export fn main(argc: i32, argv: **u8) i32 = { }; if (out_path == nil) { - os.write(2, "usage: 6l_ww -o exe [-L...] [-l...] file1.o [file2.o...]\n".ptr, 68u64); + os.write(2, "usage: w6l_ww -o exe [-L...] [-l...] file1.o [file2.o...]\n".ptr, 68u64); return 2; }; if (ninputs == 0) { - os.write(2, "6l: no inputs\n".ptr, 14u64); + os.write(2, "w6l: no inputs\n".ptr, 14u64); return 2; }; @@ -309,7 +309,7 @@ export fn main(argc: i32, argv: **u8) i32 = { for (lf < n_lflags) { let p: *u8 = resolve_lib(a, lflags[lf], libdirs, n_libdirs); if (p == nil) { - os.write(2, "6l: cannot find -l".ptr, 18u64); + os.write(2, "w6l: cannot find -l".ptr, 18u64); let nm: *u8 = lflags[lf]; os.write(2, nm, cstrlen_l(nm)); os.write(2, "\n".ptr, 1u64); @@ -330,18 +330,18 @@ export fn main(argc: i32, argv: **u8) i32 = { if (entry_sym == nil) { entry_sym = l_lookup(l, "main"); } else { if (entry_sym.defined == 0) { entry_sym = l_lookup(l, "main"); }; }; if (entry_sym == nil) { - os.write(2, "6l: no _start or main symbol\n".ptr, 29u64); + os.write(2, "w6l: no _start or main symbol\n".ptr, 29u64); return 1; }; if (entry_sym.defined == 0) { - os.write(2, "6l: no _start or main symbol\n".ptr, 29u64); + os.write(2, "w6l: no _start or main symbol\n".ptr, 29u64); return 1; }; let flags: i32 = os.O_WRONLY | os.O_CREAT | os.O_TRUNC; let fd: i32 = os.open(out_path, flags, 493i32); // 0o755 if (fd < 0) { - os.write(2, "6l: cannot open output\n".ptr, 23u64); + os.write(2, "w6l: cannot open output\n".ptr, 23u64); return 1; }; diff --git a/selfhost/cmd/6l/obj.ww b/selfhost/cmd/w6l/obj.ww similarity index 96% rename from selfhost/cmd/6l/obj.ww rename to selfhost/cmd/w6l/obj.ww index f0bff6b3..13915f3f 100644 --- a/selfhost/cmd/6l/obj.ww +++ b/selfhost/cmd/w6l/obj.ww @@ -1,6 +1,6 @@ -// selfhost/cmd/6l/obj.ww — port of cmd/6l/obj.c. +// selfhost/cmd/w6l/obj.ww — port of cmd/w6l/obj.c. // -// Loads relocatable ELF64 .o files emitted by 6a, appends .text to +// Loads relocatable ELF64 .o files emitted by w6a, appends .text to // the combined image, and pulls in symbols + relocations with // offsets adjusted to the combined section. // @@ -20,7 +20,7 @@ def SHT_STRTAB: i32 = 3; def SHT_RELA: i32 = 4; // ---- little-endian byte readers ---------------------------------------- -// 6a/6l use straight LE on amd64. Reading via byte offsets keeps us off +// w6a/w6l use straight LE on amd64. Reading via byte offsets keeps us off // the cgen's u16 field-load story for now (MOVZBQ exists; MOVZWQ doesn't). fn rd_u16(p: *u8, off: u64) u16 = { @@ -273,7 +273,7 @@ fn member_defines_undef(l: *lnk, m: *armember) bool = { return false; }; -// load_archive — port of cmd/6l/obj.c:load_archive. +// load_archive — port of cmd/w6l/obj.c:load_archive. // // Pass 1 indexes every regular member. Pass 2 iteratively pulls in any // member that supplies a currently-undefined symbol; each pull may @@ -336,7 +336,7 @@ export fn l_load(l: *lnk, path_cs: *u8) i32 = { let buflen: u64; bufp, buflen = read_all(path_cs); if (bufp == nil) { - os.write(2, "6l: cannot read object\n".ptr, 23u64); + os.write(2, "w6l: cannot read object\n".ptr, 23u64); return -1; }; if (is_archive(bufp, buflen)) { @@ -383,11 +383,11 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = { i += 1u32; }; if (idx_text < 0) { - os.write(2, "6l: missing .text\n".ptr, 18u64); + os.write(2, "w6l: missing .text\n".ptr, 18u64); return -1; }; if (idx_symtab < 0) { - os.write(2, "6l: missing .symtab\n".ptr, 20u64); + os.write(2, "w6l: missing .symtab\n".ptr, 20u64); return -1; }; @@ -434,7 +434,7 @@ fn load_image(l: *lnk, path_cs: *u8, buf: *u8, len: u64) i32 = { if (st_shndx != 0u16) { if ((st_shndx: i32) == idx_text) { if (gs.defined != 0) { - os.write(2, "6l: duplicate symbol\n".ptr, 21u64); + os.write(2, "w6l: duplicate symbol\n".ptr, 21u64); l.errs += 1; } else { gs.defined = 1; diff --git a/selfhost/cmd/6l/out.ww b/selfhost/cmd/w6l/out.ww similarity index 98% rename from selfhost/cmd/6l/out.ww rename to selfhost/cmd/w6l/out.ww index 96c9213b..22c056f0 100644 --- a/selfhost/cmd/6l/out.ww +++ b/selfhost/cmd/w6l/out.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/6l/out.ww — port of cmd/6l/out.c. +// selfhost/cmd/w6l/out.ww — port of cmd/w6l/out.c. // // Emit a static ELF64 executable. File layout (per the C original): // [0..64) Ehdr diff --git a/selfhost/cmd/6l/pass.ww b/selfhost/cmd/w6l/pass.ww similarity index 94% rename from selfhost/cmd/6l/pass.ww rename to selfhost/cmd/w6l/pass.ww index 7184c6a7..94f90c9e 100644 --- a/selfhost/cmd/6l/pass.ww +++ b/selfhost/cmd/w6l/pass.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/6l/pass.ww — port of cmd/6l/pass.c. +// selfhost/cmd/w6l/pass.ww — port of cmd/w6l/pass.c. // // Resolution + relocation. l_resolve flags every undefined symbol // referenced by a relocation, and promotes those provided by some @@ -61,7 +61,7 @@ export fn l_resolve(l: *lnk) i32 = { if (r2.sym != nil) { if (r2.sym.defined == 0) { if (r2.sym.is_dyn == 0) { - os.write(2, "6l: undefined reference to '".ptr, 28u64); + os.write(2, "w6l: undefined reference to '".ptr, 28u64); let nm: str = r2.sym.name; os.write(2, nm.ptr, nm.len: u64); os.write(2, "'\n".ptr, 2u64); @@ -104,7 +104,7 @@ export fn l_relocate(l: *lnk, base: u64) i32 = { let rel: i64 = (target - site: i64) + r.addend; patch_u32(l.text + r.off, rel: u32); } else { - os.write(2, "6l: unsupported reloc kind\n".ptr, 27u64); + os.write(2, "w6l: unsupported reloc kind\n".ptr, 27u64); l.errs += 1; };}; }; diff --git a/selfhost/cmd/6l/sym.ww b/selfhost/cmd/w6l/sym.ww similarity index 97% rename from selfhost/cmd/6l/sym.ww rename to selfhost/cmd/w6l/sym.ww index 1b286b17..fd0ba195 100644 --- a/selfhost/cmd/6l/sym.ww +++ b/selfhost/cmd/w6l/sym.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/6l/sym.ww — port of cmd/6l/sym.c. +// selfhost/cmd/w6l/sym.ww — port of cmd/w6l/sym.c. // // Linker symbol table. Singly-linked list, usually a few hundred // entries; hashing isn't worth it yet. diff --git a/selfhost/cmd/wwc/ast.ww b/selfhost/cmd/wcc/ast.ww similarity index 98% rename from selfhost/cmd/wwc/ast.ww rename to selfhost/cmd/wcc/ast.ww index 6fa28bd4..a2e84f4b 100644 --- a/selfhost/cmd/wwc/ast.ww +++ b/selfhost/cmd/wcc/ast.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/wwc/ast.ww — port of cmd/wwc/ast.c (Node defs + printer). +// selfhost/cmd/wcc/ast.ww — port of cmd/wcc/ast.c (Node defs + printer). // // Status: AST printer is fully ported. Constructor `newnode` is here. // The parser (parse.ww) is currently minimal — see its file header. @@ -14,7 +14,7 @@ use tok; // ---- Nkind ------------------------------------------------------------ // -// Mirror of cmd/wwc/ww.h Nkind. Values must stay numerically equal so +// Mirror of cmd/wcc/ww.h Nkind. Values must stay numerically equal so // the AST diff probe in 990_selfhost works. def N_NONE: i32 = 0; diff --git a/selfhost/cmd/wwc/cgen.ww b/selfhost/cmd/wcc/cgen.ww similarity index 99% rename from selfhost/cmd/wwc/cgen.ww rename to selfhost/cmd/wcc/cgen.ww index cf3482f4..b63e64cf 100644 --- a/selfhost/cmd/wwc/cgen.ww +++ b/selfhost/cmd/wcc/cgen.ww @@ -1,7 +1,7 @@ -// selfhost/cmd/wwc/cgen.ww — port of cmd/6c/cgen.c. +// selfhost/cmd/wcc/cgen.ww — port of cmd/w6c/cgen.c. // // Status: GROWING. Each subsystem we add is verified by `wwdump_ww -c` -// producing byte-identical output to C-side `6c` for the same source, +// producing byte-identical output to C-side `w6c` for the same source, // then by assembling + linking + running the result. // // Current coverage: @@ -196,7 +196,7 @@ fn local_alloc(c: *cgen, name: str, sz: i32, tnode: *node) i32 = { fn local_add(c: *cgen, name: str, sz: i32, tnode: *node) i32 = { // Name-based slot reuse for N_LETs and params: if `name` is // already declared in this function, return its existing - // offset. Mirrors C cgen (cmd/6c/cgen.c:localoff). Two + // offset. Mirrors C cgen (cmd/w6c/cgen.c:localoff). Two // disjoint scopes that declare the same name share one slot — // so `escape` in wwdump (three `let cp: pos;` across separate // branches) reserves one slot, not three. scan_locals does @@ -533,7 +533,7 @@ fn fnret_lookup(c: *cgen, name: str) *node = { // ---- def-constant registry ------------------------------------------ // -// `def NAME: T = LIT;` becomes a DATA symbol the C-side 6c emits; an +// `def NAME: T = LIT;` becomes a DATA symbol the C-side w6c emits; an // ident reference loads it via `MOVQ NAME(SB), AX`. We collect them at // file load and consult on N_IDENT lookup. @@ -1161,7 +1161,7 @@ fn node_isunsigned(c: *cgen, n: *node) bool = { // Walks the base local's declared type and pulls the element // out — *u8 → u8, [N]u32 → u32, []u64 → u64. Without this the // compare-codegen for `p[i] >= 48u8` falls back to signed JGE - // instead of JAE, diverging from C 6c on byte indexing. + // instead of JAE, diverging from C w6c on byte indexing. if (k == N_INDEX) { let base: *node = n.lhs; if (base != nil) { @@ -1634,7 +1634,7 @@ fn cgexpr(c: *cgen, n: *node) void = { // path the cgen falls through and AX retains whatever the // inner expression left there — typically the *struct pointer // itself, so reads silently get the pointer value instead of - // the field. (Showed up porting 6l/pass.ww.) + // the field. (Showed up porting w6l/pass.ww.) if (lhs != nil) { if (lhs.kind == N_DOT) { let inner_t: *node = dot_inner_struct_ptr(c, lhs); @@ -1923,7 +1923,7 @@ fn cgexpr(c: *cgen, n: *node) void = { }; cgexpr(c, n.rhs); // Push order matches C cgen - // (cmd/6c/cgen.c:1033-1041): PUSHQ AX + // (cmd/w6c/cgen.c:1033-1041): PUSHQ AX // (ptr) first, then PUSHQ BX (len) if // str, so the pop sequence is POP CX // (len) → POP AX (ptr) → MOVQ AX, @@ -2695,7 +2695,7 @@ fn cgstmt(c: *cgen, n: *node) void = { }; } else { // Bare `let x: T;` with no initializer. C cgen - // (cmd/6c/cgen.c:2181-2183) zero-inits only when + // (cmd/w6c/cgen.c:2181-2183) zero-inits only when // the underlying type's natural size is 8 — pointers, // i64/u64, function pointers, ints. Structs/arrays/ // slices/strings/tagged/tuples are left for per-field @@ -2766,7 +2766,7 @@ fn cgstmt(c: *cgen, n: *node) void = { // Tuple-destructure assign: `a, b = call();`. The call's tuple // return lands in (AX, DX); push DX to free it, store AX into // the first lvalue, then pop DX into the second. Mirrors - // cmd/6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same + // cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same // as C — no fixture uses >2 today). if (k == N_MASSIGN) { if (n.rhs != nil) { cgexpr(c, n.rhs); }; diff --git a/selfhost/cmd/wwc/check.ww b/selfhost/cmd/wcc/check.ww similarity index 98% rename from selfhost/cmd/wwc/check.ww rename to selfhost/cmd/wcc/check.ww index db04e51b..4132ddb5 100644 --- a/selfhost/cmd/wwc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -1,8 +1,8 @@ -// selfhost/cmd/wwc/check.ww — minimal port of cmd/wwc/check.c. +// selfhost/cmd/wcc/check.ww — minimal port of cmd/wcc/check.c. // // Status: name-resolution + primitive-type seeding only. Full type // inference, conversion rules, tagged-union dispatch typing, return- -// type checking, etc. all live in cmd/wwc/check.c (937 lines) and +// type checking, etc. all live in cmd/wcc/check.c (937 lines) and // will land here in subsequent commits. // // What this version does: diff --git a/selfhost/cmd/wwc/err.ww b/selfhost/cmd/wcc/err.ww similarity index 93% rename from selfhost/cmd/wwc/err.ww rename to selfhost/cmd/wcc/err.ww index e5fff225..3e14642b 100644 --- a/selfhost/cmd/wwc/err.ww +++ b/selfhost/cmd/wcc/err.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/wwc/err.ww — port of cmd/wwc/err.c. +// selfhost/cmd/wcc/err.ww — port of cmd/wcc/err.c. // // Diagnostics. Plan 9 style: short, no levels beyond fatal/error/warn. // Output goes through os.write so we don't pull in libc stdio. diff --git a/selfhost/cmd/wwc/lex.ww b/selfhost/cmd/wcc/lex.ww similarity index 99% rename from selfhost/cmd/wwc/lex.ww rename to selfhost/cmd/wcc/lex.ww index 5737a911..ed94518d 100644 --- a/selfhost/cmd/wwc/lex.ww +++ b/selfhost/cmd/wcc/lex.ww @@ -1,11 +1,11 @@ -// selfhost/cmd/wwc/lex.ww — port of cmd/wwc/lex.c. +// selfhost/cmd/wcc/lex.ww — port of cmd/wcc/lex.c. // // The DFA, the helpers, and the order of decisions all mirror the C // version exactly. The 990_selfhost test diffs the resulting token // stream against the C-side wwdump byte-for-byte; any divergence is // a port bug. // -// Calling-convention note: 6c can't yet pass or return structs >16 +// Calling-convention note: w6c can't yet pass or return structs >16 // bytes by value, so `tok` and `pos` are passed by pointer (out // params). The C version passes `Tok` by value; we differ here only // in shape, not in observable behaviour. Token kind values stay diff --git a/selfhost/cmd/wwc/mem.ww b/selfhost/cmd/wcc/mem.ww similarity index 95% rename from selfhost/cmd/wwc/mem.ww rename to selfhost/cmd/wcc/mem.ww index 9788933c..bb742d98 100644 --- a/selfhost/cmd/wwc/mem.ww +++ b/selfhost/cmd/wcc/mem.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/wwc/mem.ww — port of cmd/wwc/mem.c. +// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c. // // Bump arena allocator. Backed by the runtime page allocator // (rt_alloc / rt_free), no libc. Each chunk is mmap'd; when the @@ -6,7 +6,7 @@ // unmaps the chain. // // Memory handed out is 16-byte aligned. The C version under -// cmd/wwc/ is retained until the three-stage bootstrap diffs clean. +// cmd/wcc/ is retained until the three-stage bootstrap diffs clean. use os; diff --git a/selfhost/cmd/wwc/parse.ww b/selfhost/cmd/wcc/parse.ww similarity index 99% rename from selfhost/cmd/wwc/parse.ww rename to selfhost/cmd/wcc/parse.ww index 968e7bb0..85acd39b 100644 --- a/selfhost/cmd/wwc/parse.ww +++ b/selfhost/cmd/wcc/parse.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/wwc/parse.ww — port of cmd/wwc/parse.c. +// selfhost/cmd/wcc/parse.ww — port of cmd/wcc/parse.c. // // Status: GROWING stub. Currently handles top-level `use IDENT;`, // `def NAME: TYPE = LIT;`, `type NAME = TYPE;`, and `fn NAME(params) @@ -11,7 +11,7 @@ // surface form lands here gradually so the AST diff in 990_selfhost // grows toward whole-language coverage one increment at a time. // -// Calling-convention shim: 6c can't yet pass a sub-struct field +// Calling-convention shim: w6c can't yet pass a sub-struct field // (e.g. p.cur.line where p.cur is a `tok` of size 76). The parser // stores the current token as flat primitive fields rather than a // nested `tok` struct; `refill` copies a freshly lexed token in. @@ -746,7 +746,7 @@ fn parsestmt(p: *parser) *node = { }; // expression statement, or tuple-destructure multi-assign: // a, b = expr; - // Mirrors cmd/wwc/parse.c:1015-1031. We parse the first lvalue + // Mirrors cmd/wcc/parse.c:1015-1031. We parse the first lvalue // with parseexpr (matches the C side); subsequent lvalues go // through parsebin(parseunary, 1) so the `=` stays for us to // consume — parseexpr would absorb it. diff --git a/selfhost/cmd/wwc/sym.ww b/selfhost/cmd/wcc/sym.ww similarity index 95% rename from selfhost/cmd/wwc/sym.ww rename to selfhost/cmd/wcc/sym.ww index f3484c00..eac953e1 100644 --- a/selfhost/cmd/wwc/sym.ww +++ b/selfhost/cmd/wcc/sym.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/wwc/sym.ww — port of cmd/wwc/sym.c. +// selfhost/cmd/wcc/sym.ww — port of cmd/wcc/sym.c. // // Per-scope hashtable, chained to the parent. Lookup walks up. // Plan 9 / Hare flavoured. Duplicate definitions in the same scope @@ -8,7 +8,7 @@ use mem; use typ; use ast; -// Symbol kinds — must stay numerically aligned with cmd/wwc/ww.h Skind. +// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind. def SK_NONE: i32 = 0; def SK_VAR: i32 = 1; def SK_PARAM: i32 = 2; diff --git a/selfhost/cmd/wwc/tok.ww b/selfhost/cmd/wcc/tok.ww similarity index 95% rename from selfhost/cmd/wwc/tok.ww rename to selfhost/cmd/wcc/tok.ww index 6150bbc9..be280b98 100644 --- a/selfhost/cmd/wwc/tok.ww +++ b/selfhost/cmd/wcc/tok.ww @@ -1,5 +1,5 @@ -// selfhost/cmd/wwc/tok.ww — port of cmd/wwc/tok.c plus the Tkind / -// Tok / Pos shapes from cmd/wwc/ww.h. +// selfhost/cmd/wcc/tok.ww — port of cmd/wcc/tok.c plus the Tkind / +// Tok / Pos shapes from cmd/wcc/ww.h. // // Token kind values must stay numerically equal to the C side: the // 990_selfhost test diffs ww-side wwdump output against C-side @@ -7,13 +7,13 @@ // integers and breaks the diff. // // Bottom of file: tokprint, which emits one token per line in a -// format identical to cmd/wwc/tok.c:tokprint(). +// format identical to cmd/wcc/tok.c:tokprint(). use os; use strconv; // ---- Tkind ------------------------------------------------------------ -// Mirror of the C enum in cmd/wwc/ww.h. Don't reorder. +// Mirror of the C enum in cmd/wcc/ww.h. Don't reorder. def TK_NONE: i32 = 0; def TK_EOF: i32 = 1; @@ -108,7 +108,7 @@ def TK_LAST: i32 = 80; // ---- Pos / Tok -------------------------------------------------------- // // `pos` is used at error-reporting boundaries; we always pass it via -// *pos so the value never gets struct-copied (6c can't yet copy a +// *pos so the value never gets struct-copied (w6c can't yet copy a // 24-byte struct). // // `tok` is flat — file/line/col live directly on the token rather than @@ -146,7 +146,7 @@ fn streq_n(a: *u8, b: str, n: i32) bool = { // kwlookup — returns the matching TK_* keyword kind for a byte run, // or TK_NONE if it's an ordinary identifier. Linear search over a -// small alphabetised list, matching cmd/wwc/tok.c. +// small alphabetised list, matching cmd/wcc/tok.c. export fn kwlookup(p: *u8, n: i32) i32 = { if (streq_n(p, "as", n)) { return TK_AS; }; if (streq_n(p, "break", n)) { return TK_BREAK; }; @@ -275,7 +275,7 @@ export fn tokname(k: i32) str = { // ---- writer for tokprint ---------------------------------------------- // -// fputq mirrors cmd/wwc/tok.c:fputq — quote the string with C-style +// fputq mirrors cmd/wcc/tok.c:fputq — quote the string with C-style // escapes for \, ", \n, \t, \r and \xNN for other non-printables. fn fputc_byte(fd: i32, b: u8) void = { @@ -342,14 +342,14 @@ fn fputq(fd: i32, p: *u8, n: i32) void = { }; // tokprint — write one token line to fd. Format must match -// cmd/wwc/tok.c:tokprint() byte-for-byte: that's the diff anchor. +// cmd/wcc/tok.c:tokprint() byte-for-byte: that's the diff anchor. // ":: [ ]\n" // -// Takes `t` by pointer because 6c can't yet pass a >16-byte struct +// Takes `t` by pointer because w6c can't yet pass a >16-byte struct // by value; the C version takes Tok by value. export fn tokprint(fd: i32, t: *tok) void = { // Chained-dot field reads (`t.x.y`) on str sub-fields aren't yet - // reduced by 6c — `t.x.y` returns the whole str. Lift the str + // reduced by w6c — `t.x.y` returns the whole str. Lift the str // fields into locals so we can use the str pseudo-field path. let tfile: str = t.file; let ttext: str = t.text; diff --git a/selfhost/cmd/wwc/typ.ww b/selfhost/cmd/wcc/typ.ww similarity index 98% rename from selfhost/cmd/wwc/typ.ww rename to selfhost/cmd/wcc/typ.ww index 09e17abf..d7f83442 100644 --- a/selfhost/cmd/wwc/typ.ww +++ b/selfhost/cmd/wcc/typ.ww @@ -1,4 +1,4 @@ -// selfhost/cmd/wwc/type.ww — port of cmd/wwc/type.c. +// selfhost/cmd/wcc/type.ww — port of cmd/wcc/type.c. // // Status: full structural port. The C version uses module-globals for // the primitive types (ty_void, ty_i32, …); ww doesn't have writable @@ -10,7 +10,7 @@ use os; use mem; // ---- TypeKind --------------------------------------------------------- -// Numeric values must stay aligned with cmd/wwc/ww.h TypeKind so the +// Numeric values must stay aligned with cmd/wcc/ww.h TypeKind so the // next diff signal (typed-AST printer / cgen) can compare across the // two implementations. diff --git a/selfhost/cmd/ww/main.combined.ww b/selfhost/cmd/ww/main.combined.ww index eaabf4ba..0c286508 100644 --- a/selfhost/cmd/ww/main.combined.ww +++ b/selfhost/cmd/ww/main.combined.ww @@ -18,19 +18,21 @@ export fn assert(cond: bool, msg: str) void = { if (!cond) { abort(msg); }; }; -def SYS_READ: i64 = 0; -def SYS_WRITE: i64 = 1; -def SYS_OPEN: i64 = 2; -def SYS_CLOSE: i64 = 3; -def SYS_LSEEK: i64 = 8; -def SYS_ACCESS: i64 = 21; -def SYS_DUP2: i64 = 33; -def SYS_GETPID: i64 = 39; -def SYS_FORK: i64 = 57; -def SYS_EXECVE: i64 = 59; -def SYS_EXIT: i64 = 60; -def SYS_WAIT4: i64 = 61; -def SYS_UNLINK: i64 = 87; +def SYS_READ: i64 = 0; +def SYS_WRITE: i64 = 1; +def SYS_OPEN: i64 = 2; +def SYS_CLOSE: i64 = 3; +def SYS_LSEEK: i64 = 8; +def SYS_ACCESS: i64 = 21; +def SYS_DUP2: i64 = 33; +def SYS_GETPID: i64 = 39; +def SYS_FORK: i64 = 57; +def SYS_EXECVE: i64 = 59; +def SYS_EXIT: i64 = 60; +def SYS_WAIT4: i64 = 61; +def SYS_UNLINK: i64 = 87; +def SYS_GETCWD: i64 = 79; +def SYS_GETDENTS64: i64 = 217; // open(2) flags. Linux values, matching . def O_RDONLY: i32 = 0; @@ -65,7 +67,7 @@ export fn close(fd: i32) i32 = { // dup2(2): make `newfd` refer to the same description as `oldfd`, // closing `newfd` first if open. Returns `newfd` on success or a -// negative errno. Used by 6c_ww to redirect stdout into an output +// negative errno. Used by w6c_ww to redirect stdout into an output // file without changing the cgen emit path. export fn dup2(oldfd: i32, newfd: i32) i32 = { return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; @@ -179,7 +181,33 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { options: i64, rusage: i64): i32; }; -// selfhost/cmd/wwc/mem.ww — port of cmd/wwc/mem.c. +// getcwd(2) — Linux flavour. Writes the NUL-terminated cwd into `buf` +// and returns the number of bytes written (including the NUL), or a +// negative errno. The driver uses it to expand `.` to the cwd's +// basename for `ww build` / `ww test`. +export fn getcwd(buf: *u8, n: u64) i64 = { + return syscall2(SYS_GETCWD, buf: i64, n: i64); +}; + +// getdents64(2) — Linux directory enumeration. The fd must be opened +// with O_RDONLY on a directory. `buf` receives a packed sequence of +// linux_dirent64 records: +// +// struct linux_dirent64 { +// u64 d_ino; // 0..7 +// i64 d_off; // 8..15 +// u16 d_reclen; // 16..17 — total bytes for this record +// u8 d_type; // 18 — DT_REG/DT_DIR/... +// u8 d_name[]; // 19.. — NUL-terminated name + padding +// }; +// +// Returns bytes written into `buf` (advance by d_reclen to walk), +// 0 at end-of-directory, or a negative errno. +export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { + return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); +}; + +// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c. // // Bump arena allocator. Backed by the runtime page allocator // (rt_alloc / rt_free), no libc. Each chunk is mmap'd; when the @@ -187,7 +215,7 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { // unmaps the chain. // // Memory handed out is 16-byte aligned. The C version under -// cmd/wwc/ is retained until the three-stage bootstrap diffs clean. +// cmd/wcc/ is retained until the three-stage bootstrap diffs clean. use os; @@ -290,13 +318,13 @@ export fn freearena(a: *arena) void = { // // The user-facing driver. Plan 9 cc(1) / Hare hare(1) analogue: // -// ww build foo.ww → 6c foo.ww > foo.s ; 6a foo.s > foo.o ; -// 6l -o foo foo.o libwwrt.a +// ww build foo.ww → w6c foo.ww > foo.s ; w6a foo.s > foo.o ; +// w6l -o foo foo.o libwwrt.a // ww run foo.ww → build then exec // ww version → print version // // Tool paths default to siblings of $0 so a fresh build runs out of -// out/bin/. Env-var overrides (WW_6C / WW_6A / WW_6L / WW_LIB) are +// out/bin/. Env-var overrides (WW_W6C / WW_W6A / WW_W6L / WW_LIB) are // not yet supported in this port; the bootstrap doesn't need them. use os; @@ -696,7 +724,7 @@ fn append_lit(stem: *u8, suffix: str) *u8 = { }; // linker flags bundled as a struct so build_one stays at the wwstage -// 6c's 6-argument calling-convention limit. +// w6c's 6-argument calling-convention limit. type lflags = struct { libdirs: **u8, n_libdirs: i32, @@ -706,7 +734,7 @@ type lflags = struct { // build_one — compile `src` into the executable named `out`. // self_dir: NUL-terminated dir containing this driver and the -// wwstage tools (6c_ww/6a_ww/6l_ww) +// wwstage tools (w6c_ww/w6a_ww/w6l_ww) // src: NUL-terminated path to the .ww file // out: NUL-terminated desired output path // incs: NUL-terminated colon-list of -I dirs (may be empty) @@ -714,14 +742,14 @@ type lflags = struct { // // The ww-side driver shells to the ww-side tools so a `ww_ww build` // touches no C-built code at runtime. The C `ww` driver in cmd/ww/ -// still drives the C-built 6c/6a/6l. Test 993 pins the two +// still drives the C-built w6c/w6a/w6l. Test 993 pins the two // pipelines to byte-identical output on a corpus. fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = { let a: *arena = newarena(); - let c6: *u8 = join_path_lit(self_dir, "6c_ww"); - let a6: *u8 = join_path_lit(self_dir, "6a_ww"); - let l6: *u8 = join_path_lit(self_dir, "6l_ww"); + let c6: *u8 = join_path_lit(self_dir, "w6c_ww"); + let a6: *u8 = join_path_lit(self_dir, "w6a_ww"); + let l6: *u8 = join_path_lit(self_dir, "w6l_ww"); // Default lib search path: /../../lib let dotdot_lib: *u8 = os.alloc(PATH_MAX): *u8; @@ -774,35 +802,35 @@ fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = { }; os.close(cf); - // Step 2: 6c -o .s .combined.ww + // Step 2: w6c -o .s .combined.ww { let argv: **u8 = os.alloc(40u64): **u8; - argv[0] = "6c\0".ptr; + argv[0] = "w6c\0".ptr; argv[1] = "-o\0".ptr; argv[2] = asmf; argv[3] = combined; argv[4] = nil; if (proc_run(c6, argv) != 0) { - os.write(2, "ww: 6c failed\n".ptr, 14u64); + os.write(2, "ww: w6c failed\n".ptr, 15u64); return 1; }; }; - // Step 3: 6a -o .o .s + // Step 3: w6a -o .o .s { let argv: **u8 = os.alloc(40u64): **u8; - argv[0] = "6a\0".ptr; + argv[0] = "w6a\0".ptr; argv[1] = "-o\0".ptr; argv[2] = objf; argv[3] = asmf; argv[4] = nil; if (proc_run(a6, argv) != 0) { - os.write(2, "ww: 6a failed\n".ptr, 14u64); + os.write(2, "ww: w6a failed\n".ptr, 15u64); return 1; }; }; - // Step 4: 6l -o .o libwwrt.a [-L...] [-l...] + // Step 4: w6l -o .o libwwrt.a [-L...] [-l...] { let nl_dirs: i32 = 0; let nl_libs: i32 = 0; @@ -814,13 +842,13 @@ fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = { l_dirs = lf.libdirs; l_libs = lf.libs; }; - // argv slots: 5 fixed (6l, -o, out, objf, libwwrt) + // argv slots: 5 fixed (w6l, -o, out, objf, libwwrt) // + 2 * n_libdirs (-L, dir) // + 2 * n_libs (-l, name) // + 1 nil terminator. let total: i32 = 5 + 2 * nl_dirs + 2 * nl_libs + 1; let argv: **u8 = os.alloc((total: u64) * 8u64): **u8; - argv[0] = "6l\0".ptr; + argv[0] = "w6l\0".ptr; argv[1] = "-o\0".ptr; argv[2] = out; argv[3] = objf; @@ -842,17 +870,139 @@ fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = { }; argv[pos] = nil; if (proc_run(l6, argv) != 0) { - os.write(2, "ww: 6l failed\n".ptr, 14u64); + os.write(2, "ww: w6l failed\n".ptr, 15u64); return 1; }; }; return 0; }; +// ---- Module-by-name resolution ---------------------------------------- +// +// Mirrors cmd/ww/main.c:resolve_module. Maps a name like "foo", "lib/foo", +// "foo.ww", or "." to a concrete .ww file path: +// 1. literal .ww that exists → use as-is +// 2. "." → /.ww → that, if it exists +// 3. /.ww → that, if it exists +// 4. walk search path (cwd:incs:/../../lib): +// /.ww or //.ww + +fn cstr_endswith_lit(p: *u8, lit: str) bool = { + let plen: u64 = cstrlen(p); + let slen: u64 = lit.len: u64; + if (plen < slen) { return false; }; + let off: u64 = plen - slen; + let i: i32 = 0; + for (i < lit.len) { + let iu: u64 = i: u64; + if (p[off + iu] != lit[i]) { return false; }; + i += 1; + }; + return true; +}; + +// basename_off — return the offset of the last path segment within `p` +// (i.e. one past the final '/'). Returns 0 if there's no slash. +fn basename_off(p: *u8, plen: u64) u64 = { + let start: u64 = 0u64; + let i: u64 = 0u64; + for (i < plen) { + if (p[i] == 47u8) { start = i + 1u64; }; // '/' + i += 1u64; + }; + return start; +}; + +// arena_dup_cstr — copy `plen` bytes from `src` into a fresh NUL-sealed +// arena buffer. +fn arena_dup_cstr(a: *arena, src: *u8, plen: u64) *u8 = { + let buf: *u8 = amalloc(a, plen + 1u64): *u8; + let i: u64 = 0u64; + for (i < plen) { buf[i] = src[i]; i += 1u64; }; + buf[plen] = 0u8; + return buf; +}; + +// build_dir_module_path — alloc /.ww, NUL-terminated, in `a`. +fn build_dir_module_path(a: *arena, dir: *u8, dlen: u64, + base: *u8, blen: u64) *u8 = { + let need: u64 = dlen + 1u64 + blen + 3u64 + 1u64; + let buf: *u8 = amalloc(a, need): *u8; + let off: u64 = 0u64; + let i: u64 = 0u64; + for (i < dlen) { buf[off] = dir[i]; off += 1u64; i += 1u64; }; + buf[off] = 47u8; off += 1u64; // '/' + i = 0u64; + for (i < blen) { buf[off] = base[i]; off += 1u64; i += 1u64; }; + buf[off] = 46u8; off += 1u64; // '.' + buf[off] = 119u8; off += 1u64; // 'w' + buf[off] = 119u8; off += 1u64; // 'w' + buf[off] = 0u8; + return buf; +}; + +// build_search_path — compose the colon-separated lookup path used by +// resolve_module's case (4). Order: "." : : /../../lib +fn build_search_path(a: *arena, self_dir: *u8, incs: *u8) *u8 = { + let buf: *u8 = amalloc(a, PATH_MAX * 2u64): *u8; + let off: u64 = 0u64; + buf[off] = 46u8; off += 1u64; // '.' + if (incs != nil) { + if (incs[0u64] != 0u8) { + buf[off] = 58u8; off += 1u64; // ':' + off = cstr_into(buf, off, incs); + }; + }; + buf[off] = 58u8; off += 1u64; + off = cstr_into(buf, off, self_dir); + off = str_into(buf, off, "/../../lib"); + cstr_seal(buf, off); + return buf; +}; + +fn resolve_module(a: *arena, self_dir: *u8, name: *u8, incs: *u8) *u8 = { + let nlen: u64 = cstrlen(name); + + // (1) literal .ww file that exists + if (cstr_endswith_lit(name, ".ww")) { + if (os.access(name, 0i32) == 0) { + return arena_dup_cstr(a, name, nlen); + }; + }; + + // (2) "." → cwd's .ww + if (nlen == 1u64) { + if (name[0u64] == 46u8) { // '.' + let cwd: *u8 = amalloc(a, PATH_MAX): *u8; + let r: i64 = os.getcwd(cwd, PATH_MAX); + if (r <= 0i64) { return nil; }; + let cwdlen: u64 = (r: u64) - 1u64; // strip trailing NUL + let bo: u64 = basename_off(cwd, cwdlen); + let blen: u64 = cwdlen - bo; + let dot: *u8 = amalloc(a, 2u64): *u8; + dot[0] = 46u8; dot[1] = 0u8; + let probe: *u8 = build_dir_module_path(a, dot, 1u64, + cwd + bo, blen); + if (os.access(probe, 0i32) == 0) { return probe; }; + return nil; + }; + }; + + // (3) /.ww — directory-as-module + let bo: u64 = basename_off(name, nlen); + let probe: *u8 = build_dir_module_path(a, name, nlen, + name + bo, nlen - bo); + if (os.access(probe, 0i32) == 0) { return probe; }; + + // (4) search path lookup + let search: *u8 = build_search_path(a, self_dir, incs); + return locate_import(a, search, name, nlen); +}; + // ---- Subcommand handlers ---------------------------------------------- fn write_usage(fd: i32) void = { - let s: str = "usage: ww [-V] [args...]\n -V print version and exit\n build compile module to a static binary\n run build then exec\n version print version and exit\n"; + let s: str = "usage: ww [-V] [args...]\n -V print version and exit\n build [path] compile module to a static binary (path defaults to cwd)\n run [path] ... build then exec, passing extra args to the program\n test [path] build and run *_test.ww in the module (path defaults to cwd)\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 . build the cwd's .ww\n"; os.write(fd, s.ptr, s.len: u64); }; @@ -894,6 +1044,7 @@ fn default_out_path(src: *u8) *u8 = { }; fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { + let a: *arena = newarena(); let src: *u8 = nil; let incs: *u8 = os.alloc(PATH_MAX * 2u64): *u8; let inc_off: u64 = 0u64; @@ -974,16 +1125,23 @@ fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { }; if (src == nil) { - os.write(2, "ww build: missing source\n".ptr, 25u64); - return 2; + // default to cwd module + let dot: *u8 = amalloc(a, 2u64): *u8; + dot[0] = 46u8; dot[1] = 0u8; + src = dot; }; - let out: *u8 = default_out_path(src); + let resolved: *u8 = resolve_module(a, self_dir, src, incs); + if (resolved == nil) { + os.write(2, "ww build: cannot find module\n".ptr, 29u64); + return 1; + }; + let out: *u8 = default_out_path(resolved); let lf: lflags; lf.libdirs = libdirs; lf.n_libdirs = n_libdirs; lf.libs = libs; lf.n_libs = n_libs; - return build_one(self_dir, src, out, incs, &lf); + return build_one(self_dir, resolved, out, incs, &lf); }; // Format the scratch path /tmp/ww_run_ into buf. Returns NUL- @@ -1017,13 +1175,149 @@ fn make_run_tmp(buf: *u8) void = { }; fn do_run(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { - if (start >= argc) { - os.write(2, "ww run: missing source\n".ptr, 23u64); - return 2; + let a: *arena = newarena(); + let src: *u8 = nil; + let pass_start: i32 = -1; // first argv idx to pass through to program + let incs: *u8 = os.alloc(PATH_MAX * 2u64): *u8; + let inc_off: u64 = 0u64; + cstr_seal(incs, 0u64); + + let max_lflags: i32 = 32; + let libdirs: **u8 = os.alloc((max_lflags: u64) * 8u64): **u8; + let n_libdirs: i32 = 0; + let libs: **u8 = os.alloc((max_lflags: u64) * 8u64): **u8; + let n_libs: i32 = 0; + + let i: i32 = start; + for (i < argc) { + if (pass_start >= 0) { i = argc; } // stop, leave rest for exec + else { + let p: *u8 = argv[i]; + if (p[0u64] == 45u8) { + if (p[1u64] == 73u8) { + let dir: *u8 = nil; + if (p[2u64] != 0u8) { + dir = p + 2u64; + } else { + if (i + 1 >= argc) { + os.write(2, "ww run: -I needs an argument\n".ptr, 29u64); + return 2; + }; + i += 1; + dir = argv[i]; + }; + if (inc_off > 0u64) { + incs[inc_off] = 58u8; + inc_off += 1u64; + }; + inc_off = cstr_into(incs, inc_off, dir); + cstr_seal(incs, inc_off); + } else { if (p[1u64] == 76u8) { + let dir: *u8 = nil; + if (p[2u64] != 0u8) { + dir = p + 2u64; + } else { + if (i + 1 >= argc) { + os.write(2, "ww run: -L needs an argument\n".ptr, 29u64); + return 2; + }; + i += 1; + dir = argv[i]; + }; + if (n_libdirs >= max_lflags) { + os.write(2, "ww run: too many -L\n".ptr, 20u64); + return 2; + }; + libdirs[n_libdirs] = dir; + n_libdirs += 1; + } else { if (p[1u64] == 108u8) { + let nm: *u8 = nil; + if (p[2u64] != 0u8) { + nm = p + 2u64; + } else { + if (i + 1 >= argc) { + os.write(2, "ww run: -l needs an argument\n".ptr, 29u64); + return 2; + }; + i += 1; + nm = argv[i]; + }; + if (n_libs >= max_lflags) { + os.write(2, "ww run: too many -l\n".ptr, 20u64); + return 2; + }; + libs[n_libs] = nm; + n_libs += 1; + } else { + os.write(2, "ww run: unknown flag\n".ptr, 21u64); + return 2; + }; }; }; + i += 1; + } else { + if (src == nil) { + src = p; + i += 1; + } else { + pass_start = i; // remaining args go to the program + }; + }; + }; }; + + if (src == nil) { + let dot: *u8 = amalloc(a, 2u64): *u8; + dot[0] = 46u8; dot[1] = 0u8; + src = dot; + }; + let resolved: *u8 = resolve_module(a, self_dir, src, incs); + if (resolved == nil) { + os.write(2, "ww run: cannot find module\n".ptr, 27u64); + return 1; + }; + let tmp: *u8 = os.alloc(PATH_MAX): *u8; make_run_tmp(tmp); - if (build_one(self_dir, argv[start], tmp, "\0".ptr, nil) != 0) { return 1; }; + let lf: lflags; + lf.libdirs = libdirs; + lf.n_libdirs = n_libdirs; + lf.libs = libs; + lf.n_libs = n_libs; + if (build_one(self_dir, resolved, tmp, incs, &lf) != 0) { + os.unlink(tmp); + return 1; + }; + + // exec with [tmp, argv[pass_start..argc), nil] + let n_extra: i32 = 0; + if (pass_start >= 0) { n_extra = argc - pass_start; }; + let total: i32 = n_extra + 2; + let exec_argv: **u8 = os.alloc((total: u64) * 8u64): **u8; + exec_argv[0] = tmp; + let k: i32 = 0; + for (k < n_extra) { + exec_argv[k + 1] = argv[pass_start + k]; + k += 1; + }; + exec_argv[n_extra + 1] = nil; + let rc: i32 = proc_run(tmp, exec_argv); + os.unlink(tmp); + return rc; +}; + +// ---- ww test ---------------------------------------------------------- +// +// Mirrors cmd/ww/main.c:do_test. Two modes: +// single-file: build+run a literal *.ww file, return its exit code +// directory: open the dir, getdents64, build+run each *_test.ww, +// report ok/FAIL per file, return 0 iff all pass. + +fn run_single_test(self_dir: *u8, src: *u8) i32 = { + let tmp: *u8 = os.alloc(PATH_MAX): *u8; + make_run_tmp(tmp); + if (build_one(self_dir, src, tmp, "\0".ptr, nil) != 0) { + os.unlink(tmp); + return 1; + }; let exec_argv: **u8 = os.alloc(16u64): **u8; exec_argv[0] = tmp; exec_argv[1] = nil; @@ -1032,6 +1326,100 @@ fn do_run(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { return rc; }; +fn run_dir_tests(self_dir: *u8, dir: *u8) i32 = { + let fd: i32 = os.open(dir, os.O_RDONLY, 0i32); + if (fd < 0) { + os.write(2, "ww test: cannot open directory\n".ptr, 31u64); + return 1; + }; + + let pass: i32 = 0; + let fail: i32 = 0; + let buf: *u8 = os.alloc(8192u64): *u8; + let dirlen: u64 = cstrlen(dir); + + let n: i64 = os.getdents64(fd, buf, 8192u64); + for (n > 0i64) { + let off: u64 = 0u64; + let nu: u64 = n: u64; + for (off < nu) { + // d_reclen at offset+16 (u16 LE), d_name at offset+19 (cstr) + let blo: u64 = (buf[off + 16u64]): u64; + let bhi: u64 = (buf[off + 17u64]): u64; + let reclen: u64 = blo + (bhi * 256u64); + let name: *u8 = buf + off + 19u64; + if (cstr_endswith_lit(name, "_test.ww")) { + let nlen: u64 = cstrlen(name); + // path = / + let path: *u8 = os.alloc(PATH_MAX): *u8; + let p_off: u64 = cstr_into(path, 0u64, dir); + path[p_off] = 47u8; p_off += 1u64; + let i: u64 = 0u64; + for (i < nlen) { path[p_off + i] = name[i]; i += 1u64; }; + p_off += nlen; + cstr_seal(path, p_off); + // incs = so test files can `use ` siblings + let tincs: *u8 = os.alloc(PATH_MAX): *u8; + let ic: u64 = cstr_into(tincs, 0u64, dir); + cstr_seal(tincs, ic); + let tmp: *u8 = os.alloc(PATH_MAX): *u8; + make_run_tmp(tmp); + let bres: i32 = build_one(self_dir, path, tmp, tincs, nil); + if (bres != 0) { + fail += 1; + os.write(2, "FAIL ".ptr, 5u64); + os.write(2, name, nlen); + os.write(2, " (build)\n".ptr, 9u64); + } else { + let exec_argv: **u8 = os.alloc(16u64): **u8; + exec_argv[0] = tmp; + exec_argv[1] = nil; + let rc: i32 = proc_run(tmp, exec_argv); + if (rc == 0) { + pass += 1; + os.write(1, "ok ".ptr, 5u64); + os.write(1, name, nlen); + os.write(1, "\n".ptr, 1u64); + } else { + fail += 1; + os.write(2, "FAIL ".ptr, 5u64); + os.write(2, name, nlen); + os.write(2, "\n".ptr, 1u64); + }; + }; + os.unlink(tmp); + }; + off += reclen; + }; + n = os.getdents64(fd, buf, 8192u64); + }; + os.close(fd); + if (fail == 0) { return 0; }; + return 1; +}; + +fn do_test(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { + let a: *arena = newarena(); + let target: *u8; + if (start >= argc) { + let dot: *u8 = amalloc(a, 2u64): *u8; + dot[0] = 46u8; dot[1] = 0u8; + target = dot; + } else { + target = argv[start]; + }; + + // single-file mode: literal *.ww that exists + if (cstr_endswith_lit(target, ".ww")) { + if (os.access(target, 0i32) == 0) { + return run_single_test(self_dir, target); + }; + }; + + // otherwise treat target as a directory; enumerate *_test.ww + return run_dir_tests(self_dir, target); +}; + // ---- Entry ------------------------------------------------------------- export fn main(argc: i32, argv: **u8) i32 = { @@ -1065,6 +1453,9 @@ export fn main(argc: i32, argv: **u8) i32 = { if (cstreq_lit(cmd, "run")) { return do_run(self_dir, argv, argc, 2); }; + if (cstreq_lit(cmd, "test")) { + return do_test(self_dir, argv, argc, 2); + }; os.write(2, "ww: unknown subcommand\n".ptr, 23u64); write_usage(2); return 2; diff --git a/selfhost/cmd/ww/main.ww b/selfhost/cmd/ww/main.ww index 76bc9600..fb83fa78 100644 --- a/selfhost/cmd/ww/main.ww +++ b/selfhost/cmd/ww/main.ww @@ -2,13 +2,13 @@ // // The user-facing driver. Plan 9 cc(1) / Hare hare(1) analogue: // -// ww build foo.ww → 6c foo.ww > foo.s ; 6a foo.s > foo.o ; -// 6l -o foo foo.o libwwrt.a +// ww build foo.ww → w6c foo.ww > foo.s ; w6a foo.s > foo.o ; +// w6l -o foo foo.o libwwrt.a // ww run foo.ww → build then exec // ww version → print version // // Tool paths default to siblings of $0 so a fresh build runs out of -// out/bin/. Env-var overrides (WW_6C / WW_6A / WW_6L / WW_LIB) are +// out/bin/. Env-var overrides (WW_W6C / WW_W6A / WW_W6L / WW_LIB) are // not yet supported in this port; the bootstrap doesn't need them. use os; @@ -408,7 +408,7 @@ fn append_lit(stem: *u8, suffix: str) *u8 = { }; // linker flags bundled as a struct so build_one stays at the wwstage -// 6c's 6-argument calling-convention limit. +// w6c's 6-argument calling-convention limit. type lflags = struct { libdirs: **u8, n_libdirs: i32, @@ -418,7 +418,7 @@ type lflags = struct { // build_one — compile `src` into the executable named `out`. // self_dir: NUL-terminated dir containing this driver and the -// wwstage tools (6c_ww/6a_ww/6l_ww) +// wwstage tools (w6c_ww/w6a_ww/w6l_ww) // src: NUL-terminated path to the .ww file // out: NUL-terminated desired output path // incs: NUL-terminated colon-list of -I dirs (may be empty) @@ -426,14 +426,14 @@ type lflags = struct { // // The ww-side driver shells to the ww-side tools so a `ww_ww build` // touches no C-built code at runtime. The C `ww` driver in cmd/ww/ -// still drives the C-built 6c/6a/6l. Test 993 pins the two +// still drives the C-built w6c/w6a/w6l. Test 993 pins the two // pipelines to byte-identical output on a corpus. fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = { let a: *arena = newarena(); - let c6: *u8 = join_path_lit(self_dir, "6c_ww"); - let a6: *u8 = join_path_lit(self_dir, "6a_ww"); - let l6: *u8 = join_path_lit(self_dir, "6l_ww"); + let c6: *u8 = join_path_lit(self_dir, "w6c_ww"); + let a6: *u8 = join_path_lit(self_dir, "w6a_ww"); + let l6: *u8 = join_path_lit(self_dir, "w6l_ww"); // Default lib search path: /../../lib let dotdot_lib: *u8 = os.alloc(PATH_MAX): *u8; @@ -486,35 +486,35 @@ fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = { }; os.close(cf); - // Step 2: 6c -o .s .combined.ww + // Step 2: w6c -o .s .combined.ww { let argv: **u8 = os.alloc(40u64): **u8; - argv[0] = "6c\0".ptr; + argv[0] = "w6c\0".ptr; argv[1] = "-o\0".ptr; argv[2] = asmf; argv[3] = combined; argv[4] = nil; if (proc_run(c6, argv) != 0) { - os.write(2, "ww: 6c failed\n".ptr, 14u64); + os.write(2, "ww: w6c failed\n".ptr, 15u64); return 1; }; }; - // Step 3: 6a -o .o .s + // Step 3: w6a -o .o .s { let argv: **u8 = os.alloc(40u64): **u8; - argv[0] = "6a\0".ptr; + argv[0] = "w6a\0".ptr; argv[1] = "-o\0".ptr; argv[2] = objf; argv[3] = asmf; argv[4] = nil; if (proc_run(a6, argv) != 0) { - os.write(2, "ww: 6a failed\n".ptr, 14u64); + os.write(2, "ww: w6a failed\n".ptr, 15u64); return 1; }; }; - // Step 4: 6l -o .o libwwrt.a [-L...] [-l...] + // Step 4: w6l -o .o libwwrt.a [-L...] [-l...] { let nl_dirs: i32 = 0; let nl_libs: i32 = 0; @@ -526,13 +526,13 @@ fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = { l_dirs = lf.libdirs; l_libs = lf.libs; }; - // argv slots: 5 fixed (6l, -o, out, objf, libwwrt) + // argv slots: 5 fixed (w6l, -o, out, objf, libwwrt) // + 2 * n_libdirs (-L, dir) // + 2 * n_libs (-l, name) // + 1 nil terminator. let total: i32 = 5 + 2 * nl_dirs + 2 * nl_libs + 1; let argv: **u8 = os.alloc((total: u64) * 8u64): **u8; - argv[0] = "6l\0".ptr; + argv[0] = "w6l\0".ptr; argv[1] = "-o\0".ptr; argv[2] = out; argv[3] = objf; @@ -554,17 +554,139 @@ fn build_one(self_dir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = { }; argv[pos] = nil; if (proc_run(l6, argv) != 0) { - os.write(2, "ww: 6l failed\n".ptr, 14u64); + os.write(2, "ww: w6l failed\n".ptr, 15u64); return 1; }; }; return 0; }; +// ---- Module-by-name resolution ---------------------------------------- +// +// Mirrors cmd/ww/main.c:resolve_module. Maps a name like "foo", "lib/foo", +// "foo.ww", or "." to a concrete .ww file path: +// 1. literal .ww that exists → use as-is +// 2. "." → /.ww → that, if it exists +// 3. /.ww → that, if it exists +// 4. walk search path (cwd:incs:/../../lib): +// /.ww or //.ww + +fn cstr_endswith_lit(p: *u8, lit: str) bool = { + let plen: u64 = cstrlen(p); + let slen: u64 = lit.len: u64; + if (plen < slen) { return false; }; + let off: u64 = plen - slen; + let i: i32 = 0; + for (i < lit.len) { + let iu: u64 = i: u64; + if (p[off + iu] != lit[i]) { return false; }; + i += 1; + }; + return true; +}; + +// basename_off — return the offset of the last path segment within `p` +// (i.e. one past the final '/'). Returns 0 if there's no slash. +fn basename_off(p: *u8, plen: u64) u64 = { + let start: u64 = 0u64; + let i: u64 = 0u64; + for (i < plen) { + if (p[i] == 47u8) { start = i + 1u64; }; // '/' + i += 1u64; + }; + return start; +}; + +// arena_dup_cstr — copy `plen` bytes from `src` into a fresh NUL-sealed +// arena buffer. +fn arena_dup_cstr(a: *arena, src: *u8, plen: u64) *u8 = { + let buf: *u8 = amalloc(a, plen + 1u64): *u8; + let i: u64 = 0u64; + for (i < plen) { buf[i] = src[i]; i += 1u64; }; + buf[plen] = 0u8; + return buf; +}; + +// build_dir_module_path — alloc /.ww, NUL-terminated, in `a`. +fn build_dir_module_path(a: *arena, dir: *u8, dlen: u64, + base: *u8, blen: u64) *u8 = { + let need: u64 = dlen + 1u64 + blen + 3u64 + 1u64; + let buf: *u8 = amalloc(a, need): *u8; + let off: u64 = 0u64; + let i: u64 = 0u64; + for (i < dlen) { buf[off] = dir[i]; off += 1u64; i += 1u64; }; + buf[off] = 47u8; off += 1u64; // '/' + i = 0u64; + for (i < blen) { buf[off] = base[i]; off += 1u64; i += 1u64; }; + buf[off] = 46u8; off += 1u64; // '.' + buf[off] = 119u8; off += 1u64; // 'w' + buf[off] = 119u8; off += 1u64; // 'w' + buf[off] = 0u8; + return buf; +}; + +// build_search_path — compose the colon-separated lookup path used by +// resolve_module's case (4). Order: "." : : /../../lib +fn build_search_path(a: *arena, self_dir: *u8, incs: *u8) *u8 = { + let buf: *u8 = amalloc(a, PATH_MAX * 2u64): *u8; + let off: u64 = 0u64; + buf[off] = 46u8; off += 1u64; // '.' + if (incs != nil) { + if (incs[0u64] != 0u8) { + buf[off] = 58u8; off += 1u64; // ':' + off = cstr_into(buf, off, incs); + }; + }; + buf[off] = 58u8; off += 1u64; + off = cstr_into(buf, off, self_dir); + off = str_into(buf, off, "/../../lib"); + cstr_seal(buf, off); + return buf; +}; + +fn resolve_module(a: *arena, self_dir: *u8, name: *u8, incs: *u8) *u8 = { + let nlen: u64 = cstrlen(name); + + // (1) literal .ww file that exists + if (cstr_endswith_lit(name, ".ww")) { + if (os.access(name, 0i32) == 0) { + return arena_dup_cstr(a, name, nlen); + }; + }; + + // (2) "." → cwd's .ww + if (nlen == 1u64) { + if (name[0u64] == 46u8) { // '.' + let cwd: *u8 = amalloc(a, PATH_MAX): *u8; + let r: i64 = os.getcwd(cwd, PATH_MAX); + if (r <= 0i64) { return nil; }; + let cwdlen: u64 = (r: u64) - 1u64; // strip trailing NUL + let bo: u64 = basename_off(cwd, cwdlen); + let blen: u64 = cwdlen - bo; + let dot: *u8 = amalloc(a, 2u64): *u8; + dot[0] = 46u8; dot[1] = 0u8; + let probe: *u8 = build_dir_module_path(a, dot, 1u64, + cwd + bo, blen); + if (os.access(probe, 0i32) == 0) { return probe; }; + return nil; + }; + }; + + // (3) /.ww — directory-as-module + let bo: u64 = basename_off(name, nlen); + let probe: *u8 = build_dir_module_path(a, name, nlen, + name + bo, nlen - bo); + if (os.access(probe, 0i32) == 0) { return probe; }; + + // (4) search path lookup + let search: *u8 = build_search_path(a, self_dir, incs); + return locate_import(a, search, name, nlen); +}; + // ---- Subcommand handlers ---------------------------------------------- fn write_usage(fd: i32) void = { - let s: str = "usage: ww [-V] [args...]\n -V print version and exit\n build compile module to a static binary\n run build then exec\n version print version and exit\n"; + let s: str = "usage: ww [-V] [args...]\n -V print version and exit\n build [path] compile module to a static binary (path defaults to cwd)\n run [path] ... build then exec, passing extra args to the program\n test [path] build and run *_test.ww in the module (path defaults to cwd)\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 . build the cwd's .ww\n"; os.write(fd, s.ptr, s.len: u64); }; @@ -606,6 +728,7 @@ fn default_out_path(src: *u8) *u8 = { }; fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { + let a: *arena = newarena(); let src: *u8 = nil; let incs: *u8 = os.alloc(PATH_MAX * 2u64): *u8; let inc_off: u64 = 0u64; @@ -686,16 +809,23 @@ fn do_build(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { }; if (src == nil) { - os.write(2, "ww build: missing source\n".ptr, 25u64); - return 2; + // default to cwd module + let dot: *u8 = amalloc(a, 2u64): *u8; + dot[0] = 46u8; dot[1] = 0u8; + src = dot; }; - let out: *u8 = default_out_path(src); + let resolved: *u8 = resolve_module(a, self_dir, src, incs); + if (resolved == nil) { + os.write(2, "ww build: cannot find module\n".ptr, 29u64); + return 1; + }; + let out: *u8 = default_out_path(resolved); let lf: lflags; lf.libdirs = libdirs; lf.n_libdirs = n_libdirs; lf.libs = libs; lf.n_libs = n_libs; - return build_one(self_dir, src, out, incs, &lf); + return build_one(self_dir, resolved, out, incs, &lf); }; // Format the scratch path /tmp/ww_run_ into buf. Returns NUL- @@ -729,13 +859,149 @@ fn make_run_tmp(buf: *u8) void = { }; fn do_run(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { - if (start >= argc) { - os.write(2, "ww run: missing source\n".ptr, 23u64); - return 2; + let a: *arena = newarena(); + let src: *u8 = nil; + let pass_start: i32 = -1; // first argv idx to pass through to program + let incs: *u8 = os.alloc(PATH_MAX * 2u64): *u8; + let inc_off: u64 = 0u64; + cstr_seal(incs, 0u64); + + let max_lflags: i32 = 32; + let libdirs: **u8 = os.alloc((max_lflags: u64) * 8u64): **u8; + let n_libdirs: i32 = 0; + let libs: **u8 = os.alloc((max_lflags: u64) * 8u64): **u8; + let n_libs: i32 = 0; + + let i: i32 = start; + for (i < argc) { + if (pass_start >= 0) { i = argc; } // stop, leave rest for exec + else { + let p: *u8 = argv[i]; + if (p[0u64] == 45u8) { + if (p[1u64] == 73u8) { + let dir: *u8 = nil; + if (p[2u64] != 0u8) { + dir = p + 2u64; + } else { + if (i + 1 >= argc) { + os.write(2, "ww run: -I needs an argument\n".ptr, 29u64); + return 2; + }; + i += 1; + dir = argv[i]; + }; + if (inc_off > 0u64) { + incs[inc_off] = 58u8; + inc_off += 1u64; + }; + inc_off = cstr_into(incs, inc_off, dir); + cstr_seal(incs, inc_off); + } else { if (p[1u64] == 76u8) { + let dir: *u8 = nil; + if (p[2u64] != 0u8) { + dir = p + 2u64; + } else { + if (i + 1 >= argc) { + os.write(2, "ww run: -L needs an argument\n".ptr, 29u64); + return 2; + }; + i += 1; + dir = argv[i]; + }; + if (n_libdirs >= max_lflags) { + os.write(2, "ww run: too many -L\n".ptr, 20u64); + return 2; + }; + libdirs[n_libdirs] = dir; + n_libdirs += 1; + } else { if (p[1u64] == 108u8) { + let nm: *u8 = nil; + if (p[2u64] != 0u8) { + nm = p + 2u64; + } else { + if (i + 1 >= argc) { + os.write(2, "ww run: -l needs an argument\n".ptr, 29u64); + return 2; + }; + i += 1; + nm = argv[i]; + }; + if (n_libs >= max_lflags) { + os.write(2, "ww run: too many -l\n".ptr, 20u64); + return 2; + }; + libs[n_libs] = nm; + n_libs += 1; + } else { + os.write(2, "ww run: unknown flag\n".ptr, 21u64); + return 2; + }; }; }; + i += 1; + } else { + if (src == nil) { + src = p; + i += 1; + } else { + pass_start = i; // remaining args go to the program + }; + }; + }; }; + + if (src == nil) { + let dot: *u8 = amalloc(a, 2u64): *u8; + dot[0] = 46u8; dot[1] = 0u8; + src = dot; + }; + let resolved: *u8 = resolve_module(a, self_dir, src, incs); + if (resolved == nil) { + os.write(2, "ww run: cannot find module\n".ptr, 27u64); + return 1; + }; + let tmp: *u8 = os.alloc(PATH_MAX): *u8; make_run_tmp(tmp); - if (build_one(self_dir, argv[start], tmp, "\0".ptr, nil) != 0) { return 1; }; + let lf: lflags; + lf.libdirs = libdirs; + lf.n_libdirs = n_libdirs; + lf.libs = libs; + lf.n_libs = n_libs; + if (build_one(self_dir, resolved, tmp, incs, &lf) != 0) { + os.unlink(tmp); + return 1; + }; + + // exec with [tmp, argv[pass_start..argc), nil] + let n_extra: i32 = 0; + if (pass_start >= 0) { n_extra = argc - pass_start; }; + let total: i32 = n_extra + 2; + let exec_argv: **u8 = os.alloc((total: u64) * 8u64): **u8; + exec_argv[0] = tmp; + let k: i32 = 0; + for (k < n_extra) { + exec_argv[k + 1] = argv[pass_start + k]; + k += 1; + }; + exec_argv[n_extra + 1] = nil; + let rc: i32 = proc_run(tmp, exec_argv); + os.unlink(tmp); + return rc; +}; + +// ---- ww test ---------------------------------------------------------- +// +// Mirrors cmd/ww/main.c:do_test. Two modes: +// single-file: build+run a literal *.ww file, return its exit code +// directory: open the dir, getdents64, build+run each *_test.ww, +// report ok/FAIL per file, return 0 iff all pass. + +fn run_single_test(self_dir: *u8, src: *u8) i32 = { + let tmp: *u8 = os.alloc(PATH_MAX): *u8; + make_run_tmp(tmp); + if (build_one(self_dir, src, tmp, "\0".ptr, nil) != 0) { + os.unlink(tmp); + return 1; + }; let exec_argv: **u8 = os.alloc(16u64): **u8; exec_argv[0] = tmp; exec_argv[1] = nil; @@ -744,6 +1010,100 @@ fn do_run(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { return rc; }; +fn run_dir_tests(self_dir: *u8, dir: *u8) i32 = { + let fd: i32 = os.open(dir, os.O_RDONLY, 0i32); + if (fd < 0) { + os.write(2, "ww test: cannot open directory\n".ptr, 31u64); + return 1; + }; + + let pass: i32 = 0; + let fail: i32 = 0; + let buf: *u8 = os.alloc(8192u64): *u8; + let dirlen: u64 = cstrlen(dir); + + let n: i64 = os.getdents64(fd, buf, 8192u64); + for (n > 0i64) { + let off: u64 = 0u64; + let nu: u64 = n: u64; + for (off < nu) { + // d_reclen at offset+16 (u16 LE), d_name at offset+19 (cstr) + let blo: u64 = (buf[off + 16u64]): u64; + let bhi: u64 = (buf[off + 17u64]): u64; + let reclen: u64 = blo + (bhi * 256u64); + let name: *u8 = buf + off + 19u64; + if (cstr_endswith_lit(name, "_test.ww")) { + let nlen: u64 = cstrlen(name); + // path = / + let path: *u8 = os.alloc(PATH_MAX): *u8; + let p_off: u64 = cstr_into(path, 0u64, dir); + path[p_off] = 47u8; p_off += 1u64; + let i: u64 = 0u64; + for (i < nlen) { path[p_off + i] = name[i]; i += 1u64; }; + p_off += nlen; + cstr_seal(path, p_off); + // incs = so test files can `use ` siblings + let tincs: *u8 = os.alloc(PATH_MAX): *u8; + let ic: u64 = cstr_into(tincs, 0u64, dir); + cstr_seal(tincs, ic); + let tmp: *u8 = os.alloc(PATH_MAX): *u8; + make_run_tmp(tmp); + let bres: i32 = build_one(self_dir, path, tmp, tincs, nil); + if (bres != 0) { + fail += 1; + os.write(2, "FAIL ".ptr, 5u64); + os.write(2, name, nlen); + os.write(2, " (build)\n".ptr, 9u64); + } else { + let exec_argv: **u8 = os.alloc(16u64): **u8; + exec_argv[0] = tmp; + exec_argv[1] = nil; + let rc: i32 = proc_run(tmp, exec_argv); + if (rc == 0) { + pass += 1; + os.write(1, "ok ".ptr, 5u64); + os.write(1, name, nlen); + os.write(1, "\n".ptr, 1u64); + } else { + fail += 1; + os.write(2, "FAIL ".ptr, 5u64); + os.write(2, name, nlen); + os.write(2, "\n".ptr, 1u64); + }; + }; + os.unlink(tmp); + }; + off += reclen; + }; + n = os.getdents64(fd, buf, 8192u64); + }; + os.close(fd); + if (fail == 0) { return 0; }; + return 1; +}; + +fn do_test(self_dir: *u8, argv: **u8, argc: i32, start: i32) i32 = { + let a: *arena = newarena(); + let target: *u8; + if (start >= argc) { + let dot: *u8 = amalloc(a, 2u64): *u8; + dot[0] = 46u8; dot[1] = 0u8; + target = dot; + } else { + target = argv[start]; + }; + + // single-file mode: literal *.ww that exists + if (cstr_endswith_lit(target, ".ww")) { + if (os.access(target, 0i32) == 0) { + return run_single_test(self_dir, target); + }; + }; + + // otherwise treat target as a directory; enumerate *_test.ww + return run_dir_tests(self_dir, target); +}; + // ---- Entry ------------------------------------------------------------- export fn main(argc: i32, argv: **u8) i32 = { @@ -777,6 +1137,9 @@ export fn main(argc: i32, argv: **u8) i32 = { if (cstreq_lit(cmd, "run")) { return do_run(self_dir, argv, argc, 2); }; + if (cstreq_lit(cmd, "test")) { + return do_test(self_dir, argv, argc, 2); + }; os.write(2, "ww: unknown subcommand\n".ptr, 23u64); write_usage(2); return 2; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 92ab289f..657890f4 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -18,19 +18,21 @@ export fn assert(cond: bool, msg: str) void = { if (!cond) { abort(msg); }; }; -def SYS_READ: i64 = 0; -def SYS_WRITE: i64 = 1; -def SYS_OPEN: i64 = 2; -def SYS_CLOSE: i64 = 3; -def SYS_LSEEK: i64 = 8; -def SYS_ACCESS: i64 = 21; -def SYS_DUP2: i64 = 33; -def SYS_GETPID: i64 = 39; -def SYS_FORK: i64 = 57; -def SYS_EXECVE: i64 = 59; -def SYS_EXIT: i64 = 60; -def SYS_WAIT4: i64 = 61; -def SYS_UNLINK: i64 = 87; +def SYS_READ: i64 = 0; +def SYS_WRITE: i64 = 1; +def SYS_OPEN: i64 = 2; +def SYS_CLOSE: i64 = 3; +def SYS_LSEEK: i64 = 8; +def SYS_ACCESS: i64 = 21; +def SYS_DUP2: i64 = 33; +def SYS_GETPID: i64 = 39; +def SYS_FORK: i64 = 57; +def SYS_EXECVE: i64 = 59; +def SYS_EXIT: i64 = 60; +def SYS_WAIT4: i64 = 61; +def SYS_UNLINK: i64 = 87; +def SYS_GETCWD: i64 = 79; +def SYS_GETDENTS64: i64 = 217; // open(2) flags. Linux values, matching . def O_RDONLY: i32 = 0; @@ -65,7 +67,7 @@ export fn close(fd: i32) i32 = { // dup2(2): make `newfd` refer to the same description as `oldfd`, // closing `newfd` first if open. Returns `newfd` on success or a -// negative errno. Used by 6c_ww to redirect stdout into an output +// negative errno. Used by w6c_ww to redirect stdout into an output // file without changing the cgen emit path. export fn dup2(oldfd: i32, newfd: i32) i32 = { return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; @@ -179,7 +181,33 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { options: i64, rusage: i64): i32; }; -// selfhost/cmd/wwc/mem.ww — port of cmd/wwc/mem.c. +// getcwd(2) — Linux flavour. Writes the NUL-terminated cwd into `buf` +// and returns the number of bytes written (including the NUL), or a +// negative errno. The driver uses it to expand `.` to the cwd's +// basename for `ww build` / `ww test`. +export fn getcwd(buf: *u8, n: u64) i64 = { + return syscall2(SYS_GETCWD, buf: i64, n: i64); +}; + +// getdents64(2) — Linux directory enumeration. The fd must be opened +// with O_RDONLY on a directory. `buf` receives a packed sequence of +// linux_dirent64 records: +// +// struct linux_dirent64 { +// u64 d_ino; // 0..7 +// i64 d_off; // 8..15 +// u16 d_reclen; // 16..17 — total bytes for this record +// u8 d_type; // 18 — DT_REG/DT_DIR/... +// u8 d_name[]; // 19.. — NUL-terminated name + padding +// }; +// +// Returns bytes written into `buf` (advance by d_reclen to walk), +// 0 at end-of-directory, or a negative errno. +export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { + return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); +}; + +// selfhost/cmd/wcc/mem.ww — port of cmd/wcc/mem.c. // // Bump arena allocator. Backed by the runtime page allocator // (rt_alloc / rt_free), no libc. Each chunk is mmap'd; when the @@ -187,7 +215,7 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { // unmaps the chain. // // Memory handed out is 16-byte aligned. The C version under -// cmd/wwc/ is retained until the three-stage bootstrap diffs clean. +// cmd/wcc/ is retained until the three-stage bootstrap diffs clean. use os; @@ -406,8 +434,8 @@ export fn parseu64(s: str) (u64 | str) = { return v; }; -// selfhost/cmd/wwc/tok.ww — port of cmd/wwc/tok.c plus the Tkind / -// Tok / Pos shapes from cmd/wwc/ww.h. +// selfhost/cmd/wcc/tok.ww — port of cmd/wcc/tok.c plus the Tkind / +// Tok / Pos shapes from cmd/wcc/ww.h. // // Token kind values must stay numerically equal to the C side: the // 990_selfhost test diffs ww-side wwdump output against C-side @@ -415,13 +443,13 @@ export fn parseu64(s: str) (u64 | str) = { // integers and breaks the diff. // // Bottom of file: tokprint, which emits one token per line in a -// format identical to cmd/wwc/tok.c:tokprint(). +// format identical to cmd/wcc/tok.c:tokprint(). use os; use strconv; // ---- Tkind ------------------------------------------------------------ -// Mirror of the C enum in cmd/wwc/ww.h. Don't reorder. +// Mirror of the C enum in cmd/wcc/ww.h. Don't reorder. def TK_NONE: i32 = 0; def TK_EOF: i32 = 1; @@ -516,7 +544,7 @@ def TK_LAST: i32 = 80; // ---- Pos / Tok -------------------------------------------------------- // // `pos` is used at error-reporting boundaries; we always pass it via -// *pos so the value never gets struct-copied (6c can't yet copy a +// *pos so the value never gets struct-copied (w6c can't yet copy a // 24-byte struct). // // `tok` is flat — file/line/col live directly on the token rather than @@ -554,7 +582,7 @@ fn streq_n(a: *u8, b: str, n: i32) bool = { // kwlookup — returns the matching TK_* keyword kind for a byte run, // or TK_NONE if it's an ordinary identifier. Linear search over a -// small alphabetised list, matching cmd/wwc/tok.c. +// small alphabetised list, matching cmd/wcc/tok.c. export fn kwlookup(p: *u8, n: i32) i32 = { if (streq_n(p, "as", n)) { return TK_AS; }; if (streq_n(p, "break", n)) { return TK_BREAK; }; @@ -683,7 +711,7 @@ export fn tokname(k: i32) str = { // ---- writer for tokprint ---------------------------------------------- // -// fputq mirrors cmd/wwc/tok.c:fputq — quote the string with C-style +// fputq mirrors cmd/wcc/tok.c:fputq — quote the string with C-style // escapes for \, ", \n, \t, \r and \xNN for other non-printables. fn fputc_byte(fd: i32, b: u8) void = { @@ -750,14 +778,14 @@ fn fputq(fd: i32, p: *u8, n: i32) void = { }; // tokprint — write one token line to fd. Format must match -// cmd/wwc/tok.c:tokprint() byte-for-byte: that's the diff anchor. +// cmd/wcc/tok.c:tokprint() byte-for-byte: that's the diff anchor. // ":: [ ]\n" // -// Takes `t` by pointer because 6c can't yet pass a >16-byte struct +// Takes `t` by pointer because w6c can't yet pass a >16-byte struct // by value; the C version takes Tok by value. export fn tokprint(fd: i32, t: *tok) void = { // Chained-dot field reads (`t.x.y`) on str sub-fields aren't yet - // reduced by 6c — `t.x.y` returns the whole str. Lift the str + // reduced by w6c — `t.x.y` returns the whole str. Lift the str // fields into locals so we can use the str pseudo-field path. let tfile: str = t.file; let ttext: str = t.text; @@ -894,14 +922,14 @@ export fn toupper(c: u8) u8 = { return c; }; -// selfhost/cmd/wwc/lex.ww — port of cmd/wwc/lex.c. +// selfhost/cmd/wcc/lex.ww — port of cmd/wcc/lex.c. // // The DFA, the helpers, and the order of decisions all mirror the C // version exactly. The 990_selfhost test diffs the resulting token // stream against the C-side wwdump byte-for-byte; any divergence is // a port bug. // -// Calling-convention note: 6c can't yet pass or return structs >16 +// Calling-convention note: w6c can't yet pass or return structs >16 // bytes by value, so `tok` and `pos` are passed by pointer (out // params). The C version passes `Tok` by value; we differ here only // in shape, not in observable behaviour. Token kind values stay @@ -1551,7 +1579,7 @@ export fn lexnext(l: *lex, out: *tok) void = { out.text = astrndup(l.a, one.ptr, 1u64); }; -// selfhost/cmd/wwc/ast.ww — port of cmd/wwc/ast.c (Node defs + printer). +// selfhost/cmd/wcc/ast.ww — port of cmd/wcc/ast.c (Node defs + printer). // // Status: AST printer is fully ported. Constructor `newnode` is here. // The parser (parse.ww) is currently minimal — see its file header. @@ -1567,7 +1595,7 @@ use tok; // ---- Nkind ------------------------------------------------------------ // -// Mirror of cmd/wwc/ww.h Nkind. Values must stay numerically equal so +// Mirror of cmd/wcc/ww.h Nkind. Values must stay numerically equal so // the AST diff probe in 990_selfhost works. def N_NONE: i32 = 0; @@ -1887,7 +1915,7 @@ export fn astprint(fd: i32, n: *node) void = { pr(fd, n, 0); }; -// selfhost/cmd/wwc/parse.ww — port of cmd/wwc/parse.c. +// selfhost/cmd/wcc/parse.ww — port of cmd/wcc/parse.c. // // Status: GROWING stub. Currently handles top-level `use IDENT;`, // `def NAME: TYPE = LIT;`, `type NAME = TYPE;`, and `fn NAME(params) @@ -1900,7 +1928,7 @@ export fn astprint(fd: i32, n: *node) void = { // surface form lands here gradually so the AST diff in 990_selfhost // grows toward whole-language coverage one increment at a time. // -// Calling-convention shim: 6c can't yet pass a sub-struct field +// Calling-convention shim: w6c can't yet pass a sub-struct field // (e.g. p.cur.line where p.cur is a `tok` of size 76). The parser // stores the current token as flat primitive fields rather than a // nested `tok` struct; `refill` copies a freshly lexed token in. @@ -2635,7 +2663,7 @@ fn parsestmt(p: *parser) *node = { }; // expression statement, or tuple-destructure multi-assign: // a, b = expr; - // Mirrors cmd/wwc/parse.c:1015-1031. We parse the first lvalue + // Mirrors cmd/wcc/parse.c:1015-1031. We parse the first lvalue // with parseexpr (matches the C side); subsequent lvalues go // through parsebin(parseunary, 1) so the `=` stays for us to // consume — parseexpr would absorb it. @@ -2870,7 +2898,7 @@ export fn parsefile(p: *parser) *node = { return f; }; -// selfhost/cmd/wwc/type.ww — port of cmd/wwc/type.c. +// selfhost/cmd/wcc/type.ww — port of cmd/wcc/type.c. // // Status: full structural port. The C version uses module-globals for // the primitive types (ty_void, ty_i32, …); ww doesn't have writable @@ -2882,7 +2910,7 @@ use os; use mem; // ---- TypeKind --------------------------------------------------------- -// Numeric values must stay aligned with cmd/wwc/ww.h TypeKind so the +// Numeric values must stay aligned with cmd/wcc/ww.h TypeKind so the // next diff signal (typed-AST printer / cgen) can compare across the // two implementations. @@ -3200,7 +3228,7 @@ export fn type_eq(a: *tinfo, b: *tinfo) bool = { return true; // primitives match by kind alone }; -// selfhost/cmd/wwc/sym.ww — port of cmd/wwc/sym.c. +// selfhost/cmd/wcc/sym.ww — port of cmd/wcc/sym.c. // // Per-scope hashtable, chained to the parent. Lookup walks up. // Plan 9 / Hare flavoured. Duplicate definitions in the same scope @@ -3210,7 +3238,7 @@ use mem; use typ; use ast; -// Symbol kinds — must stay numerically aligned with cmd/wwc/ww.h Skind. +// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind. def SK_NONE: i32 = 0; def SK_VAR: i32 = 1; def SK_PARAM: i32 = 2; @@ -3314,11 +3342,11 @@ export fn scope_define(s: *scope, name: str, k: i32, t: *tinfo, decl: *node) *sy return sy; }; -// selfhost/cmd/wwc/check.ww — minimal port of cmd/wwc/check.c. +// selfhost/cmd/wcc/check.ww — minimal port of cmd/wcc/check.c. // // Status: name-resolution + primitive-type seeding only. Full type // inference, conversion rules, tagged-union dispatch typing, return- -// type checking, etc. all live in cmd/wwc/check.c (937 lines) and +// type checking, etc. all live in cmd/wcc/check.c (937 lines) and // will land here in subsequent commits. // // What this version does: @@ -3562,10 +3590,10 @@ export fn check_file(c: *checker, file: *node) void = { }; }; -// selfhost/cmd/wwc/cgen.ww — port of cmd/6c/cgen.c. +// selfhost/cmd/wcc/cgen.ww — port of cmd/w6c/cgen.c. // // Status: GROWING. Each subsystem we add is verified by `wwdump_ww -c` -// producing byte-identical output to C-side `6c` for the same source, +// producing byte-identical output to C-side `w6c` for the same source, // then by assembling + linking + running the result. // // Current coverage: @@ -3760,7 +3788,7 @@ fn local_alloc(c: *cgen, name: str, sz: i32, tnode: *node) i32 = { fn local_add(c: *cgen, name: str, sz: i32, tnode: *node) i32 = { // Name-based slot reuse for N_LETs and params: if `name` is // already declared in this function, return its existing - // offset. Mirrors C cgen (cmd/6c/cgen.c:localoff). Two + // offset. Mirrors C cgen (cmd/w6c/cgen.c:localoff). Two // disjoint scopes that declare the same name share one slot — // so `escape` in wwdump (three `let cp: pos;` across separate // branches) reserves one slot, not three. scan_locals does @@ -4097,7 +4125,7 @@ fn fnret_lookup(c: *cgen, name: str) *node = { // ---- def-constant registry ------------------------------------------ // -// `def NAME: T = LIT;` becomes a DATA symbol the C-side 6c emits; an +// `def NAME: T = LIT;` becomes a DATA symbol the C-side w6c emits; an // ident reference loads it via `MOVQ NAME(SB), AX`. We collect them at // file load and consult on N_IDENT lookup. @@ -4725,7 +4753,7 @@ fn node_isunsigned(c: *cgen, n: *node) bool = { // Walks the base local's declared type and pulls the element // out — *u8 → u8, [N]u32 → u32, []u64 → u64. Without this the // compare-codegen for `p[i] >= 48u8` falls back to signed JGE - // instead of JAE, diverging from C 6c on byte indexing. + // instead of JAE, diverging from C w6c on byte indexing. if (k == N_INDEX) { let base: *node = n.lhs; if (base != nil) { @@ -5198,7 +5226,7 @@ fn cgexpr(c: *cgen, n: *node) void = { // path the cgen falls through and AX retains whatever the // inner expression left there — typically the *struct pointer // itself, so reads silently get the pointer value instead of - // the field. (Showed up porting 6l/pass.ww.) + // the field. (Showed up porting w6l/pass.ww.) if (lhs != nil) { if (lhs.kind == N_DOT) { let inner_t: *node = dot_inner_struct_ptr(c, lhs); @@ -5487,7 +5515,7 @@ fn cgexpr(c: *cgen, n: *node) void = { }; cgexpr(c, n.rhs); // Push order matches C cgen - // (cmd/6c/cgen.c:1033-1041): PUSHQ AX + // (cmd/w6c/cgen.c:1033-1041): PUSHQ AX // (ptr) first, then PUSHQ BX (len) if // str, so the pop sequence is POP CX // (len) → POP AX (ptr) → MOVQ AX, @@ -6259,7 +6287,7 @@ fn cgstmt(c: *cgen, n: *node) void = { }; } else { // Bare `let x: T;` with no initializer. C cgen - // (cmd/6c/cgen.c:2181-2183) zero-inits only when + // (cmd/w6c/cgen.c:2181-2183) zero-inits only when // the underlying type's natural size is 8 — pointers, // i64/u64, function pointers, ints. Structs/arrays/ // slices/strings/tagged/tuples are left for per-field @@ -6330,7 +6358,7 @@ fn cgstmt(c: *cgen, n: *node) void = { // Tuple-destructure assign: `a, b = call();`. The call's tuple // return lands in (AX, DX); push DX to free it, store AX into // the first lvalue, then pop DX into the second. Mirrors - // cmd/6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same + // cmd/w6c/cgen.c:2424-2440. Lvalues beyond two are dropped (same // as C — no fixture uses >2 today). if (k == N_MASSIGN) { if (n.rhs != nil) { cgexpr(c, n.rhs); }; diff --git a/selfhost/test/smoke.combined.ww b/selfhost/test/smoke.combined.ww index 52ff859f..5e5ab5a4 100644 --- a/selfhost/test/smoke.combined.ww +++ b/selfhost/test/smoke.combined.ww @@ -18,19 +18,21 @@ export fn assert(cond: bool, msg: str) void = { if (!cond) { abort(msg); }; }; -def SYS_READ: i64 = 0; -def SYS_WRITE: i64 = 1; -def SYS_OPEN: i64 = 2; -def SYS_CLOSE: i64 = 3; -def SYS_LSEEK: i64 = 8; -def SYS_ACCESS: i64 = 21; -def SYS_DUP2: i64 = 33; -def SYS_GETPID: i64 = 39; -def SYS_FORK: i64 = 57; -def SYS_EXECVE: i64 = 59; -def SYS_EXIT: i64 = 60; -def SYS_WAIT4: i64 = 61; -def SYS_UNLINK: i64 = 87; +def SYS_READ: i64 = 0; +def SYS_WRITE: i64 = 1; +def SYS_OPEN: i64 = 2; +def SYS_CLOSE: i64 = 3; +def SYS_LSEEK: i64 = 8; +def SYS_ACCESS: i64 = 21; +def SYS_DUP2: i64 = 33; +def SYS_GETPID: i64 = 39; +def SYS_FORK: i64 = 57; +def SYS_EXECVE: i64 = 59; +def SYS_EXIT: i64 = 60; +def SYS_WAIT4: i64 = 61; +def SYS_UNLINK: i64 = 87; +def SYS_GETCWD: i64 = 79; +def SYS_GETDENTS64: i64 = 217; // open(2) flags. Linux values, matching . def O_RDONLY: i32 = 0; @@ -65,7 +67,7 @@ export fn close(fd: i32) i32 = { // dup2(2): make `newfd` refer to the same description as `oldfd`, // closing `newfd` first if open. Returns `newfd` on success or a -// negative errno. Used by 6c_ww to redirect stdout into an output +// negative errno. Used by w6c_ww to redirect stdout into an output // file without changing the cgen emit path. export fn dup2(oldfd: i32, newfd: i32) i32 = { return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; @@ -179,6 +181,32 @@ export fn wait4(pid: i32, status_out: *i32, options: i32, rusage: *void) i32 = { options: i64, rusage: i64): i32; }; +// getcwd(2) — Linux flavour. Writes the NUL-terminated cwd into `buf` +// and returns the number of bytes written (including the NUL), or a +// negative errno. The driver uses it to expand `.` to the cwd's +// basename for `ww build` / `ww test`. +export fn getcwd(buf: *u8, n: u64) i64 = { + return syscall2(SYS_GETCWD, buf: i64, n: i64); +}; + +// getdents64(2) — Linux directory enumeration. The fd must be opened +// with O_RDONLY on a directory. `buf` receives a packed sequence of +// linux_dirent64 records: +// +// struct linux_dirent64 { +// u64 d_ino; // 0..7 +// i64 d_off; // 8..15 +// u16 d_reclen; // 16..17 — total bytes for this record +// u8 d_type; // 18 — DT_REG/DT_DIR/... +// u8 d_name[]; // 19.. — NUL-terminated name + padding +// }; +// +// Returns bytes written into `buf` (advance by d_reclen to walk), +// 0 at end-of-directory, or a negative errno. +export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { + return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); +}; + // strconv — number↔string conversions. Decimal i64 to/from a fixed // buffer. Two error idioms ship side by side: // - Plan 9 style (atoi64): tuple `(value, ok)`. Pre-dates the @@ -406,7 +434,7 @@ export fn toupper(c: u8) u8 = { // indicating which probe broke. The 990_selfhost test asserts 42. // // Note: only stack-local mutable state. Top-level `let` mutation -// requires a writable .data segment in 6l, which is a separate +// requires a writable .data segment in w6l, which is a separate // task; until then we exercise polymorphism via ctx pointers, which // is what the real port wants anyway. @@ -423,7 +451,7 @@ type arena = struct { }; // In-place init. Returning a 24-byte struct by value isn't yet -// supported in 6c (SysV requires a hidden return-slot pointer for +// supported in w6c (SysV requires a hidden return-slot pointer for // structs >16 bytes), so we initialize through a pointer like the // real compiler does today. fn arena_init(a: *arena, buf: *u8, cap: u64) void = { diff --git a/selfhost/test/smoke.ww b/selfhost/test/smoke.ww index a2203bb7..7ab7d8eb 100644 --- a/selfhost/test/smoke.ww +++ b/selfhost/test/smoke.ww @@ -12,7 +12,7 @@ // indicating which probe broke. The 990_selfhost test asserts 42. // // Note: only stack-local mutable state. Top-level `let` mutation -// requires a writable .data segment in 6l, which is a separate +// requires a writable .data segment in w6l, which is a separate // task; until then we exercise polymorphism via ctx pointers, which // is what the real port wants anyway. @@ -29,7 +29,7 @@ type arena = struct { }; // In-place init. Returning a 24-byte struct by value isn't yet -// supported in 6c (SysV requires a hidden return-slot pointer for +// supported in w6c (SysV requires a hidden return-slot pointer for // structs >16 bytes), so we initialize through a pointer like the // real compiler does today. fn arena_init(a: *arena, buf: *u8, cap: u64) void = { diff --git a/selfhost/test/uses.ww b/selfhost/test/uses.ww index f9adc3c3..4c14a4ad 100644 --- a/selfhost/test/uses.ww +++ b/selfhost/test/uses.ww @@ -3,7 +3,7 @@ // `type NAME = TYPE;` (alias + struct), top-level `let NAME: TYPE = LIT;`. // // Function declarations are still recovered past — the body parser -// is the next major chunk. See selfhost/cmd/wwc/parse.ww header. +// is the next major chunk. See selfhost/cmd/wcc/parse.ww header. use os; use mem; diff --git a/test/run b/test/run index 393366a8..72f5a693 100755 --- a/test/run +++ b/test/run @@ -2,7 +2,7 @@ # test/run — driver for `make test`. Plan 9 rc-flavoured but plain sh. # # Walks the test/ tree: -# test/wwc/_.c → out/bin/test_ binary, run it. +# test/wcc/_.c → out/bin/test_ binary, run it. # test/lang//*.ww → compile/run via $WW, compare stdout/exit # with header comments (// expected: ...). # Exit non-zero on first failure. @@ -16,7 +16,7 @@ fail=0 ran=0 # ---- C-side unit tests -------------------------------------------------- -for t in test/wwc/*.c; do +for t in test/wcc/*.c; do [ -f "$t" ] || continue name=${t##*/} name=${name%.c} diff --git a/test/wwc/000_smoke.c b/test/wcc/000_smoke.c similarity index 96% rename from test/wwc/000_smoke.c rename to test/wcc/000_smoke.c index c64f37f7..d9f8ccb2 100644 --- a/test/wwc/000_smoke.c +++ b/test/wcc/000_smoke.c @@ -2,7 +2,7 @@ * 000_smoke — Phase 0 smoke test. * * Asserts: - * - libwwc symbols (newarena/amalloc/freearena, fatal/errorf/warnf) + * - libwcc symbols (newarena/amalloc/freearena, fatal/errorf/warnf) * are linkable. * - `ww -V` exits 0 and prints the configured version. */ diff --git a/test/wwc/100_lex.c b/test/wcc/100_lex.c similarity index 100% rename from test/wwc/100_lex.c rename to test/wcc/100_lex.c diff --git a/test/wwc/200_parse.c b/test/wcc/200_parse.c similarity index 100% rename from test/wwc/200_parse.c rename to test/wcc/200_parse.c diff --git a/test/wwc/300_check.c b/test/wcc/300_check.c similarity index 100% rename from test/wwc/300_check.c rename to test/wcc/300_check.c diff --git a/test/wwc/400_6c.c b/test/wcc/400_w6c.c similarity index 91% rename from test/wwc/400_6c.c rename to test/wcc/400_w6c.c index d281ce9b..bbe50cf7 100644 --- a/test/wwc/400_6c.c +++ b/test/wcc/400_w6c.c @@ -22,7 +22,7 @@ run6c(const char *src, char **out) char cmd[256]; const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; - snprintf(cmd, sizeof cmd, "%s/6c %s 2>&1", bin, path); + snprintf(cmd, sizeof cmd, "%s/w6c %s 2>&1", bin, path); FILE *p = popen(cmd, "r"); if (p == NULL) { unlink(path); return -1; } size_t cap = 4096, n = 0; @@ -72,7 +72,7 @@ main(void) char *out = NULL; int rc = run6c(rows[i].src, &out); if (rc != 0) { - fprintf(stderr, "row %d: 6c rc=%d\n", i, rc); + fprintf(stderr, "row %d: w6c rc=%d\n", i, rc); fail++; free(out); continue; @@ -84,7 +84,7 @@ main(void) } free(out); } - if (fail) { fprintf(stderr, "%d/%d 6c tests failed\n", fail, n); return 1; } - printf("6c: %d/%d ok\n", n, n); + if (fail) { fprintf(stderr, "%d/%d w6c tests failed\n", fail, n); return 1; } + printf("w6c: %d/%d ok\n", n, n); return 0; } diff --git a/test/wwc/500_6a.c b/test/wcc/500_w6a.c similarity index 68% rename from test/wwc/500_6a.c rename to test/wcc/500_w6a.c index 4a259301..72958819 100644 --- a/test/wwc/500_6a.c +++ b/test/wcc/500_w6a.c @@ -1,6 +1,6 @@ /* - * 500_asm — assembler smoke. Drive 6c on a tiny program, feed the - * output to 6a, then read back the .o magic to confirm we produced + * 500_asm — assembler smoke. Drive w6c on a tiny program, feed the + * output to w6a, then read back the .o magic to confirm we produced * a valid ELF64 relocatable object. */ #include @@ -39,10 +39,10 @@ main(void) fclose(f); char cmd[512]; - snprintf(cmd, sizeof cmd, "%s/6c -o %s %s", bin, asmf, src); - if (run(cmd) != 0) { fprintf(stderr, "6c fail: %s\n", programs[i]); fail++; continue; } - snprintf(cmd, sizeof cmd, "%s/6a -o %s %s", bin, obj, asmf); - if (run(cmd) != 0) { fprintf(stderr, "6a fail: %s\n", programs[i]); fail++; continue; } + snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, asmf, src); + if (run(cmd) != 0) { fprintf(stderr, "w6c fail: %s\n", programs[i]); fail++; continue; } + snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, obj, asmf); + if (run(cmd) != 0) { fprintf(stderr, "w6a fail: %s\n", programs[i]); fail++; continue; } FILE *of = fopen(obj, "rb"); if (of == NULL) { fail++; continue; } @@ -56,7 +56,7 @@ main(void) } } unlink(src); unlink(asmf); unlink(obj); - if (fail) { fprintf(stderr, "%d/%d 6a tests failed\n", fail, n); return 1; } - printf("6a: %d/%d ok\n", n, n); + if (fail) { fprintf(stderr, "%d/%d w6a tests failed\n", fail, n); return 1; } + printf("w6a: %d/%d ok\n", n, n); return 0; } diff --git a/test/wwc/600_6l.c b/test/wcc/600_w6l.c similarity index 78% rename from test/wwc/600_6l.c rename to test/wcc/600_w6l.c index ef90abca..edd8a278 100644 --- a/test/wwc/600_6l.c +++ b/test/wcc/600_w6l.c @@ -1,5 +1,5 @@ /* - * 600_6l — linker smoke. Drive 6c → 6a → 6l on a tiny program, + * 600_6l — linker smoke. Drive w6c → w6a → w6l on a tiny program, * confirm the result is a static ELF executable with no PT_INTERP. */ #include @@ -24,12 +24,12 @@ main(void) fclose(f); char cmd[1024]; - snprintf(cmd, sizeof cmd, "%s/6c -o %s %s", bin, asmf, src); - if (system(cmd) != 0) { fprintf(stderr, "6c failed\n"); return 1; } - snprintf(cmd, sizeof cmd, "%s/6a -o %s %s", bin, obj, asmf); - if (system(cmd) != 0) { fprintf(stderr, "6a failed\n"); return 1; } - snprintf(cmd, sizeof cmd, "%s/6l -o %s %s", bin, exe, obj); - if (system(cmd) != 0) { fprintf(stderr, "6l failed\n"); return 1; } + snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s", bin, asmf, src); + if (system(cmd) != 0) { fprintf(stderr, "w6c failed\n"); return 1; } + snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s", bin, obj, asmf); + if (system(cmd) != 0) { fprintf(stderr, "w6a failed\n"); return 1; } + snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s", bin, exe, obj); + if (system(cmd) != 0) { fprintf(stderr, "w6l failed\n"); return 1; } /* validate ELF magic + e_type=EXEC */ FILE *of = fopen(exe, "rb"); @@ -62,6 +62,6 @@ main(void) } unlink(src); unlink(asmf); unlink(obj); unlink(exe); - printf("6l: ok\n"); + printf("w6l: ok\n"); return 0; } diff --git a/test/wwc/610_arch.c b/test/wcc/610_arch.c similarity index 87% rename from test/wwc/610_arch.c rename to test/wcc/610_arch.c index aad64d3b..3336fe99 100644 --- a/test/wwc/610_arch.c +++ b/test/wcc/610_arch.c @@ -4,7 +4,7 @@ * undefined external. Linking should pull only the needed member; * the other one's bad reference must NOT cause a link error. * - * If 6l were still pulling all members, this test would fail with + * If w6l were still pulling all members, this test would fail with * "undefined reference to 'this_symbol_does_not_exist'". */ #include @@ -56,11 +56,11 @@ main(void) char cmd[2048]; snprintf(cmd, sizeof cmd, - "set -e; cd %s && %s/6c -o lib_good.s lib_good.ww && " - "%s/6c -o lib_bad.s lib_bad.ww && " - "%s/6a -o lib_good.o lib_good.s && %s/6a -o lib_bad.o lib_bad.s && " + "set -e; cd %s && %s/w6c -o lib_good.s lib_good.ww && " + "%s/w6c -o lib_bad.s lib_bad.ww && " + "%s/w6a -o lib_good.o lib_good.s && %s/w6a -o lib_bad.o lib_bad.s && " "ar rcs libfoo.a lib_good.o lib_bad.o && " - "%s/6c -o m.s m.ww && %s/6a -o m.o m.s", + "%s/w6c -o m.s m.ww && %s/w6a -o m.o m.s", dir, bin, bin, bin, bin, bin, bin); if (run(cmd) != 0) { fprintf(stderr, "build failed\n"); return 1; } @@ -69,7 +69,7 @@ main(void) snprintf(startobj, sizeof startobj, "%s/../obj/rt/start.o", bin); snprintf(cmd, sizeof cmd, - "%s/6l -o %s/m %s/m.o %s %s/libfoo.a 2>%s/link.err", + "%s/w6l -o %s/m %s/m.o %s %s/libfoo.a 2>%s/link.err", bin, dir, dir, startobj, dir, dir); if (run(cmd) != 0) { FILE *ef = fopen("/dev/null", "r"); diff --git a/test/wwc/700_e2e.c b/test/wcc/700_e2e.c similarity index 100% rename from test/wwc/700_e2e.c rename to test/wcc/700_e2e.c diff --git a/test/wwc/800_ffi.c b/test/wcc/800_ffi.c similarity index 90% rename from test/wwc/800_ffi.c rename to test/wcc/800_ffi.c index e46c1e83..f56b09d8 100644 --- a/test/wwc/800_ffi.c +++ b/test/wcc/800_ffi.c @@ -22,7 +22,7 @@ run6c(const char *bin, const char *src) fclose(f); char cmd[512]; - snprintf(cmd, sizeof cmd, "%s/6c %s 2>&1", bin, path); + snprintf(cmd, sizeof cmd, "%s/w6c %s 2>&1", bin, path); FILE *p = popen(cmd, "r"); size_t cap = 4096, n = 0; char *buf = malloc(cap); @@ -66,10 +66,10 @@ main(void) char path[256]; snprintf(path, sizeof path, "%s/../../lib/c/libc/libc.ww", bin); char cmd[512]; - snprintf(cmd, sizeof cmd, "%s/6c %s > /dev/null 2>&1", bin, path); + snprintf(cmd, sizeof cmd, "%s/w6c %s > /dev/null 2>&1", bin, path); int rc = system(cmd); if (rc != 0) { - fprintf(stderr, "ffi: 6c failed on libc.ww (rc=%d)\n", rc); + fprintf(stderr, "ffi: w6c failed on libc.ww (rc=%d)\n", rc); fail++; } } diff --git a/test/wwc/810_dyn.c b/test/wcc/810_dyn.c similarity index 98% rename from test/wwc/810_dyn.c rename to test/wcc/810_dyn.c index abfe10e9..47059ff0 100644 --- a/test/wwc/810_dyn.c +++ b/test/wcc/810_dyn.c @@ -1,5 +1,5 @@ /* - * 810_dyn — end-to-end test of 6l's dynamic linker. + * 810_dyn — end-to-end test of w6l's dynamic linker. * * Drives `ww build -L /usr/lib -l c` over a small ww program that * binds libc symbols via @symbol, then runs the produced binary and diff --git a/test/wwc/900_stdlib.c b/test/wcc/900_stdlib.c similarity index 96% rename from test/wwc/900_stdlib.c rename to test/wcc/900_stdlib.c index 2e81d8be..a62e4238 100644 --- a/test/wwc/900_stdlib.c +++ b/test/wcc/900_stdlib.c @@ -51,7 +51,7 @@ main(void) for (int i = 0; modules[i]; i++, n++) { char path[1024], cmd[2048]; snprintf(path, sizeof path, "%s/%s", cwd, modules[i]); - snprintf(cmd, sizeof cmd, "%s/6c %s > /dev/null 2>&1", + snprintf(cmd, sizeof cmd, "%s/w6c %s > /dev/null 2>&1", bin, path); int rc = system(cmd); if (rc != 0) { diff --git a/test/wwc/990_selfhost.c b/test/wcc/990_selfhost.c similarity index 89% rename from test/wwc/990_selfhost.c rename to test/wcc/990_selfhost.c index d74d5ee2..8abd34c3 100644 --- a/test/wwc/990_selfhost.c +++ b/test/wcc/990_selfhost.c @@ -2,7 +2,7 @@ * 990_selfhost — phase-10 marker test. Three probes: * * 1. The selfhost stubs (mem.ww, err.ww) must parse, typecheck and - * codegen via C-side `6c`. + * codegen via C-side `w6c`. * * 2. selfhost/test/smoke.ww — an actual ww program exercising the * patterns the real port will use (bump arena, error union, @@ -65,13 +65,13 @@ slurp(const char *path, char **outbuf, size_t *outlen) return 0; } -/* Probe 1: each file must compile through `6c`. */ +/* Probe 1: each file must compile through `w6c`. */ static int probe_codegen(const char *bin) { static const char *files[] = { - "selfhost/cmd/wwc/mem.ww", - "selfhost/cmd/wwc/err.ww", + "selfhost/cmd/wcc/mem.ww", + "selfhost/cmd/wcc/err.ww", NULL, }; char cwd[1024]; @@ -80,7 +80,7 @@ probe_codegen(const char *bin) for (int i = 0; files[i]; i++) { char cmd[2048]; snprintf(cmd, sizeof cmd, - "%s/6c %s/%s > /dev/null 2>&1", bin, cwd, files[i]); + "%s/w6c %s/%s > /dev/null 2>&1", bin, cwd, files[i]); if (runwait(cmd) != 0) { fprintf(stderr, "codegen FAIL: %s\n", files[i]); fail++; @@ -169,12 +169,12 @@ static int probe_dump_diff(const char *bin) { const char *tok_inputs[] = { - "selfhost/cmd/wwc/mem.ww", - "selfhost/cmd/wwc/err.ww", - "selfhost/cmd/wwc/lex.ww", - "selfhost/cmd/wwc/tok.ww", - "selfhost/cmd/wwc/ast.ww", - "selfhost/cmd/wwc/parse.ww", + "selfhost/cmd/wcc/mem.ww", + "selfhost/cmd/wcc/err.ww", + "selfhost/cmd/wcc/lex.ww", + "selfhost/cmd/wcc/tok.ww", + "selfhost/cmd/wcc/ast.ww", + "selfhost/cmd/wcc/parse.ww", "selfhost/cmd/wwdump/main.ww", "selfhost/test/smoke.ww", NULL, @@ -186,16 +186,16 @@ probe_dump_diff(const char *bin) * against the C parser on every selfhost file plus the stdlib. */ const char *ast_inputs[] = { "selfhost/test/uses.ww", - "selfhost/cmd/wwc/err.ww", - "selfhost/cmd/wwc/mem.ww", - "selfhost/cmd/wwc/lex.ww", - "selfhost/cmd/wwc/tok.ww", - "selfhost/cmd/wwc/ast.ww", - "selfhost/cmd/wwc/parse.ww", - "selfhost/cmd/wwc/typ.ww", - "selfhost/cmd/wwc/sym.ww", - "selfhost/cmd/wwc/check.ww", - "selfhost/cmd/wwc/cgen.ww", + "selfhost/cmd/wcc/err.ww", + "selfhost/cmd/wcc/mem.ww", + "selfhost/cmd/wcc/lex.ww", + "selfhost/cmd/wcc/tok.ww", + "selfhost/cmd/wcc/ast.ww", + "selfhost/cmd/wcc/parse.ww", + "selfhost/cmd/wcc/typ.ww", + "selfhost/cmd/wcc/sym.ww", + "selfhost/cmd/wcc/check.ww", + "selfhost/cmd/wcc/cgen.ww", "selfhost/cmd/wwdump/main.ww", "selfhost/test/smoke.ww", "lib/strconv/strconv.ww", @@ -226,7 +226,7 @@ probe_dump_diff(const char *bin) } /* Probe 5: ww-side cgen (wwdump_ww -c) emits Plan 9 amd64 asm. For - * each fixture we (a) diff the ww asm against C-side 6c and (b) + * each fixture we (a) diff the ww asm against C-side w6c and (b) * assemble + link + run the ww output and compare exit codes. Both * must succeed. The cgen port handles literals, +/-/star/slash, * compound assignment, idents, calls, if/else, for loops, and @@ -349,7 +349,7 @@ probe_ww_compile(const char *bin) /* Chained N_DOT: o.ptr_to_inner.val (read field through a * pointer-to-struct field). Pre-fix the cgen fell through * silently and returned the inner pointer instead of the - * dereferenced field. Surfaced porting 6l/pass.ww. */ + * dereferenced field. Surfaced porting w6l/pass.ww. */ { "@symbol(\"rt_alloc\") fn rt_alloc(n: u64) *void;\n" "type inner = struct { val: u64 };\n" "type outer = struct { p: *inner };\n" @@ -384,7 +384,7 @@ probe_ww_compile(const char *bin) fprintf(stderr, "ww-compile prog %d: wwdump_ww -c errored\n", i); fail++; goto cleanup; } - snprintf(cmd, sizeof cmd, "%s/6c %s > %s 2>/dev/null", bin, src, cs); + snprintf(cmd, sizeof cmd, "%s/w6c %s > %s 2>/dev/null", bin, src, cs); runwait(cmd); char *bw = NULL, *bc = NULL; @@ -398,15 +398,15 @@ probe_ww_compile(const char *bin) } free(bw); free(bc); - snprintf(cmd, sizeof cmd, "%s/6a -o %s %s 2>/dev/null", bin, obj, wws); + snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s 2>/dev/null", bin, obj, wws); if (runwait(cmd) != 0) { - fprintf(stderr, "ww-compile prog %d: 6a failed\n", i); + fprintf(stderr, "ww-compile prog %d: w6a failed\n", i); fail++; goto cleanup; } - snprintf(cmd, sizeof cmd, "%s/6l -o %s %s %s 2>/dev/null", + snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s %s 2>/dev/null", bin, exe, obj, rt); if (runwait(cmd) != 0) { - fprintf(stderr, "ww-compile prog %d: 6l failed\n", i); + fprintf(stderr, "ww-compile prog %d: w6l failed\n", i); fail++; goto cleanup; } int rc = runwait(exe); @@ -433,9 +433,9 @@ probe_resolve(const char *bin) char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return -1; const char *complete[] = { - "selfhost/cmd/wwc/err.ww", - "selfhost/cmd/wwc/mem.ww", - "selfhost/cmd/wwc/tok.ww", + "selfhost/cmd/wcc/err.ww", + "selfhost/cmd/wcc/mem.ww", + "selfhost/cmd/wcc/tok.ww", "selfhost/test/smoke.ww", NULL, }; @@ -463,8 +463,8 @@ probe_dump_stable(const char *bin) char cwd[1024]; if (getcwd(cwd, sizeof cwd) == NULL) return -1; const char *inputs[] = { - "selfhost/cmd/wwc/mem.ww", - "selfhost/cmd/wwc/err.ww", + "selfhost/cmd/wcc/mem.ww", + "selfhost/cmd/wcc/err.ww", "selfhost/test/smoke.ww", NULL, }; @@ -540,19 +540,19 @@ probe_bootstrap_fixed_point(const char *bin) "%s/wwdump_ww -c %s/selfhost/cmd/wwdump/main.combined.ww > %s 2>/dev/null", bin, cwd, ww2s); if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: ww1 self-compile\n"); fail++; goto cleanup; } - snprintf(cmd, sizeof cmd, "%s/6a -o %s %s 2>/dev/null", bin, ww2o, ww2s); - if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: 6a ww2.s\n"); fail++; goto cleanup; } - snprintf(cmd, sizeof cmd, "%s/6l -o %s %s %s 2>/dev/null", bin, ww2e, ww2o, rt); - if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: 6l ww2\n"); fail++; goto cleanup; } + snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s 2>/dev/null", bin, ww2o, ww2s); + if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: w6a ww2.s\n"); fail++; goto cleanup; } + snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s %s 2>/dev/null", bin, ww2e, ww2o, rt); + if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: w6l ww2\n"); fail++; goto cleanup; } /* ww2 -> ww3 */ snprintf(cmd, sizeof cmd, "%s -c %s/selfhost/cmd/wwdump/main.combined.ww > %s 2>/dev/null", ww2e, cwd, ww3s); if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: ww2 self-compile\n"); fail++; goto cleanup; } - snprintf(cmd, sizeof cmd, "%s/6a -o %s %s 2>/dev/null", bin, ww3o, ww3s); - if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: 6a ww3.s\n"); fail++; goto cleanup; } - snprintf(cmd, sizeof cmd, "%s/6l -o %s %s %s 2>/dev/null", bin, ww3e, ww3o, rt); - if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: 6l ww3\n"); fail++; goto cleanup; } + snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s 2>/dev/null", bin, ww3o, ww3s); + if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: w6a ww3.s\n"); fail++; goto cleanup; } + snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s %s 2>/dev/null", bin, ww3e, ww3o, rt); + if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: w6l ww3\n"); fail++; goto cleanup; } /* The fixed-point check: ww2.s == ww3.s, ww2 == ww3. */ snprintf(cmd, sizeof cmd, "cmp -s %s %s", ww2s, ww3s); if (runwait(cmd) != 0) { fprintf(stderr, "fp FAIL: ww2.s != ww3.s (no fixed point)\n"); fail++; } @@ -566,7 +566,7 @@ cleanup: /* Probe 8: ww1 → ww2 ladder. wwdump_ww (= ww1, built by C tools) * compiles its own concatenated source. The output assembles + links - * via 6a/6l into ww2 (a fresh ww-built compiler binary). Confirms + * via w6a/w6l into ww2 (a fresh ww-built compiler binary). Confirms * the full ww-cgen pipeline is functionally correct end-to-end on * the entire frontend (lex+parse+check+cgen+ast+typ+sym+mem+err+ * driver). Pre-fix-set this segfaulted at 5919 lines of asm; post- @@ -592,18 +592,18 @@ probe_ww1_to_ww2(const char *bin) fail++; goto cleanup; } - snprintf(cmd, sizeof cmd, "%s/6a -o %s %s 2>/dev/null", bin, obj_p, asm_p); + snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s 2>/dev/null", bin, obj_p, asm_p); if (runwait(cmd) != 0) { - fprintf(stderr, "ww1->ww2 FAIL: 6a errored on ww1 output\n"); + fprintf(stderr, "ww1->ww2 FAIL: w6a errored on ww1 output\n"); fail++; goto cleanup; } char rt[1024]; snprintf(rt, sizeof rt, "%s/../lib/libwwrt.a", bin); - snprintf(cmd, sizeof cmd, "%s/6l -o %s %s %s 2>/dev/null", + snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s %s 2>/dev/null", bin, exe_p, obj_p, rt); if (runwait(cmd) != 0) { - fprintf(stderr, "ww1->ww2 FAIL: 6l errored\n"); + fprintf(stderr, "ww1->ww2 FAIL: w6l errored\n"); fail++; goto cleanup; } @@ -619,8 +619,8 @@ cleanup: } /* Probe 7: end-to-end via wwdump_ww. Use `ww build` to produce the - * concatenated source as a side effect, then drive ww-cgen → 6a → - * 6l → run, comparing the binary's exit code to the fixture's + * concatenated source as a side effect, then drive ww-cgen → w6a → + * w6l → run, comparing the binary's exit code to the fixture's * declared expectation. This is the bootstrap-grade signal: it * doesn't require ww-cgen output to be byte-identical to C-cgen, * only to be functionally correct. The byte-match probe above is @@ -656,7 +656,7 @@ probe_ww_links(const char *bin) runwait(cmd); /* ww build to get the .combined.ww as a side effect. */ snprintf(cmd, sizeof cmd, - "cd %s && %s/ww build -I %s/selfhost/cmd/wwc %s >/dev/null 2>&1", + "cd %s && %s/ww build -I %s/selfhost/cmd/wcc %s >/dev/null 2>&1", tmpdir, bin, cwd, tmpsrc); if (runwait(cmd) != 0) { fprintf(stderr, "ww-links FAIL: ww build %s\n", fix); @@ -676,19 +676,19 @@ probe_ww_links(const char *bin) fail++; goto cleanup; } - snprintf(cmd, sizeof cmd, "%s/6a -o %s %s 2>/dev/null", + snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s 2>/dev/null", bin, wop, wsp); if (runwait(cmd) != 0) { - fprintf(stderr, "ww-links FAIL: 6a %s\n", fix); + fprintf(stderr, "ww-links FAIL: w6a %s\n", fix); fail++; goto cleanup; } char rt[1024]; snprintf(rt, sizeof rt, "%s/../lib/libwwrt.a", bin); - snprintf(cmd, sizeof cmd, "%s/6l -o %s %s %s 2>/dev/null", + snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s %s 2>/dev/null", bin, wexep, wop, rt); if (runwait(cmd) != 0) { - fprintf(stderr, "ww-links FAIL: 6l %s\n", fix); + fprintf(stderr, "ww-links FAIL: w6l %s\n", fix); fail++; goto cleanup; } @@ -708,16 +708,16 @@ cleanup: } /* Probe 6: ww-cgen byte-matches C-cgen on every selfhost file that - * raw 6c can compile in isolation (no driver concatenation). The list + * raw w6c can compile in isolation (no driver concatenation). The list * is the convergence set: when this stays empty, the cmp ww2 ww3 * ceiling becomes feasible. */ static int probe_cgen_match(const char *bin) { const char *files[] = { - "selfhost/cmd/wwc/mem.ww", - "selfhost/cmd/wwc/err.ww", - "selfhost/cmd/wwc/tok.ww", + "selfhost/cmd/wcc/mem.ww", + "selfhost/cmd/wcc/err.ww", + "selfhost/cmd/wcc/tok.ww", "selfhost/test/smoke.ww", NULL, }; @@ -728,7 +728,7 @@ probe_cgen_match(const char *bin) char a[256], b[256], cmd[2048]; snprintf(a, sizeof a, "/tmp/wwd_cm_%d_c", getpid()); snprintf(b, sizeof b, "/tmp/wwd_cm_%d_w", getpid()); - snprintf(cmd, sizeof cmd, "%s/6c %s/%s > %s 2>/dev/null", + snprintf(cmd, sizeof cmd, "%s/w6c %s/%s > %s 2>/dev/null", bin, cwd, files[i], a); runwait(cmd); snprintf(cmd, sizeof cmd, "%s/wwdump_ww -c %s/%s > %s 2>/dev/null", diff --git a/test/wwc/991_6a_ww.c b/test/wcc/991_w6a_ww.c similarity index 72% rename from test/wwc/991_6a_ww.c rename to test/wcc/991_w6a_ww.c index aeb1c308..e3200113 100644 --- a/test/wwc/991_6a_ww.c +++ b/test/wcc/991_w6a_ww.c @@ -1,15 +1,15 @@ /* - * 991_6a_ww — phase-10 marker for the ww-side 6a port. + * 991_w6a_ww — phase-10 marker for the ww-side w6a port. * - * Diffs the C-built `6a` against the ww-built `6a_ww` on a corpus of + * Diffs the C-built `w6a` against the ww-built `w6a_ww` on a corpus of * .s files. Both must produce byte-identical ELF .o output. The corpus * spans: * - rt/X.s hand-written runtime; small, varied operands * - generated .s from the existing selfhost cgen pipeline - * (wwdump/6a/6l main.s) - large, exercises - * every encoding path 6c emits + * (wwdump/w6a/w6l main.s) - large, exercises + * every encoding path w6c emits * - * Any divergence is a port bug in selfhost/cmd/6a/. + * Any divergence is a port bug in selfhost/cmd/w6a/. */ #include #include @@ -65,15 +65,15 @@ diff_one(const char *bin, const char *src) snprintf(co, sizeof co, "/tmp/wwa_%d_c.o", getpid()); snprintf(wo, sizeof wo, "/tmp/wwa_%d_w.o", getpid()); - snprintf(cmd, sizeof cmd, "%s/6a -o %s %s 2>/dev/null", bin, co, src); + snprintf(cmd, sizeof cmd, "%s/w6a -o %s %s 2>/dev/null", bin, co, src); if (runwait(cmd) != 0) { - fprintf(stderr, "6a_ww FAIL: C 6a errored on %s\n", src); + fprintf(stderr, "w6a_ww FAIL: C w6a errored on %s\n", src); unlink(co); return -1; } - snprintf(cmd, sizeof cmd, "%s/6a_ww -o %s %s 2>/dev/null", bin, wo, src); + snprintf(cmd, sizeof cmd, "%s/w6a_ww -o %s %s 2>/dev/null", bin, wo, src); if (runwait(cmd) != 0) { - fprintf(stderr, "6a_ww FAIL: ww 6a errored on %s\n", src); + fprintf(stderr, "w6a_ww FAIL: ww w6a errored on %s\n", src); unlink(co); unlink(wo); return -1; } @@ -81,10 +81,10 @@ diff_one(const char *bin, const char *src) size_t nc = 0, nw = 0; int rc = 0; if (slurp(co, &bc, &nc) < 0 || slurp(wo, &bw, &nw) < 0) { - fprintf(stderr, "6a_ww FAIL: cannot read .o for %s\n", src); + fprintf(stderr, "w6a_ww FAIL: cannot read .o for %s\n", src); rc = -1; } else if (nc != nw || memcmp(bc, bw, nc) != 0) { - fprintf(stderr, "6a_ww FAIL: %s — C %zu vs ww %zu bytes\n", + fprintf(stderr, "w6a_ww FAIL: %s — C %zu vs ww %zu bytes\n", src, nc, nw); rc = -1; } @@ -110,11 +110,11 @@ main(void) "rt/alloc.s", "rt/streq.s", /* generated .s from the existing selfhost stack — large, - * exercises every encoding 6c emits (the only way to hit + * exercises every encoding w6c emits (the only way to hit * the long tail of operand shapes). */ "selfhost/cmd/wwdump/main.s", - "selfhost/cmd/6l/main.s", - "selfhost/cmd/6a/main.s", + "selfhost/cmd/w6l/main.s", + "selfhost/cmd/w6a/main.s", NULL, }; int fail = 0; @@ -126,10 +126,10 @@ main(void) n++; } if (fail) { - fprintf(stderr, "6a_ww: %d/%d diff(s) failed\n", fail, n); + fprintf(stderr, "w6a_ww: %d/%d diff(s) failed\n", fail, n); return 1; } - printf("6a_ww: byte-identical to C 6a on %d corpus files " + printf("w6a_ww: byte-identical to C w6a on %d corpus files " "(rt/*.s + selfhost generated main.s)\n", n); return 0; } diff --git a/test/wwc/992_6l_ww.c b/test/wcc/992_w6l_ww.c similarity index 76% rename from test/wwc/992_6l_ww.c rename to test/wcc/992_w6l_ww.c index 1b0389b1..8d5b0d3f 100644 --- a/test/wwc/992_6l_ww.c +++ b/test/wcc/992_w6l_ww.c @@ -1,13 +1,13 @@ /* - * 992_6l_ww — phase-10 marker for the ww-side 6l port. + * 992_w6l_ww — phase-10 marker for the ww-side w6l port. * - * Diffs the C-built `6l` against the ww-built `6l_ww` on a corpus of + * Diffs the C-built `w6l` against the ww-built `w6l_ww` on a corpus of * link inputs. Both must produce byte-identical static ELF binaries. * The corpus mixes: * - a single .o input (no archive resolution) * - a .o + libwwrt.a link, exercising the SysV `ar` two-pass loader * - * Any divergence is a port bug in selfhost/cmd/6l/. + * Any divergence is a port bug in selfhost/cmd/w6l/. */ #include #include @@ -65,15 +65,15 @@ diff_one(const char *bin, const char *label, const char *inputs) snprintf(co, sizeof co, "/tmp/wwl_%d_c", getpid()); snprintf(wo, sizeof wo, "/tmp/wwl_%d_w", getpid()); - snprintf(cmd, sizeof cmd, "%s/6l -o %s %s 2>/dev/null", bin, co, inputs); + snprintf(cmd, sizeof cmd, "%s/w6l -o %s %s 2>/dev/null", bin, co, inputs); if (runwait(cmd) != 0) { - fprintf(stderr, "6l_ww FAIL: C 6l errored on %s\n", label); + fprintf(stderr, "w6l_ww FAIL: C w6l errored on %s\n", label); unlink(co); return -1; } - snprintf(cmd, sizeof cmd, "%s/6l_ww -o %s %s 2>/dev/null", bin, wo, inputs); + snprintf(cmd, sizeof cmd, "%s/w6l_ww -o %s %s 2>/dev/null", bin, wo, inputs); if (runwait(cmd) != 0) { - fprintf(stderr, "6l_ww FAIL: ww 6l errored on %s\n", label); + fprintf(stderr, "w6l_ww FAIL: ww w6l errored on %s\n", label); unlink(co); unlink(wo); return -1; } @@ -81,10 +81,10 @@ diff_one(const char *bin, const char *label, const char *inputs) size_t nc = 0, nw = 0; int rc = 0; if (slurp(co, &bc, &nc) < 0 || slurp(wo, &bw, &nw) < 0) { - fprintf(stderr, "6l_ww FAIL: cannot read output for %s\n", label); + fprintf(stderr, "w6l_ww FAIL: cannot read output for %s\n", label); rc = -1; } else if (nc != nw || memcmp(bc, bw, nc) != 0) { - fprintf(stderr, "6l_ww FAIL: %s — C %zu vs ww %zu bytes\n", + fprintf(stderr, "w6l_ww FAIL: %s — C %zu vs ww %zu bytes\n", label, nc, nw); rc = -1; } @@ -112,10 +112,10 @@ main(void) * are pulled from libwwrt.a as undefs are encountered). */ { "wwdump+libwwrt", "%s/selfhost/cmd/wwdump/main.o %s/out/lib/libwwrt.a" }, - { "6a+libwwrt", - "%s/selfhost/cmd/6a/main.o %s/out/lib/libwwrt.a" }, - { "6l+libwwrt", - "%s/selfhost/cmd/6l/main.o %s/out/lib/libwwrt.a" }, + { "w6a+libwwrt", + "%s/selfhost/cmd/w6a/main.o %s/out/lib/libwwrt.a" }, + { "w6l+libwwrt", + "%s/selfhost/cmd/w6l/main.o %s/out/lib/libwwrt.a" }, { NULL, NULL }, }; @@ -128,10 +128,10 @@ main(void) n++; } if (fail) { - fprintf(stderr, "6l_ww: %d/%d diff(s) failed\n", fail, n); + fprintf(stderr, "w6l_ww: %d/%d diff(s) failed\n", fail, n); return 1; } - printf("6l_ww: byte-identical to C 6l on %d corpus links " + printf("w6l_ww: byte-identical to C w6l on %d corpus links " "(.o + libwwrt.a archive resolution)\n", n); return 0; } diff --git a/test/wwc/993_ww_ww.c b/test/wcc/993_ww_ww.c similarity index 97% rename from test/wwc/993_ww_ww.c rename to test/wcc/993_ww_ww.c index a7406032..2069067e 100644 --- a/test/wwc/993_ww_ww.c +++ b/test/wcc/993_ww_ww.c @@ -2,7 +2,7 @@ * 993_ww_ww — phase-10 marker for the ww-side driver port. * * Diffs the C-built `ww` against the ww-built `ww_ww`. Both drivers - * orchestrate the same 6c/6a/6l, so the resulting binaries must be + * orchestrate the same w6c/w6a/w6l, so the resulting binaries must be * byte-identical for every program in the corpus. The corpus mixes: * - a tiny single-file program (no -I flags) * - a multi-import build (selfhost/cmd/wwdump/main.ww), exercising @@ -152,7 +152,7 @@ main(void) * regardless of the per-driver workdir. */ static char wwdump_src[2048], wwdump_inc_a[2048], wwdump_inc_b[2048]; snprintf(wwdump_src, sizeof wwdump_src, "%s/selfhost/cmd/wwdump/main.ww", cwd); - snprintf(wwdump_inc_a, sizeof wwdump_inc_a, "%s/selfhost/cmd/wwc", cwd); + snprintf(wwdump_inc_a, sizeof wwdump_inc_a, "%s/selfhost/cmd/wcc", cwd); wwdump_inc_b[0] = '\0'; cases[1].src = wwdump_src; cases[1].inc_a = wwdump_inc_a; diff --git a/test/wwc/994_6c_ww.c b/test/wcc/994_w6c_ww.c similarity index 81% rename from test/wwc/994_6c_ww.c rename to test/wcc/994_w6c_ww.c index 14af2138..43a12562 100644 --- a/test/wwc/994_6c_ww.c +++ b/test/wcc/994_w6c_ww.c @@ -1,13 +1,13 @@ /* - * 994_6c_ww — phase-10 marker for the ww-side 6c port. + * 994_w6c_ww — phase-10 marker for the ww-side w6c port. * - * 6c_ww is a thin packaging of selfhost/cmd/wwc/cgen.ww: it slurps a + * w6c_ww is a thin packaging of selfhost/cmd/wcc/cgen.ww: it slurps a * .ww file, runs lex+parse+cgen, and writes Plan 9 amd64 asm to the * file given by -o. The same cgen is reached through `wwdump_ww -c`, - * so 6c_ww must produce byte-identical output to wwdump_ww -c on + * so w6c_ww must produce byte-identical output to wwdump_ww -c on * every program — this test pins that. * - * (We do not diff against C-side `6c` here because the C compiler has + * (We do not diff against C-side `w6c` here because the C compiler has * features the ww cgen has not yet ported — float compare, fn-address * @symbol, indirect call. Test 990 probe 5 covers the C-vs-ww diff on * the subset that the ww cgen handles today.) @@ -69,13 +69,13 @@ diff_one(const char *bin, const char *label, const char *src) snprintf(cmd, sizeof cmd, "%s/wwdump_ww -c %s > %s 2>/dev/null", bin, src, ws); if (runwait(cmd) != 0) { - fprintf(stderr, "6c_ww FAIL: wwdump_ww -c errored on %s\n", label); + fprintf(stderr, "w6c_ww FAIL: wwdump_ww -c errored on %s\n", label); unlink(ws); return -1; } - snprintf(cmd, sizeof cmd, "%s/6c_ww -o %s %s 2>/dev/null", bin, cs, src); + snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null", bin, cs, src); if (runwait(cmd) != 0) { - fprintf(stderr, "6c_ww FAIL: 6c_ww errored on %s\n", label); + fprintf(stderr, "w6c_ww FAIL: w6c_ww errored on %s\n", label); unlink(ws); unlink(cs); return -1; } @@ -83,10 +83,10 @@ diff_one(const char *bin, const char *label, const char *src) size_t nw = 0, nc = 0; int rc = 0; if (slurp(ws, &bw, &nw) < 0 || slurp(cs, &bc, &nc) < 0) { - fprintf(stderr, "6c_ww FAIL: cannot read .s for %s\n", label); + fprintf(stderr, "w6c_ww FAIL: cannot read .s for %s\n", label); rc = -1; } else if (nw != nc || memcmp(bw, bc, nw) != 0) { - fprintf(stderr, "6c_ww FAIL: %s — wwdump_ww %zu vs 6c_ww %zu bytes\n", + fprintf(stderr, "w6c_ww FAIL: %s — wwdump_ww %zu vs w6c_ww %zu bytes\n", label, nw, nc); rc = -1; } @@ -115,7 +115,7 @@ main(void) /* In-source corpus: programs the ww-side cgen handles today. * Mirrors probe 5 in 990_selfhost without the parts that drift - * against C 6c. */ + * against C w6c. */ struct { const char *label; const char *src; } progs[] = { { "ret42", "fn main() i32 = { return 42; };" }, @@ -147,12 +147,12 @@ main(void) }; /* Source-tree corpus: the same .combined.ww files the bootstrap - * fixed point chews on. Confirms 6c_ww handles realistic inputs, + * fixed point chews on. Confirms w6c_ww handles realistic inputs, * not just hand-tailored ones. */ const char *combined_rel[] = { "selfhost/cmd/wwdump/main.combined.ww", - "selfhost/cmd/6a/main.combined.ww", - "selfhost/cmd/6l/main.combined.ww", + "selfhost/cmd/w6a/main.combined.ww", + "selfhost/cmd/w6l/main.combined.ww", "selfhost/cmd/ww/main.combined.ww", NULL, }; @@ -175,10 +175,10 @@ main(void) } if (fail) { - fprintf(stderr, "6c_ww: %d/%d diff(s) failed\n", fail, n); + fprintf(stderr, "w6c_ww: %d/%d diff(s) failed\n", fail, n); return 1; } - printf("6c_ww: byte-identical to wwdump_ww -c on %d corpus inputs " + printf("w6c_ww: byte-identical to wwdump_ww -c on %d corpus inputs " "(in-source + selfhost combined.ww)\n", n); return 0; } diff --git a/test/wwc/995_self_rebuild.c b/test/wcc/995_self_rebuild.c similarity index 85% rename from test/wwc/995_self_rebuild.c rename to test/wcc/995_self_rebuild.c index d3e6983b..7f37bd6e 100644 --- a/test/wwc/995_self_rebuild.c +++ b/test/wcc/995_self_rebuild.c @@ -1,7 +1,7 @@ /* * 995_self_rebuild — the wwstage rebuilds itself. * - * Drives ww_ww (the ww-side driver, which shells to 6c_ww/6a_ww/6l_ww) + * Drives ww_ww (the ww-side driver, which shells to w6c_ww/w6a_ww/w6l_ww) * over each wwstage tool's source and diffs the result byte-for-byte * against the cstage-built binary in $BIN. A green run means the * toolchain can recompile itself without touching cc, modulo the @@ -59,9 +59,9 @@ slurp_eq(const char *a, const char *b) return rc; } -/* Each tool builds via `ww_ww build -I -I selfhost/cmd/wwc src`. - * Some tools have a local module dir (6a, 6l with sibling .ww files); - * wwc-only tools (6c, ww, wwdump) just need the wwc -I. inc_local is +/* Each tool builds via `ww_ww build -I -I selfhost/cmd/wcc src`. + * Some tools have a local module dir (w6a, w6l with sibling .ww files); + * wwc-only tools (w6c, ww, wwdump) just need the wwc -I. inc_local is * "" for those. */ static int @@ -76,12 +76,12 @@ rebuild_one(const char *bin, const char *cwd, const char *tool, if (inc_local && inc_local[0]) { snprintf(cmd, sizeof cmd, - "cd %s && %s/ww_ww build -I %s/%s -I %s/selfhost/cmd/wwc " + "cd %s && %s/ww_ww build -I %s/%s -I %s/selfhost/cmd/wcc " "%s/%s >/dev/null 2>&1", workdir, bin, cwd, inc_local, cwd, cwd, src_rel); } else { snprintf(cmd, sizeof cmd, - "cd %s && %s/ww_ww build -I %s/selfhost/cmd/wwc " + "cd %s && %s/ww_ww build -I %s/selfhost/cmd/wcc " "%s/%s >/dev/null 2>&1", workdir, bin, cwd, cwd, src_rel); } @@ -121,9 +121,9 @@ main(void) const char *src; const char *inc_local; } tools[] = { - { "6c", "selfhost/cmd/6c/main.ww", "" }, - { "6a", "selfhost/cmd/6a/main.ww", "selfhost/cmd/6a" }, - { "6l", "selfhost/cmd/6l/main.ww", "selfhost/cmd/6l" }, + { "w6c", "selfhost/cmd/w6c/main.ww", "" }, + { "w6a", "selfhost/cmd/w6a/main.ww", "selfhost/cmd/w6a" }, + { "w6l", "selfhost/cmd/w6l/main.ww", "selfhost/cmd/w6l" }, { "ww", "selfhost/cmd/ww/main.ww", "" }, { "wwdump", "selfhost/cmd/wwdump/main.ww", "" }, { NULL, NULL, NULL }, @@ -141,6 +141,6 @@ main(void) return 1; } printf("self-rebuild: %d wwstage tool(s) round-trip byte-identical " - "through ww_ww + 6c_ww + 6a_ww + 6l_ww\n", n); + "through ww_ww + w6c_ww + w6a_ww + w6l_ww\n", n); return 0; } diff --git a/test/wwc/996_dyn_ww.c b/test/wcc/996_dyn_ww.c similarity index 78% rename from test/wwc/996_dyn_ww.c rename to test/wcc/996_dyn_ww.c index 33bcc7f3..07bd45a2 100644 --- a/test/wwc/996_dyn_ww.c +++ b/test/wcc/996_dyn_ww.c @@ -1,8 +1,8 @@ /* * 996_dyn_ww — phase-8 marker for ET_DYN linking on the ww side. * - * Drives 6l_ww with -L/-l flags over examples/snake/snake.o and diffs - * the result against the C-built 6l on the same inputs. A green run + * Drives w6l_ww with -L/-l flags over examples/snake/snake.o and diffs + * the result against the C-built w6l on the same inputs. A green run * means the ww-side linker emits PT_INTERP/PT_DYNAMIC binaries byte- * for-byte identical to the C linker — i.e. the dynamic linking story * is fully ported and snake no longer depends on Cstage. @@ -71,7 +71,7 @@ main(void) "-l ncurses -l c >/dev/null 2>&1", cwd, bin); if (runwait(cmd) != 0) { - fprintf(stderr, "6l_ww-dyn FAIL: cannot build snake.o via C driver\n"); + fprintf(stderr, "w6l_ww-dyn FAIL: cannot build snake.o via C driver\n"); return 1; } @@ -79,24 +79,24 @@ main(void) snprintf(co, sizeof co, "/tmp/wwld_%d_c", getpid()); snprintf(wo, sizeof wo, "/tmp/wwld_%d_w", getpid()); - /* C-side 6l with the dynamic flags. */ + /* C-side w6l with the dynamic flags. */ snprintf(cmd, sizeof cmd, - "%s/6l -o %s %s/examples/snake/snake.o -L /usr/lib " + "%s/w6l -o %s %s/examples/snake/snake.o -L /usr/lib " "-l ncurses -l c %s/out/lib/libwwrt.a 2>/dev/null", bin, co, cwd, cwd); if (runwait(cmd) != 0) { - fprintf(stderr, "6l_ww-dyn FAIL: C 6l errored\n"); + fprintf(stderr, "w6l_ww-dyn FAIL: C w6l errored\n"); unlink(co); return 1; } - /* ww-side 6l with the same flags. */ + /* ww-side w6l with the same flags. */ snprintf(cmd, sizeof cmd, - "%s/6l_ww -o %s %s/examples/snake/snake.o -L /usr/lib " + "%s/w6l_ww -o %s %s/examples/snake/snake.o -L /usr/lib " "-l ncurses -l c %s/out/lib/libwwrt.a 2>/dev/null", bin, wo, cwd, cwd); if (runwait(cmd) != 0) { - fprintf(stderr, "6l_ww-dyn FAIL: ww 6l errored\n"); + fprintf(stderr, "w6l_ww-dyn FAIL: ww w6l errored\n"); unlink(co); unlink(wo); return 1; } @@ -105,14 +105,14 @@ main(void) size_t nc = 0, nw = 0; int rc = 0; if (slurp(co, &bc, &nc) < 0 || slurp(wo, &bw, &nw) < 0) { - fprintf(stderr, "6l_ww-dyn FAIL: cannot read outputs\n"); + fprintf(stderr, "w6l_ww-dyn FAIL: cannot read outputs\n"); rc = 1; } else if (nc != nw || memcmp(bc, bw, nc) != 0) { - fprintf(stderr, "6l_ww-dyn FAIL: C %zu vs ww %zu bytes\n", + fprintf(stderr, "w6l_ww-dyn FAIL: C %zu vs ww %zu bytes\n", nc, nw); rc = 1; } else { - printf("6l_ww-dyn: byte-identical to C 6l on snake " + printf("w6l_ww-dyn: byte-identical to C w6l on snake " "(PT_INTERP + PT_DYNAMIC + .rela.plt + .gnu.version_r, " "%zu bytes)\n", nc); }