passes.nim 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## This module implements the passes functionality. A pass must implement the
  10. ## `TPass` interface.
  11. import
  12. options, ast, llstream, msgs,
  13. idents,
  14. syntaxes, idgen, modulegraphs, reorder, rod,
  15. lineinfos, pathutils
  16. type
  17. TPassData* = tuple[input: PNode, closeOutput: PNode]
  18. # a pass is a tuple of procedure vars ``TPass.close`` may produce additional
  19. # nodes. These are passed to the other close procedures.
  20. # This mechanism used to be used for the instantiation of generics.
  21. proc makePass*(open: TPassOpen = nil,
  22. process: TPassProcess = nil,
  23. close: TPassClose = nil,
  24. isFrontend = false): TPass =
  25. result.open = open
  26. result.close = close
  27. result.process = process
  28. result.isFrontend = isFrontend
  29. proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
  30. # can be used by codegen passes to determine whether they should do
  31. # something with `n`. Currently, this ignores `n` and uses the global
  32. # error count instead.
  33. result = config.errorCounter > 0
  34. const
  35. maxPasses = 10
  36. type
  37. TPassContextArray = array[0..maxPasses - 1, PPassContext]
  38. proc clearPasses*(g: ModuleGraph) =
  39. g.passes.setLen(0)
  40. proc registerPass*(g: ModuleGraph; p: TPass) =
  41. internalAssert g.config, g.passes.len < maxPasses
  42. g.passes.add(p)
  43. proc carryPass*(g: ModuleGraph; p: TPass, module: PSym;
  44. m: TPassData): TPassData =
  45. var c = p.open(g, module)
  46. result.input = p.process(c, m.input)
  47. result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput)
  48. else: m.closeOutput
  49. proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym;
  50. passes: openArray[TPass]) =
  51. var passdata: TPassData
  52. passdata.input = nodes
  53. for pass in passes:
  54. passdata = carryPass(g, pass, module, passdata)
  55. proc openPasses(g: ModuleGraph; a: var TPassContextArray;
  56. module: PSym) =
  57. for i in 0..<g.passes.len:
  58. if not isNil(g.passes[i].open):
  59. a[i] = g.passes[i].open(g, module)
  60. else: a[i] = nil
  61. proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
  62. var m: PNode = nil
  63. for i in 0..<graph.passes.len:
  64. if not isNil(graph.passes[i].close): m = graph.passes[i].close(graph, a[i], m)
  65. a[i] = nil # free the memory here
  66. proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray): bool =
  67. # this implements the code transformation pipeline
  68. var m = n
  69. for i in 0..<graph.passes.len:
  70. if not isNil(graph.passes[i].process):
  71. m = graph.passes[i].process(a[i], m)
  72. if isNil(m): return false
  73. result = true
  74. proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
  75. let fullPath = findModule(conf, module, relativeTo)
  76. if fullPath.isEmpty:
  77. result = InvalidFileIdx
  78. else:
  79. result = fileInfoIdx(conf, fullPath)
  80. proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
  81. a: var TPassContextArray; m: PSym) =
  82. # XXX fixme this should actually be relative to the config file!
  83. let relativeTo = toFullPath(graph.config, m.info)
  84. for module in items(implicits):
  85. # implicit imports should not lead to a module importing itself
  86. if m.position != resolveMod(graph.config, module, relativeTo).int32:
  87. var importStmt = newNodeI(nodeKind, m.info)
  88. var str = newStrNode(nkStrLit, module)
  89. str.info = m.info
  90. importStmt.add str
  91. if not processTopLevelStmt(graph, importStmt, a): break
  92. const
  93. imperativeCode = {low(TNodeKind)..high(TNodeKind)} - {nkTemplateDef, nkProcDef, nkMethodDef,
  94. nkMacroDef, nkConverterDef, nkIteratorDef, nkFuncDef, nkPragma,
  95. nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt}
  96. proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
  97. if sfMainModule in module.flags:
  98. graph.config.mainPackageId = module.owner.id
  99. # don't be verbose unless the module belongs to the main package:
  100. if module.owner.id == graph.config.mainPackageId:
  101. graph.config.notes = graph.config.mainPackageNotes
  102. else:
  103. if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
  104. graph.config.notes = graph.config.foreignPackageNotes
  105. proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
  106. result = module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
  107. proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} =
  108. if graph.stopCompile(): return true
  109. var
  110. p: TParsers
  111. a: TPassContextArray
  112. s: PLLStream
  113. fileIdx = module.fileIdx
  114. prepareConfigNotes(graph, module)
  115. if module.id < 0:
  116. # new module caching mechanism:
  117. for i in 0..<graph.passes.len:
  118. if not isNil(graph.passes[i].open) and not graph.passes[i].isFrontend:
  119. a[i] = graph.passes[i].open(graph, module)
  120. else:
  121. a[i] = nil
  122. if not graph.stopCompile():
  123. let n = loadNode(graph, module)
  124. var m = n
  125. for i in 0..<graph.passes.len:
  126. if not isNil(graph.passes[i].process) and not graph.passes[i].isFrontend:
  127. m = graph.passes[i].process(a[i], m)
  128. if isNil(m):
  129. break
  130. var m: PNode = nil
  131. for i in 0..<graph.passes.len:
  132. if not isNil(graph.passes[i].close) and not graph.passes[i].isFrontend:
  133. m = graph.passes[i].close(graph, a[i], m)
  134. a[i] = nil
  135. else:
  136. openPasses(graph, a, module)
  137. if stream == nil:
  138. let filename = toFullPathConsiderDirty(graph.config, fileIdx)
  139. s = llStreamOpen(filename, fmRead)
  140. if s == nil:
  141. rawMessage(graph.config, errCannotOpenFile, filename.string)
  142. return false
  143. else:
  144. s = stream
  145. while true:
  146. openParsers(p, fileIdx, s, graph.cache, graph.config)
  147. if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros":
  148. # XXX what about caching? no processing then? what if I change the
  149. # modules to include between compilation runs? we'd need to track that
  150. # in ROD files. I think we should enable this feature only
  151. # for the interactive mode.
  152. if module.name.s != "nimscriptapi":
  153. processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module
  154. processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module
  155. while true:
  156. if graph.stopCompile(): break
  157. var n = parseTopLevelStmt(p)
  158. if n.kind == nkEmpty: break
  159. if (sfSystemModule notin module.flags and
  160. ({sfNoForward, sfReorder} * module.flags != {} or
  161. codeReordering in graph.config.features)):
  162. # read everything, no streaming possible
  163. var sl = newNodeI(nkStmtList, n.info)
  164. sl.add n
  165. while true:
  166. var n = parseTopLevelStmt(p)
  167. if n.kind == nkEmpty: break
  168. sl.add n
  169. if sfReorder in module.flags or codeReordering in graph.config.features:
  170. sl = reorder(graph, sl, module)
  171. discard processTopLevelStmt(graph, sl, a)
  172. break
  173. elif n.kind in imperativeCode:
  174. # read everything until the next proc declaration etc.
  175. var sl = newNodeI(nkStmtList, n.info)
  176. sl.add n
  177. var rest: PNode = nil
  178. while true:
  179. var n = parseTopLevelStmt(p)
  180. if n.kind == nkEmpty or n.kind notin imperativeCode:
  181. rest = n
  182. break
  183. sl.add n
  184. #echo "-----\n", sl
  185. if not processTopLevelStmt(graph, sl, a): break
  186. if rest != nil:
  187. #echo "-----\n", rest
  188. if not processTopLevelStmt(graph, rest, a): break
  189. else:
  190. #echo "----- single\n", n
  191. if not processTopLevelStmt(graph, n, a): break
  192. closeParsers(p)
  193. if s.kind != llsStdIn: break
  194. closePasses(graph, a)
  195. # id synchronization point for more consistent code generation:
  196. idSynchronizationPoint(1000)
  197. result = true