mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-25 16:11:44 +00:00
cleanups: the .t.nif files are not negotiable, follows Nimony's hexer split design
This commit is contained in:
@@ -198,8 +198,8 @@ type
|
||||
depSuffixes: HashSet[string] # module suffixes already emitted as `(import ...)` deps
|
||||
emittedBackendTypes: HashSet[int32] # backend-local type items already def'd this module
|
||||
emittedBackendSyms: HashSet[int32] # backend-local sym items already def'd this module
|
||||
lowering: bool # serializing the `lower` stage's `.t.nif` (per-entry self-contained)
|
||||
emittedFieldSyms: HashSet[ItemId] # lowering: derived env-field syms already def'd this entry
|
||||
lowering: bool # serializing the `lower` stage's whole-module `.t.nif`
|
||||
emittedFieldSyms: HashSet[ItemId] # lowering: derived env-field syms already def'd
|
||||
inTypeReclist: int # >0 while writing a type's OWN reclist: fields must be SELF-CONTAINED
|
||||
# defs (the type can be seek-loaded in isolation), not entry-deduped uses
|
||||
|
||||
@@ -718,7 +718,6 @@ var implTag = registerTag("implementation")
|
||||
var reexpModTag = registerTag("reexpmod")
|
||||
var offerTag = registerTag("offer")
|
||||
var typeOfferTag = registerTag("toffer")
|
||||
var loweredTag = registerTag("lowered")
|
||||
|
||||
proc registerNifAstTags*() =
|
||||
## (Re)registers ast2nif's NIF tags explicitly. The top-level `registerTag`
|
||||
@@ -751,7 +750,6 @@ proc registerNifAstTags*() =
|
||||
reexpModTag = registerTag("reexpmod")
|
||||
offerTag = registerTag("offer")
|
||||
typeOfferTag = registerTag("toffer")
|
||||
loweredTag = registerTag("lowered")
|
||||
|
||||
proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
|
||||
if n == nil:
|
||||
@@ -1518,71 +1516,6 @@ proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode;
|
||||
writeImplCookie(config, thisModule, dest, ifaceHex)
|
||||
writeEdgesFile(config, thisModule, implDeps)
|
||||
|
||||
proc collectLoweredLocals(w: var Writer; n: PNode) =
|
||||
## Record every transform-created local (a `Complete`, non-`@bk` sym owned by
|
||||
## the module being serialized) in `w.locals` so it is written SUFFIX-LESS as
|
||||
## an inline def. Pre-existing entities (params, result, callees) were sealed
|
||||
## before serialization, so they are skipped here and emit as index SymUses.
|
||||
if n == nil: return
|
||||
if n.kind == nkSym and n.sym != nil:
|
||||
let s = n.sym
|
||||
if not s.itemId.isBackendMinted and s.itemId.module == w.currentModule and
|
||||
s.state == Complete:
|
||||
w.locals.incl s.itemId
|
||||
for i in 0 ..< n.safeLen:
|
||||
collectLoweredLocals(w, n[i])
|
||||
|
||||
proc serializeLoweredBodies*(config: ConfigRef; ownerModule: int32;
|
||||
entries: openArray[tuple[name: string; body: PNode]];
|
||||
hooks: openArray[LogEntry];
|
||||
outfile: string) =
|
||||
## Write the `lower` backend stage's `.t.nif`. Three sections, all covered by
|
||||
## the file's embedded index (which the `cg` loader registers as the module's
|
||||
## SECOND index/stream — see loadLoweredBodies):
|
||||
## - `(repdestroy/repcopy/... key sym)`: the type-bound ops the lower stage
|
||||
## lifted while transforming (closure-env `=destroy` etc.). `cg`'s
|
||||
## `registerLoadedHooks` re-attaches them so `injectDestructorCalls` (kept in
|
||||
## cg) resolves them via `getAttachedOp`'s key fallback.
|
||||
## - the hook ROUTINES as full `(sd)`: NEW in the lower stage (no `.s.nif`
|
||||
## signature), so serialize sig + transformed body whole; cg loads them via
|
||||
## the `.t.nif` index when the HookEntry's SymUse resolves, then demand-emits.
|
||||
## - owned routines' transformed bodies as `(lowered "<nifname>" <body>)`: only
|
||||
## the body (signature comes from the `.s.nif`); applyLoweredBodies sets it on
|
||||
## the existing sym.
|
||||
## A `@bk` closure-env type/sym referenced anywhere is emitted inline AND
|
||||
## indexed, so it resolves through the embedded index regardless of section.
|
||||
var w = Writer(infos: LineInfoWriter(config: config), currentModule: ownerModule)
|
||||
w.inProc = 1 # we are serializing routine *bodies*
|
||||
w.lowering = true
|
||||
var dest = createTokenBuf(256)
|
||||
createStmtList(dest, NoLineInfo)
|
||||
for op in hooks:
|
||||
writeOp(w, dest, op)
|
||||
var emittedHooks = initHashSet[int32]()
|
||||
for op in hooks:
|
||||
if op.sym != nil and op.sym.kindImpl in routineKinds and
|
||||
not emittedHooks.containsOrIncl(op.sym.itemId.item):
|
||||
w.emittedBackendTypes.clear()
|
||||
w.emittedBackendSyms.clear()
|
||||
w.emittedFieldSyms.clear()
|
||||
w.locals.clear()
|
||||
writeSymDef(w, dest, op.sym)
|
||||
for e in entries:
|
||||
# Each `(lowered ...)` entry is loaded independently, so it must be
|
||||
# SELF-CONTAINED: reset the per-Writer backend-local (@bk) dedup so every
|
||||
# entry re-emits the def of any closure-env type/sym it references.
|
||||
w.emittedBackendTypes.clear()
|
||||
w.emittedBackendSyms.clear()
|
||||
w.emittedFieldSyms.clear()
|
||||
w.locals.clear()
|
||||
collectLoweredLocals(w, e.body)
|
||||
dest.addParLe loweredTag, NoLineInfo
|
||||
dest.addStrLit e.name
|
||||
writeNode(w, dest, e.body)
|
||||
dest.addParRi
|
||||
dest.addParRi()
|
||||
writeFile(dest, outfile)
|
||||
|
||||
# --------------------------- Loader (lazy!) -----------------------------------------------
|
||||
|
||||
proc nodeKind(n: Cursor): TNodeKind {.inline.} =
|
||||
@@ -1644,15 +1577,6 @@ type
|
||||
suffix: string
|
||||
contentStart: int # stream offset of the module body, so a full-AST load can
|
||||
# rewind after lazy symbol loads moved the cursor
|
||||
# The module's `.t.nif` (the `lower` stage's transformed bodies): a SECOND
|
||||
# embedded-index + stream consulted when the main `.s.nif` index misses. The
|
||||
# transformed bodies' backend-minted (`@bk`) entities (closure-env types/syms,
|
||||
# temporaries) live ONLY here; they are named with this module's suffix, so
|
||||
# `createTypeStub`/`loadSymStub` look them up here on a `.s.nif` miss and load
|
||||
# them from `tStream` (see applyLoweredBodies). "Just use NIF's embedded index."
|
||||
hasTIndex: bool
|
||||
tStream: nifstreams.Stream
|
||||
tIndex: Table[string, NifIndexEntry]
|
||||
|
||||
DecodeContext* = object
|
||||
infos: LineInfoWriter
|
||||
@@ -1688,8 +1612,8 @@ proc loadedState(c: DecodeContext): ItemState {.inline.} =
|
||||
if c.infos.config.cmd == cmdNifC: Complete else: Sealed
|
||||
|
||||
proc cursorFromIndexEntry(c: var DecodeContext; module: FileIndex; entry: NifIndexEntry;
|
||||
buf: var TokenBuf; fromT = false): Cursor =
|
||||
let s = if fromT: addr c.mods[module].tStream else: addr c.mods[module].stream
|
||||
buf: var TokenBuf): Cursor =
|
||||
let s = addr c.mods[module].stream
|
||||
s.r.jumpTo entry.offset
|
||||
# A seek-load is self-contained: its tokens must decode their relative line
|
||||
# info against `entry.info` ALONE. The stream's `parents` stack can be left at
|
||||
@@ -1847,13 +1771,7 @@ proc createTypeStub(c: var DecodeContext; t: SymId): PType =
|
||||
let ii = addr c.mods[modFi].index
|
||||
var offs = ii[].getOrDefault(name)
|
||||
if offs.offset == 0:
|
||||
# A backend-minted (`@bk`) type produced by the `lower` stage lives in this
|
||||
# module's `.t.nif`, not its `.s.nif` index. Resolve it through the `.t.nif`
|
||||
# embedded index (loadType picks `tStream` for a tIndex-only name).
|
||||
if c.mods[modFi].hasTIndex:
|
||||
offs = c.mods[modFi].tIndex.getOrDefault(name)
|
||||
if offs.offset == 0:
|
||||
raiseAssert "symbol has no offset: " & name
|
||||
raiseAssert "symbol has no offset: " & name
|
||||
result = PType(itemId: id, uniqueId: id, kind: TTypeKind(k), state: Partial)
|
||||
c.types[name] = (result, offs)
|
||||
|
||||
@@ -1949,15 +1867,6 @@ proc loadSymStub(c: var DecodeContext; t: SymId; thisModule: string;
|
||||
let id = if isBk: backendItemId(module.int32, val[]) else: itemId(module.int32, val[])
|
||||
|
||||
var offs = c.mods[module].index.getOrDefault(symAsStr)
|
||||
if offs.offset == 0 and c.mods[module].hasTIndex:
|
||||
# A sym produced by the `lower` stage lives in this module's `.t.nif`, not
|
||||
# its `.s.nif` index. This covers BOTH backend-minted (`@bk`) entities
|
||||
# (closure `:env` param, `:tmp`/`res` temporaries) AND module-homed derived
|
||||
# closure-env FIELDS (e.g. a captured `i`): the latter have a normal
|
||||
# module suffix but are added to the env object only in the backend, so
|
||||
# they are indexed solely in `.t.nif`. Resolve through that embedded index
|
||||
# (loadSym picks `tStream` for a tIndex-only name).
|
||||
offs = c.mods[module].tIndex.getOrDefault(symAsStr)
|
||||
if offs.offset == 0:
|
||||
# Only module/package self-syms are never written as `(sd)` entries, so a
|
||||
# missing index offset means this is such a sym — typically the OWNER of an
|
||||
@@ -2093,11 +2002,7 @@ proc loadType*(c: var DecodeContext; t: PType) =
|
||||
else:
|
||||
typeToNifSym(t, c.infos.config)
|
||||
let modFi = t.itemId.module.FileIndex
|
||||
# A name resolved through the `.t.nif` (tIndex) — a `lower`-stage closure-env
|
||||
# type — must seek in `tStream`, not the `.s.nif` stream.
|
||||
let fromT = c.mods[modFi].hasTIndex and not c.mods[modFi].index.hasKey(typeName) and
|
||||
c.mods[modFi].tIndex.hasKey(typeName)
|
||||
var n = cursorFromIndexEntry(c, modFi, c.types[typeName][1], buf, fromT = fromT)
|
||||
var n = cursorFromIndexEntry(c, modFi, c.types[typeName][1], buf)
|
||||
var localSyms = initTable[string, PSym]()
|
||||
loadTypeFromCursor(c, n, t, localSyms)
|
||||
|
||||
@@ -2199,10 +2104,7 @@ proc loadSym*(c: var DecodeContext; s: PSym) =
|
||||
var buf = createTokenBuf(30)
|
||||
let symsModule = s.itemId.module.FileIndex
|
||||
let nifname = globalName(s, c.infos.config)
|
||||
# A `@bk` sym resolved through the `.t.nif` (tIndex) seeks in `tStream`.
|
||||
let fromT = c.mods[symsModule].hasTIndex and not c.mods[symsModule].index.hasKey(nifname) and
|
||||
c.mods[symsModule].tIndex.hasKey(nifname)
|
||||
var n = cursorFromIndexEntry(c, symsModule, c.syms[nifname][1], buf, fromT = fromT)
|
||||
var n = cursorFromIndexEntry(c, symsModule, c.syms[nifname][1], buf)
|
||||
|
||||
expect n, ParLe
|
||||
if n.tagId != sdefTag:
|
||||
@@ -2220,110 +2122,22 @@ proc loadSym*(c: var DecodeContext; s: PSym) =
|
||||
inc n
|
||||
loadSymFromCursor(c, s, n, c.mods[symsModule].suffix, localSyms)
|
||||
|
||||
proc sealLoadedBackendEntities*(c: var DecodeContext) =
|
||||
## Before the `lower` stage serializes its transformed bodies, mark every
|
||||
## index-loaded sym/type `Sealed`. The backend loads them `Complete` (mutable
|
||||
## for the transform); without this, `writeNode`'s `shouldWriteSymDef` would
|
||||
## emit a duplicate `(sd)`/`(td)` def for a param/result/existing-local/owner
|
||||
## type — and the `cg` body-loader would then bind the body's `result` to a
|
||||
## FRESH sym instead of the one in `prc.ast[resultPos]` (the #6/#7 "result
|
||||
## cannot be captured" class). Sealed ⟹ SymUse ⟹ resolved via `cg`'s module
|
||||
## index. The transform-CREATED entities are absent from `c.syms`/`c.types`
|
||||
## (or are backend-minted), so they stay `Complete`/`@bk` and still get the
|
||||
## inline defs the loader's local-sym pre-scan needs.
|
||||
for _, v in c.syms:
|
||||
if v[0] != nil and v[0].state == Complete: v[0].state = Sealed
|
||||
for _, v in c.types:
|
||||
if v[0] != nil and v[0].state == Complete: v[0].state = Sealed
|
||||
|
||||
proc sealLoadedRoutines*(c: var DecodeContext) =
|
||||
## Whole-module-lowering variant of `sealLoadedBackendEntities`: seal ONLY the
|
||||
## Before `writeLoweredModule` re-serializes the lowered module, seal ONLY the
|
||||
## module's ROUTINE syms. A `.t.nif` written by `writeLoweredModule` is the
|
||||
## SOLE source the `cg` stage loads (there is no `.s.nif` fallback for its
|
||||
## bodies), so unlike the per-`(lowered)`-entry path, every type, global, param
|
||||
## and local must still emit a REAL def in it — only cross-routine references
|
||||
## may be `SymUse`s (each routine's def is emitted once, at module scope, by
|
||||
## the explicit stub loop). Types/globals stay `Complete` so `writeType`/
|
||||
## `writeGlobals` emit them; routines become `Sealed` so a body referencing
|
||||
## another routine writes a `SymUse` resolved through the module index.
|
||||
## bodies), so every type, global, param and local must still emit a REAL def
|
||||
## in it — only cross-routine references may be `SymUse`s (each routine's def is
|
||||
## emitted once, at module scope, by the explicit stub loop). Types/globals stay
|
||||
## `Complete` so `writeType`/`writeGlobals` emit them; routines become `Sealed`
|
||||
## so a body referencing another routine writes a `SymUse` resolved through the
|
||||
## module index.
|
||||
for _, v in c.syms:
|
||||
if v[0] != nil and v[0].state == Complete and v[0].kindImpl in routineKinds:
|
||||
v[0].state = Sealed
|
||||
|
||||
proc resolveHookSym*(c: var DecodeContext; symId: nifstreams.SymId): PSym
|
||||
|
||||
proc repTagToOp(tagId: TagId): (bool, TTypeAttachedOp) =
|
||||
## Map a `(rep…)` hook tag to its attached-op kind (and whether it IS one).
|
||||
if tagId == repDestroyTag: (true, attachedDestructor)
|
||||
elif tagId == repCopyTag: (true, attachedAsgn)
|
||||
elif tagId == repWasMovedTag: (true, attachedWasMoved)
|
||||
elif tagId == repDupTag: (true, attachedDup)
|
||||
elif tagId == repSinkTag: (true, attachedSink)
|
||||
elif tagId == repTraceTag: (true, attachedTrace)
|
||||
elif tagId == repDeepCopyTag: (true, attachedDeepCopy)
|
||||
else: (false, attachedDestructor)
|
||||
|
||||
proc loadLoweredBodies*(c: var DecodeContext; module: FileIndex; suffix: string;
|
||||
infile: string; loadBodies = true):
|
||||
tuple[bodies: seq[tuple[name: string; body: PNode]]; hooks: seq[LogEntry]] =
|
||||
## Reconstruct the `lower` stage's `.t.nif`. Registers its embedded index as the
|
||||
## module's SECOND index/stream (`tIndex`/`tStream`) so every backend-minted
|
||||
## (`@bk`) closure-env entity resolves through NIF's own index — `createTypeStub`/
|
||||
## `loadSymStub` fall through to it on a `.s.nif` miss, `loadType`/`loadSym` seek
|
||||
## `tStream`. Returns the owned routines' transformed bodies (the `(lowered …)`
|
||||
## entries) and the lifted type-bound ops (`(rep… key sym)`); the hook ROUTINES'
|
||||
## `(sd)` defs are loaded lazily through the index when their op's sym resolves.
|
||||
result = (@[], @[])
|
||||
if not fileExists(infile): return
|
||||
var tstream = nifstreams.open(infile)
|
||||
let tindex = readEmbeddedIndex(tstream) # leaves the cursor at the content start
|
||||
let contentStart = offset(tstream.r)
|
||||
c.mods[module].tStream = tstream
|
||||
c.mods[module].tIndex = tindex
|
||||
c.mods[module].hasTIndex = true
|
||||
# Parse the WHOLE content up front: the per-body loads below lazily seek
|
||||
# `tStream` for `@bk` entities, which would otherwise clobber a live walk cursor.
|
||||
var buf = createTokenBuf(256)
|
||||
tstream.r.jumpTo contentStart
|
||||
nifcursors.parse(tstream, buf, NoLineInfo)
|
||||
var n = beginRead(buf)
|
||||
if n.kind != ParLe: return
|
||||
inc n # into (stmts -> flags dot
|
||||
inc n # -> type dot
|
||||
inc n # -> first entry or ParRi
|
||||
while n.kind == ParLe:
|
||||
if n.tagId == loweredTag:
|
||||
if not loadBodies:
|
||||
# A dependency: register its `.t.nif` (tIndex + hooks) so the consumer
|
||||
# can destroy the dep's closures, but don't reconstruct its bodies (only
|
||||
# the dep's OWN cg emits them).
|
||||
skip n
|
||||
continue
|
||||
inc n # -> StringLit name
|
||||
let name = pool.strings[n.litId]
|
||||
inc n # -> body tree
|
||||
var localSyms = initTable[string, PSym]()
|
||||
var scanCursor = n
|
||||
extractLocalSymsFromTree(c, scanCursor, suffix, localSyms)
|
||||
let body = loadNode(c, n, suffix, localSyms)
|
||||
result.bodies.add (name, body)
|
||||
skipParRi n # close (lowered ...)
|
||||
else:
|
||||
let (isHook, op) = repTagToOp(n.tagId)
|
||||
if isHook:
|
||||
inc n # -> StringLit key
|
||||
let key = pool.strings[n.litId]
|
||||
inc n # -> Symbol sym
|
||||
let sym = resolveHookSym(c, n.symId)
|
||||
inc n
|
||||
if sym != nil:
|
||||
result.hooks.add LogEntry(kind: HookEntry, op: op, module: module.int,
|
||||
key: key, sym: sym)
|
||||
skipParRi n
|
||||
else:
|
||||
skip n # a hook routine's `(sd)` def — loaded lazily via the index
|
||||
|
||||
|
||||
template withNode(c: var DecodeContext; n: var Cursor; result: PNode; kind: TNodeKind; body: untyped) =
|
||||
let info = c.infos.oldLineInfo(n.info)
|
||||
inc n
|
||||
@@ -2420,8 +2234,7 @@ proc loadNode(c: var DecodeContext; n: var Cursor; thisModule: string;
|
||||
# dereferences a nil field type. Detect the unindexed case and FILL
|
||||
# the sym from the inline def instead.
|
||||
let m = moduleId(c, sn.module)
|
||||
let indexed = c.mods[m].index.hasKey(symName) or
|
||||
(c.mods[m].hasTIndex and c.mods[m].tIndex.hasKey(symName))
|
||||
let indexed = c.mods[m].index.hasKey(symName)
|
||||
if indexed:
|
||||
sym = c.loadSymStub(name.symId, thisModule, localSyms)
|
||||
skip n # skip the entire sdef for indexed symbols
|
||||
@@ -2598,9 +2411,9 @@ proc moduleSymbolStubs*(c: var DecodeContext; module: FileIndex): seq[PSym] =
|
||||
proc toNifFilename*(conf: ConfigRef; f: FileIndex): string =
|
||||
let suffix = moduleSuffix(conf, f)
|
||||
# The `cg`/`emit` backend stages load the lowered whole-module NIF (transformed
|
||||
# bodies + lifted sigs baked in) when `-d:icWholeLowered` is on; the `lower`
|
||||
# stage and the frontend (`cmdM`) still read the semchecked `.s.nif`.
|
||||
if isDefined(conf, "icWholeLowered") and conf.cmd == cmdNifC and
|
||||
# bodies + lifted sigs baked in); the `lower` stage and the frontend (`cmdM`)
|
||||
# read the semchecked `.s.nif`.
|
||||
if conf.cmd == cmdNifC and
|
||||
(conf.icBackendStage == "cg" or conf.icBackendStage == "emit"):
|
||||
let t = toGeneratedFile(conf, AbsoluteFile(suffix), ".t.nif").string
|
||||
if fileExists(t):
|
||||
@@ -2627,11 +2440,6 @@ proc resolveSym(c: var DecodeContext; symAsStr: string; alsoConsiderPrivate: boo
|
||||
# Try the format without module suffix
|
||||
let localKey = sn.name & "." & $sn.count & "."
|
||||
offs = c.mods[module].index.getOrDefault(localKey)
|
||||
if offs.offset == 0 and c.mods[module].hasTIndex:
|
||||
# A `lower`-stage entity (an `@bk` hook routine, OR a module-homed derived
|
||||
# closure-env field added only in the backend) lives in the module's
|
||||
# `.t.nif`, not its `.s.nif`: resolve it through the second (tIndex) index.
|
||||
offs = c.mods[module].tIndex.getOrDefault(symAsStr)
|
||||
if offs.offset == 0:
|
||||
return nil
|
||||
if not alsoConsiderPrivate and offs.vis == Hidden:
|
||||
|
||||
@@ -358,52 +358,6 @@ proc readCnifHeads*(f: string): CnifHeads =
|
||||
endRead(c)
|
||||
result.valid = sawMeta and version == CnifVersion
|
||||
|
||||
proc writeLoweredArtifact*(outfile: string; entries: openArray[string]) =
|
||||
## The `.t.nif` "lowered" artifact: one `(lowered "<nifname>" <body>)` per
|
||||
## routine the module OWNS, written by the per-module `lower` backend stage
|
||||
## for the `cg` stage to read instead of re-deriving the transformed body
|
||||
## (re-derivation in each parallel `cg` process is what makes a closure
|
||||
## `:env`'s identity diverge across modules — see transf.transformBody).
|
||||
##
|
||||
## SKELETON: every body is the empty-marker `.` ("transformed body == sem
|
||||
## body"), so `cg` falls back to its own `transformBody` and output stays
|
||||
## byte-identical. The real transformed body fills this slot in a later step;
|
||||
## the `.` then means "unchanged by lowering" (the dedup Araq sketched).
|
||||
var b = nifbuilder.open(outfile)
|
||||
b.withTree "stmts":
|
||||
for name in entries:
|
||||
b.withTree "lowered":
|
||||
b.addStrLit name
|
||||
b.addEmpty()
|
||||
b.close()
|
||||
|
||||
proc readLoweredArtifact*(f: string): seq[string] =
|
||||
## The routine NIF names recorded in a `.t.nif`. (Bodies are not returned
|
||||
## yet: the skeleton records only empty-markers; reading proves the artifact
|
||||
## round-trips and that the `cg` rule depends on it.)
|
||||
result = @[]
|
||||
if not fileExists(f): return
|
||||
var pool = newPool()
|
||||
var tags = newTagPool()
|
||||
let stmtsTag = tags.registerTag("stmts")
|
||||
let loweredTag = tags.registerTag("lowered")
|
||||
var buf = parseFromFile(f, 1000, pool, tags)
|
||||
var c = beginRead(buf)
|
||||
if c.kind != TagLit or c.cursorTagId != stmtsTag:
|
||||
endRead(c)
|
||||
return
|
||||
c.loopInto:
|
||||
if c.kind == TagLit and c.cursorTagId == loweredTag:
|
||||
c.loopInto:
|
||||
if c.kind == StrLit:
|
||||
result.add strVal(c)
|
||||
inc c
|
||||
else:
|
||||
skip c
|
||||
else:
|
||||
skip c
|
||||
endRead(c)
|
||||
|
||||
type
|
||||
CnifLiveness* = object
|
||||
defs*: int ## proc definitions emitted across all modules
|
||||
|
||||
@@ -942,17 +942,13 @@ proc generateBackendBuildFile(c: DepContext; forwardedArgs: seq[string]): string
|
||||
var cnifFiles = newSeq[string](c.nodes.len)
|
||||
var cFiles = newSeq[string](c.nodes.len)
|
||||
var tFiles = newSeq[string](c.nodes.len)
|
||||
# Whole-module lowering writes a PROPER module NIF the cg/emit stages load via
|
||||
# The `lower` stage writes a PROPER module NIF the cg/emit stages load via
|
||||
# `toNifFilename` (a `.s.nif` sibling), so its `.t.nif` lives at the suffix base
|
||||
# (mirroring `semmedFile`), not next to the throwaway `.c`. The side-car path
|
||||
# keeps the `.c`-relative name (loaded directly by `registerLoweredModule`).
|
||||
let wholeMode = isDefined(c.config, "icWholeLowered")
|
||||
# (mirroring `semmedFile`), not next to the throwaway `.c`.
|
||||
for i, node in c.nodes:
|
||||
cFiles[i] = backendCFile(c, node)
|
||||
cnifFiles[i] = cFiles[i] & ".nif"
|
||||
tFiles[i] =
|
||||
if wholeMode: nimcache / node.files[0].modname & ".t.nif"
|
||||
else: cFiles[i] & ".t.nif"
|
||||
tFiles[i] = nimcache / node.files[0].modname & ".t.nif"
|
||||
|
||||
var b = nifbuilder.open(result)
|
||||
defer: b.close()
|
||||
@@ -1019,13 +1015,9 @@ proc generateBackendBuildFile(c: DepContext; forwardedArgs: seq[string]): string
|
||||
inputStr mainNif
|
||||
for n2 in c.nodes:
|
||||
inputStr c.semmedFile(n2.files[0])
|
||||
# Whole-module mode loads dependencies FROM their `.t.nif` (toNifFilename), so
|
||||
# every module's lowered NIF must precede this cg rule; the side-car only needs
|
||||
# this module's own `.t.nif`.
|
||||
if wholeMode:
|
||||
for j in 0 ..< c.nodes.len: inputStr tFiles[j]
|
||||
else:
|
||||
inputStr tFiles[i]
|
||||
# cg loads dependencies FROM their `.t.nif` (toNifFilename), so every module's
|
||||
# lowered NIF must precede this cg rule.
|
||||
for j in 0 ..< c.nodes.len: inputStr tFiles[j]
|
||||
if node.id == 0:
|
||||
for j in 0 ..< c.nodes.len:
|
||||
if c.nodes[j].id != 0:
|
||||
@@ -1053,10 +1045,9 @@ proc generateBackendBuildFile(c: DepContext; forwardedArgs: seq[string]): string
|
||||
inputStr mainNif
|
||||
inputStr cnifFiles[i]
|
||||
inputStr mergeFile
|
||||
# emit loads modules like cg (for getCFile/type resolution); under whole mode
|
||||
# those come from the `.t.nif`s, so they must precede this rule.
|
||||
if wholeMode:
|
||||
for j in 0 ..< c.nodes.len: inputStr tFiles[j]
|
||||
# emit loads modules like cg (for getCFile/type resolution); those come from
|
||||
# the `.t.nif`s, so they must precede this rule.
|
||||
for j in 0 ..< c.nodes.len: inputStr tFiles[j]
|
||||
outputStr cFiles[i]
|
||||
b.endTree()
|
||||
|
||||
|
||||
@@ -245,7 +245,7 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode
|
||||
let canon = c.graph.canonTypes.getOrDefault(h)
|
||||
if canon != nil:
|
||||
op = getAttachedOp(c.graph, canon, kind)
|
||||
if (op == nil or op.ast.isGenericRoutine) and icLoweredBodies(c.graph.config):
|
||||
if op == nil or op.ast.isGenericRoutine:
|
||||
# IC: injectDestructorCalls is demand-driven and runs HERE (cg), not in the
|
||||
# `lower` stage, so a structural, env-agnostic op the lower stage never had
|
||||
# reason to serialize — most often a closure PROC type's `=destroy`/`=sink`
|
||||
|
||||
@@ -27,7 +27,6 @@ import ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
||||
cnif
|
||||
from cgmeth import generateIfMethodDispatchers
|
||||
from transf import transformBody
|
||||
from lambdalifting import getEnvParam, addHiddenParam, paramName
|
||||
import ic / replayer
|
||||
|
||||
proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex;
|
||||
@@ -324,110 +323,17 @@ proc findTargetModule(g: ModuleGraph; modules: seq[PrecompiledModule];
|
||||
cachedModuleSuffix(g.config, FileIndex precompSys.module.position) == suffix:
|
||||
return precompSys
|
||||
|
||||
proc findHiddenEnvParam(n: PNode; owner: PSym): PSym =
|
||||
## Locate the hidden env param (`:envP`) that belongs to `owner` in its loaded
|
||||
## transformed body, so it can be re-welded into `owner`'s signature. Matching
|
||||
## by `owner` is essential: a body can ALSO reference a callee's `:envP` (a
|
||||
## closure call passes the callee env), so the first `:envP` in DFS order is
|
||||
## not necessarily this proc's own.
|
||||
if n == nil: return nil
|
||||
if n.kind == nkSym:
|
||||
if n.sym != nil and n.sym.kind == skParam and n.sym.name.s == paramName and
|
||||
n.sym.owner == owner:
|
||||
return n.sym
|
||||
else:
|
||||
for i in 0 ..< n.safeLen:
|
||||
let r = findHiddenEnvParam(n[i], owner)
|
||||
if r != nil: return r
|
||||
return nil
|
||||
|
||||
proc registerLoweredModule(g: ModuleGraph; m: PSym; applyBodies: bool) =
|
||||
## Load module `m`'s `<m>.t.nif` and register its `lower`-stage output: its
|
||||
## embedded index (so its closure-env `@bk` entities resolve) and its lifted
|
||||
## type-bound ops (so any cg that DESTROYS one of `m`'s closures finds the env
|
||||
## `=destroy` via getAttachedOp). For the cg TARGET (`applyBodies`), also set
|
||||
## each owned routine's `transformedBody` so cg's `transformBody` short-circuits
|
||||
## (transf.nim:1383) instead of re-deriving. `injectDestructorCalls` stays in cg.
|
||||
let modPos = m.position
|
||||
let bmod = BModuleList(g.backend).mods[modPos]
|
||||
if bmod == nil: return
|
||||
let artifact = getCFile(bmod).string & ".t.nif"
|
||||
if not fileExists(artifact): return
|
||||
let suffix = cachedModuleSuffix(g.config, FileIndex modPos)
|
||||
let (bodies, hooks) = loadLoweredBodies(ast.program, FileIndex modPos, suffix,
|
||||
artifact, loadBodies = applyBodies)
|
||||
registerLoadedHooks(g, hooks)
|
||||
if not applyBodies: return
|
||||
var byName = initTable[string, PSym]()
|
||||
for s in moduleSymbolStubs(ast.program, FileIndex modPos):
|
||||
# Owned routines AND nested closure routines (the `:anonymous` procs the
|
||||
# lower stage emits as their own entries) — both are index-resolvable syms
|
||||
# of this module whose transformed body the lower stage authored.
|
||||
if s.kind in routineKinds and s.itemId.module == modPos:
|
||||
byName[globalName(s, g.config)] = s
|
||||
for (name, body) in bodies:
|
||||
let s = byName.getOrDefault(name)
|
||||
# `.s.nif` wins: only fill from `.t.nif` if sem did not already transform it.
|
||||
if s != nil and body != nil and s.transformedBody == nil:
|
||||
s.transformedBody = body
|
||||
# Lambda-lift in the lower stage gave this proc a hidden `:envP` env param
|
||||
# (a captured-var closure env), but cg loaded the PRE-lift signature from
|
||||
# `.s.nif`. The transformed body references that `@bk` `:envP`; re-weld it
|
||||
# into the proc's params so genProc assigns it a loc (else "param not
|
||||
# init"). `transformBody` short-circuits on the cached body, skipping the
|
||||
# lift that normally adds it. This applies to BOTH a true `ccClosure` proc
|
||||
# (env arrives via the closure ABI `ClE_0`, needs `tfCapturesEnv`) and a
|
||||
# plain nested `nimcall` proc that merely captures (env is a regular last
|
||||
# param). Match the env param by owner — a body can also reference a
|
||||
# callee's `:envP`.
|
||||
if s.typ != nil and getEnvParam(s) == nil:
|
||||
let ep = findHiddenEnvParam(body, s)
|
||||
if ep != nil:
|
||||
# From-source, `ast[paramsPos]` and `typ.n` are the SAME node, but a
|
||||
# NIF-loaded routine has two distinct param nodes. genProc reads
|
||||
# `typ.n`, so unify them first — else addHiddenParam appends to
|
||||
# `ast[paramsPos]` and the env param never reaches genProc's loc setup.
|
||||
if s.typ.n != nil:
|
||||
s.ast[paramsPos] = s.typ.n
|
||||
addHiddenParam(s, ep)
|
||||
# The lower stage's lambda-lift converts EVERY captured nested proc to a
|
||||
# closure (collectNestedClosureBodies only emits `ccClosure` entries),
|
||||
# and the serialized call sites use the closure ABI. cg loaded the
|
||||
# pre-lift signature, which for a proc only ever CALLED (never used as a
|
||||
# value) is still `nimcall`. Re-apply the lift's `ccClosure` +
|
||||
# `tfCapturesEnv` so closureSetup maps the env param to `ClE_0` and the
|
||||
# calls match.
|
||||
s.typ.callConv = ccClosure
|
||||
incl(s.typ, {tfCapturesEnv})
|
||||
|
||||
proc applyLoweredBodies(g: ModuleGraph; modules: seq[PrecompiledModule];
|
||||
precompSys: PrecompiledModule; target: PrecompiledModule) =
|
||||
## Register every loaded module's `.t.nif` (env entities + lifted hooks),
|
||||
## applying transformed bodies only for the cg target.
|
||||
if not icLoweredBodies(g.config): return # Stage 0 (lazy): nothing to apply
|
||||
# Whole-module mode: the cg stage already loaded each module FROM its `.t.nif`
|
||||
# (toNifFilename), so transformed bodies arrive via loadSymFromCursor and lifted
|
||||
# hooks via moduleFromNifFile's registerLoadedHooks — no side-car to apply.
|
||||
if isDefined(g.config, "icWholeLowered"): return
|
||||
if precompSys.module != nil:
|
||||
registerLoweredModule(g, precompSys.module, applyBodies = false)
|
||||
for m in modules:
|
||||
if m.module != nil:
|
||||
registerLoweredModule(g, m.module,
|
||||
applyBodies = (m.module.position == target.module.position))
|
||||
|
||||
proc collectNestedClosureBodies(g: ModuleGraph; idgen: IdGenerator; n: PNode;
|
||||
owner: PSym; seen: var IntSet;
|
||||
entries: var seq[tuple[name: string; body: PNode]]) =
|
||||
proc setNestedClosureBodies(g: ModuleGraph; idgen: IdGenerator; n: PNode;
|
||||
owner: PSym; seen: var IntSet) =
|
||||
## A closure routine nested in `owner` (the `:anonymous` proc lambda-lifting
|
||||
## minted, plus any deeper nesting) gets its captured-var→env rewrite produced
|
||||
## as part of the OWNER's `transformBody`, but only the owner's body is emitted
|
||||
## as a `(lowered)` entry. The nested proc itself IS index-resolvable (it has a
|
||||
## `.s.nif` sdef from sem, with its PRE-lift body), so cg loads that and
|
||||
## re-derives — and the capture mapping is gone (it accesses `x` directly
|
||||
## instead of `ClE_0->x0`). Walk the transformed body and emit each nested
|
||||
## closure routine's transformed body as its OWN `(lowered)` entry so
|
||||
## applyLoweredBodies installs it and cg reuses it verbatim.
|
||||
## as part of the OWNER's `transformBody`. The nested proc is a module-indexed
|
||||
## sym whose `.s.nif` sdef carries its PRE-lift body, so without help the whole
|
||||
## module re-serializer would write that pre-lift body and cg would lose the
|
||||
## capture mapping (it accesses `x` directly instead of `ClE_0->x0`). Walk the
|
||||
## owner's transformed body and cache each nested closure's transformed body on
|
||||
## its sym so `writeSymDef` serializes the lifted body into the routine's
|
||||
## 2-way-body slot.
|
||||
if n == nil: return
|
||||
if n.kind == nkSym:
|
||||
let s = n.sym
|
||||
@@ -437,11 +343,10 @@ proc collectNestedClosureBodies(g: ModuleGraph; idgen: IdGenerator; n: PNode;
|
||||
s.typ != nil and s.typ.callConv == ccClosure:
|
||||
if s.transformedBody == nil:
|
||||
s.transformedBody = transformBody(g, idgen, s, {})
|
||||
entries.add (globalName(s, g.config), s.transformedBody)
|
||||
collectNestedClosureBodies(g, idgen, s.transformedBody, s, seen, entries)
|
||||
setNestedClosureBodies(g, idgen, s.transformedBody, s, seen)
|
||||
else:
|
||||
for i in 0 ..< n.safeLen:
|
||||
collectNestedClosureBodies(g, idgen, n[i], owner, seen, entries)
|
||||
setNestedClosureBodies(g, idgen, n[i], owner, seen)
|
||||
|
||||
proc reownFromTwin(n: PNode; twin, s: PSym) =
|
||||
## Re-own to `s` every entity the frontend attributed to `s`'s forward-decl
|
||||
@@ -499,102 +404,72 @@ proc generateLowerStage(g: ModuleGraph; mainFileIdx: FileIndex) =
|
||||
rawMessage(g.config, errGenerated,
|
||||
"per-module lowering: no backend module for suffix: " & g.config.icBackendModule)
|
||||
return
|
||||
let artifact = getCFile(tb).string & ".t.nif"
|
||||
if icLoweredBodies(g.config):
|
||||
# STAGE 1 (DEFAULT; `-d:icNoLowerBodies` opts out): transform every owned routine
|
||||
# ONCE in this single process's id space and serialize the results, so `cg`
|
||||
# reads them instead of re-deriving (the single-writer-per-owner that keeps
|
||||
# closure-`:env` identity stable). `transformBody` with flags {} mirrors the
|
||||
# cg call (cgen.nim:1409); we keep only its return value (it clears
|
||||
# `transformedBody` for non-cached procs). `injectDestructorCalls` is NOT run
|
||||
# — it stays in `cg` on the loaded body.
|
||||
# Whole-module mode (`-d:icWholeLowered`): re-serialize the ENTIRE module as a
|
||||
# proper indexed NIF (`writeLoweredModule`) with transformed bodies baked into
|
||||
# the routine sdefs, instead of the per-routine `(lowered)` side-car. cg loads
|
||||
# it through the normal module loader, so nested procs (incl. async state
|
||||
# machines) arrive as real defs with their lifted bodies — no re-weld, no
|
||||
# `(lowered)` entries. `entries` stays unused; we only need `transformedBody`
|
||||
# set on each routine sym so `writeSymDef` serializes it.
|
||||
let wholeMode = isDefined(g.config, "icWholeLowered")
|
||||
var entries: seq[tuple[name: string; body: PNode]] = @[]
|
||||
# `transformBody`/lambda-lifting LIFTS the closure env's type-bound ops
|
||||
# (`=destroy` etc.) into `g.opsLog`; snapshot its length so we can serialize
|
||||
# exactly the ops THIS stage created (not those loaded from `.s.nif`).
|
||||
let opsLogStart = g.opsLog.len
|
||||
for s in moduleSymbolStubs(ast.program, FileIndex modPos):
|
||||
if ownsRuntimeRoutine(s, modPos):
|
||||
# `.s.nif` wins: a routine already transformed during sem (CT eval /
|
||||
# macro / VM transform) carries its lowered body in the `.s.nif` slot —
|
||||
# don't re-transform it here, just leave its `.t.nif` entry empty.
|
||||
if s.transformedBody != nil: continue
|
||||
# A routine serialized as a forward-decl + impl pair (the writeSymDef
|
||||
# "separate forward declaration and implementation" design) loads as TWO
|
||||
# syms; the impl `s` we transform here can carry body entities (`result`,
|
||||
# locals, nested routines) owned by its fwd-decl TWIN, not by `s`.
|
||||
# lambda-lifting compares owners by reference → `illegalCapture` rejects a
|
||||
# twin-owned `result` and the lifting pass can't find twin-owned locals in
|
||||
# `s`'s env. Pervasive on chronos `{.async.}` methods. Re-own them to `s`,
|
||||
# matching the single-sym non-IC case. Backend-only (the lowered body is a
|
||||
# `.t.nif` artifact), so frontend effect/exception inference is untouched.
|
||||
if s.ast != nil and s.ast.len > resultPos and
|
||||
s.ast[resultPos].kind == nkSym and s.ast[resultPos].sym.owner != s:
|
||||
reownFromTwin(s.ast, s.ast[resultPos].sym.owner, s)
|
||||
let tbody = transformBody(g, tb.idgen, s, {})
|
||||
if wholeMode:
|
||||
# Retain the transformed body on the sym so `writeSymDef` serializes it
|
||||
# in the routine's `(sd)` 2-way-body slot.
|
||||
s.transformedBody = tbody
|
||||
else:
|
||||
entries.add (globalName(s, g.config), tbody)
|
||||
# Set `transformedBody` on nested ccClosure routines too (so a
|
||||
# module-indexed nested closure gets its lifted body); in side-car mode
|
||||
# this also appends their `(lowered)` entries.
|
||||
var seenNested = initIntSet()
|
||||
collectNestedClosureBodies(g, tb.idgen, tbody, s, seenNested, entries)
|
||||
# Collect the hooks this stage lifted, and transform each hook ROUTINE's body
|
||||
# too (it is itself lowered into NIFC). The hooks' `(sd)` + transformed body go
|
||||
# into the `.t.nif`; `cg` re-attaches them so `injectDestructorCalls` resolves
|
||||
# the loaded env's `=destroy`. Iterate to a fixpoint: a hook body can lift
|
||||
# further hooks (a field's `=destroy`).
|
||||
var hooks: seq[LogEntry] = @[]
|
||||
var i = opsLogStart
|
||||
while i < g.opsLog.len:
|
||||
let e = g.opsLog[i]
|
||||
if e.kind == HookEntry and e.sym != nil and e.sym.kind in routineKinds and
|
||||
e.sym.transformedBody == nil:
|
||||
hooks.add e
|
||||
# Transform the hook routine's body and cache it on the sym so
|
||||
# `writeSymDef` serializes it in the hook's `(sd)` transformed-body slot
|
||||
# (`transformBody {}` returns the body but does not cache it).
|
||||
e.sym.transformedBody = transformBody(g, tb.idgen, e.sym, {})
|
||||
inc i
|
||||
if wholeMode:
|
||||
# Re-serialize the whole module to its suffix-based `.t.nif` (the path
|
||||
# `toNifFilename` resolves for the cg/emit stages). `writeLoweredModule`
|
||||
# seals routines itself.
|
||||
let suffix = cachedModuleSuffix(g.config, FileIndex modPos)
|
||||
let wholeArtifact = toGeneratedFile(g.config, AbsoluteFile(suffix), ".t.nif").string
|
||||
writeLoweredModule(ast.program, g.config, target, hooks, wholeArtifact)
|
||||
if isDefined(g.config, "icDceCheck"):
|
||||
stderr.writeLine "[icLowerWhole] " & extractFilename(wholeArtifact) & " " &
|
||||
$hooks.len & " hooks"
|
||||
else:
|
||||
# Seal the index-loaded entities so their references in the bodies serialize
|
||||
# as SymUses (resolved via the module index in cg), not duplicate defs.
|
||||
sealLoadedBackendEntities(ast.program)
|
||||
serializeLoweredBodies(g.config, modPos.int32, entries, hooks, artifact)
|
||||
if isDefined(g.config, "icDceCheck"):
|
||||
stderr.writeLine "[icLower] " & extractFilename(artifact) & " " &
|
||||
$entries.len & " routines transformed, " & $hooks.len & " hooks"
|
||||
else:
|
||||
# DEFAULT (Stage 0, byte-neutral): record one empty-marker per owned routine.
|
||||
# `cg` derives the transformed body itself, so output is unchanged; this only
|
||||
# exercises the artifact + scheduling the transform-move builds on.
|
||||
var names: seq[string] = @[]
|
||||
for s in moduleSymbolStubs(ast.program, FileIndex modPos):
|
||||
if ownsRuntimeRoutine(s, modPos):
|
||||
names.add globalName(s, g.config)
|
||||
writeLoweredArtifact(artifact, names)
|
||||
# Transform every owned routine ONCE in this single process's id space and
|
||||
# re-serialize the ENTIRE module as a proper indexed NIF (`writeLoweredModule`)
|
||||
# with the transformed bodies baked into the routine `(sd)` entries. `cg` loads
|
||||
# it through the normal module loader, so nested procs (incl. async state
|
||||
# machines) arrive as real defs with their lifted bodies — no re-derivation.
|
||||
# This single-writer-per-owner is what keeps closure-`:env` identity stable
|
||||
# across the parallel `cg` processes (re-derivation per process was the root of
|
||||
# the `:env` identity drift). `transformBody` with flags {} mirrors the cg call
|
||||
# (cgen.nim); `injectDestructorCalls` is NOT run here — it stays in `cg` on the
|
||||
# loaded body.
|
||||
#
|
||||
# `transformBody`/lambda-lifting LIFTS the closure env's type-bound ops
|
||||
# (`=destroy` etc.) into `g.opsLog`; snapshot its length so we serialize exactly
|
||||
# the ops THIS stage created (not those loaded from `.s.nif`).
|
||||
let opsLogStart = g.opsLog.len
|
||||
for s in moduleSymbolStubs(ast.program, FileIndex modPos):
|
||||
if ownsRuntimeRoutine(s, modPos):
|
||||
# `.s.nif` wins: a routine already transformed during sem (CT eval / macro /
|
||||
# VM transform) carries its lowered body in the `.s.nif` slot — don't
|
||||
# re-transform it here.
|
||||
if s.transformedBody != nil: continue
|
||||
# A routine serialized as a forward-decl + impl pair (writeSymDef's
|
||||
# "separate forward declaration and implementation") loads as TWO syms; the
|
||||
# impl `s` we transform here can carry body entities (`result`, locals,
|
||||
# nested routines) owned by its fwd-decl TWIN, not by `s`. lambda-lifting
|
||||
# compares owners by reference → `illegalCapture` rejects a twin-owned
|
||||
# `result` and the lifting pass can't find twin-owned locals in `s`'s env.
|
||||
# Pervasive on chronos `{.async.}` methods. Re-own them to `s`, matching the
|
||||
# single-sym non-IC case. Backend-only, so frontend effect/exception
|
||||
# inference is untouched.
|
||||
if s.ast != nil and s.ast.len > resultPos and
|
||||
s.ast[resultPos].kind == nkSym and s.ast[resultPos].sym.owner != s:
|
||||
reownFromTwin(s.ast, s.ast[resultPos].sym.owner, s)
|
||||
# Retain the transformed body on the sym so `writeSymDef` serializes it in
|
||||
# the routine's `(sd)` 2-way-body slot.
|
||||
s.transformedBody = transformBody(g, tb.idgen, s, {})
|
||||
# Cache the lifted body on nested ccClosure routines too, so a module-indexed
|
||||
# nested closure serializes its lifted (capture-rewritten) body.
|
||||
var seenNested = initIntSet()
|
||||
setNestedClosureBodies(g, tb.idgen, s.transformedBody, s, seenNested)
|
||||
# Collect the hooks this stage lifted, and transform each hook ROUTINE's body
|
||||
# too (it is itself lowered into NIFC). The hooks' `(sd)` + transformed body go
|
||||
# into the `.t.nif`; `cg` re-attaches them so `injectDestructorCalls` resolves
|
||||
# the loaded env's `=destroy`. Iterate to a fixpoint: a hook body can lift
|
||||
# further hooks (a field's `=destroy`).
|
||||
var hooks: seq[LogEntry] = @[]
|
||||
var i = opsLogStart
|
||||
while i < g.opsLog.len:
|
||||
let e = g.opsLog[i]
|
||||
if e.kind == HookEntry and e.sym != nil and e.sym.kind in routineKinds and
|
||||
e.sym.transformedBody == nil:
|
||||
hooks.add e
|
||||
# Transform the hook routine's body and cache it on the sym so `writeSymDef`
|
||||
# serializes it in the hook's `(sd)` transformed-body slot (`transformBody
|
||||
# {}` returns the body but does not cache it).
|
||||
e.sym.transformedBody = transformBody(g, tb.idgen, e.sym, {})
|
||||
inc i
|
||||
# Re-serialize the whole module to its suffix-based `.t.nif` (the path
|
||||
# `toNifFilename` resolves for the cg/emit stages). `writeLoweredModule` seals
|
||||
# routines itself.
|
||||
let suffix = cachedModuleSuffix(g.config, FileIndex modPos)
|
||||
let wholeArtifact = toGeneratedFile(g.config, AbsoluteFile(suffix), ".t.nif").string
|
||||
writeLoweredModule(ast.program, g.config, target, hooks, wholeArtifact)
|
||||
if isDefined(g.config, "icDceCheck"):
|
||||
stderr.writeLine "[icLower] " & extractFilename(wholeArtifact) & " " &
|
||||
$hooks.len & " hooks"
|
||||
|
||||
proc generateCgStage(g: ModuleGraph; mainFileIdx: FileIndex) =
|
||||
## Per-module backend codegen (`--icBackendStage:cg --icBackendModule:<suffix>`):
|
||||
@@ -638,7 +513,10 @@ proc generateCgStage(g: ModuleGraph; mainFileIdx: FileIndex) =
|
||||
"per-module codegen: module not found for suffix: " & g.config.icBackendModule)
|
||||
return
|
||||
|
||||
applyLoweredBodies(g, modules, precompSys, target)
|
||||
# The `lower` stage already wrote each module's transformed bodies + lifted
|
||||
# hooks into its `.t.nif`, which the loaders above read directly (toNifFilename
|
||||
# resolves the `.t.nif`); transformed bodies arrive via loadSymFromCursor and
|
||||
# lifted hooks via moduleFromNifFile's registerLoadedHooks. Nothing to apply.
|
||||
generateCodeForModule(g, target)
|
||||
let bl = BModuleList(g.backend)
|
||||
# The main module also owns the whole-program method dispatchers + NimMain.
|
||||
@@ -780,8 +658,8 @@ proc generateLinkStage(g: ModuleGraph; mainFileIdx: FileIndex) =
|
||||
# `strutils.escape`) — so its body must be at link or that reference is
|
||||
# undefined. Link every emitted `.c` the merge decision says OWNS a LIVE symbol;
|
||||
# a node that owns nothing live (a Windows-only winsock node on Linux) is
|
||||
# correctly skipped. Whole-module only: the side-car default is unchanged.
|
||||
if isDefined(g.config, "icWholeLowered"):
|
||||
# correctly skipped.
|
||||
block:
|
||||
let nimcache = getNimcacheDir(g.config).string
|
||||
let decision = readMergeDecision(nimcache / MergeDecisionFile)
|
||||
if not decision.broken:
|
||||
|
||||
@@ -29,7 +29,7 @@ const
|
||||
|
||||
nimEnableCovariance* = defined(nimEnableCovariance)
|
||||
|
||||
icFormatVersion* = "20"
|
||||
icFormatVersion* = "21"
|
||||
## 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`
|
||||
@@ -786,13 +786,6 @@ template quitOrRaise*(conf: ConfigRef, msg = "") =
|
||||
else:
|
||||
quit(msg) # quits with QuitFailure
|
||||
|
||||
proc icLoweredBodies*(conf: ConfigRef): bool {.inline.} =
|
||||
## Whether the `nim ic` backend uses the EAGER per-module `lower` stage
|
||||
## (transformBody serialized to `.t.nif`, cg reuses it) instead of the lazy
|
||||
## Stage-0 path (cg re-derives every transformed body). This is now the
|
||||
## DEFAULT; `-d:icNoLowerBodies` opts back into the lazy path for A/B testing.
|
||||
not isDefined(conf, "icNoLowerBodies")
|
||||
|
||||
proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools}
|
||||
proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
|
||||
proc usesSso*(conf: ConfigRef): bool {.inline.} = conf.selectedStrings == stringSso
|
||||
|
||||
Reference in New Issue
Block a user