passes.nim 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. strutils, options, ast, astalgo, llstream, msgs, platform, os,
  13. condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
  14. nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod,
  15. lineinfos, pathutils
  16. type
  17. TPassContext* = object of RootObj # the pass's context
  18. PPassContext* = ref TPassContext
  19. TPassOpen* = proc (graph: ModuleGraph; module: PSym): PPassContext {.nimcall.}
  20. TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
  21. TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
  22. TPass* = tuple[open: TPassOpen, process: TPassProcess, close: TPassClose,
  23. isFrontend: bool]
  24. TPassData* = tuple[input: PNode, closeOutput: PNode]
  25. TPasses* = openArray[TPass]
  26. # a pass is a tuple of procedure vars ``TPass.close`` may produce additional
  27. # nodes. These are passed to the other close procedures.
  28. # This mechanism used to be used for the instantiation of generics.
  29. proc makePass*(open: TPassOpen = nil,
  30. process: TPassProcess = nil,
  31. close: TPassClose = nil,
  32. isFrontend = false): TPass =
  33. result.open = open
  34. result.close = close
  35. result.process = process
  36. result.isFrontend = isFrontend
  37. proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
  38. # can be used by codegen passes to determine whether they should do
  39. # something with `n`. Currently, this ignores `n` and uses the global
  40. # error count instead.
  41. result = config.errorCounter > 0
  42. const
  43. maxPasses = 10
  44. type
  45. TPassContextArray = array[0..maxPasses - 1, PPassContext]
  46. var
  47. gPasses: array[0..maxPasses - 1, TPass]
  48. gPassesLen*: int
  49. proc clearPasses*(g: ModuleGraph) =
  50. gPassesLen = 0
  51. proc registerPass*(g: ModuleGraph; p: TPass) =
  52. gPasses[gPassesLen] = p
  53. inc(gPassesLen)
  54. proc carryPass*(g: ModuleGraph; p: TPass, module: PSym;
  55. m: TPassData): TPassData =
  56. var c = p.open(g, module)
  57. result.input = p.process(c, m.input)
  58. result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput)
  59. else: m.closeOutput
  60. proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym;
  61. passes: TPasses) =
  62. var passdata: TPassData
  63. passdata.input = nodes
  64. for pass in passes:
  65. passdata = carryPass(g, pass, module, passdata)
  66. proc openPasses(g: ModuleGraph; a: var TPassContextArray;
  67. module: PSym) =
  68. for i in countup(0, gPassesLen - 1):
  69. if not isNil(gPasses[i].open):
  70. a[i] = gPasses[i].open(g, module)
  71. else: a[i] = nil
  72. proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
  73. var m: PNode = nil
  74. for i in countup(0, gPassesLen - 1):
  75. if not isNil(gPasses[i].close): m = gPasses[i].close(graph, a[i], m)
  76. a[i] = nil # free the memory here
  77. proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
  78. # this implements the code transformation pipeline
  79. var m = n
  80. for i in countup(0, gPassesLen - 1):
  81. if not isNil(gPasses[i].process):
  82. m = gPasses[i].process(a[i], m)
  83. if isNil(m): return false
  84. result = true
  85. proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
  86. let fullPath = findModule(conf, module, relativeTo)
  87. if fullPath.isEmpty:
  88. result = InvalidFileIDX
  89. else:
  90. result = fileInfoIdx(conf, fullPath)
  91. proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKind,
  92. a: var TPassContextArray; m: PSym) =
  93. # XXX fixme this should actually be relative to the config file!
  94. let gCmdLineInfo = newLineInfo(FileIndex(0), 1, 1)
  95. let relativeTo = toFullPath(conf, m.info)
  96. for module in items(implicits):
  97. # implicit imports should not lead to a module importing itself
  98. if m.position != resolveMod(conf, module, relativeTo).int32:
  99. var importStmt = newNodeI(nodeKind, gCmdLineInfo)
  100. var str = newStrNode(nkStrLit, module)
  101. str.info = gCmdLineInfo
  102. importStmt.addSon str
  103. if not processTopLevelStmt(importStmt, a): break
  104. const
  105. imperativeCode = {low(TNodeKind)..high(TNodeKind)} - {nkTemplateDef, nkProcDef, nkMethodDef,
  106. nkMacroDef, nkConverterDef, nkIteratorDef, nkFuncDef, nkPragma,
  107. nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt}
  108. proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} =
  109. if graph.stopCompile(): return true
  110. var
  111. p: TParsers
  112. a: TPassContextArray
  113. s: PLLStream
  114. fileIdx = module.fileIdx
  115. if module.id < 0:
  116. # new module caching mechanism:
  117. for i in 0..<gPassesLen:
  118. if not isNil(gPasses[i].open) and not gPasses[i].isFrontend:
  119. a[i] = gPasses[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..<gPassesLen:
  126. if not isNil(gPasses[i].process) and not gPasses[i].isFrontend:
  127. m = gPasses[i].process(a[i], m)
  128. if isNil(m):
  129. break
  130. var m: PNode = nil
  131. for i in 0..<gPassesLen:
  132. if not isNil(gPasses[i].close) and not gPasses[i].isFrontend:
  133. m = gPasses[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 sfSystemModule notin module.flags:
  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. processImplicits graph.config, graph.config.implicitImports, nkImportStmt, a, module
  153. processImplicits graph.config, graph.config.implicitIncludes, nkIncludeStmt, a, module
  154. while true:
  155. if graph.stopCompile(): break
  156. var n = parseTopLevelStmt(p)
  157. if n.kind == nkEmpty: break
  158. if {sfNoForward, sfReorder} * module.flags != {}:
  159. # read everything, no streaming possible
  160. var sl = newNodeI(nkStmtList, n.info)
  161. sl.add n
  162. while true:
  163. var n = parseTopLevelStmt(p)
  164. if n.kind == nkEmpty: break
  165. sl.add n
  166. if sfReorder in module.flags:
  167. sl = reorder(graph, sl, module)
  168. discard processTopLevelStmt(sl, a)
  169. break
  170. elif n.kind in imperativeCode:
  171. # read everything until the next proc declaration etc.
  172. var sl = newNodeI(nkStmtList, n.info)
  173. sl.add n
  174. var rest: PNode = nil
  175. while true:
  176. var n = parseTopLevelStmt(p)
  177. if n.kind == nkEmpty or n.kind notin imperativeCode:
  178. rest = n
  179. break
  180. sl.add n
  181. #echo "-----\n", sl
  182. if not processTopLevelStmt(sl, a): break
  183. if rest != nil:
  184. #echo "-----\n", rest
  185. if not processTopLevelStmt(rest, a): break
  186. else:
  187. #echo "----- single\n", n
  188. if not processTopLevelStmt(n, a): break
  189. closeParsers(p)
  190. if s.kind != llsStdIn: break
  191. closePasses(graph, a)
  192. # id synchronization point for more consistent code generation:
  193. idSynchronizationPoint(1000)
  194. result = true