IC: code generation progress (#25379)

This commit is contained in:
Andreas Rumpf
2025-12-29 00:20:33 +01:00
committed by GitHub
parent 91d51923b9
commit 02893e2f4c
14 changed files with 465 additions and 303 deletions

View File

@@ -164,7 +164,7 @@ type
const
# Symbol kinds that are always local to a proc and should never have module suffix
skLocalSymKinds = {skParam, skGenericParam, skForVar, skResult, skTemp}
skLocalSymKinds = {skParam, skForVar, skResult, skTemp}
proc isLocalSym(sym: PSym): bool {.inline.} =
sym.kindImpl in skLocalSymKinds or
@@ -362,10 +362,7 @@ proc writeSymDef(w: var Writer; dest: var TokenBuf; sym: PSym) =
writeSym(w, dest, sym.ownerFieldImpl)
# Store the AST for routine symbols and constants
# Constants need their AST for astdef() to return the constant's value
if sym.kindImpl in routineKinds + {skConst}:
writeNode(w, dest, sym.astImpl, forAst = true)
else:
dest.addDotToken
writeNode(w, dest, sym.astImpl, forAst = true)
writeLoc w, dest, sym.locImpl
writeNode(w, dest, sym.constraintImpl)
writeSym(w, dest, sym.instantiatedFromImpl)
@@ -438,14 +435,20 @@ proc addLocalSym(w: var Writer; n: PNode) =
w.locals.incl(n.sym.itemId)
proc addLocalSyms(w: var Writer; n: PNode) =
if n.kind in {nkIdentDefs, nkVarTuple}:
case n.kind
of nkIdentDefs, nkVarTuple:
# nkIdentDefs: [ident1, ident2, ..., type, default]
# All children except the last two are identifiers
for i in 0 ..< max(0, n.len - 2):
addLocalSyms(w, n[i])
elif n.kind == nkSym:
of nkPostfix:
addLocalSyms(w, n[1])
of nkPragmaExpr:
addLocalSyms(w, n[0])
of nkSym:
addLocalSym(w, n)
else:
discard
proc trInclude(w: var Writer; n: PNode) =
w.deps.addParLe pool.tags.getOrIncl(toNifTag(n.kind)), trLineInfo(w, n.info)
@@ -456,6 +459,9 @@ proc trInclude(w: var Writer; n: PNode) =
w.deps.addStrLit child.strVal # raw string literal, no wrapper needed
w.deps.addParRi
proc moduleSuffix(conf: ConfigRef; f: FileIndex): string =
cachedModuleSuffix(conf, f)
proc trImport(w: var Writer; n: PNode) =
for child in n:
if child.kind == nkSym:
@@ -464,7 +470,7 @@ proc trImport(w: var Writer; n: PNode) =
w.deps.addDotToken # type
let s = child.sym
assert s.kindImpl == skModule
let fp = toFullPath(w.infos.config, s.positionImpl.FileIndex)
let fp = moduleSuffix(w.infos.config, s.positionImpl.FileIndex)
w.deps.addStrLit fp # raw string literal, no wrapper needed
w.deps.addParRi
@@ -512,14 +518,14 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
of nkNilLit:
w.withNode dest, n:
discard
of nkLetSection, nkVarSection, nkConstSection, nkGenericParams:
of nkLetSection, nkVarSection, nkConstSection:
# Track local variables declared in let/var sections
w.withNode dest, n:
for child in n:
addLocalSyms w, child
# Process the child node
writeNode(w, dest, child, forAst)
of nkForStmt, nkTypeDef:
of nkForStmt:
# Track for loop variable (first child is the loop variable)
w.withNode dest, n:
if n.len > 0:
@@ -535,7 +541,7 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
addLocalSyms(w, n[i])
writeNode(w, dest, n[i], forAst)
dec w.inProc
of nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef, nkMacroDef:
of nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef, nkMacroDef, nkTemplateDef:
# For top-level named routines (not forAst), just write the symbol.
# The full AST will be stored in the symbol's sdef.
if not forAst and n[namePos].kind == nkSym:
@@ -602,16 +608,53 @@ proc writeNode(w: var Writer; dest: var TokenBuf; n: PNode; forAst = false) =
# Write the export statement as a regular node
w.withNode dest, n:
for i in 0 ..< n.len:
writeNode(w, dest, n[i], forAst)
if n[i].kind == nkSym and n[i].sym.kindImpl == skModule:
discard "do not write module syms here"
else:
writeNode(w, dest, n[i], forAst)
else:
w.withNode dest, n:
for i in 0 ..< n.len:
writeNode(w, dest, n[i], forAst)
proc writeToplevelNode(w: var Writer; dest: var TokenBuf; n: PNode) =
proc writeGlobal(w: var Writer; dest: var TokenBuf; n: PNode) =
case n.kind
of nkVarTuple:
writeNode(w, dest, n)
of nkIdentDefs, nkConstDef:
# nkIdentDefs: [ident1, ident2, ..., type, default]
# All children except the last two are identifiers
for i in 0 ..< max(0, n.len - 2):
writeGlobal(w, dest, n[i])
of nkPostfix:
writeGlobal(w, dest, n[1])
of nkPragmaExpr:
writeGlobal(w, dest, n[0])
of nkSym:
writeSym(w, dest, n.sym)
else:
discard
proc writeGlobals(w: var Writer; dest: var TokenBuf; n: PNode) =
w.withNode dest, n:
for child in n:
writeGlobal(w, dest, child)
proc writeToplevelNode(w: var Writer; dest, bottom: var TokenBuf; n: PNode) =
case n.kind
of nkStmtList, nkStmtListExpr:
for son in n: writeToplevelNode(w, dest, son)
for son in n: writeToplevelNode(w, dest, bottom, son)
of nkEmpty:
discard "ignore"
of nkTypeSection, nkCommentStmt, nkMixinStmt, nkBindStmt, nkUsingStmt,
nkPragma,
nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef, nkMacroDef, nkTemplateDef:
# We write purely declarative nodes at the bottom of the file
writeNode(w, bottom, n)
of nkConstSection:
writeGlobals(w, bottom, n)
of nkLetSection, nkVarSection:
writeGlobals(w, dest, n)
else:
writeNode w, dest, n
@@ -652,6 +695,7 @@ let repMethodTag = registerTag("repmethod")
#let repClassTag = registerTag("repclass")
let includeTag = registerTag("include")
let importTag = registerTag("import")
let implTag = registerTag("implementation")
proc writeOp(w: var Writer; content: var TokenBuf; op: LogEntry) =
case op.kind
@@ -706,8 +750,14 @@ proc writeNifModule*(config: ConfigRef; thisModule: int32; n: PNode;
if op.module == thisModule.int:
writeOp(w, content, op)
w.writeToplevelNode content, n
var bottom = createTokenBuf(300)
w.writeToplevelNode content, bottom, n
# the implTag is used to tell the loader that the
# bottom of the file is the implementation of the module:
content.addParLe implTag, NoLineInfo
content.addParRi()
content.add bottom
content.addParRi()
let m = modname(w.currentModule, w.infos.config)
@@ -817,10 +867,14 @@ proc cursorFromIndexEntry(c: var DecodeContext; module: FileIndex; entry: NifInd
nifcursors.parse(s[], buf, entry.info)
result = cursorAt(buf, 0)
proc moduleId(c: var DecodeContext; suffix: string): FileIndex =
type
LoadFlag* = enum
LoadFullAst, AlwaysLoadInterface
proc moduleId(c: var DecodeContext; suffix: string; flags: set[LoadFlag] = {}): FileIndex =
var isKnownFile = false
result = c.infos.config.registerNifSuffix(suffix, isKnownFile)
if not isKnownFile:
if not isKnownFile or AlwaysLoadInterface in flags:
let modFile = (getNimcacheDir(c.infos.config) / RelativeFile(suffix & ".nif")).string
let idxFile = (getNimcacheDir(c.infos.config) / RelativeFile(suffix & ".s.idx.nif")).string
if not fileExists(modFile):
@@ -1099,6 +1153,8 @@ proc loadSymFromCursor(c: var DecodeContext; s: PSym; n: var Cursor; thisModule:
inc n
var isKnownFile = false
s.positionImpl = int c.infos.config.registerNifSuffix(thisModule, isKnownFile)
# do to the precompiled mechanism things end up as main modules which are not!
excl s.flagsImpl, sfMainModule
else:
loadField s.positionImpl
@@ -1110,12 +1166,7 @@ proc loadSymFromCursor(c: var DecodeContext; s: PSym; n: var Cursor; thisModule:
s.ownerFieldImpl = loadSymStub(c, n, thisModule, localSyms)
# Load the AST for routine symbols and constants
# Constants need their AST for astdef() to return the constant's value
if s.kindImpl in routineKinds + {skConst}:
s.astImpl = loadNode(c, n, thisModule, localSyms)
elif n.kind == DotToken:
inc n
else:
raiseAssert "expected '.' for non-routine symbol AST but got " & $n.kind
s.astImpl = loadNode(c, n, thisModule, localSyms)
loadLoc c, n, s.locImpl
s.constraintImpl = loadNode(c, n, thisModule, localSyms)
s.instantiatedFromImpl = loadSymStub(c, n, thisModule, localSyms)
@@ -1303,9 +1354,6 @@ proc loadNode(c: var DecodeContext; n: var Cursor; thisModule: string;
else:
raiseAssert "expected string literal but got " & $n.kind
proc moduleSuffix(conf: ConfigRef; f: FileIndex): string =
cachedModuleSuffix(conf, f)
proc loadSymFromIndexEntry(c: var DecodeContext; module: FileIndex;
nifName: string; entry: NifIndexEntry; thisModule: string): PSym =
## Loads a symbol from the NIF index entry using the entry directly.
@@ -1494,8 +1542,32 @@ proc nextSubtree(r: var Stream; dest: var TokenBuf; tok: var PackedToken) =
dec nested
if nested == 0: break
proc processTopLevel(c: var DecodeContext; s: var Stream; loadFullAst: bool; suffix: string; logOps: var seq[LogEntry]; module: int): PNode =
result = newNode(nkStmtList)
type
ModuleSuffix* = distinct string
PrecompiledModule* = object
topLevel*: PNode # top level statements of the main module
deps*: seq[ModuleSuffix] # other modules we need to process the top level statements of
logOps*: seq[LogEntry]
module*: PSym # set by modulegraphs.nim!
proc loadImport(c: var DecodeContext; s: var Stream; deps: var seq[ModuleSuffix]; tok: var PackedToken) =
tok = next(s) # skip `(import`
if tok.kind == DotToken:
tok = next(s) # skip dot
if tok.kind == DotToken:
tok = next(s) # skip dot
if tok.kind == StringLit:
deps.add ModuleSuffix(pool.strings[tok.litId])
tok = next(s)
else:
raiseAssert "expected StringLit but got " & $tok.kind
if tok.kind == ParRi:
tok = next(s) # skip )
else:
raiseAssert "expected ParRi but got " & $tok.kind
proc processTopLevel(c: var DecodeContext; s: var Stream; flags: set[LoadFlag] = {}; suffix: string; module: int): PrecompiledModule =
result = PrecompiledModule(topLevel: newNode(nkStmtList))
var localSyms = initTable[string, PSym]()
var t = next(s) # skip dot
@@ -1512,60 +1584,62 @@ proc processTopLevel(c: var DecodeContext; s: var Stream; loadFullAst: bool; suf
var cursor = cursorAt(buf, 0)
let replayNode = loadNode(c, cursor, suffix, localSyms)
if replayNode != nil:
result.sons.add replayNode
result.topLevel.sons.add replayNode
t = next(s)
if t.kind == ParRi:
t = next(s)
else:
raiseAssert "expected ParRi but got " & $t.kind
elif t.tagId == repConverterTag:
t = loadLogOp(c, logOps, s, ConverterEntry, attachedTrace, module)
t = loadLogOp(c, result.logOps, s, ConverterEntry, attachedTrace, module)
elif t.tagId == repDestroyTag:
t = loadLogOp(c, logOps, s, HookEntry, attachedDestructor, module)
t = loadLogOp(c, result.logOps, s, HookEntry, attachedDestructor, module)
elif t.tagId == repWasMovedTag:
t = loadLogOp(c, logOps, s, HookEntry, attachedWasMoved, module)
t = loadLogOp(c, result.logOps, s, HookEntry, attachedWasMoved, module)
elif t.tagId == repCopyTag:
t = loadLogOp(c, logOps, s, HookEntry, attachedAsgn, module)
t = loadLogOp(c, result.logOps, s, HookEntry, attachedAsgn, module)
elif t.tagId == repSinkTag:
t = loadLogOp(c, logOps, s, HookEntry, attachedSink, module)
t = loadLogOp(c, result.logOps, s, HookEntry, attachedSink, module)
elif t.tagId == repDupTag:
t = loadLogOp(c, logOps, s, HookEntry, attachedDup, module)
t = loadLogOp(c, result.logOps, s, HookEntry, attachedDup, module)
elif t.tagId == repTraceTag:
t = loadLogOp(c, logOps, s, HookEntry, attachedTrace, module)
t = loadLogOp(c, result.logOps, s, HookEntry, attachedTrace, module)
elif t.tagId == repDeepCopyTag:
t = loadLogOp(c, logOps, s, HookEntry, attachedDeepCopy, module)
t = loadLogOp(c, result.logOps, s, HookEntry, attachedDeepCopy, module)
elif t.tagId == repEnumToStrTag:
t = loadLogOp(c, logOps, s, EnumToStrEntry, attachedTrace, module)
t = loadLogOp(c, result.logOps, s, EnumToStrEntry, attachedTrace, module)
elif t.tagId == repMethodTag:
t = loadLogOp(c, logOps, s, MethodEntry, attachedTrace, module)
t = loadLogOp(c, result.logOps, s, MethodEntry, attachedTrace, module)
#elif t.tagId == repClassTag:
# t = loadLogOp(c, logOps, s, ClassEntry, attachedTrace, module)
elif t.tagId == includeTag or t.tagId == importTag:
elif t.tagId == includeTag:
t = skipTree(s)
elif loadFullAst:
elif t.tagId == importTag:
loadImport(c, s, result.deps, t)
elif t.tagId == implTag:
cont = false
elif LoadFullAst in flags:
# Parse the full statement
var buf = createTokenBuf(50)
nextSubtree(s, buf, t)
t = next(s) # skip ParRi
var cursor = cursorAt(buf, 0)
let stmtNode = loadNode(c, cursor, suffix, localSyms)
if stmtNode != nil:
result.sons.add stmtNode
result.topLevel.sons.add stmtNode
else:
cont = false
else:
cont = false
proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: var TStrTable;
logOps: var seq[LogEntry];
loadFullAst: bool = false): PNode =
let suffix = moduleSuffix(c.infos.config, f)
# Ensure module index is loaded - moduleId returns the FileIndex for this suffix
let module = moduleId(c, suffix)
proc loadNifModule*(c: var DecodeContext; suffix: ModuleSuffix; interf, interfHidden: var TStrTable;
flags: set[LoadFlag] = {}): PrecompiledModule =
# Ensure module index is loaded - moduleId returns the FileIndex for this suffix
let module = moduleId(c, string(suffix), flags)
# Populate interface tables from the NIF index structure
# Symbols are created as stubs (Partial state) and will be loaded lazily via loadSym
populateInterfaceTablesFromIndex(c, module, interf, interfHidden, suffix)
populateInterfaceTablesFromIndex(c, module, interf, interfHidden, string(suffix))
# Load the module AST (or just replay actions if loadFullAst is false)
let s = addr c.mods[module].stream
@@ -1575,10 +1649,14 @@ proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: va
if t.kind == ParLe and pool.tags[t.tagId] == toNifTag(nkStmtList):
t = next(s[]) # skip (stmts
t = next(s[]) # skip flags
result = processTopLevel(c, s[], loadFullAst, suffix, logOps, f.int)
result = processTopLevel(c, s[], flags, string(suffix), module.int)
else:
result = newNode(nkStmtList)
result = PrecompiledModule(topLevel: newNode(nkStmtList))
proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: var TStrTable;
flags: set[LoadFlag] = {}): PrecompiledModule =
let suffix = ModuleSuffix(moduleSuffix(c.infos.config, f))
result = loadNifModule(c, suffix, interf, interfHidden, flags)
when isMainModule:
import std / syncio

View File

@@ -126,7 +126,7 @@ proc genVarTuple(p: BProc, n: PNode) =
let vn = n[i]
let v = vn.sym
if sfCompileTime in v.flags: continue
ensureMutable v
backendEnsureMutable v
if sfGlobal in v.flags:
assignGlobalVar(p, vn, "")
genObjectInit(p, cpsInit, v.typ, v.locImpl, constructObj)
@@ -136,7 +136,7 @@ proc genVarTuple(p: BProc, n: PNode) =
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
var field = initLoc(locExpr, vn, tup.storage)
let rtup = rdLoc(tup)
let fieldName =
let fieldName =
if t.kind == tyTuple:
"Field" & $i
else:
@@ -490,14 +490,17 @@ proc genClosureVar(p: BProc, a: PNode) =
constructLoc(p, v)
proc genVarStmt(p: BProc, n: PNode) =
for it in n.sons:
if it.kind == nkCommentStmt: continue
if it.kind == nkIdentDefs:
for it in n:
case it.kind
of nkCommentStmt: discard
of nkIdentDefs:
# can be a lifted var nowadays ...
if it[0].kind == nkSym:
genSingleVar(p, it)
else:
genClosureVar(p, it)
of nkSym:
genSingleVar(p, it.sym, newSymNode(it.sym), it.sym.astdef)
else:
genVarTuple(p, it)
@@ -740,9 +743,10 @@ proc genBlock(p: BProc, n: PNode, d: var TLoc) =
# named block?
assert(n[0].kind == nkSym)
var sym = n[0].sym
ensureMutable sym
backendEnsureMutable sym
sym.locImpl.k = locOther
sym.position = p.breakIdx+1
sym.positionImpl = p.breakIdx+1
# ^ IC: review this
expr(p, n[1], d)
endSimpleBlock(p, scope)
@@ -1255,7 +1259,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
initElifBranch(p.s(cpsStmts), ifStmt, orExpr)
if exvar != nil:
fillLocalName(p, exvar.sym)
ensureMutable exvar.sym
backendEnsureMutable exvar.sym
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnStack)
linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ),
rdLoc(exvar.sym.loc), rope(etmp+1)])
@@ -1304,7 +1308,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
if isImportedException(typeNode.typ, p.config):
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
fillLocalName(p, exvar.sym)
ensureMutable exvar.sym
backendEnsureMutable exvar.sym
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnStack)
startBlockWith(p):
lineCg(p, cpsStmts, "catch ($1& $2) {$n", [getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc)])
@@ -1396,7 +1400,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
if t[i][j].isInfixAs():
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
fillLocalName(p, exvar.sym)
ensureMutable exvar.sym
backendEnsureMutable exvar.sym
fillLoc(exvar.sym.locImpl, locTemp, exvar, OnUnknown)
startBlockWith(p):
lineCg(p, cpsStmts, "catch ($1& $2) {$n", [getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc)])

View File

@@ -1864,7 +1864,7 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn
proc myModuleOpenForCodegen(m: BModule; idx: FileIndex): bool {.inline.} =
if moduleOpenForCodegen(m.g.graph, idx):
result = idx.int < m.g.modules.len and m.g.modules[idx.int] != nil
result = idx.int < m.g.mods.len and m.g.mods[idx.int] != nil
else:
result = false
@@ -1898,7 +1898,7 @@ proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope =
let owner = t.skipTypes(typedescPtrs).itemId.module
if owner != m.module.position and myModuleOpenForCodegen(m, FileIndex owner):
# make sure the type info is created in the owner module
discard genTypeInfoV2(m.g.modules[owner], origType, info)
discard genTypeInfoV2(m.g.mods[owner], origType, info)
# reference the type info as extern here
cgsym(m, "TNimTypeV2")
declareNimType(m, "TNimTypeV2", result, owner)
@@ -1983,7 +1983,7 @@ proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope =
var owner = t.skipTypes(typedescPtrs).itemId.module
if owner != m.module.position and myModuleOpenForCodegen(m, FileIndex owner):
# make sure the type info is created in the owner module
discard genTypeInfoV1(m.g.modules[owner], origType, info)
discard genTypeInfoV1(m.g.mods[owner], origType, info)
# reference the type info as extern here
cgsym(m, "TNimType")
cgsym(m, "TNimNode")

View File

@@ -67,19 +67,19 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
# TODO fixme
if m.config.symbolFiles == v2Sf or optCompress in m.config.globalOptions:
let ms = s.itemId.module #getModule(s)
result = m.g.modules[ms]
result = m.g.mods[ms]
elif m.config.cmd in {cmdNifC, cmdM}:
var ms = getModule(s)
registerModule m.g.graph, ms
if ms.position >= m.g.modules.len:
if ms.position >= m.g.mods.len:
result = newModule(m.g, ms, m.config, idGeneratorFromModule(ms))
else:
result = m.g.modules[ms.position]
result = m.g.mods[ms.position]
if result == nil:
result = newModule(m.g, ms, m.config, idGeneratorFromModule(ms))
else:
var ms = getModule(s)
result = m.g.modules[ms.position]
result = m.g.mods[ms.position]
proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc =
result = TLoc(k: k, storage: s, lode: lode,
@@ -133,10 +133,10 @@ proc getModuleDllPath(m: BModule): Rope =
result = makeCString(dir.string & "/" & filename)
proc getModuleDllPath(m: BModule, module: int): Rope =
result = getModuleDllPath(m.g.modules[module])
result = getModuleDllPath(m.g.mods[module])
proc getModuleDllPath(m: BModule, s: PSym): Rope =
result = getModuleDllPath(m.g.modules[s.itemId.module])
result = getModuleDllPath(m.g.mods[s.itemId.module])
import std/macros
@@ -1720,9 +1720,12 @@ proc genMainProcs(m: BModule) =
proc genMainProcsWithResult(m: BModule) =
genMainProcs(m)
var res = "nim_program_result"
if m.hcrOn: res = cDeref(res)
m.s[cfsProcs].addReturn(res)
if m.config.cmd != cmdNifC:
var res = "nim_program_result"
if m.hcrOn: res = cDeref(res)
m.s[cfsProcs].addReturn(res)
else:
m.s[cfsProcs].addReturn(cIntValue(0))
proc genNimMainInner(m: BModule) =
m.s[cfsProcs].addDeclWithVisibility(Private):
@@ -1960,7 +1963,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
if m.hcrOn:
var hcrModuleMeta = newBuilder("")
let systemModulePath = getModuleDllPath(m, g.modules[g.graph.config.m.systemFileIdx.int].module)
let systemModulePath = getModuleDllPath(m, g.mods[g.graph.config.m.systemFileIdx.int].module)
let mainModulePath = getModuleDllPath(m, m.module)
hcrModuleMeta.addDeclWithVisibility(Private):
hcrModuleMeta.addArrayVarWithInitializer(kind = Local,
@@ -1977,7 +1980,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
g.graph.importDeps.withValue(FileIndex(m.module.position), deps):
for curr in deps[]:
hcrModuleMeta.addField(modules, ""):
hcrModuleMeta.add(getModuleDllPath(m, g.modules[curr.int].module))
hcrModuleMeta.add(getModuleDllPath(m, g.mods[curr.int].module))
hcrModuleMeta.addField(modules, ""):
hcrModuleMeta.add("\"\"")
hcrModuleMeta.addDeclWithVisibility(ExportLib):
@@ -2170,6 +2173,8 @@ proc genInitCode(m: BModule) =
else:
prcBody.add(extract(m.thing.s(section)))
#echo "PRE INIT PROC ", m.module.name.s, " ", m.s[cfsVars].buf.len
if m.preInitProc.s(cpsInit).buf.len > 0 or m.preInitProc.s(cpsStmts).buf.len > 0:
# Give this small function its own scope
prcBody.addScope():
@@ -2386,10 +2391,10 @@ proc newModule(g: BModuleList; module: PSym; conf: ConfigRef; idgen: IdGenerator
# we should create only one cgen module for each module sym
result = rawNewModule(g, module, conf)
result.idgen = idgen
if module.position >= g.modules.len:
setLen(g.modules, module.position + 1)
if module.position >= g.mods.len:
setLen(g.mods, module.position + 1)
#growCache g.modules, module.position
g.modules[module.position] = result
g.mods[module.position] = result
template injectG() {.dirty.} =
if graph.backend == nil:
@@ -2523,13 +2528,7 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
result = true
# We need 2 different logics here: pending modules (including
# 'nim__dat') may require file merging for the combination of dead code
# elimination and incremental compilation! Non pending modules need no
# such logic and in fact the logic hurts for the main module at least;
# it would generate multiple 'main' procs, for instance.
proc writeModule(m: BModule, pending: bool) =
proc writeModule(m: BModule) =
let cfile = getCFile(m)
if moduleHasChanged(m.g.graph, m.module):
genInitCode(m)
@@ -2658,7 +2657,7 @@ proc genForwardedProcs(g: BModuleList) =
while g.forwardedProcs.len > 0:
let
prc = g.forwardedProcs.pop()
m = g.modules[prc.itemId.module]
m = g.mods[prc.itemId.module]
if sfForward in prc.flags:
internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
@@ -2674,6 +2673,6 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
genForwardedProcs(g)
for m in cgenModules(g):
m.writeModule(pending=true)
m.writeModule()
writeMapping(config, g.mapping)
if g.generatedHeader != nil: writeHeader(g.generatedHeader)

View File

@@ -117,7 +117,7 @@ type
BModuleList* = ref object of RootObj
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Builder
mapping*: Rope # the generated mapping file (if requested)
modules*: seq[BModule] # list of all compiled modules
mods*: seq[BModule] # list of all compiled modules
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
forwardedProcs*: seq[PSym] # procs that did not yet have a body
generatedHeader*: BModule

View File

@@ -40,7 +40,7 @@ proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config, idgenFromLoadedModule(m))
proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
var bmod = BModuleList(g.backend).modules[m.module.position]
var bmod = BModuleList(g.backend).mods[m.module.position]
assert bmod != nil
bmod.flags.incl useAliveDataFromDce
bmod.alive = move alive[m.module.position]

View File

@@ -404,140 +404,140 @@ proc parse*(t: typedesc[TSymKind]; s: string): TSymKind =
proc toNifTag*(s: TTypeKind): string =
case s
of tyNone: "none"
of tyBool: "bool"
of tyChar: "char"
of tyEmpty: "empty"
of tyAlias: "alias"
of tyNil: "nil"
of tyUntyped: "untyped"
of tyTyped: "typed"
of tyTypeDesc: "typedesc"
of tyGenericInvocation: "ginvoke"
of tyGenericBody: "gbody"
of tyGenericInst: "ginst"
of tyGenericParam: "gparam"
of tyDistinct: "distinct"
of tyEnum: "enum"
of tyOrdinal: "ordinal"
of tyArray: "array"
of tyObject: "object"
of tyTuple: "tuple"
of tySet: "set"
of tyRange: "range"
of tyPtr: "ptr"
of tyRef: "ref"
of tyVar: "mut"
of tySequence: "seq"
of tyProc: "proctype"
of tyPointer: "pointer"
of tyOpenArray: "openarray"
of tyString: "string"
of tyCstring: "cstring"
of tyForward: "forward"
of tyInt: "int"
of tyInt8: "int8"
of tyInt16: "int16"
of tyInt32: "int32"
of tyInt64: "int64"
of tyFloat: "float"
of tyFloat32: "float32"
of tyFloat64: "float64"
of tyFloat128: "float128"
of tyUInt: "uint"
of tyUInt8: "uint8"
of tyUInt16: "uint16"
of tyUInt32: "uint32"
of tyUInt64: "uint64"
of tyOwned: "owned"
of tySink: "sink"
of tyLent: "lent"
of tyVarargs: "varargs"
of tyUncheckedArray: "uarray"
of tyError: "error"
of tyBuiltInTypeClass: "bconcept"
of tyUserTypeClass: "uconcept"
of tyUserTypeClassInst: "uconceptinst"
of tyCompositeTypeClass: "cconcept"
of tyInferred: "inferred"
of tyAnd: "and"
of tyOr: "or"
of tyNot: "not"
of tyAnything: "anything"
of tyStatic: "static"
of tyFromExpr: "fromx"
of tyConcept: "concept"
of tyVoid: "void"
of tyIterable: "iterable"
of tyNone: "n0"
of tyBool: "b0"
of tyChar: "c0"
of tyEmpty: "e0"
of tyAlias: "a0"
of tyNil: "n1"
of tyUntyped: "U0"
of tyTyped: "t0"
of tyTypeDesc: "t1"
of tyGenericInvocation: "g0"
of tyGenericBody: "g1"
of tyGenericInst: "g2"
of tyGenericParam: "g4"
of tyDistinct: "d0"
of tyEnum: "e1"
of tyOrdinal: "o0"
of tyArray: "a1"
of tyObject: "o1"
of tyTuple: "t2"
of tySet: "s0"
of tyRange: "r0"
of tyPtr: "p0"
of tyRef: "r1"
of tyVar: "v0"
of tySequence: "s1"
of tyProc: "p1"
of tyPointer: "p2"
of tyOpenArray: "o3"
of tyString: "s2"
of tyCstring: "c1"
of tyForward: "F0"
of tyInt: "i0"
of tyInt8: "i1"
of tyInt16: "i2"
of tyInt32: "i3"
of tyInt64: "i4"
of tyFloat: "f0"
of tyFloat32: "f1"
of tyFloat64: "f2"
of tyFloat128: "f3"
of tyUInt: "u0"
of tyUInt8: "u1"
of tyUInt16: "u2"
of tyUInt32: "u3"
of tyUInt64: "u4"
of tyOwned: "o2"
of tySink: "s3"
of tyLent: "L0"
of tyVarargs: "v1"
of tyUncheckedArray: "U1"
of tyError: "e2"
of tyBuiltInTypeClass: "b1"
of tyUserTypeClass: "U2"
of tyUserTypeClassInst: "U3"
of tyCompositeTypeClass: "c2"
of tyInferred: "I0"
of tyAnd: "a2"
of tyOr: "o4"
of tyNot: "n2"
of tyAnything: "a3"
of tyStatic: "s4"
of tyFromExpr: "F1"
of tyConcept: "c3"
of tyVoid: "v2"
of tyIterable: "I1"
proc parse*(t: typedesc[TTypeKind]; s: string): TTypeKind =
case s
of "none": tyNone
of "bool": tyBool
of "char": tyChar
of "empty": tyEmpty
of "alias": tyAlias
of "nil": tyNil
of "untyped": tyUntyped
of "typed": tyTyped
of "typedesc": tyTypeDesc
of "ginvoke": tyGenericInvocation
of "gbody": tyGenericBody
of "ginst": tyGenericInst
of "gparam": tyGenericParam
of "distinct": tyDistinct
of "enum": tyEnum
of "ordinal": tyOrdinal
of "array": tyArray
of "object": tyObject
of "tuple": tyTuple
of "set": tySet
of "range": tyRange
of "ptr": tyPtr
of "ref": tyRef
of "mut": tyVar
of "seq": tySequence
of "proctype": tyProc
of "pointer": tyPointer
of "openarray": tyOpenArray
of "string": tyString
of "cstring": tyCstring
of "forward": tyForward
of "int": tyInt
of "int8": tyInt8
of "int16": tyInt16
of "int32": tyInt32
of "int64": tyInt64
of "float": tyFloat
of "float32": tyFloat32
of "float64": tyFloat64
of "float128": tyFloat128
of "uint": tyUInt
of "uint8": tyUInt8
of "uint16": tyUInt16
of "uint32": tyUInt32
of "uint64": tyUInt64
of "owned": tyOwned
of "sink": tySink
of "lent": tyLent
of "varargs": tyVarargs
of "uarray": tyUncheckedArray
of "error": tyError
of "bconcept": tyBuiltInTypeClass
of "uconcept": tyUserTypeClass
of "uconceptinst": tyUserTypeClassInst
of "cconcept": tyCompositeTypeClass
of "inferred": tyInferred
of "and": tyAnd
of "or": tyOr
of "not": tyNot
of "anything": tyAnything
of "static": tyStatic
of "fromx": tyFromExpr
of "concept": tyConcept
of "void": tyVoid
of "iterable": tyIterable
of "n0": tyNone
of "b0": tyBool
of "c0": tyChar
of "e0": tyEmpty
of "a0": tyAlias
of "n1": tyNil
of "U0": tyUntyped
of "t0": tyTyped
of "t1": tyTypeDesc
of "g0": tyGenericInvocation
of "g1": tyGenericBody
of "g2": tyGenericInst
of "g4": tyGenericParam
of "d0": tyDistinct
of "e1": tyEnum
of "o0": tyOrdinal
of "a1": tyArray
of "o1": tyObject
of "t2": tyTuple
of "s0": tySet
of "r0": tyRange
of "p0": tyPtr
of "r1": tyRef
of "v0": tyVar
of "s1": tySequence
of "p1": tyProc
of "p2": tyPointer
of "o3": tyOpenArray
of "s2": tyString
of "c1": tyCstring
of "F0": tyForward
of "i0": tyInt
of "i1": tyInt8
of "i2": tyInt16
of "i3": tyInt32
of "i4": tyInt64
of "f0": tyFloat
of "f1": tyFloat32
of "f2": tyFloat64
of "f3": tyFloat128
of "u0": tyUInt
of "u1": tyUInt8
of "u2": tyUInt16
of "u3": tyUInt32
of "u4": tyUInt64
of "o2": tyOwned
of "s3": tySink
of "L0": tyLent
of "v1": tyVarargs
of "U1": tyUncheckedArray
of "e2": tyError
of "b1": tyBuiltInTypeClass
of "U2": tyUserTypeClass
of "U3": tyUserTypeClassInst
of "c2": tyCompositeTypeClass
of "I0": tyInferred
of "a2": tyAnd
of "o4": tyOr
of "n2": tyNot
of "a3": tyAnything
of "s4": tyStatic
of "F1": tyFromExpr
of "c3": tyConcept
of "v2": tyVoid
of "I1": tyIterable
else: tyNone

View File

@@ -471,7 +471,7 @@ proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
result = nil
if g.config.symbolFiles == disabledSf:
if g.config.symbolFiles == disabledSf and optWithinConfigSystem notin g.config.globalOptions:
# For NIF-based compilation, search in loaded NIF modules
when not defined(nimKochBootstrap):
# Only try to resolve from NIF if we're actually using NIF files (cmdNifC)
@@ -599,9 +599,10 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
if m.position >= g.packed.len:
setLen(g.packed.pm, m.position + 1)
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
uniqueName: rope(uniqueModuleName(g.config, m)))
initStrTables(g, m)
if g.ifaces[m.position].module == nil:
g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
uniqueName: rope(uniqueModuleName(g.config, m)))
initStrTables(g, m)
proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
registerModule(g, g.packed[int m].module)
@@ -814,31 +815,33 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
when not defined(nimKochBootstrap):
proc moduleFromNifFile*(g: ModuleGraph; fileIdx: FileIndex;
cachedModules: var seq[FileIndex];
loadFullAst: bool = false): PSym =
flags: set[LoadFlag] = {}): PrecompiledModule =
## Returns 'nil' if the module needs to be recompiled.
## Loads module from NIF file when optCompress is enabled.
## When loadFullAst is true, loads the complete module AST for code generation.
if not fileExists(toNifFilename(g.config, fileIdx)):
return nil
return PrecompiledModule(module: nil)
# Create module symbol
let filename = AbsoluteFile toFullPath(g.config, fileIdx)
result = PSym(
let m = PSym(
kindImpl: skModule,
itemId: ItemId(module: int32(fileIdx), item: 0'i32),
name: getIdent(g.cache, splitFile(filename).name),
infoImpl: newLineInfo(fileIdx, 1, 1),
positionImpl: int(fileIdx))
setOwner(result, getPackage(g.config, g.cache, fileIdx))
setOwner(m, getPackage(g.config, g.cache, fileIdx))
# Register module in graph
registerModule(g, result)
var opsLog: seq[LogEntry] = @[]
result.astImpl = loadNifModule(ast.program, fileIdx, g.ifaces[fileIdx.int].interf,
g.ifaces[fileIdx.int].interfHidden, opsLog, loadFullAst)
registerModule(g, m)
result = loadNifModule(ast.program, fileIdx,
g.ifaces[fileIdx.int].interf,
g.ifaces[fileIdx.int].interfHidden, flags)
result.module = m
# Register hooks from NIF index with the module graph
for x in opsLog:
for x in result.logOps:
case x.kind
of HookEntry:
g.loadedOps[x.op][x.key] = x.sym
@@ -852,7 +855,6 @@ when not defined(nimKochBootstrap):
raiseAssert "GenericInstEntry should not be in the NIF index"
# Register methods per type from NIF index
discard "todo"
cachedModules.add fileIdx
proc configComplete*(g: ModuleGraph) =
rememberStartupConfig(g.startupPackedConfig, g.config)

View File

@@ -25,30 +25,36 @@ when defined(nimPreviewSlimSystem):
import ast, options, lineinfos, modulegraphs, cgendata, cgen,
pathutils, extccomp, msgs, modulepaths, idents, types, ast2nif
proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[PSym] =
proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[PrecompiledModule] =
## Traverse the module dependency graph using a stack.
## Returns all modules that need code generation, in dependency order.
var visited = initIntSet()
var stack: seq[FileIndex] = @[mainFileIdx]
let mainModule = moduleFromNifFile(g, mainFileIdx, {LoadFullAst})
var stack: seq[ModuleSuffix] = @[]
result = @[]
var cachedModules: seq[FileIndex] = @[]
if mainModule.module != nil:
incl mainModule.module.flagsImpl, sfMainModule
for dep in mainModule.deps:
stack.add dep
var visited = initHashSet[string]()
while stack.len > 0:
let fileIdx = stack.pop()
let suffix = stack.pop()
if not visited.containsOrIncl(int(fileIdx)):
# Only load full AST for main module; others are loaded lazily by codegen
let isMainModule = fileIdx == mainFileIdx
let module = moduleFromNifFile(g, fileIdx, cachedModules, loadFullAst=isMainModule)
if module != nil:
result.add module
if isMainModule:
incl module.flagsImpl, sfMainModule
# Add dependencies to stack (they come from cachedModules)
for dep in cachedModules:
if not visited.contains(int(dep)):
if not visited.containsOrIncl(suffix.string):
let nifFile = toGeneratedFile(g.config, AbsoluteFile(suffix.string), ".nif")
let fileIdx = msgs.fileInfoIdx(g.config, nifFile)
let precomp = moduleFromNifFile(g, fileIdx, {LoadFullAst})
if precomp.module != nil:
result.add precomp
for dep in precomp.deps:
if not visited.contains(dep.string):
stack.add dep
cachedModules.setLen(0)
if mainModule.module != nil:
result.add mainModule
proc setupNifBackendModule(g: ModuleGraph; module: PSym): BModule =
## Set up a BModule for code generation from a NIF module.
@@ -56,38 +62,44 @@ proc setupNifBackendModule(g: ModuleGraph; module: PSym): BModule =
g.backend = cgendata.newModuleList(g)
result = cgen.newModule(BModuleList(g.backend), module, g.config, idGeneratorFromModule(module))
proc generateCodeForModule(g: ModuleGraph; module: PSym) =
## Generate C code for a single module.
let moduleId = module.position
var bmod = BModuleList(g.backend).modules[moduleId]
if bmod == nil:
bmod = setupNifBackendModule(g, module)
# Generate code for the module's top-level statements
if module.ast != nil:
cgen.genTopLevelStmt(bmod, module.ast)
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
let initStmt = newNodeI(nkStmtList, module.info)
let initStmt = newNode(nkStmtList)
finalCodegenActions(g, bmod, initStmt)
# Generate dispatcher methods
for disp in getDispatchers(g):
genProcLvl3(bmod, disp)
proc generateCodeForModule(g: ModuleGraph; precomp: PrecompiledModule) =
## Generate C code for a single module.
let moduleId = precomp.module.position
var bmod = BModuleList(g.backend).mods[moduleId]
if bmod == nil:
bmod = setupNifBackendModule(g, precomp.module)
# Generate code for the module's top-level statements
if precomp.topLevel != nil:
cgen.genTopLevelStmt(bmod, precomp.topLevel)
proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) =
## Main entry point for NIF-based C code generation.
## Traverses the module dependency graph and generates C code.
# Reset backend state
resetForBackend(g)
let mainModule = g.getModule(mainFileIdx)
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 cachedModules: seq[FileIndex] = @[]
if g.config.m.systemFileIdx != InvalidFileIdx:
g.systemModule = moduleFromNifFile(g, g.config.m.systemFileIdx, cachedModules)
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
@@ -98,29 +110,35 @@ proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) =
return
# Set up backend modules for all modules that need code generation
for module in modules:
discard setupNifBackendModule(g, module)
for m in modules:
discard setupNifBackendModule(g, m.module)
# Also ensure system module is set up and generated first if it exists
if g.systemModule != nil and g.systemModule != mainModule:
let systemBmod = BModuleList(g.backend).modules[g.systemModule.position]
if systemBmod == nil:
discard setupNifBackendModule(g, g.systemModule)
generateCodeForModule(g, g.systemModule)
if precompSys.module != nil:
discard setupNifBackendModule(g, precompSys.module)
generateCodeForModule(g, precompSys)
# Generate code for all modules except main (main goes last)
# This ensures all modules are added to modulesClosed
for module in modules:
if module != mainModule and module != g.systemModule:
generateCodeForModule(g, module)
# Track which modules have been processed to avoid duplicates
var processed = initIntSet()
if precompSys.module != nil:
processed.incl precompSys.module.position
# Generate main module last (so all init procs are registered)
if mainModule != nil:
generateCodeForModule(g, mainModule)
# 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)
# 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!
for m in BModuleList(g.backend).mods:
if m != nil:
assert m.module != nil
#if sfMainModule notin m.module.flags:
finishModule g, m
# Write C files
if g.backend != nil:
cgenWriteModules(g.backend, g.config)
cgenWriteModules(g.backend, g.config)
# Run C compiler
if g.config.cmd != cmdTcc:

View File

@@ -112,6 +112,7 @@ type # please make sure we have under 32 options
optJsBigInt64 # use bigints for 64-bit integers in JS
optItaniumMangle # mangling follows the Itanium spec
optCompress # turn on AST compression by converting it to NIF
optWithinConfigSystem # we still compile within the configuration system
TGlobalOptions* = set[TGlobalOption]

View File

@@ -286,8 +286,8 @@ proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymF
sfMainModule notin flags and
not graph.withinSystem and
not graph.config.isDefined("nimscript"):
result = moduleFromNifFile(graph, fileIdx, cachedModules)
if result == nil:
let precomp = moduleFromNifFile(graph, fileIdx)
if precomp.module == nil:
let nifPath = toNifFilename(graph.config, fileIdx)
localError(graph.config, unknownLineInfo,
"nim m requires precompiled NIF for import: " & toFullPath(graph.config, fileIdx) &
@@ -385,7 +385,8 @@ proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx
graph.config.libpath / RelativeFile"system.nim")
var cachedModules: seq[FileIndex] = @[]
when not defined(nimKochBootstrap):
graph.systemModule = moduleFromNifFile(graph, graph.config.m.systemFileIdx, cachedModules)
let precomp = moduleFromNifFile(graph, graph.config.m.systemFileIdx)
graph.systemModule = precomp.module
if graph.systemModule == nil:
let nifPath = toNifFilename(graph.config, graph.config.m.systemFileIdx)
localError(graph.config, unknownLineInfo,

View File

@@ -1836,6 +1836,9 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
putWithSpace(g, tkSymbol, "error")
#gcomma(g, n, c)
gsub(g, n[0], c)
of nkReplayAction:
put(g, tkSymbol, "replayaction")
#gsons(g, n, c, 0)
else:
#nkNone, nkExplicitTypeListCall:
internalError(g.config, n.info, "renderer.gsub(" & $n.kind & ')')

View File

@@ -213,6 +213,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
unregisterArcOrc(conf)
conf.globalOptions.excl optOwnedRefs
conf.selectedGC = gcUnselected
conf.globalOptions.incl optWithinConfigSystem
var m = graph.makeModule(scriptName)
incl(m, sfMainModule)
@@ -251,4 +252,5 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
#initDefines()
undefSymbol(conf.symbols, "nimscript")
undefSymbol(conf.symbols, "nimconfig")
conf.globalOptions.excl optWithinConfigSystem
conf.symbolFiles = oldSymbolFiles