The plumbing behind the routing fix. `--icBackendModules:<a,b,c>` gives
the lower/cg/emit stages a LIST, `loadDepClosure` loads the batch's union
closure once instead of once per module, each stage loops over its
members, and `deps.nim` emits one nifmake rule per batch declaring all
its members' outputs. `-d:icBatchSize:N` is the dial; `0` means one batch
per job.
The default is 1, which reproduces the per-module fan-out exactly: `.c`,
`.c.nif` AND `.t.bif` byte-identical to the previous commit, ic 40/40,
`koch boot -d:release` equal executables.
Turning the dial up found three real bugs, none of which could exist
while a process wrote one TU:
* `loadDepClosure` deduplicated batch members against `visited`, which
already contains system — so a batch whose member IS system loaded
nothing and produced no artifact at all. System is an ordinary live
node with its own `.t.bif`/`.c.nif`; members now have their own set.
* `cg` finished each member's TU as it went. `finishModule` closes a TU,
and a later member's codegen routes definitions INTO an earlier
member's TU, which then silently dropped them. Generate every member,
then finish every member.
* `emitsBodyInThisModule(m, prc)` was asked with the DEMANDING module
where it means the module the body goes INTO. Identical while
`findPendingModule` always returned `m`; with a batch the definition
was marked declared in its owner's TU and then emitted by nobody — 18
undefined symbols at link.
Where it stands at batch size 4 and 8: builds, links, runs correctly, and
CPU drops from 9.8 s to 7.2 s / 6.2 s on a 67-module program. But the
artifacts are NOT invariant along the dial — 16 of 67 `.c` differ at 4 —
and the divergence is of two kinds. Most are the recorded gcc command
comment: per-module `{.passC.}` flags leak between batch members through
`writeBackendActions`. The rest is real: the set of minted type-bound
hooks moves (four `=destroy`/`=trace`/RTTI hooks vanish, one appears),
because which process mints a hook decides who owns it and batching
changes that. Duplication is also still 2.19x, unchanged — at these
sizes most demands are still for modules outside the batch.
So the dial stays at 1 until that invariant holds. It exists to be
turned, and it now reports what it finds when you do.