diff --git a/HANDOFF-x86-cost.md b/HANDOFF-x86-cost.md index 2c029fd..b1cdcbd 100644 --- a/HANDOFF-x86-cost.md +++ b/HANDOFF-x86-cost.md @@ -8,14 +8,66 @@ Nothing in `lib/` is touched. Another lane is rewriting `lib/x86.ml` at the same `spike/x86` and in `test/dune`, and anything this lane finds that would want a compiler change is written down at the bottom rather than made. -## The plan, in the order it is being done +## 1. The `@x86` alias — done, and it fails when it should -1. `test/dune` grows an `@x86` alias beside `@sanitize` and `@valgrind`, running `spike/x86/survey.sh` in a strict - mode that exits nonzero when anything DIFFERs or is refused by name. Done first because it is the half that - stops the rot and it is bounded. -2. `spike/x86/cost.sh` measures binary size, the program's own text, and best-of-N wall time for each corpus - program both ways, and `spike/x86/COST.md` reads the numbers out. +`test/dune` has a fourth alias beside `@sanitize` and `@valgrind`, shaped the same way and opt-in for the same +reason: it builds every corpus program twice and runs both, which is minutes where `dune test` is forty seconds. -## Status + dune build --root . @x86 -Stub. Nothing measured yet. +It is a `(rule ...)` with no `(executable ...)` beside it, which is where it differs from its two neighbours. The +check already exists — `spike/x86/survey.sh` is what every handoff quotes its counts from — and a second +implementation of it in OCaml would be a second thing to drift, which is exactly the failure this alias is meant +to prevent. So the rule runs the script, and the script grew three environment variables to make that possible: + +- `SURVEY_STRICT=1` turns the report into an exit status. A DIFFER is a wrong answer and a refusal by name is a + node the backend has stopped lowering; either fails the build. NOX86 and SKIP do not — the first is usually a + toolchain that is not installed on the machine, the second is the frontend refusing the program on both sides. +- `FLAN=` hands it a compiler somebody else built. Without it the script runs `dune build` on `bin/main.exe`, + and a dune inside a dune action waits on a lock it will never get. The rule has `bin/main.exe` in its deps + instead, exactly as the `(tests ...)` stanza already does. +- `SURVEY_CORPUS` for where the programs are read from, which under dune is the build tree. + +Standalone, with nothing set, the script is byte-for-byte the measurement it always was. + +**Verified both directions.** `dune build --root . @x86` passes on a clean tree and prints the same +**97 MATCH / 0 DIFFER / 0 REFUSED / 0 NOX86 / 36 SKIP** the script prints standalone — the build tree and the +source tree agree about the corpus, which was not obvious in advance. Broken deliberately (a fake DIFFER injected +into the script, the rule narrowed to one program so the round trip was thirty seconds) it fails the dune build +with `x86 survey FAILED: 1 differ, 0 refused` and a nonzero status. Both edits reverted. + +`dune test --root .` is still green and still about forty seconds. The new stanza is a rule on its own alias and +touches no `(modules ...)` list, so it cannot be pulled into `@runtest` by accident. The two +`clang: error: linker command failed` lines it prints are the `dev-robust` fixture doing its job and are called +out in `HANDOFF-x86-rt.md` §4 already. + +## 2. The measurement — `spike/x86/cost.sh`, `spike/x86/bench.sh`, `spike/x86/COST.md` + +`COST.md` is the deliverable and it carries the numbers and the reading of them. The two scripts are how it was +produced: + +- `cost.sh` sweeps the corpus and prints a TSV: file size, `.text`, and **the sum of the `flan.*` defined symbols**, + which is the program's own code with the runtime excluded. `COST_FLAGS=--dev` is the `SURVEY_FLAGS` precedent. +- `bench.sh` runs four programs in `spike/x86/bench/`, each written so that one suspected cost is most of what the + program does, because every program in `test/programs` runs in about two and a half milliseconds of which nearly + all is `execve`. + +They live in `spike/x86/bench/`, a subdirectory, *deliberately*: `survey.sh` globs `spike/x86/*.flan`, and a +benchmark landing in the survey would move the 97 that three handoffs quote. + +## 3. One trap, paid for once + +Two copies of `cost.sh` sharing a scratch directory silently corrupt each other's numbers. The first release sweep +was run, killed, and restarted; `pkill` missed a child, and the survivor kept writing the same `l` and `x` files +the new run was building and timing. The result was a row for `math` carrying `dev-locals`' binaries — which is +visible only because those two happened to collide *exactly*, and a collision between two programs of merely +similar size would have produced a plausible row nobody would question. + +`cost.sh` now makes a `run.$$` subdirectory with a plain `mkdir`, so a second copy cannot land in the first one's. +The sweep behind `COST.md` was re-run clean afterwards. If you extend these scripts, keep that property. + +## 4. Nothing was changed in `lib/`, and nothing needed to be + +No finding here wants a compiler change that this lane was not allowed to make. The costs are all in +`lib/x86.ml`'s lowering and the next lane rewriting that file will meet them; `COST.md`'s last section says which +ones are worth its attention and which are not, which was the point of measuring rather than guessing.