From 20a337557136b825673cc9a19aedd6185b333079 Mon Sep 17 00:00:00 2001 From: araq Date: Sun, 30 Aug 2026 23:42:06 +0200 Subject: [PATCH] IC: a backend stage stops building the interface half it cannot read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `populateInterfaceTablesFromIndex` stubbed a `PSym` for every global symbol a module declares and inserted it into `interf` and/or `interfHidden`. On a 68-module target that is 2.37M stubs across the build — and 2.04M of them go into `interfHidden` alone, which a `cg`/`emit` stage has no way to reach. `interfHidden` is selected exclusively by `modulegraphs.interfSelect`, and only when `optImportHidden` is in the module's options. That flag is set in exactly one place — `importer.importModuleAs`, i.e. during sem. A backend stage builds its module symbols in `moduleFromNifFile` and never imports anything, so the flag cannot be set and the table cannot be selected. Skipping the hidden-only branch there is unobservable, not merely unlikely to be observed. Exported symbols still go into both tables. That is 0.33M inserts against the 2.0M saved, and it leaves `interfHidden` a coherent view — a module with no hidden symbols — rather than an empty one, should anything ever consult it. interface tables 1401ms -> 863ms loadDepClosure 2771ms -> 2309ms cold --ic:on build 9.38s -> 8.91s Skipping the tables WHOLESALE in a backend stage does not work, and the failure is worth recording since it is the obvious next thought: the exported half is what `magicsys.getSysSym` resolves through, so the stage dies with "system module needs: pointer". Skipping them in the FRONTEND is worse still — `nim m` loses `defined`. Verified: both configurations build; `tests/ic` 40/40; 67/67 generated `.c` byte-identical, cursor still identical to `PNode`. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FMyRHByv7hhaQJ4Pa1bHbE --- compiler/ast2nif.nim | 21 ++++++++++++++++++++- compiler/icprof.nim | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/compiler/ast2nif.nim b/compiler/ast2nif.nim index b24c6d2fe0..f32e1cb62b 100644 --- a/compiler/ast2nif.nim +++ b/compiler/ast2nif.nim @@ -3688,13 +3688,32 @@ proc populateInterfaceTablesFromIndex(c: var DecodeContext; module: FileIndex; var indexTab = move c.mods[module].index # Add all symbols to interf (exported interface) and interfHidden + # A BACKEND stage cannot read the hidden-only half, so it does not build it. + # `interfHidden` is reached exclusively through `modulegraphs.interfSelect`, + # which picks it only when `optImportHidden` is in the module's options — and + # that flag is set in exactly one place, `importer.importModuleAs`, i.e. + # during sem. `cg`/`emit` build their module symbols in `moduleFromNifFile` + # and never import anything, so the flag cannot be set and the table cannot be + # selected. The saving is the bulk of this proc: on a 68-module target the + # hidden branch stubs 2.0M symbols against the exported branch's 0.33M. + # + # Exported symbols are still added to BOTH tables. That costs little beside + # the above and leaves `interfHidden` a coherent view (a module with no hidden + # symbols) rather than an empty one, should anything ever consult it. + let conf = c.infos.config + let backendStage = conf.cmd == cmdNifC and + (conf.icBackendStage == "cg" or conf.icBackendStage == "emit") + + prof pIfaceModules for nifName, entry in indexTab: if entry.vis == Exported: + prof pIfaceExported let sym = loadSymFromIndexEntry(c, module, nifName, entry, thisModule) if sym != nil: strTableAdd(interf, sym) strTableAdd(interfHidden, sym) - elif not nifName.startsWith("`t"): + elif not backendStage and not nifName.startsWith("`t"): + prof pIfaceHidden # do not load types, they are not part of an interface but an implementation detail! #echo "LOADING SYM ", nifName, " ", entry.offset let sym = loadSymFromIndexEntry(c, module, nifName, entry, thisModule) diff --git a/compiler/icprof.nim b/compiler/icprof.nim index 195cf0ac7f..2f2eadbfff 100644 --- a/compiler/icprof.nim +++ b/compiler/icprof.nim @@ -31,7 +31,7 @@ when defined(icBNodeProf): ProfSlot* = enum pKind, pTagKindHit, pTagKindMiss, pAstChildren, pSkip, pSon, pLen, pLastSon, pIterYield, pSym, pTyp, pTypTagLit, pOrigin, pNilType, - pGenBodyCalls, pInfo + pGenBodyCalls, pInfo, pIfaceExported, pIfaceHidden, pIfaceModules TimeSlot* = enum tLoadClosure, tModuleId, tBifLoad, tPosIndex, tTopLevel, tInterfTables, tTransform, tHandOff, tGenBody, tAnalyses,