pipelines.nim 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
  2. lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
  3. packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg,
  4. liftdestructors
  5. import pipelineutils
  6. import ../dist/checksums/src/checksums/sha1
  7. when not defined(leanCompiler):
  8. import jsgen, docgen2
  9. import std/[syncio, objectdollar, assertions, tables, strutils, strtabs]
  10. import renderer
  11. import ic/replayer
  12. import nir/nir
  13. proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
  14. graph.pipelinePass = pass
  15. proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): PNode =
  16. case graph.pipelinePass
  17. of CgenPass:
  18. result = semNode
  19. if bModule != nil:
  20. genTopLevelStmt(BModule(bModule), result)
  21. of JSgenPass:
  22. when not defined(leanCompiler):
  23. result = processJSCodeGen(bModule, semNode)
  24. else:
  25. result = nil
  26. of GenDependPass:
  27. result = addDotDependency(bModule, semNode)
  28. of SemPass:
  29. result = graph.emptyNode
  30. of Docgen2Pass, Docgen2TexPass:
  31. when not defined(leanCompiler):
  32. result = processNode(bModule, semNode)
  33. else:
  34. result = nil
  35. of Docgen2JsonPass:
  36. when not defined(leanCompiler):
  37. result = processNodeJson(bModule, semNode)
  38. else:
  39. result = nil
  40. of EvalPass, InterpreterPass:
  41. result = interpreterCode(bModule, semNode)
  42. of NirReplPass:
  43. result = runCode(bModule, semNode)
  44. of NirPass:
  45. result = nirBackend(bModule, semNode)
  46. of NonePass:
  47. raiseAssert "use setPipeLinePass to set a proper PipelinePass"
  48. proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
  49. m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
  50. ) =
  51. # XXX fixme this should actually be relative to the config file!
  52. let relativeTo = toFullPath(graph.config, m.info)
  53. for module in items(implicits):
  54. # implicit imports should not lead to a module importing itself
  55. if m.position != resolveMod(graph.config, module, relativeTo).int32:
  56. var importStmt = newNodeI(nodeKind, m.info)
  57. var str = newStrNode(nkStrLit, module)
  58. str.info = m.info
  59. importStmt.add str
  60. message(graph.config, importStmt.info, hintProcessingStmt, $idgen[])
  61. let semNode = semWithPContext(ctx, importStmt)
  62. if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
  63. break
  64. proc prePass(c: PContext; n: PNode) =
  65. for son in n:
  66. if son.kind == nkPragma:
  67. for s in son:
  68. var key = if s.kind in nkPragmaCallKinds and s.len > 1: s[0] else: s
  69. if key.kind in {nkBracketExpr, nkCast} or key.kind notin nkIdentKinds:
  70. continue
  71. let ident = whichKeyword(considerQuotedIdent(c, key))
  72. case ident
  73. of wReorder:
  74. pragmaNoForward(c, s, flag = sfReorder)
  75. of wExperimental:
  76. if isTopLevel(c) and s.kind in nkPragmaCallKinds and s.len == 2:
  77. let name = c.semConstExpr(c, s[1])
  78. case name.kind
  79. of nkStrLit, nkRStrLit, nkTripleStrLit:
  80. try:
  81. let feature = parseEnum[Feature](name.strVal)
  82. if feature == codeReordering:
  83. c.features.incl feature
  84. c.module.flags.incl sfReorder
  85. except ValueError:
  86. discard
  87. else:
  88. discard
  89. else:
  90. discard
  91. proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
  92. stream: PLLStream): bool =
  93. if graph.stopCompile(): return true
  94. var
  95. p: Parser
  96. s: PLLStream
  97. fileIdx = module.fileIdx
  98. prepareConfigNotes(graph, module)
  99. let ctx = preparePContext(graph, module, idgen)
  100. let bModule: PPassContext =
  101. case graph.pipelinePass
  102. of CgenPass:
  103. setupCgen(graph, module, idgen)
  104. of NirPass:
  105. openNirBackend(graph, module, idgen)
  106. of JSgenPass:
  107. when not defined(leanCompiler):
  108. setupJSgen(graph, module, idgen)
  109. else:
  110. nil
  111. of EvalPass, InterpreterPass:
  112. setupEvalGen(graph, module, idgen)
  113. of NirReplPass:
  114. setupNirReplGen(graph, module, idgen)
  115. of GenDependPass:
  116. setupDependPass(graph, module, idgen)
  117. of Docgen2Pass:
  118. when not defined(leanCompiler):
  119. openHtml(graph, module, idgen)
  120. else:
  121. nil
  122. of Docgen2TexPass:
  123. when not defined(leanCompiler):
  124. openTex(graph, module, idgen)
  125. else:
  126. nil
  127. of Docgen2JsonPass:
  128. when not defined(leanCompiler):
  129. openJson(graph, module, idgen)
  130. else:
  131. nil
  132. of SemPass:
  133. nil
  134. of NonePass:
  135. raiseAssert "use setPipeLinePass to set a proper PipelinePass"
  136. if stream == nil:
  137. let filename = toFullPathConsiderDirty(graph.config, fileIdx)
  138. s = llStreamOpen(filename, fmRead)
  139. if s == nil:
  140. rawMessage(graph.config, errCannotOpenFile, filename.string)
  141. return false
  142. graph.interactive = false
  143. else:
  144. s = stream
  145. graph.interactive = stream.kind == llsStdIn
  146. while true:
  147. syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config)
  148. if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"):
  149. # XXX what about caching? no processing then? what if I change the
  150. # modules to include between compilation runs? we'd need to track that
  151. # in ROD files. I think we should enable this feature only
  152. # for the interactive mode.
  153. if module.name.s != "nimscriptapi":
  154. processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen
  155. processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen
  156. checkFirstLineIndentation(p)
  157. block processCode:
  158. if graph.stopCompile(): break processCode
  159. var n = parseTopLevelStmt(p)
  160. if n.kind == nkEmpty: break processCode
  161. # read everything, no streaming possible
  162. var sl = newNodeI(nkStmtList, n.info)
  163. sl.add n
  164. while true:
  165. var n = parseTopLevelStmt(p)
  166. if n.kind == nkEmpty: break
  167. sl.add n
  168. prePass(ctx, sl)
  169. if sfReorder in module.flags or codeReordering in graph.config.features:
  170. sl = reorder(graph, sl, module)
  171. if graph.pipelinePass != EvalPass:
  172. message(graph.config, sl.info, hintProcessingStmt, $idgen[])
  173. var semNode = semWithPContext(ctx, sl)
  174. discard processPipeline(graph, semNode, bModule)
  175. closeParser(p)
  176. if s.kind != llsStdIn: break
  177. let finalNode = closePContext(graph, ctx, nil)
  178. case graph.pipelinePass
  179. of CgenPass:
  180. if bModule != nil:
  181. let m = BModule(bModule)
  182. finalCodegenActions(graph, m, finalNode)
  183. if graph.dispatchers.len > 0:
  184. let ctx = preparePContext(graph, module, idgen)
  185. for disp in getDispatchers(graph):
  186. let retTyp = disp.typ.returnType
  187. if retTyp != nil:
  188. # TODO: properly semcheck the code of dispatcher?
  189. createTypeBoundOps(graph, ctx, retTyp, disp.ast.info, idgen)
  190. genProcAux(m, disp)
  191. discard closePContext(graph, ctx, nil)
  192. of JSgenPass:
  193. when not defined(leanCompiler):
  194. discard finalJSCodeGen(graph, bModule, finalNode)
  195. of EvalPass, InterpreterPass:
  196. discard interpreterCode(bModule, finalNode)
  197. of NirReplPass:
  198. discard runCode(bModule, finalNode)
  199. of NirPass:
  200. closeNirBackend(bModule, finalNode)
  201. of SemPass, GenDependPass:
  202. discard
  203. of Docgen2Pass, Docgen2TexPass:
  204. when not defined(leanCompiler):
  205. discard closeDoc(graph, bModule, finalNode)
  206. of Docgen2JsonPass:
  207. when not defined(leanCompiler):
  208. discard closeJson(graph, bModule, finalNode)
  209. of NonePass:
  210. raiseAssert "use setPipeLinePass to set a proper PipelinePass"
  211. if graph.config.backend notin {backendC, backendCpp, backendObjc}:
  212. # We only write rod files here if no C-like backend is active.
  213. # The C-like backends have been patched to support the IC mechanism.
  214. # They are responsible for closing the rod files. See `cbackend.nim`.
  215. closeRodFile(graph, module)
  216. result = true
  217. proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags; fromModule: PSym = nil): PSym =
  218. var flags = flags
  219. if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
  220. result = graph.getModule(fileIdx)
  221. template processModuleAux(moduleStatus) =
  222. onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
  223. var s: PLLStream = nil
  224. if sfMainModule in flags:
  225. if graph.config.projectIsStdin: s = stdin.llStreamOpen
  226. elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
  227. discard processPipelineModule(graph, result, idGeneratorFromModule(result), s)
  228. if result == nil:
  229. var cachedModules: seq[FileIndex] = @[]
  230. result = moduleFromRodFile(graph, fileIdx, cachedModules)
  231. let path = toFullPath(graph.config, fileIdx)
  232. let filename = AbsoluteFile path
  233. if fileExists(filename): # it could be a stdinfile
  234. graph.cachedFiles[path] = $secureHashFile(path)
  235. if result == nil:
  236. result = newModule(graph, fileIdx)
  237. result.flags.incl flags
  238. registerModule(graph, result)
  239. processModuleAux("import")
  240. else:
  241. if sfSystemModule in flags:
  242. graph.systemModule = result
  243. if sfMainModule in flags and graph.config.cmd == cmdM:
  244. result.flags.incl flags
  245. registerModule(graph, result)
  246. processModuleAux("import")
  247. partialInitModule(result, graph, fileIdx, filename)
  248. for m in cachedModules:
  249. registerModuleById(graph, m)
  250. if sfMainModule in flags and graph.config.cmd == cmdM:
  251. discard
  252. else:
  253. replayStateChanges(graph.packed.pm[m.int].module, graph)
  254. replayGenericCacheInformation(graph, m.int)
  255. elif graph.isDirty(result):
  256. result.flags.excl sfDirty
  257. # reset module fields:
  258. initStrTables(graph, result)
  259. result.ast = nil
  260. processModuleAux("import(dirty)")
  261. graph.markClientsDirty(fileIdx)
  262. proc importPipelineModule(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
  263. # this is called by the semantic checking phase
  264. assert graph.config != nil
  265. result = compilePipelineModule(graph, fileIdx, {}, s)
  266. graph.addDep(s, fileIdx)
  267. # keep track of import relationships
  268. if graph.config.hcrOn:
  269. graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
  270. #if sfSystemModule in result.flags:
  271. # localError(result.info, errAttemptToRedefine, result.name.s)
  272. # restore the notes for outer module:
  273. graph.config.notes =
  274. if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
  275. else: graph.config.foreignPackageNotes
  276. proc connectPipelineCallbacks*(graph: ModuleGraph) =
  277. graph.includeFileCallback = modules.includeModule
  278. graph.importModuleCallback = importPipelineModule
  279. proc compilePipelineSystemModule*(graph: ModuleGraph) =
  280. if graph.systemModule == nil:
  281. connectPipelineCallbacks(graph)
  282. graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
  283. graph.config.libpath / RelativeFile"system.nim")
  284. discard graph.compilePipelineModule(graph.config.m.systemFileIdx, {sfSystemModule})
  285. proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
  286. connectPipelineCallbacks(graph)
  287. let conf = graph.config
  288. wantMainModule(conf)
  289. configComplete(graph)
  290. let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
  291. let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
  292. conf.projectMainIdx2 = projectFile
  293. let packSym = getPackage(graph, projectFile)
  294. graph.config.mainPackageId = packSym.getPackageId
  295. graph.importStack.add projectFile
  296. if projectFile == systemFileIdx:
  297. discard graph.compilePipelineModule(projectFile, {sfMainModule, sfSystemModule})
  298. else:
  299. graph.compilePipelineSystemModule()
  300. discard graph.compilePipelineModule(projectFile, {sfMainModule})