The last commit's finding took three refuted hypotheses to reach. The switches
that produced it are kept, and so is the answer, because the next person will
ask the same question:
* `-d:icBNodeProf` counts every accessor and times the phases (`handOffBody`,
`genProcBody`, the analyses, `sym`/`typ`/`info`/`origin`). Each backend
process appends a line to `$NIM_IC_BNODE_PROF`, so a parallel build still
produces attributable output. Costs nothing when the define is off.
* `-d:icBridgeOnly` builds the buffer but generates off the tree. It is a
MEASUREMENT switch, not a mode, and it is the only way to separate what the
encoder costs from what reading costs — which is how encoding was shown to
be free and the reader identified as the thing to profile.
Cursor-driven generation is now the same speed as tree-driven: over the 2420
routines of a 68-module target, `genProcBody` is 369ms off a cursor against
366ms off a `PNode`. It was 1877ms.
The three suspects that measured out as wrong are written down so they are not
re-guessed: the tag-name string compares in `typ`/`flags` (0.3% of the build),
`Cursor`'s reference-counting lifetime hooks (6M calls, 60ms of 1900ms), and
the structural accessors as a whole — `kind`, `son` and the iterators together
are 32ms of 1877ms.
The one real change here is the tag memo, which now resolves a tag's
`TNodeKind` and its wrapper role together instead of comparing tag NAMES on
every `typ`/`flags`/`hasExplicitNilType` call. Small — it is the 0.3% above —
but it removes 197k string comparisons and lets those three read a `case`.
`tagCachePool` holds the pool by reference for the same reason `readPool` does:
that is what stops a freed pool from being replaced at the same address.
Verified with the parent commit: `tests/ic` 39/39, grinder clean, `.c`
byte-identical both under `--ic:on` and without it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMyRHByv7hhaQJ4Pa1bHbE