`ww test <file> <pattern>` runs only the @test fns whose names match the fnmatch glob; no pattern runs all (byte-for-byte the pre-filter path); zero matches prints "No tests run" and exits 0 (Hare ground truth ref/hare/test/+test.ha:114-117). A pattern in directory mode is rejected "ww test: pattern needs a single test file" (rc 2), identical wording in both twins (cmd/ww/main.c do_test + selfhost/cmd/ww/main.ww dotest). Mechanism (a): rt/start.s stashes argc/argv into rt_argc/rt_argv getters (rt_envp twin shape, -T synth untouched so 990-997 byte-id holds); lib/os.args() rebuilds the []str view, build-once-cached; lib/test/run.ww imports fnmatch and filters av[1..] (argv[0] is the binary path). The driver forwards the 2nd positional as argv[1] via fork/execv (cstage) / procrun (wwstage) so glob metachars aren't shell-expanded. os.args() is the first `alloc`-caller in the base os module, so os.ww now imports rt — the `alloc` builtin's malloc lowers to rt_malloc only when the rt binding is bundled (mirror lib/strings/strings.ww:30); without it a plain `ww build` of any os-importing program links bare libc `malloc` (undefined). os is bundled by ~every program, so this is load-bearing. The lib/test floor rises os-only -> os+fnmatch+ascii+strings in every -T build; the bundled `ascii` module vs a `@test fn ascii` collision that exposed is closed by the preceding #30 promote commit. 989_test_filter pins the full matrix on both twins byte-identically; 949 gains the dir-mode reject row. (#17)
72 lines
3.0 KiB
ArmAsm
72 lines
3.0 KiB
ArmAsm
// rt/start.s — process entry. Linux sets up the stack so that on
|
|
// entry [SP] holds argc, [SP+8] starts argv (NULL-terminated array
|
|
// of *u8), [SP+8+(argc+1)*8] starts envp (also NULL-terminated).
|
|
//
|
|
// We pull argc into DI and the argv pointer into SI, then jump to
|
|
// `main`. ww programs that declare `fn main(argc: i32, argv: **u8)
|
|
// i32` see them; programs declaring `fn main() i32` simply ignore
|
|
// the regs. Either way, main's return value is fed to the exit
|
|
// syscall.
|
|
//
|
|
// envp is captured into the rt_envp_slot DATAW cell before CALL main
|
|
// so lib/os.getenv (via the rt_envp getter below) can walk it. We
|
|
// compute envp = SP + 16 + argc*8 in AX using three doublings and
|
|
// adds — w6a's syntax doesn't ship SIB-style indexed addressing
|
|
// (no `LEAQ 16(SP)(DI*8), AX`), so the explicit shift-by-three is
|
|
// the portable form. AX/DI/SI are scratch on entry; no callee-
|
|
// saved discipline applies until we reach main.
|
|
TEXT _start,$0
|
|
MOVQ (SP), DI // argc → DI (1st arg to main)
|
|
LEAQ 8(SP), SI // &argv[0] → SI (2nd arg to main)
|
|
MOVQ DI, AX // AX = argc
|
|
ADDQ AX, AX // AX = argc * 2
|
|
ADDQ AX, AX // AX = argc * 4
|
|
ADDQ AX, AX // AX = argc * 8
|
|
ADDQ SP, AX // AX = SP + argc*8
|
|
ADDQ $16, AX // AX = SP + argc*8 + 16 = &argv[argc+1] = envp
|
|
MOVQ AX, rt_envp_slot(SB)
|
|
// argc/argv survive the envp calc (only AX was clobbered): stash both
|
|
// so lib/os.args can rebuild the []str view after entry. Same DATAW-
|
|
// slot + TEXT-getter shape as rt_envp below (task #17 fnmatch filter).
|
|
MOVQ DI, rt_argc_slot(SB)
|
|
MOVQ SI, rt_argv_slot(SB)
|
|
CALL main(SB)
|
|
MOVQ AX, DI
|
|
MOVQ $60, AX
|
|
SYSCALL
|
|
|
|
// rt_envp — getter that returns the envp pointer captured at process
|
|
// entry. Bound from lib/os via `@symbol("rt_envp") fn rtenvp() **u8;`,
|
|
// matching the rt_syscall / rt_malloc / rt_abort pattern. Read-only
|
|
// view of the kernel-supplied table; the bytes live for the process
|
|
// lifetime. A future setenv that grows the table re-points the slot
|
|
// (separate task).
|
|
TEXT rt_envp,$0
|
|
MOVQ rt_envp_slot(SB), AX
|
|
RET
|
|
|
|
// rt_envp_slot — 8-byte writable cell holding the envp pointer.
|
|
// Initialised to zero in .data; _start overwrites it before
|
|
// transferring control to main. DATAW gives us the writable .data
|
|
// slot (w6a has no GLOBL; same shape as lib/log's silent/default/
|
|
// global cells in lib/log/log.s).
|
|
DATAW rt_envp_slot(SB),"\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
|
|
// rt_argc — getter returning the process argc captured at entry. Bound
|
|
// from lib/os as `@symbol("rt_argc") fn rtargc() i64;` (rt_envp twin).
|
|
TEXT rt_argc,$0
|
|
MOVQ rt_argc_slot(SB), AX
|
|
RET
|
|
|
|
// rt_argv — getter returning &argv[0], a NUL-terminated table of *u8
|
|
// argument strings. Bound from lib/os as
|
|
// `@symbol("rt_argv") fn rtargv() **u8;`.
|
|
TEXT rt_argv,$0
|
|
MOVQ rt_argv_slot(SB), AX
|
|
RET
|
|
|
|
// rt_argc_slot / rt_argv_slot — 8-byte writable cells, zero in .data,
|
|
// overwritten by _start before CALL main. Same shape as rt_envp_slot.
|
|
DATAW rt_argc_slot(SB),"\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
DATAW rt_argv_slot(SB),"\x00\x00\x00\x00\x00\x00\x00\x00"
|