mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-14 15:43:45 +00:00
more unnecessary workarounds removed
This commit is contained in:
@@ -1980,14 +1980,6 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
||||
loadInto(p, le, ri, a)
|
||||
|
||||
proc genStmts(p: BProc, t: PNode) =
|
||||
if p.config.cmd == cmdNifC and t.kind == nkSym and
|
||||
t.sym.kind in {skProc, skFunc, skConverter, skIterator} and
|
||||
not icDceLive(p.module, t.sym):
|
||||
# Under IC a module's top-level routine definitions reappear as bare
|
||||
# symbol statements in the loaded statement list and were generated
|
||||
# eagerly. Skip the ones dce.nim proved unreachable; anything a live
|
||||
# body references is still generated on demand via `genProc`.
|
||||
return
|
||||
var a: TLoc = default(TLoc)
|
||||
|
||||
let isPush = p.config.hasHint(hintExtendedContext)
|
||||
|
||||
@@ -904,16 +904,6 @@ proc initLocExprSingleUse(p: BProc, e: PNode): TLoc =
|
||||
result.flags.incl lfSingleUse
|
||||
expr(p, e, result)
|
||||
|
||||
proc icDceLive(m: BModule; sym: PSym): bool =
|
||||
## Under `nim nifc` the eagerly emitted top-level routine listing is
|
||||
## filtered through dce.nim's liveness result. Symbols generated on
|
||||
## demand (`genProc` from a use site) never consult this.
|
||||
let g = m.g.graph
|
||||
if not g.icDceEnabled or sym.itemId.isBackendMinted:
|
||||
result = true
|
||||
else:
|
||||
result = globalName(sym, m.config) in g.icLiveNames
|
||||
|
||||
include ccgcalls, "ccgstmts.nim"
|
||||
|
||||
proc initFrame(p: BProc, procname, filename: Rope): Rope =
|
||||
@@ -1719,13 +1709,6 @@ proc isActivated(prc: PSym): bool = prc.typ != nil
|
||||
|
||||
proc genProc(m: BModule, prc: PSym) =
|
||||
if sfBorrow in prc.flags or not isActivated(prc): return
|
||||
if m.config.cmd == cmdNifC and m.g.graph.icDceEnabled and
|
||||
sfImportc notin prc.flags and not icDceLive(m, prc):
|
||||
# Stage-2 readiness check: in the current single-process backend, demand
|
||||
# always wins over the liveness analysis (we generate the proc anyway).
|
||||
# But per-module codegen will have to trust the analysis, so a proc that
|
||||
# is demanded yet not marked live is an analysis bug — report it.
|
||||
m.g.graph.icDceMisses.incl globalName(prc, m.config)
|
||||
if sfForward in prc.flags:
|
||||
addForwardedProc(m, prc)
|
||||
fillProcLoc(m, prc.ast[namePos])
|
||||
@@ -2897,9 +2880,6 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
|
||||
if cl.broken: stripCnifMarks(codes[i])
|
||||
else: renderMarkedC(codes[i], cl.live, dropped)
|
||||
registerModuleCode(mods[i], cfs[i], rendered)
|
||||
g.graph.icCDefs = cl.defs
|
||||
g.graph.icCLiveDefs = cl.liveDefs
|
||||
g.graph.icCDropped = dropped
|
||||
else:
|
||||
for m in cgenModules(g):
|
||||
m.writeModule()
|
||||
|
||||
263
compiler/dce.nim
263
compiler/dce.nim
@@ -1,263 +0,0 @@
|
||||
#
|
||||
#
|
||||
# The Nim Compiler
|
||||
# (c) Copyright 2026 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Dead code analysis over per-module NIF files — a port of Nimony's
|
||||
## `hexer/dce1.nim`/`dce2.nim` ideas onto the `nifcore` API.
|
||||
##
|
||||
## Per module we collect, in a single token walk over its `.nif` file:
|
||||
## - `roots`: symbols that are alive by construction — anything referenced
|
||||
## from top-level init code (every module's init proc is always emitted),
|
||||
## plus flag-based entry points (see below)
|
||||
## - `uses`: edges `definition -> symbols referenced inside its body`
|
||||
##
|
||||
## A global mark&sweep over the union of all modules' graphs then yields the
|
||||
## set of live symbols.
|
||||
##
|
||||
## The NIF files contain *semchecked* (unlowered) AST, so uses that only
|
||||
## materialize during the backend's lowering passes are invisible to the
|
||||
## token walk. Those are covered by conservative roots instead:
|
||||
## - registered hooks and `$enum` procs (the `(rep* "key" sym)` entries):
|
||||
## `injectdestructors` and magic lowering insert calls to them at codegen
|
||||
## - `{.compilerproc.}` symbols: requested by name via `cgsym`
|
||||
## - `{.exportc.}` symbols, methods and dispatchers: external entry points
|
||||
## resp. reachable through dynamic dispatch only
|
||||
##
|
||||
## In the current single-process backend the result is consumed as a skip
|
||||
## filter for the eagerly generated top-level routine listing
|
||||
## (`ccgstmts.genStmts`); cgen's demand-driven `genProc` remains in place,
|
||||
## so an analysis miss can only cost code size, never correctness. The same
|
||||
## analysis is the building block for per-module incremental codegen later,
|
||||
## where it has to stand on its own.
|
||||
|
||||
import std / [tables, sets, os, assertions]
|
||||
from std / strutils import rfind
|
||||
import "../dist/nimony/src/lib" / nifcoreparse
|
||||
import ast, options, pathutils
|
||||
import ic / enum2nif
|
||||
|
||||
type
|
||||
DceContext = object
|
||||
pool: Pool # shared literal pool: same name <=> same SymId everywhere
|
||||
tags: TagPool # shared tag pool: tag ids fixed by the registrations below
|
||||
uses: Table[SymId, HashSet[SymId]]
|
||||
roots: HashSet[SymId]
|
||||
stmtsTag, sdefTag, implTag, replayTag, importTag, includeTag: TagId
|
||||
methodKindTag: TagId
|
||||
hookTags: HashSet[TagId]
|
||||
routineKindTags: HashSet[TagId]
|
||||
offers: HashSet[SymId] # generic routine instances defined by the modules
|
||||
broken: bool # a module failed to parse; the result must not be used
|
||||
|
||||
DceStats* = object
|
||||
instances*: int ## routine instance definitions across all modules
|
||||
uniqueInstances*: int ## distinct instantiation keys (name.disamb)
|
||||
## `instances - uniqueInstances` = definitions a merge step would drop
|
||||
|
||||
const
|
||||
NoSym = SymId(0) # pool ids start at 1
|
||||
|
||||
proc symIdAt(c: Cursor): SymId {.inline.} =
|
||||
# Every symbol in our NIFs is written with its `.disamb.modulesuffix`, so
|
||||
# the name is always longer than nifcore's 3-byte inline-string cutoff and
|
||||
# lands in the (shared) pool: pool ids are stable identities across all
|
||||
# modules' token buffers.
|
||||
assert not isInlineLit(c), "unexpectedly short NIF symbol name"
|
||||
SymId(combinedPayload(c) shr 1)
|
||||
|
||||
proc recordUse(ctx: var DceContext; sym, owner: SymId) =
|
||||
if owner == NoSym:
|
||||
ctx.roots.incl sym
|
||||
else:
|
||||
ctx.uses.mgetOrPut(owner, initHashSet[SymId]()).incl sym
|
||||
|
||||
proc walkDef(ctx: var DceContext; c: var Cursor; owner: SymId; declarative: bool)
|
||||
|
||||
proc walk(ctx: var DceContext; c: var Cursor; owner: SymId; declarative: bool) =
|
||||
## Generic walk. `owner == NoSym and not declarative` is init-code context:
|
||||
## symbol uses become roots. With an owner they become `uses` edges. In
|
||||
## declarative context (the listing after the `(implementation)` marker)
|
||||
## bare uses record nothing — only definitions found inside contribute.
|
||||
case c.kind
|
||||
of TagLit:
|
||||
if c.cursorTagId == ctx.sdefTag:
|
||||
walkDef(ctx, c, owner, declarative)
|
||||
else:
|
||||
c.loopInto:
|
||||
walk(ctx, c, owner, declarative)
|
||||
of Symbol:
|
||||
if not declarative:
|
||||
recordUse(ctx, symIdAt(c), owner)
|
||||
inc c
|
||||
else:
|
||||
skip c
|
||||
|
||||
proc walkDef(ctx: var DceContext; c: var Cursor; owner: SymId; declarative: bool) =
|
||||
# Layout (ast2nif.writeSymDef):
|
||||
# (sd SymbolDef <x|.> (symkind ...) magic flags options offset ...)
|
||||
# NB: no `return` inside `into` — it would skip the cursor rescoping.
|
||||
c.into:
|
||||
if c.kind == SymbolDef:
|
||||
let self = symIdAt(c)
|
||||
# An sdef is emitted at the symbol's *first reference*; in use
|
||||
# positions that reference counts like a plain symbol use.
|
||||
if not declarative:
|
||||
recordUse(ctx, self, owner)
|
||||
inc c
|
||||
if c.hasMore: skip c # export marker: "x" or dot
|
||||
var rooted = false
|
||||
var isRoutine = false
|
||||
if c.hasMore and c.kind == TagLit: # symbol kind tree
|
||||
if c.cursorTagId == ctx.methodKindTag:
|
||||
rooted = true # reachable via dynamic dispatch
|
||||
isRoutine = c.cursorTagId in ctx.routineKindTags
|
||||
c.loopInto:
|
||||
walk(ctx, c, self, false) # guard sym/bitsize for vars
|
||||
if c.hasMore: skip c # magic: ident or dot
|
||||
if c.hasMore: # flags: ident or dot
|
||||
if c.kind == Ident:
|
||||
let fl = parse(TSymFlag, strVal(c))
|
||||
if sfExportc in fl or sfCompilerProc in fl or sfDispatcher in fl:
|
||||
rooted = true
|
||||
if isRoutine and sfFromGeneric in fl:
|
||||
ctx.offers.incl self
|
||||
skip c
|
||||
if rooted: ctx.roots.incl self
|
||||
# rest: options, offset, position, lib, type, owner, ast, loc,
|
||||
# constraint, instantiatedFrom — all walked as the definition's body
|
||||
while c.hasMore:
|
||||
walk(ctx, c, self, false)
|
||||
else:
|
||||
# malformed sdef; consume defensively
|
||||
while c.hasMore:
|
||||
walk(ctx, c, owner, declarative)
|
||||
|
||||
proc rootHookSyms(ctx: var DceContext; c: var Cursor) =
|
||||
# (repdestroy "typekey" hookSym) and friends
|
||||
c.loopInto:
|
||||
if c.kind == Symbol:
|
||||
ctx.roots.incl symIdAt(c)
|
||||
inc c
|
||||
else:
|
||||
skip c
|
||||
|
||||
proc analyzeNifFile(ctx: var DceContext; filename: string;
|
||||
imports: var seq[string]) =
|
||||
if not fileExists(filename):
|
||||
ctx.broken = true
|
||||
return
|
||||
var buf = parseFromFile(filename, 1000, ctx.pool, ctx.tags)
|
||||
var c = beginRead(buf)
|
||||
if c.kind == TagLit and c.cursorTagId == ctx.stmtsTag:
|
||||
var declarative = false
|
||||
c.loopInto:
|
||||
case c.kind
|
||||
of TagLit:
|
||||
let tag = c.cursorTagId
|
||||
if tag == ctx.implTag:
|
||||
# marks the start of the declarative listing (routines, type
|
||||
# sections, consts); everything before it is init code
|
||||
declarative = true
|
||||
skip c
|
||||
elif tag == ctx.importTag:
|
||||
# (import . . "modsuffix") — the analysis discovers the module
|
||||
# closure itself; the backend's own module list omits modules that
|
||||
# are only reached through system or through demand-driven codegen
|
||||
c.loopInto:
|
||||
if c.kind == StrLit:
|
||||
imports.add strVal(c)
|
||||
inc c
|
||||
else:
|
||||
skip c
|
||||
elif tag == ctx.replayTag or tag == ctx.includeTag:
|
||||
skip c # compile directives and include info
|
||||
elif tag in ctx.hookTags:
|
||||
rootHookSyms(ctx, c)
|
||||
elif tag == ctx.sdefTag:
|
||||
# a definition listed at section level (globals before the marker,
|
||||
# announced hooks after it): a declaration, not a use
|
||||
walkDef(ctx, c, NoSym, true)
|
||||
else:
|
||||
walk(ctx, c, NoSym, declarative)
|
||||
of Symbol:
|
||||
inc c # bare re-listing of a written definition
|
||||
else:
|
||||
skip c # the stmts wrapper's flag/type dots
|
||||
else:
|
||||
ctx.broken = true
|
||||
endRead(c)
|
||||
|
||||
proc markLive(ctx: DceContext): HashSet[SymId] =
|
||||
result = initHashSet[SymId]()
|
||||
var work = newSeqOfCap[SymId](ctx.roots.len)
|
||||
for r in ctx.roots: work.add r
|
||||
while work.len > 0:
|
||||
let s = work.pop()
|
||||
if not result.containsOrIncl(s):
|
||||
if ctx.uses.hasKey(s):
|
||||
for dep in ctx.uses[s]:
|
||||
if dep notin result:
|
||||
work.add dep
|
||||
|
||||
proc computeLiveSymbols*(conf: ConfigRef; seedFiles: openArray[string];
|
||||
live: var HashSet[string]; stats: var DceStats;
|
||||
nifDeps: var Table[string, seq[string]]): bool =
|
||||
## Global liveness over a program's NIF modules: the seeds plus the
|
||||
## transitive closure of their `(import ...)` entries. On success fills
|
||||
## `live` with the NIF names (`name.disamb.modsuffix`) of every reachable
|
||||
## symbol and returns true. Returns false when any module could not be
|
||||
## analyzed — the caller must then treat everything as live.
|
||||
## `nifDeps` receives the import graph over NIF file paths — the full
|
||||
## closure including the modules the backend's own module list omits;
|
||||
## the artifact-reuse decision needs it for transitive invalidation.
|
||||
var ctx = DceContext(pool: newPool(), tags: newTagPool())
|
||||
ctx.stmtsTag = ctx.tags.registerTag("stmts")
|
||||
ctx.sdefTag = ctx.tags.registerTag("sd")
|
||||
ctx.implTag = ctx.tags.registerTag("implementation")
|
||||
ctx.replayTag = ctx.tags.registerTag("replay")
|
||||
ctx.importTag = ctx.tags.registerTag("import")
|
||||
ctx.includeTag = ctx.tags.registerTag("include")
|
||||
ctx.methodKindTag = ctx.tags.registerTag("method")
|
||||
for t in ["repdestroy", "repcopy", "repwasmoved", "repdup", "repsink",
|
||||
"reptrace", "repdeepcopy", "repenumtostr"]:
|
||||
ctx.hookTags.incl ctx.tags.registerTag(t)
|
||||
for t in ["proc", "func", "iterator", "converter", "method"]:
|
||||
ctx.routineKindTags.incl ctx.tags.registerTag(t)
|
||||
var queue = newSeq[string](seedFiles.len)
|
||||
for i in 0..<seedFiles.len: queue[i] = seedFiles[i]
|
||||
var seen = initHashSet[string]()
|
||||
var i = 0
|
||||
while i < queue.len:
|
||||
let f = queue[i]
|
||||
inc i
|
||||
if seen.containsOrIncl(f): continue
|
||||
var imports: seq[string] = @[]
|
||||
analyzeNifFile(ctx, f, imports)
|
||||
if ctx.broken: return false
|
||||
if conf != nil:
|
||||
var depFiles = newSeqOfCap[string](imports.len)
|
||||
for suffix in imports:
|
||||
let depFile = toGeneratedFile(conf, AbsoluteFile(suffix), ".nif").string
|
||||
depFiles.add depFile
|
||||
queue.add depFile
|
||||
nifDeps[f] = depFiles
|
||||
let liveIds = markLive(ctx)
|
||||
live = initHashSet[string](liveIds.len)
|
||||
for s in liveIds:
|
||||
live.incl ctx.pool.syms[s]
|
||||
# Instance duplication stats: with content-derived instance disambs the
|
||||
# NIF name minus the module suffix is the instantiation key, so the same
|
||||
# instantiation made by several modules counts as one unique instance.
|
||||
stats = DceStats(instances: ctx.offers.len)
|
||||
var uniq = initHashSet[string]()
|
||||
for s in ctx.offers:
|
||||
let name = ctx.pool.syms[s]
|
||||
let suffixStart = rfind(name, '.')
|
||||
uniq.incl(if suffixStart >= 0: name[0..<suffixStart] else: name)
|
||||
stats.uniqueInstances = uniq.len
|
||||
result = true
|
||||
@@ -455,7 +455,12 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of cmdUnknown, cmdNone, cmdIdeTools:
|
||||
rawMessage(conf, errGenerated, "invalid command: " & conf.command)
|
||||
|
||||
if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
|
||||
if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop} and
|
||||
not (conf.cmd == cmdNifC and conf.icBackendStage.len > 0):
|
||||
# Per-module backend stages (cg/emit/merge/link) run as many parallel child
|
||||
# processes; each would print a `[SuccessX]` summary line that misleadingly
|
||||
# reports `out: <the whole compiler>` for a step that only wrote one
|
||||
# `.c.nif`/`.c`. The driving `nim ic` (and koch) reports the real result.
|
||||
if optProfileVM in conf.globalOptions:
|
||||
echo conf.dump(conf.vmProfileData)
|
||||
genSuccessX(conf)
|
||||
|
||||
@@ -68,16 +68,10 @@ type
|
||||
enumToStringProcs*: Table[ItemId, PSym]
|
||||
loadedEnumToStringProcs: Table[string, PSym]
|
||||
emittedTypeInfo*: Table[string, FileIndex]
|
||||
icLiveNames*: HashSet[string] # NIF names of reachable symbols (dce.nim);
|
||||
# filters the top-level listing under `nim nifc`
|
||||
icDceEnabled*: bool
|
||||
icDceMisses*: HashSet[string] # demand-generated but not marked live:
|
||||
# analysis bugs that per-module codegen would hit
|
||||
instDisambs: Table[(int, int32), ItemId] # (name id, content disamb) ->
|
||||
# instance, for collision probing in
|
||||
# `setInstanceDisamb`
|
||||
icCnifFiles*: seq[string] # `.c.nif` artifacts written by this run
|
||||
icCDefs*, icCLiveDefs*, icCDropped*: int # render-time DCE stats
|
||||
pendingMethodReplays*: seq[PSym] # method registrations loaded under
|
||||
# `nim nifc`, bucketed only after every
|
||||
# module is loaded (`flushMethodReplays`)
|
||||
|
||||
@@ -23,7 +23,7 @@ when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
||||
pathutils, extccomp, msgs, modulepaths, idents, types, ast2nif, typekeys, dce,
|
||||
pathutils, extccomp, msgs, modulepaths, idents, types, ast2nif, typekeys,
|
||||
cnif
|
||||
from cgmeth import generateIfMethodDispatchers
|
||||
import ic / replayer
|
||||
@@ -92,29 +92,6 @@ proc isMetaIter(t: PType, closure: RootRef): bool =
|
||||
# descriptor for what must remain a (ptr, len) parameter expansion
|
||||
t.kind in tyMetaTypes + {tyTyped, tyUntyped, tyNone, tyVarargs, tyOpenArray}
|
||||
|
||||
proc eagerHookCandidate(sym: PSym): bool =
|
||||
## Announced hooks that can actually be code-generated: generic hook
|
||||
## announcements and meta-typed ones (`varargs[typed]` etc.) are replay
|
||||
## information for sem, not code.
|
||||
let typ = sym.typ
|
||||
if typ == nil or containsGenericType(typ): return false
|
||||
if typ.n == nil: return false
|
||||
for i in 1..<typ.n.len:
|
||||
let pt = typ.n[i].typ
|
||||
if pt == nil: return false
|
||||
if iterOverType(pt, isMetaIter, nil): return false
|
||||
# a `=dup` of an imported type returns it by value; for "lying" importc
|
||||
# typedefs like `jmp_buf` (declared as `object`, really a C array) that
|
||||
# signature does not compile. Demand-driven codegen never demands such
|
||||
# sem-bookkeeping hooks (under refc nothing dups a `C_JmpBuf`), and no
|
||||
# working artifact can call one — its prototype would be the same
|
||||
# invalid C — so they are safe to skip.
|
||||
let ret = typ.returnType
|
||||
if ret != nil:
|
||||
let r = ret.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
|
||||
if r.sym != nil and sfImportc in r.sym.flags: return false
|
||||
true
|
||||
|
||||
proc finishModule(g: ModuleGraph; bmod: BModule) =
|
||||
# Finalize the module (this adds it to modulesClosed)
|
||||
# Create an empty stmt list as the init body - genInitCode in writeModule will set it up properly
|
||||
@@ -223,20 +200,6 @@ proc generateCodeForModule(g: ModuleGraph; precomp: PrecompiledModule) =
|
||||
# a concrete, non-generic, runtime routine with a real body, owned here
|
||||
requestProcDef(bmod, s)
|
||||
|
||||
# The hooks and `$enum` procs this module announces are liveness roots:
|
||||
# a cached TU from a previous run may call them without any demand
|
||||
# arising in this run (the demanding instance body sits inside a reused
|
||||
# TU). Demand them unconditionally so a regenerated TU never *loses*
|
||||
# definitions that cached TUs link against.
|
||||
if g.icDceEnabled and not isDefined(g.config, "icNoReuse"):
|
||||
for op in precomp.logOps:
|
||||
if op.kind in {HookEntry, EnumToStrEntry} and op.sym != nil and
|
||||
eagerHookCandidate(op.sym):
|
||||
when defined(icDbg):
|
||||
stderr.writeLine "[icHook] " & $op.kind & " " & op.sym.name.s &
|
||||
" typ: " & typeToString(op.sym.typ) & " in " & precomp.module.name.s
|
||||
requestProcDef(bmod, op.sym)
|
||||
|
||||
proc loadBackendModules(g: ModuleGraph; mainFileIdx: FileIndex):
|
||||
tuple[modules: seq[PrecompiledModule], precompSys: PrecompiledModule,
|
||||
nifFiles: seq[string]] =
|
||||
@@ -386,17 +349,14 @@ proc generateCgStage(g: ModuleGraph; mainFileIdx: FileIndex) =
|
||||
rawMessage(g.config, errGenerated,
|
||||
"Cannot load NIF file for main module: " & toFullPath(g.config, mainFileIdx))
|
||||
return
|
||||
# No whole-program DCE here, exactly as for a non-main target: `icDceEnabled`
|
||||
# stays false so each module emits the routines it owns and the MERGE stage
|
||||
# recomputes the one program-wide live set across all `.c.nif`s. Running
|
||||
# `computeLiveSymbols` over all ~260 NIFs in the main `cg` cost ~900 MB for a
|
||||
# result the merge stage throws away — pure redundancy now that the funnel is
|
||||
# gone (the main module no longer emits its transitive closure's bodies).
|
||||
# No whole-program DCE here: each module emits the routines it owns and the
|
||||
# MERGE stage recomputes the one program-wide live set across all `.c.nif`s.
|
||||
# Running a whole-program liveness pass over all ~260 NIFs in the main `cg`
|
||||
# would cost ~900 MB for a result the merge stage throws away.
|
||||
target = findTargetModule(g, modules, precompSys, g.config.icBackendModule)
|
||||
else:
|
||||
# No whole-program load, hence no whole-program DCE: `icDceEnabled` stays
|
||||
# false, so `icDceLive` keeps every top-level routine and the target emits
|
||||
# its full demanded closure. The merge stage drops what is globally dead.
|
||||
# No whole-program load, hence no whole-program DCE: the target emits its
|
||||
# full demanded closure and the merge stage drops what is globally dead.
|
||||
(modules, precompSys, target) = loadDepClosure(g, g.config.icBackendModule)
|
||||
if target.module == nil:
|
||||
rawMessage(g.config, errGenerated,
|
||||
@@ -552,120 +512,6 @@ proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) =
|
||||
elif g.config.icBackendStage == "link":
|
||||
generateLinkStage(g, mainFileIdx)
|
||||
return
|
||||
elif g.config.icBackendStage.len > 0:
|
||||
else:
|
||||
rawMessage(g.config, errGenerated,
|
||||
"per-module backend stage not implemented yet: " & g.config.icBackendStage)
|
||||
return
|
||||
|
||||
# Phase timing, enabled with `-d:icTimings` on the nifc command line.
|
||||
let icTimings = isDefined(g.config, "icTimings")
|
||||
var phaseStart = epochTime()
|
||||
template phaseDone(name: string) =
|
||||
if icTimings:
|
||||
let now = epochTime()
|
||||
stderr.writeLine "[icTime] " & name & ": " &
|
||||
formatFloat(now - phaseStart, ffDecimal, 2) & "s"
|
||||
phaseStart = now
|
||||
|
||||
# Reset backend state
|
||||
resetForBackend(g)
|
||||
|
||||
var isKnownFile = false
|
||||
let systemFileIdx = registerNifSuffix(g.config, "sysma2dyk", isKnownFile)
|
||||
g.config.m.systemFileIdx = systemFileIdx
|
||||
#msgs.fileInfoIdx(g.config,
|
||||
# g.config.libpath / RelativeFile"system.nim")
|
||||
|
||||
# Load system module first - it's always needed and contains essential hooks
|
||||
var precompSys = PrecompiledModule(module: nil)
|
||||
precompSys = moduleFromNifFile(g, systemFileIdx, {LoadFullAst, AlwaysLoadInterface})
|
||||
g.systemModule = precompSys.module
|
||||
|
||||
# Load all modules in dependency order using stack traversal
|
||||
# This must happen BEFORE any code generation so that hooks are loaded into loadedOps
|
||||
var nifFiles: seq[string] = @[toNifFilename(g.config, systemFileIdx)]
|
||||
let modules = loadModuleDependencies(g, mainFileIdx, nifFiles)
|
||||
# build the method dispatch buckets now that every module is loaded
|
||||
flushMethodReplays(g)
|
||||
phaseDone "load (" & $ (modules.len + 1) & " modules)"
|
||||
if modules.len == 0:
|
||||
rawMessage(g.config, errGenerated,
|
||||
"Cannot load NIF file for main module: " & toFullPath(g.config, mainFileIdx))
|
||||
return
|
||||
|
||||
# Compute the global live set so that the top-level routine listing can be
|
||||
# filtered (see `ccgstmts.genStmts`). On analysis failure everything stays
|
||||
# alive — demand-driven `genProc` makes this a size optimization only.
|
||||
var dceStats = DceStats()
|
||||
var nifDeps = initTable[string, seq[string]]()
|
||||
if not isDefined(g.config, "icNoDce"):
|
||||
g.icDceEnabled = computeLiveSymbols(g.config, nifFiles, g.icLiveNames,
|
||||
dceStats, nifDeps)
|
||||
phaseDone "dce"
|
||||
|
||||
# Set up backend modules for all modules that need code generation
|
||||
for m in modules:
|
||||
discard setupNifBackendModule(g, m.module)
|
||||
if precompSys.module != nil:
|
||||
discard setupNifBackendModule(g, precompSys.module)
|
||||
|
||||
# System module is generated first if it exists
|
||||
if precompSys.module != nil:
|
||||
generateCodeForModule(g, precompSys)
|
||||
|
||||
# Track which modules have been processed to avoid duplicates
|
||||
var processed = initIntSet()
|
||||
if precompSys.module != nil:
|
||||
processed.incl precompSys.module.position
|
||||
|
||||
# Generate code for all modules (skip system since it's already processed)
|
||||
for m in modules:
|
||||
if not processed.containsOrIncl(m.module.position):
|
||||
generateCodeForModule(g, m)
|
||||
|
||||
emitMethodDispatchers(g)
|
||||
phaseDone "cgen"
|
||||
|
||||
# during code generation of `main.nim` we can trigger the code generation
|
||||
# of symbols in different modules so we need to finish these modules
|
||||
# here later, after the above loop!
|
||||
# Important: The main module must be finished LAST so that all other modules
|
||||
# have registered their init procs before genMainProc uses them.
|
||||
var mainModule: BModule = nil
|
||||
for m in BModuleList(g.backend).mods:
|
||||
if m != nil:
|
||||
assert m.module != nil
|
||||
if sfMainModule in m.module.flags:
|
||||
mainModule = m
|
||||
else:
|
||||
finishModule g, m
|
||||
if mainModule != nil:
|
||||
finishModule g, mainModule
|
||||
phaseDone "finish"
|
||||
|
||||
if g.icDceEnabled and isDefined(g.config, "icDceCheck"):
|
||||
var misses: seq[string] = @[]
|
||||
for n in g.icDceMisses: misses.add n
|
||||
sort misses
|
||||
for n in misses:
|
||||
stderr.writeLine "[icDce] MISS (generated on demand, not marked live): " & n
|
||||
stderr.writeLine "[icDce] live: " & $g.icLiveNames.len & " misses: " & $misses.len &
|
||||
" modules: " & $nifFiles.len
|
||||
stderr.writeLine "[icDce] instances: " & $dceStats.instances &
|
||||
" unique: " & $dceStats.uniqueInstances &
|
||||
" mergeable: " & $(dceStats.instances - dceStats.uniqueInstances)
|
||||
|
||||
# Write C files
|
||||
cgenWriteModules(g.backend, g.config)
|
||||
phaseDone "write"
|
||||
|
||||
if isDefined(g.config, "icDceCheck") and g.icCnifFiles.len > 0:
|
||||
stderr.writeLine "[icDceC] cdefs: " & $g.icCDefs & " live: " & $g.icCLiveDefs &
|
||||
" dropped: " & $g.icCDropped
|
||||
|
||||
# Run C compiler
|
||||
if g.config.cmd != cmdTcc:
|
||||
extccomp.callCCompiler(g.config)
|
||||
phaseDone "cc+link"
|
||||
if not g.config.hcrOn:
|
||||
extccomp.writeJsonBuildInstructions(g.config, g.cachedFiles)
|
||||
"the per-module NIF backend requires --icBackendStage:cg|merge|emit|link")
|
||||
|
||||
Reference in New Issue
Block a user