diff options
Diffstat (limited to 'compiler/passes.nim')
-rw-r--r-- | compiler/passes.nim | 358 |
1 files changed, 191 insertions, 167 deletions
diff --git a/compiler/passes.nim b/compiler/passes.nim index b84fe2f4d..d6b141078 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -7,72 +7,45 @@ # distribution, for details about the copyright. # -# This module implements the passes functionality. A pass must implement the -# `TPass` interface. +## This module implements the passes functionality. A pass must implement the +## `TPass` interface. import - strutils, options, ast, astalgo, llstream, msgs, platform, os, - condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, - nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder + options, ast, llstream, msgs, + idents, + syntaxes, modulegraphs, reorder, + lineinfos, + pipelineutils, + modules, pathutils, packages, + sem, semdata +import ic/replayer -type - TPassContext* = object of RootObj # the pass's context - fromCache*: bool # true if created by "openCached" +export skipCodegen, resolveMod, prepareConfigNotes - PPassContext* = ref TPassContext +when defined(nimsuggest): + import ../dist/checksums/src/checksums/sha1 - TPassOpen* = proc (graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext {.nimcall.} - TPassOpenCached* = - proc (graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext {.nimcall.} - TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.} - TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.} +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] - TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached, - process: TPassProcess, close: TPassClose] +import std/tables +type TPassData* = tuple[input: PNode, closeOutput: PNode] - TPasses* = openArray[TPass] # a pass is a tuple of procedure vars ``TPass.close`` may produce additional # nodes. These are passed to the other close procedures. # This mechanism used to be used for the instantiation of generics. proc makePass*(open: TPassOpen = nil, - openCached: TPassOpenCached = nil, process: TPassProcess = nil, - close: TPassClose = nil): TPass = + close: TPassClose = nil, + isFrontend = false): TPass = result.open = open - result.openCached = openCached result.close = close result.process = process - -# the semantic checker needs these: -var - gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.} - gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.} - -# implementation - -proc skipCodegen*(n: PNode): bool {.inline.} = - # can be used by codegen passes to determine whether they should do - # something with `n`. Currently, this ignores `n` and uses the global - # error count instead. - result = msgs.gErrorCounter > 0 - -proc astNeeded*(s: PSym): bool = - # The ``rodwrite`` module uses this to determine if the body of a proc - # needs to be stored. The passes manager frees s.sons[codePos] when - # appropriate to free the procedure body's memory. This is important - # to keep memory usage down. - if (s.kind in {skMethod, skProc, skFunc}) and - ({sfCompilerProc, sfCompileTime} * s.flags == {}) and - (s.typ.callConv != ccInline) and - (s.ast.sons[genericParamsPos].kind == nkEmpty): - result = false - # XXX this doesn't really make sense with excessive CTFE - else: - result = true + result.isFrontend = isFrontend const maxPasses = 10 @@ -80,152 +53,203 @@ const type TPassContextArray = array[0..maxPasses - 1, PPassContext] -var - gPasses: array[0..maxPasses - 1, TPass] - gPassesLen*: int - -proc clearPasses* = - gPassesLen = 0 - -proc registerPass*(p: TPass) = - gPasses[gPassesLen] = p - inc(gPassesLen) - -proc carryPass*(g: ModuleGraph; p: TPass, module: PSym; cache: IdentCache; - m: TPassData): TPassData = - var c = p.open(g, module, cache) - result.input = p.process(c, m.input) - result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput) - else: m.closeOutput +proc clearPasses*(g: ModuleGraph) = + g.passes.setLen(0) -proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym; - cache: IdentCache; passes: TPasses) = - var passdata: TPassData - passdata.input = nodes - for pass in passes: - passdata = carryPass(g, pass, module, cache, passdata) +proc registerPass*(g: ModuleGraph; p: TPass) = + internalAssert g.config, g.passes.len < maxPasses + g.passes.add(p) proc openPasses(g: ModuleGraph; a: var TPassContextArray; - module: PSym; cache: IdentCache) = - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].open): - a[i] = gPasses[i].open(g, module, cache) + module: PSym; idgen: IdGenerator) = + for i in 0..<g.passes.len: + if not isNil(g.passes[i].open): + a[i] = g.passes[i].open(g, module, idgen) else: a[i] = nil -proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym, - rd: PRodReader) = - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].openCached): - a[i] = gPasses[i].openCached(g, module, rd) - if a[i] != nil: - a[i].fromCache = true - else: - a[i] = nil - proc closePasses(graph: ModuleGraph; a: var TPassContextArray) = var m: PNode = nil - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].close): m = gPasses[i].close(graph, a[i], m) + for i in 0..<graph.passes.len: + if not isNil(graph.passes[i].close): + m = graph.passes[i].close(graph, a[i], m) a[i] = nil # free the memory here -proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool = +proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray): bool = # this implements the code transformation pipeline var m = n - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].process): - m = gPasses[i].process(a[i], m) + for i in 0..<graph.passes.len: + if not isNil(graph.passes[i].process): + m = graph.passes[i].process(a[i], m) if isNil(m): return false result = true -proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) = - # this implements the code transformation pipeline - var m = n - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m) - -proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) = - var m: PNode = nil - for i in countup(0, gPassesLen - 1): - if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close): - m = gPasses[i].close(graph, a[i], m) - a[i] = nil # free the memory here - -proc resolveMod(module, relativeTo: string): int32 = - let fullPath = findModule(module, relativeTo) - if fullPath.len == 0: - result = InvalidFileIDX - else: - result = fullPath.fileInfoIdx - -proc processImplicits(implicits: seq[string], nodeKind: TNodeKind, +proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind, a: var TPassContextArray; m: PSym) = # XXX fixme this should actually be relative to the config file! - let relativeTo = m.info.toFullPath + let relativeTo = toFullPath(graph.config, m.info) for module in items(implicits): # implicit imports should not lead to a module importing itself - if m.position != resolveMod(module, relativeTo): - var importStmt = newNodeI(nodeKind, gCmdLineInfo) + if m.position != resolveMod(graph.config, module, relativeTo).int32: + var importStmt = newNodeI(nodeKind, m.info) var str = newStrNode(nkStrLit, module) - str.info = gCmdLineInfo - importStmt.addSon str - if not processTopLevelStmt(importStmt, a): break + str.info = m.info + importStmt.add str + if not processTopLevelStmt(graph, importStmt, a): break -proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream, - rd: PRodReader; cache: IdentCache): bool {.discardable.} = +proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; + stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true var - p: TParsers + p: Parser a: TPassContextArray s: PLLStream fileIdx = module.fileIdx - if rd == nil: - openPasses(graph, a, module, cache) - if stream == nil: - let filename = fileIdx.toFullPathConsiderDirty - s = llStreamOpen(filename, fmRead) - if s == nil: - rawMessage(errCannotOpenFile, filename) - return false - else: - s = stream - while true: - openParsers(p, fileIdx, s, cache) - - if sfSystemModule notin module.flags: - # XXX what about caching? no processing then? what if I change the - # modules to include between compilation runs? we'd need to track that - # in ROD files. I think we should enable this feature only - # for the interactive mode. - processImplicits implicitImports, nkImportStmt, a, module - processImplicits implicitIncludes, nkIncludeStmt, a, module - + prepareConfigNotes(graph, module) + openPasses(graph, a, module, idgen) + if stream == nil: + let filename = toFullPathConsiderDirty(graph.config, fileIdx) + s = llStreamOpen(filename, fmRead) + if s == nil: + rawMessage(graph.config, errCannotOpenFile, filename.string) + return false + else: + s = stream + + when defined(nimsuggest): + let filename = toFullPathConsiderDirty(graph.config, fileIdx).string + msgs.setHash(graph.config, fileIdx, $sha1.secureHashFile(filename)) + + while true: + openParser(p, fileIdx, s, graph.cache, graph.config) + + if (not belongsToStdlib(graph, module)) or module.name.s == "distros": + # XXX what about caching? no processing then? what if I change the + # modules to include between compilation runs? we'd need to track that + # in ROD files. I think we should enable this feature only + # for the interactive mode. + if module.name.s != "nimscriptapi": + processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module + processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module + + checkFirstLineIndentation(p) + block processCode: + if graph.stopCompile(): break processCode + var n = parseTopLevelStmt(p) + if n.kind == nkEmpty: break processCode + + # read everything, no streaming possible + var sl = newNodeI(nkStmtList, n.info) + sl.add n while true: - if graph.stopCompile(): break var n = parseTopLevelStmt(p) if n.kind == nkEmpty: break - if {sfNoForward, sfReorder} * module.flags != {}: - # read everything, no streaming possible - var sl = newNodeI(nkStmtList, n.info) - sl.add n - while true: - var n = parseTopLevelStmt(p) - if n.kind == nkEmpty: break - sl.add n - if sfReorder in module.flags: - sl = reorder(graph, sl, module, cache) - discard processTopLevelStmt(sl, a) - break - elif not processTopLevelStmt(n, a): break - closeParsers(p) - if s.kind != llsStdIn: break - closePasses(graph, a) - # id synchronization point for more consistent code generation: - idSynchronizationPoint(1000) - else: - openPassesCached(graph, a, module, rd) - var n = loadInitSection(rd) - for i in countup(0, sonsLen(n) - 1): - if graph.stopCompile(): break - processTopLevelStmtCached(n.sons[i], a) - closePassesCached(graph, a) + sl.add n + if sfReorder in module.flags or codeReordering in graph.config.features: + sl = reorder(graph, sl, module) + discard processTopLevelStmt(graph, sl, a) + + closeParser(p) + if s.kind != llsStdIn: break + closePasses(graph, a) + if graph.config.backend notin {backendC, backendCpp, backendObjc}: + # We only write rod files here if no C-like backend is active. + # The C-like backends have been patched to support the IC mechanism. + # They are responsible for closing the rod files. See `cbackend.nim`. + closeRodFile(graph, module) result = true + +proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym = + var flags = flags + if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule + result = graph.getModule(fileIdx) + + template processModuleAux(moduleStatus) = + onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule) + var s: PLLStream = nil + if sfMainModule in flags: + if graph.config.projectIsStdin: s = stdin.llStreamOpen + elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput) + discard processModule(graph, result, idGeneratorFromModule(result), s) + if result == nil: + var cachedModules: seq[FileIndex] = @[] + result = moduleFromRodFile(graph, fileIdx, cachedModules) + let filename = AbsoluteFile toFullPath(graph.config, fileIdx) + if result == nil: + result = newModule(graph, fileIdx) + result.flags.incl flags + registerModule(graph, result) + processModuleAux("import") + else: + if sfSystemModule in flags: + graph.systemModule = result + partialInitModule(result, graph, fileIdx, filename) + for m in cachedModules: + registerModuleById(graph, m) + replayStateChanges(graph.packed.pm[m.int].module, graph) + replayGenericCacheInformation(graph, m.int) + elif graph.isDirty(result): + result.flags.excl sfDirty + # reset module fields: + initStrTables(graph, result) + result.ast = nil + processModuleAux("import(dirty)") + graph.markClientsDirty(fileIdx) + +proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = + # this is called by the semantic checking phase + assert graph.config != nil + result = compileModule(graph, fileIdx, {}, s) + graph.addDep(s, fileIdx) + # keep track of import relationships + if graph.config.hcrOn: + graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx) + #if sfSystemModule in result.flags: + # localError(result.info, errAttemptToRedefine, result.name.s) + # restore the notes for outer module: + graph.config.notes = + if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes + else: graph.config.foreignPackageNotes + +proc connectCallbacks*(graph: ModuleGraph) = + graph.includeFileCallback = modules.includeModule + graph.importModuleCallback = importModule + +proc compileSystemModule*(graph: ModuleGraph) = + if graph.systemModule == nil: + connectCallbacks(graph) + graph.config.m.systemFileIdx = fileInfoIdx(graph.config, + graph.config.libpath / RelativeFile"system.nim") + discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule}) + +proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = + connectCallbacks(graph) + let conf = graph.config + wantMainModule(conf) + configComplete(graph) + + let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim") + let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx + conf.projectMainIdx2 = projectFile + + let packSym = getPackage(graph, projectFile) + graph.config.mainPackageId = packSym.getPackageId + graph.importStack.add projectFile + + if projectFile == systemFileIdx: + discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule}) + else: + graph.compileSystemModule() + discard graph.compileModule(projectFile, {sfMainModule}) + +proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = + result = preparePContext(graph, module, idgen) + +proc mySemClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = + var c = PContext(context) + closePContext(graph, c, n) + +proc mySemProcess(context: PPassContext, n: PNode): PNode = + result = semWithPContext(PContext(context), n) + +const semPass* = makePass(mySemOpen, mySemProcess, mySemClose, + isFrontend = true) |