IC: progress (#25339)

This commit is contained in:
Andreas Rumpf
2025-12-07 13:07:44 +01:00
committed by GitHub
parent 099ee1ce4a
commit fa4d79f519
9 changed files with 150 additions and 62 deletions

View File

@@ -211,7 +211,7 @@ proc toNifSymName(w: var Writer; sym: PSym): string =
result = sym.name.s
result.add '.'
result.addInt sym.disamb
if sym.itemId notin w.locals and sym.kindImpl notin skLocalSymKinds:
if sym.kindImpl notin skLocalSymKinds and sym.itemId notin w.locals:
# Global symbol: ident.disamb.moduleSuffix
let module = sym.itemId.module
result.add '.'
@@ -406,13 +406,13 @@ proc writeSymDef(w: var Writer; dest: var TokenBuf; sym: PSym) =
dest.addParRi
# Collect for later unloading after entire module is written
if sym.kindImpl notin {skModule, skPackage}:
if sym.kindImpl notin {skPackage}:
# do not unload modules
w.writtenSyms.add sym
proc shouldWriteSymDef(w: Writer; sym: PSym): bool {.inline.} =
# Don't write module/package symbols - they don't have NIF files
if sym.kindImpl in {skModule, skPackage}:
if sym.kindImpl in {skPackage}:
return false
# Already written - don't write again
if sym.state == Sealed:
@@ -430,7 +430,7 @@ proc shouldWriteSymDef(w: Writer; sym: PSym): bool {.inline.} =
proc writeSym(w: var Writer; dest: var TokenBuf; sym: PSym) =
if sym == nil:
dest.addDotToken()
elif sym.kindImpl in {skModule, skPackage}:
elif sym.kindImpl in {skPackage}:
# Write module/package symbols as dots - they're resolved differently
# (by position/FileIndex, not by NIF lookup)
dest.addDotToken()
@@ -1084,6 +1084,8 @@ proc loadSymFromCursor(c: var DecodeContext; s: PSym; n: var Cursor; thisModule:
if s.kindImpl == skModule:
expect n, DotToken
inc n
var isKnownFile = false
s.positionImpl = int c.infos.config.registerNifSuffix(thisModule, isKnownFile)
else:
loadField s.positionImpl
@@ -1097,11 +1099,10 @@ proc loadSymFromCursor(c: var DecodeContext; s: PSym; n: var Cursor; thisModule:
# Load the AST for routine symbols (procs, funcs, etc.)
if s.kindImpl in routineKinds:
s.astImpl = loadNode(c, n, thisModule, localSyms)
elif n.kind == DotToken:
inc n
else:
if n.kind == DotToken:
inc n
else:
raiseAssert "expected '.' for non-routine symbol AST but got " & $n.kind
raiseAssert "expected '.' for non-routine symbol AST but got " & $n.kind
loadLoc c, n, s.locImpl
s.constraintImpl = loadNode(c, n, thisModule, localSyms)
s.instantiatedFromImpl = loadSymStub(c, n, thisModule, localSyms)
@@ -1420,10 +1421,37 @@ proc resolveHookSym*(c: var DecodeContext; symId: nifstreams.SymId): PSym =
disamb: sn.count.int32, state: Partial)
c.syms[id] = (result, offs)
proc tryResolveCompilerProc*(c: var DecodeContext; name: string; moduleFileIdx: FileIndex): PSym =
## Tries to resolve a compiler proc from a module by checking the NIF index.
## Returns nil if the symbol doesn't exist.
let suffix = moduleSuffix(c.infos.config, moduleFileIdx)
let symName = name & ".0." & suffix
# Check if module index is loaded, if not load it
let module = moduleId(c, suffix)
# Check if symbol exists in the index (check both public and private)
var offs = c.mods[module].index.public.getOrDefault(symName)
if offs.offset == 0:
offs = c.mods[module].index.private.getOrDefault(symName)
if offs.offset == 0:
return nil
# Create a stub symbol
let val = addr c.mods[module].symCounter
inc val[]
let id = ItemId(module: int32(module), item: val[])
result = c.syms.getOrDefault(id)[0]
if result == nil:
result = PSym(itemId: id, kindImpl: skProc, name: c.cache.getIdent(name),
disamb: 0, state: Partial)
c.syms[id] = (result, offs)
proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: var TStrTable;
hooks: var Table[nifstreams.SymId, HooksPerType];
converters: var seq[(string, string)];
classes: var seq[ClassIndexEntry]): PNode =
classes: var seq[ClassIndexEntry];
loadFullAst: bool = false): PNode =
let suffix = moduleSuffix(c.infos.config, f)
# Ensure module index is loaded - moduleId returns the FileIndex for this suffix
@@ -1440,29 +1468,62 @@ proc loadNifModule*(c: var DecodeContext; f: FileIndex; interf, interfHidden: va
# Return classes/methods from the index
classes = move c.mods[module].index.classes
# Check for replay actions at the start of the NIF file
# Load the module AST (or just replay actions if loadFullAst is false)
result = newNode(nkStmtList)
let s = addr c.mods[module].stream
s.r.jumpTo 0 # Start from beginning
discard processDirectives(s.r)
var localSyms = initTable[string, PSym]()
# Read root stmts node
var t = next(s[])
if t.kind == ParLe and pool.tags[t.tagId] == toNifTag(nkStmtList):
t = next(s[]) # skip flags
t = next(s[]) # skip type
# Check if first node is a (replay ...) container
if t.kind == ParLe and pool.tags[t.tagId] == "replay":
t = next(s[]) # move past (replay
# Parse all replay actions inside the container
while t.kind != ParRi and t.kind != EofToken:
if t.kind == ParLe:
# Process all top-level statements
while t.kind != ParRi and t.kind != EofToken:
if t.kind == ParLe:
let tag = pool.tags[t.tagId]
if tag == "replay":
# Always load replay actions (macro cache operations)
t = next(s[]) # move past (replay
while t.kind != ParRi and t.kind != EofToken:
if t.kind == ParLe:
var buf = createTokenBuf(50)
nifcursors.parse(s[], buf, t.info)
var cursor = cursorAt(buf, 0)
let replayNode = loadNode(c, cursor, suffix, localSyms)
if replayNode != nil:
result.sons.add replayNode
t = next(s[])
elif loadFullAst:
# Parse the full statement
var buf = createTokenBuf(50)
nifcursors.parse(s[], buf, t.info)
buf.add t # Add the ParLe token we already read
var nested = 1
while nested > 0:
t = next(s[])
buf.add t
if t.kind == ParLe:
inc nested
elif t.kind == ParRi:
dec nested
elif t.kind == EofToken:
break
var cursor = cursorAt(buf, 0)
let replayNode = loadNode(c, cursor, suffix, localSyms)
if replayNode != nil:
result.sons.add replayNode
let stmtNode = loadNode(c, cursor, suffix, localSyms)
if stmtNode != nil:
result.sons.add stmtNode
else:
# Skip over the statement by counting parentheses
var nested = 1
while nested > 0:
t = next(s[])
if t.kind == ParLe:
inc nested
elif t.kind == ParRi:
dec nested
elif t.kind == EofToken:
break
else:
t = next(s[])
when isMainModule:

View File

@@ -16,13 +16,10 @@
## implementation.
template detectVersion(field, corename) =
if m.g.field == 0:
let core = getCompilerProc(m.g.graph, corename)
if core == nil or core.kind != skConst:
m.g.field = 1
else:
m.g.field = toInt(ast.getInt(core.astdef))
result = m.g.field
if m.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc, gcHooks}:
result = 2
else:
result = 1
proc detectStrVersion(m: BModule): int =
detectVersion(strVersion, "nimStrVersion")

View File

@@ -721,7 +721,7 @@ proc genRecordFieldsAux(m: BModule; n: PNode,
# have to recurse via 'getTypeDescAux'. And not doing so prevents problems
# with heavily templatized C++ code:
if not isImportedCppType(rectype):
let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
let fieldType = field.loc.t.skipTypes(abstractInst)
var typ: Rope = ""
var isFlexArray = false
var initializer = ""

View File

@@ -447,6 +447,7 @@ proc mainCommand*(graph: ModuleGraph) =
of cmdNifC:
# Generate C code from NIF files
wantMainModule(conf)
setOutFile(conf)
commandNifC(graph)
of cmdDeps:
# Generate .build.nif for nifmake

View File

@@ -435,7 +435,30 @@ proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
result = nil
if g.config.symbolFiles == disabledSf: return nil
if g.config.symbolFiles == disabledSf:
# 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)
if g.config.cmd == cmdNifC:
# First try system module (most compilerprocs are there)
let systemFileIdx = g.config.m.systemFileIdx
if systemFileIdx != InvalidFileIdx:
result = tryResolveCompilerProc(ast.program, name, systemFileIdx)
if result != nil:
strTableAdd(g.compilerprocs, result)
return result
# Try threadpool module (some compilerprocs like FlowVar are there)
# Find threadpool module by searching loaded modules
for moduleIdx in 0..<g.ifaces.len:
let module = g.ifaces[moduleIdx].module
if module != nil and module.name.s == "threadpool":
let threadpoolFileIdx = module.position.FileIndex
result = tryResolveCompilerProc(ast.program, name, threadpoolFileIdx)
if result != nil:
strTableAdd(g.compilerprocs, result)
return result
return nil
# slow, linear search, but the results are cached:
for module in 0..<len(g.packed):
@@ -755,9 +778,11 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
when not defined(nimKochBootstrap):
proc moduleFromNifFile*(g: ModuleGraph; fileIdx: FileIndex;
cachedModules: var seq[FileIndex]): PSym =
cachedModules: var seq[FileIndex];
loadFullAst: bool = false): PSym =
## 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
@@ -779,7 +804,7 @@ when not defined(nimKochBootstrap):
var converters: seq[(string, string)] = @[]
var classes: seq[ClassIndexEntry] = @[]
result.astImpl = loadNifModule(ast.program, fileIdx, g.ifaces[fileIdx.int].interf,
g.ifaces[fileIdx.int].interfHidden, hooks, converters, classes)
g.ifaces[fileIdx.int].interfHidden, hooks, converters, classes, loadFullAst)
# Register hooks from NIF index with the module graph
for typSymId, hooksPerType in hooks:
let typeItemId = parseTypeSymIdToItemId(ast.program, typSymId)

View File

@@ -30,29 +30,23 @@ proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[PSym] =
## Returns all modules that need code generation, in dependency order.
var visited = initIntSet()
var stack: seq[FileIndex] = @[mainFileIdx]
var modules: seq[PSym] = @[]
result = @[]
var cachedModules: seq[FileIndex] = @[]
while stack.len > 0:
let fileIdx = stack.pop()
if visited.containsOrIncl(int(fileIdx)):
continue
# Load module from NIF
let module = moduleFromNifFile(g, fileIdx, cachedModules)
if module == nil:
continue
modules.add module
# Add dependencies to stack (they come from cachedModules)
for dep in cachedModules:
if not visited.contains(int(dep)):
stack.add dep
cachedModules.setLen(0)
result = modules
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
# Add dependencies to stack (they come from cachedModules)
for dep in cachedModules:
if not visited.contains(int(dep)):
stack.add dep
cachedModules.setLen(0)
proc setupNifBackendModule(g: ModuleGraph; module: PSym): BModule =
## Set up a BModule for code generation from a NIF module.
@@ -71,8 +65,10 @@ proc generateCodeForModule(g: ModuleGraph; module: PSym) =
if module.ast != nil:
cgen.genTopLevelStmt(bmod, module.ast)
# Finalize the module
finalCodegenActions(g, bmod, newNodeI(nkStmtList, module.info))
# 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)
finalCodegenActions(g, bmod, initStmt)
# Generate dispatcher methods
for disp in getDispatchers(g):
@@ -84,6 +80,14 @@ proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) =
# Reset backend state
resetForBackend(g)
let mainModule = g.getModule(mainFileIdx)
# Also ensure system module is set up and generated 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)
# Load all modules in dependency order using stack traversal
let modules = loadModuleDependencies(g, mainFileIdx)
@@ -92,12 +96,12 @@ proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) =
"Cannot load NIF file for main module: " & toFullPath(g.config, mainFileIdx))
return
# Set up backend modules
# Set up backend modules for all modules that need code generation
for module in modules:
discard setupNifBackendModule(g, module)
# Generate code for all modules except main (main goes last)
let mainModule = g.getModule(mainFileIdx)
# This ensures all modules are added to modulesClosed
for module in modules:
if module != mainModule:
generateCodeForModule(g, module)

View File

@@ -118,8 +118,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
if conf.selectedGC == gcUnselected:
if conf.backend in {backendC, backendCpp, backendObjc} or
(conf.cmd in cmdDocLike and conf.backend != backendJs) or
conf.cmd == cmdGendepend or
conf.cmd == cmdM:
conf.cmd in {cmdGendepend, cmdNifC, cmdDeps, cmdM}:
initOrcDefines(conf)
mainCommand(graph)

View File

@@ -85,9 +85,3 @@ func countSetBitsImpl*(x: SomeInteger): int {.inline.} =
else:
when sizeof(x) <= 4: result = countBitsImpl(x.uint32)
else: result = countBitsImpl(x.uint64)
proc countBits32*(n: uint32): int {.compilerproc, inline.} =
result = countSetBitsImpl(n)
proc countBits64*(n: uint64): int {.compilerproc, inline.} =
result = countSetBitsImpl(n)

View File

@@ -10,6 +10,13 @@
# set handling
# IC: compilerprocs now must be defined in system.nim or threadpool.nim!
proc countBits32*(n: uint32): int {.compilerproc, inline.} =
result = countSetBitsImpl(n)
proc countBits64*(n: uint64): int {.compilerproc, inline.} =
result = countSetBitsImpl(n)
proc cardSetImpl(s: ptr UncheckedArray[uint8], len: int): int {.inline.} =
var i = 0
result = 0