From b4c71af517fe5f5ed5138dc1f96a28ce2a30e635 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 13 Jun 2026 22:09:16 +0200 Subject: [PATCH] IC: module suffix is now the trailing token of mangled C names Reorder mangleProcNameExt and makeUnique so the module suffix comes LAST: name_u__ (was name___u). The suffix is now a strippable trailing token, so content-addressed cross-module merging (the per-module backend's instance/hook dedup) can recover a mint-site-independent name by chopping everything from the final "__" -- no reference rewriting. Also drops the main-module special case in mangleProcNameExt: it omitted the suffix because the main module's symbols key on its NIF-suffix file index. But the backend already aliases that suffix to the main's source index (nifbackend.loadModuleDependencies), so graph.ifaces[s.itemId.module] is populated for the main module too -- the guard was redundant. Main-module procs now mangle uniformly (e.g. mainProc_u0__). icFormatVersion 3 -> 4: cached .c.nif artifacts hold the old name scheme and must be wiped. Validated: koch boot (non-IC self-host) reaches fixed point; koch ic thallo tconverter timp tmiscs tparseutils all green; a 3-module diamond IC build runs correctly. Co-Authored-By: Claude Opus 4.8 --- compiler/ccgutils.nim | 5 +++-- compiler/mangleutils.nim | 17 +++++++---------- compiler/options.nim | 5 ++++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 59188e2837..5d16881604 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -112,12 +112,13 @@ proc encodeName*(name: string): string = proc makeUnique(m: BModule; s: PSym, name: string = ""): string = result = if name == "": s.name.s else: name - result.add "__" - result.add m.g.graph.ifaces[s.itemId.module].uniqueName # keep backend-minted ids out of the `_u` namespace; their item counter # restarts at 0 and would collide with loaded symbols' ids result.add(if s.itemId.isBackendMinted: "_c" else: "_u") result.add $s.itemId.item + # module suffix LAST (a strippable trailing token; see `mangleProcNameExt`) + result.add "__" + result.add m.g.graph.ifaces[s.itemId.module].uniqueName proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false; extra: string = ""): string = #Module::Type diff --git a/compiler/mangleutils.nim b/compiler/mangleutils.nim index c8aa10dd93..7bb633f61d 100644 --- a/compiler/mangleutils.nim +++ b/compiler/mangleutils.nim @@ -53,14 +53,9 @@ proc mangleParamExt*(s: PSym): string = result.addInt s.position proc mangleProcNameExt*(graph: ModuleGraph, s: PSym): string = - result = "__" - # Under incremental compilation the main module is registered at its source - # file index, but its symbols are keyed by the NIF-suffix file index, which has - # no `ifaces` slot. Only the main module has this gap (dependencies are - # registered at their suffix index), so omitting the unique name for it stays - # collision-free: the mangled base name plus `disamb` already disambiguate. - if s.itemId.module >= 0 and s.itemId.module < graph.ifaces.len: - result.add graph.ifaces[s.itemId.module].uniqueName + # The disambiguator comes first and the module suffix LAST, so the suffix is + # a strippable trailing token: content-addressed cross-module merging chops + # everything from the final `__` to recover a mint-site-independent name. if s.itemId.isBackendMinted: # A symbol minted during IC codegen (`idGeneratorForBackend`): its idgen # starts with an EMPTY per-name disamb table, so its `disamb` restarts at 0 @@ -70,10 +65,10 @@ proc mangleProcNameExt*(graph: ModuleGraph, s: PSym): string = # lifts, emits and compiles them in one run), so the per-module-unique # item id is a safe and deterministic discriminator; the `_c` marker keeps # the namespace disjoint from `_u`. - result.add "_c" + result = "_c" result.addInt s.itemId.item else: - result.add "_u" + result = "_u" # Use `disamb` rather than `itemId.item`: under incremental compilation a # symbol loaded from a NIF file gets a fresh, load-order-dependent `itemId.item` # (from the per-module symbol counter), which is neither stable across the @@ -82,3 +77,5 @@ proc mangleProcNameExt*(graph: ModuleGraph, s: PSym): string = # and, together with the already-prepended mangled name, yields a unique and # stable C identifier. result.addInt s.disamb + result.add "__" + result.add graph.ifaces[s.itemId.module].uniqueName diff --git a/compiler/options.nim b/compiler/options.nim index fd194e0b23..6e42fa7c8e 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -29,7 +29,7 @@ const nimEnableCovariance* = defined(nimEnableCovariance) - icFormatVersion* = "3" + icFormatVersion* = "4" ## Version of the IC cache format (the sem-NIF module layout written by ## ast2nif.nim plus the iface/impl/edges side files). Bump it whenever ## that layout changes: `commandIc` wipes a nimcache whose `ic.version` @@ -42,6 +42,9 @@ const ## wiped rather than warm-rebuilt. ## v3: added the `.s.deps` sidecar (real post-sem imports) and switched the ## macro-generated-import discovery from `icmissing.txt` to it. + ## v4: backend C-name scheme change — the module suffix is now the trailing + ## token (`name_u__`, was `name___u`), so + ## cached `.c.nif` artifacts hold incompatible names and must be wiped. type # please make sure we have under 32 options # (improves code efficiency a lot!)