From b3305a6568b7809899f6a85fd96f63e5fda0ea2a Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 13 Jun 2026 22:52:01 +0200 Subject: [PATCH] IC: NimMain init orchestration for the per-module backend (Phase 2b) In the per-module model each module's init/datInit proc is generated by its own cg process, but NimMain (emitted with the main module) must call them all. Previously the main module's cg only registered its own init, so a/b's globals stayed at their defaults -- the linked exe ran but printed wrong values. When the cg target is the main module, register every other loaded module's init/datInit into NimMain via registerReusedModuleToMain (exported), reading each module's initRequired/datInitRequired from its .c.nif meta head. This is the same no-codegen registration the whole-program backend uses for reused TUs; it requires the main module's cg to run last, after every other .c.nif exists (the per-module nifmake graph will order it so). Modules without init code have no .c.nif and register nothing. Validated: the 3-module diamond now builds end-to-end through the per-module pipeline (cg all with main last, merge, emit all, cc, link) and the resulting executable prints the correct "16 23" -- matching the whole-program build. koch ic thallo green. Co-Authored-By: Claude Opus 4.8 --- compiler/cgen.nim | 2 +- compiler/nifbackend.nim | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 94edbd9375..8013cfb2be 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -2270,7 +2270,7 @@ proc registerReusedInit*(g: BModuleList; moduleBase: string; g.mainModProcs.finishProcHeaderAsProto() g.otherModsInit.addCallStmt(init) -proc registerReusedModuleToMain(g: BModuleList; m: BModule; +proc registerReusedModuleToMain*(g: BModuleList; m: BModule; initRequired, datInitRequired: bool) = ## `registerModuleToMain` for a module whose cached translation unit is ## reused: the init/datInit presence comes from the artifact's meta head diff --git a/compiler/nifbackend.nim b/compiler/nifbackend.nim index ca8712840c..9514a4aa79 100644 --- a/compiler/nifbackend.nim +++ b/compiler/nifbackend.nim @@ -569,10 +569,19 @@ proc generateCgStage(g: ModuleGraph; mainFileIdx: FileIndex) = return generateCodeForModule(g, target) + let bl = BModuleList(g.backend) # The main module also owns the whole-program method dispatchers + NimMain. if sfMainModule in target.module.flags: emitMethodDispatchers(g) - let bl = BModuleList(g.backend) + # NimMain (generated when the main module is finished) must call every other + # module's init/datInit. Those translation units are produced by their own + # `cg` processes, so the calls are registered here from each `.c.nif` meta + # head — which is why the main module's `cg` runs last, after every other + # `.c.nif` exists. Modules without init code (no `.c.nif`) register nothing. + for m in bl.mods: + if m != nil and sfMainModule notin m.module.flags: + let heads = readCnifHeads(getCFile(m).string & ".nif") + registerReusedModuleToMain(bl, m, heads.initRequired, heads.datInitRequired) let tb = bl.mods[target.module.position] if tb != nil: finishModule(g, tb)