passes.nim 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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, modulegraphs, reorder,
  15. lineinfos, pathutils, packages
  16. when defined(nimsuggest):
  17. import std/sha1
  18. when defined(nimPreviewSlimSystem):
  19. import std/syncio
  20. type
  21. TPassData* = tuple[input: PNode, closeOutput: PNode]
  22. # a pass is a tuple of procedure vars ``TPass.close`` may produce additional
  23. # nodes. These are passed to the other close procedures.
  24. # This mechanism used to be used for the instantiation of generics.
  25. proc makePass*(open: TPassOpen = nil,
  26. process: TPassProcess = nil,
  27. close: TPassClose = nil,
  28. isFrontend = false): TPass =
  29. result.open = open
  30. result.close = close
  31. result.process = process
  32. result.isFrontend = isFrontend
  33. proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
  34. # can be used by codegen passes to determine whether they should do
  35. # something with `n`. Currently, this ignores `n` and uses the global
  36. # error count instead.
  37. result = config.errorCounter > 0
  38. const
  39. maxPasses = 10
  40. type
  41. TPassContextArray = array[0..maxPasses - 1, PPassContext]
  42. proc clearPasses*(g: ModuleGraph) =
  43. g.passes.setLen(0)
  44. proc registerPass*(g: ModuleGraph; p: TPass) =
  45. internalAssert g.config, g.passes.len < maxPasses
  46. g.passes.add(p)
  47. proc openPasses(g: ModuleGraph; a: var TPassContextArray;
  48. module: PSym; idgen: IdGenerator) =
  49. for i in 0..<g.passes.len:
  50. if not isNil(g.passes[i].open):
  51. a[i] = g.passes[i].open(g, module, idgen)
  52. else: a[i] = nil
  53. proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
  54. var m: PNode = nil
  55. for i in 0..<graph.passes.len:
  56. if not isNil(graph.passes[i].close):
  57. m = graph.passes[i].close(graph, a[i], m)
  58. a[i] = nil # free the memory here
  59. proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray): bool =
  60. # this implements the code transformation pipeline
  61. var m = n
  62. for i in 0..<graph.passes.len:
  63. if not isNil(graph.passes[i].process):
  64. m = graph.passes[i].process(a[i], m)
  65. if isNil(m): return false
  66. result = true
  67. proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
  68. let fullPath = findModule(conf, module, relativeTo)
  69. if fullPath.isEmpty:
  70. result = InvalidFileIdx
  71. else:
  72. result = fileInfoIdx(conf, fullPath)
  73. proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
  74. a: var TPassContextArray; m: PSym) =
  75. # XXX fixme this should actually be relative to the config file!
  76. let relativeTo = toFullPath(graph.config, m.info)
  77. for module in items(implicits):
  78. # implicit imports should not lead to a module importing itself
  79. if m.position != resolveMod(graph.config, module, relativeTo).int32:
  80. var importStmt = newNodeI(nodeKind, m.info)
  81. var str = newStrNode(nkStrLit, module)
  82. str.info = m.info
  83. importStmt.add str
  84. if not processTopLevelStmt(graph, importStmt, a): break
  85. const
  86. imperativeCode = {low(TNodeKind)..high(TNodeKind)} - {nkTemplateDef, nkProcDef, nkMethodDef,
  87. nkMacroDef, nkConverterDef, nkIteratorDef, nkFuncDef, nkPragma,
  88. nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt}
  89. proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
  90. # don't be verbose unless the module belongs to the main package:
  91. if graph.config.belongsToProjectPackage(module):
  92. graph.config.notes = graph.config.mainPackageNotes
  93. else:
  94. if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
  95. graph.config.notes = graph.config.foreignPackageNotes
  96. proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
  97. result = true
  98. #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
  99. proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
  100. stream: PLLStream): bool {.discardable.} =
  101. if graph.stopCompile(): return true
  102. var
  103. p: Parser
  104. a: TPassContextArray
  105. s: PLLStream
  106. fileIdx = module.fileIdx
  107. prepareConfigNotes(graph, module)
  108. openPasses(graph, a, module, idgen)
  109. if stream == nil:
  110. let filename = toFullPathConsiderDirty(graph.config, fileIdx)
  111. s = llStreamOpen(filename, fmRead)
  112. if s == nil:
  113. rawMessage(graph.config, errCannotOpenFile, filename.string)
  114. return false
  115. else:
  116. s = stream
  117. when defined(nimsuggest):
  118. let filename = toFullPathConsiderDirty(graph.config, fileIdx).string
  119. msgs.setHash(graph.config, fileIdx, $sha1.secureHashFile(filename))
  120. while true:
  121. openParser(p, fileIdx, s, graph.cache, graph.config)
  122. if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"):
  123. # XXX what about caching? no processing then? what if I change the
  124. # modules to include between compilation runs? we'd need to track that
  125. # in ROD files. I think we should enable this feature only
  126. # for the interactive mode.
  127. if module.name.s != "nimscriptapi":
  128. processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module
  129. processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module
  130. checkFirstLineIndentation(p)
  131. while true:
  132. if graph.stopCompile(): break
  133. var n = parseTopLevelStmt(p)
  134. if n.kind == nkEmpty: break
  135. if (sfSystemModule notin module.flags and
  136. ({sfNoForward, sfReorder} * module.flags != {} or
  137. codeReordering in graph.config.features)):
  138. # read everything, no streaming possible
  139. var sl = newNodeI(nkStmtList, n.info)
  140. sl.add n
  141. while true:
  142. var n = parseTopLevelStmt(p)
  143. if n.kind == nkEmpty: break
  144. sl.add n
  145. if sfReorder in module.flags or codeReordering in graph.config.features:
  146. sl = reorder(graph, sl, module)
  147. discard processTopLevelStmt(graph, sl, a)
  148. break
  149. elif n.kind in imperativeCode:
  150. # read everything until the next proc declaration etc.
  151. var sl = newNodeI(nkStmtList, n.info)
  152. sl.add n
  153. var rest: PNode = nil
  154. while true:
  155. var n = parseTopLevelStmt(p)
  156. if n.kind == nkEmpty or n.kind notin imperativeCode:
  157. rest = n
  158. break
  159. sl.add n
  160. #echo "-----\n", sl
  161. if not processTopLevelStmt(graph, sl, a): break
  162. if rest != nil:
  163. #echo "-----\n", rest
  164. if not processTopLevelStmt(graph, rest, a): break
  165. else:
  166. #echo "----- single\n", n
  167. if not processTopLevelStmt(graph, n, a): break
  168. closeParser(p)
  169. if s.kind != llsStdIn: break
  170. closePasses(graph, a)
  171. if graph.config.backend notin {backendC, backendCpp, backendObjc}:
  172. # We only write rod files here if no C-like backend is active.
  173. # The C-like backends have been patched to support the IC mechanism.
  174. # They are responsible for closing the rod files. See `cbackend.nim`.
  175. closeRodFile(graph, module)
  176. result = true