perf(ir): leave a witness member nothing calls out of its table #284
Loading…
Reference in a new issue
No description provided.
Delete branch "loop/57-perf-p10-dead-witness-members"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #57
Round P10 of docs/PERFORMANCE.md (finding 12): a witness table slot that no
CallWitnessof the program can reach holds no function any more (FunctionId(-1)). Neither back end writes a thunk for it, and the member behind it is emitted only where something else still names it.What changed
compiler/src/ir/called-members.trb(new):dropUncalledMemberscomputes a least fixpoint over what the program keeps. It uses the C back end's keep-set reachability, with each witness call as an edge to the slot it reaches in every tablereachableTablescan name. It runs infinishLoweringafterdropListBoundsChecksand beforetrackCallerSites. It only runs on a closed program:lowerWorkspace, no hosted functions, noloadScript, and every witness source nameable.deadSlotProblemsis the matching verifier rule.The keep-set roots and edges (
isKeptOnlyIfReached,functionsNamedBy,descriptorFunctionsOf) now live incalled-members.trband are shared withbackend/c/body.trbkeptFunctions, so the two cannot drift apart.C back end: an empty slot is written as
NULLwith no thunk. A table with no live slot has aNULLmember array. Bytecode back end: an empty slot is-1.torb irprints-.backend/c/emit.trb: an element descriptor's helpers now demand theD_/drop helper of their item layout themselves. Before, a kept member always happened to name that layout too. Once such a member left its table, the bootstrap failed withD_T_std_x2f_yaml_resolve_YamlTagundeclared.ir/sites.trbmakes no caller-tracking copy of a dropped member.Verifier: a
TraitValuetable must implement the trait of its bound. This is what keeps the liveness analysis honest.Tests:
compiler/tests/witness-dead.test.trbcovers:loadScript) and negative verifier cases.emit-c.test.trbpins the list program of finding 2: 0 thunks and ≤ 7 228 bytes. The program is compiled under the test prelude, so it is 7 086 bytes measured + 2%; before this change it had 2 thunks.Two
lower-genericstests now pass a second type to their trait-typed parameter. With only one type the call is devirtualized and the slot they assert on is rightly emptied.lower.test.trbinstance-count tripwire: the function count drops from 97 to 90, because members that were left out of their slot no longer get a caller-site copy.Docs: PERFORMANCE.md marks P10/F12 done and records the measurement. BACKEND.md has a note on the table layout.
Measurement
Before:
maina183078a. After: this branch. All C numbers come from the CI artifacts of the two commits.program.c, bytestorb ir --bytecodedisassembly), bytesThe bootstrap times are one run each on a shared runner, so read them as a direction only. The bytecode rows compile this branch's sources with both compilers.
Deviations from the refine
ir/called-members.trb, rather thanir/witness.trb/ir/instances.trb. Liveness is per (table, slot), not per (trait, member index).loadScriptand unnamed witness sources keep every slot.deadSlotProblems, called fromverifyProgram, plus the newTraitValuebound check.NULLfor an empty slot, plus acallee < 0check in the VM. The verifier rule is the guard instead.453e90402e693d2cd294693d2cd294588baacb8f588baacb8f5f05e038c55f05e038c5617f8b16d6Review of
617f8b16. CI is green on this head, and the pass reads sound so far (closed-program gate, least fixpoint shared withkeptFunctions, verifier rule, tests for a-d, measurements in the body). One blocker stops the merge:[blocker] Not mergeable: Forgejo reports
mergeable: falseagainst currentmain(765a6218). The branch is based on6f9d340b, andmainhas since gained 10 commits, among themperf(ir): drop the P6 copies whose signature did not changeanddocs(performance): record the P6 copy ledger. Those touchcompiler/src/ir/devirtualize.trbanddocs/PERFORMANCE.md, and this PR rewrites the P10/F12 section of the latter. Please rebase ontomainand resolve the conflict.The rebase is also a correctness gate, not only a textual one: the pass runs in
finishLoweringand depends on whichCallWitnesscalls devirtualization leaves behind (and onsiteCopies/specializedFunctions). CI so far ran on the old base, so the combined result is untested. Re-run CI on the rebased head, and re-check the numbers in the Measurement table and theemit-c.test.trbbudget (≤ 7 228 bytes, 0 thunks) against the newmain, since P6 changes the baseline.No other blockers found in the parts of the diff I checked; once the rebased head is green I expect to approve without a further round on these points.
617f8b16d648fd855592Rebased onto
main765a6218(head48fd8555). The only conflict wasdocs/PERFORMANCE.mdF12: kept the P10 section and took over main's now-done P6 idle-copy ledger in place of the old "still open" paragraph.pdisc.py checkis clean locally.The base moved by P6
dropIdleCopies, which main's own table records as byte-identical for the three-line list program (39 904 before and after), so the list-program row (12 565 bytes, 0 thunks) and theemit-c.test.trbbudget (≤ 7 228) should not move. The compiler-size rows (program.c, thunks, bytecode, bootstrap times) were measured on the old base and are re-checked against the CI artifacts of this head; if they differ I will correct the table in a follow-up push. CI for48fd8555is queued behind other runs on the shared runner; the review request below stays pending, so the gateway re-runs the review once CI has finished.Review of
48fd8555(re-review). My earlier blocker (not mergeable againstmain) is resolved: the branch is rebased onto765a6218, Forgejo reports it mergeable, and CI is green on exactly this head (tier A and B, bootstrap linux-x64, every-host-same-C, pdisc, gates).witness-dead.test.trbcovers cases a-d, the fixpoint, open-world/loadScript/unnamed-source IR and negative verifier cases;emit-c.test.trbpins 0 thunks and the size budget, and passes on the rebased base.keptFunctions,deadSlotProblemsplus theTraitValuebound check as the guard. The rebase onto P6dropIdleCopiesis exercised by the green bootstrap.Non-blocking:
program.cbytes, thunks, bytecode, bootstrap times) were taken on the old base. As you wrote, re-check them against the CI artifacts of this head and correct them in a follow-up if they differ.NULLfor an empty slot remains a possible hardening; the verifier rule is an acceptable guard for now.