passes.nim 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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, rodread, idgen, modulegraphs, reorder
  15. type
  16. TPassContext* = object of RootObj # the pass's context
  17. fromCache*: bool # true if created by "openCached"
  18. PPassContext* = ref TPassContext
  19. TPassOpen* = proc (graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext {.nimcall.}
  20. TPassOpenCached* =
  21. proc (graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext {.nimcall.}
  22. TPassClose* = proc (graph: ModuleGraph; p: PPassContext, n: PNode): PNode {.nimcall.}
  23. TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
  24. TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached,
  25. process: TPassProcess, close: TPassClose]
  26. TPassData* = tuple[input: PNode, closeOutput: PNode]
  27. TPasses* = openArray[TPass]
  28. # a pass is a tuple of procedure vars ``TPass.close`` may produce additional
  29. # nodes. These are passed to the other close procedures.
  30. # This mechanism used to be used for the instantiation of generics.
  31. proc makePass*(open: TPassOpen = nil,
  32. openCached: TPassOpenCached = nil,
  33. process: TPassProcess = nil,
  34. close: TPassClose = nil): TPass =
  35. result.open = open
  36. result.openCached = openCached
  37. result.close = close
  38. result.process = process
  39. # the semantic checker needs these:
  40. var
  41. gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.}
  42. gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.}
  43. # implementation
  44. proc skipCodegen*(n: PNode): bool {.inline.} =
  45. # can be used by codegen passes to determine whether they should do
  46. # something with `n`. Currently, this ignores `n` and uses the global
  47. # error count instead.
  48. result = msgs.gErrorCounter > 0
  49. proc astNeeded*(s: PSym): bool =
  50. # The ``rodwrite`` module uses this to determine if the body of a proc
  51. # needs to be stored. The passes manager frees s.sons[codePos] when
  52. # appropriate to free the procedure body's memory. This is important
  53. # to keep memory usage down.
  54. if (s.kind in {skMethod, skProc, skFunc}) and
  55. ({sfCompilerProc, sfCompileTime} * s.flags == {}) and
  56. (s.typ.callConv != ccInline) and
  57. (s.ast.sons[genericParamsPos].kind == nkEmpty):
  58. result = false
  59. # XXX this doesn't really make sense with excessive CTFE
  60. else:
  61. result = true
  62. const
  63. maxPasses = 10
  64. type
  65. TPassContextArray = array[0..maxPasses - 1, PPassContext]
  66. var
  67. gPasses: array[0..maxPasses - 1, TPass]
  68. gPassesLen*: int
  69. proc clearPasses* =
  70. gPassesLen = 0
  71. proc registerPass*(p: TPass) =
  72. gPasses[gPassesLen] = p
  73. inc(gPassesLen)
  74. proc carryPass*(g: ModuleGraph; p: TPass, module: PSym; cache: IdentCache;
  75. m: TPassData): TPassData =
  76. var c = p.open(g, module, cache)
  77. result.input = p.process(c, m.input)
  78. result.closeOutput = if p.close != nil: p.close(g, c, m.closeOutput)
  79. else: m.closeOutput
  80. proc carryPasses*(g: ModuleGraph; nodes: PNode, module: PSym;
  81. cache: IdentCache; passes: TPasses) =
  82. var passdata: TPassData
  83. passdata.input = nodes
  84. for pass in passes:
  85. passdata = carryPass(g, pass, module, cache, passdata)
  86. proc openPasses(g: ModuleGraph; a: var TPassContextArray;
  87. module: PSym; cache: IdentCache) =
  88. for i in countup(0, gPassesLen - 1):
  89. if not isNil(gPasses[i].open):
  90. a[i] = gPasses[i].open(g, module, cache)
  91. else: a[i] = nil
  92. proc openPassesCached(g: ModuleGraph; a: var TPassContextArray, module: PSym,
  93. rd: PRodReader) =
  94. for i in countup(0, gPassesLen - 1):
  95. if not isNil(gPasses[i].openCached):
  96. a[i] = gPasses[i].openCached(g, module, rd)
  97. if a[i] != nil:
  98. a[i].fromCache = true
  99. else:
  100. a[i] = nil
  101. proc closePasses(graph: ModuleGraph; a: var TPassContextArray) =
  102. var m: PNode = nil
  103. for i in countup(0, gPassesLen - 1):
  104. if not isNil(gPasses[i].close): m = gPasses[i].close(graph, a[i], m)
  105. a[i] = nil # free the memory here
  106. proc processTopLevelStmt(n: PNode, a: var TPassContextArray): bool =
  107. # this implements the code transformation pipeline
  108. var m = n
  109. for i in countup(0, gPassesLen - 1):
  110. if not isNil(gPasses[i].process):
  111. m = gPasses[i].process(a[i], m)
  112. if isNil(m): return false
  113. result = true
  114. proc processTopLevelStmtCached(n: PNode, a: var TPassContextArray) =
  115. # this implements the code transformation pipeline
  116. var m = n
  117. for i in countup(0, gPassesLen - 1):
  118. if not isNil(gPasses[i].openCached): m = gPasses[i].process(a[i], m)
  119. proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) =
  120. var m: PNode = nil
  121. for i in countup(0, gPassesLen - 1):
  122. if not isNil(gPasses[i].openCached) and not isNil(gPasses[i].close):
  123. m = gPasses[i].close(graph, a[i], m)
  124. a[i] = nil # free the memory here
  125. proc resolveMod(module, relativeTo: string): int32 =
  126. let fullPath = findModule(module, relativeTo)
  127. if fullPath.len == 0:
  128. result = InvalidFileIDX
  129. else:
  130. result = fullPath.fileInfoIdx
  131. proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
  132. a: var TPassContextArray; m: PSym) =
  133. # XXX fixme this should actually be relative to the config file!
  134. let relativeTo = m.info.toFullPath
  135. for module in items(implicits):
  136. # implicit imports should not lead to a module importing itself
  137. if m.position != resolveMod(module, relativeTo):
  138. var importStmt = newNodeI(nodeKind, gCmdLineInfo)
  139. var str = newStrNode(nkStrLit, module)
  140. str.info = gCmdLineInfo
  141. importStmt.addSon str
  142. if not processTopLevelStmt(importStmt, a): break
  143. proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
  144. rd: PRodReader; cache: IdentCache): bool {.discardable.} =
  145. if graph.stopCompile(): return true
  146. var
  147. p: TParsers
  148. a: TPassContextArray
  149. s: PLLStream
  150. fileIdx = module.fileIdx
  151. if rd == nil:
  152. openPasses(graph, a, module, cache)
  153. if stream == nil:
  154. let filename = fileIdx.toFullPathConsiderDirty
  155. s = llStreamOpen(filename, fmRead)
  156. if s == nil:
  157. rawMessage(errCannotOpenFile, filename)
  158. return false
  159. else:
  160. s = stream
  161. while true:
  162. openParsers(p, fileIdx, s, cache)
  163. if sfSystemModule notin module.flags:
  164. # XXX what about caching? no processing then? what if I change the
  165. # modules to include between compilation runs? we'd need to track that
  166. # in ROD files. I think we should enable this feature only
  167. # for the interactive mode.
  168. processImplicits implicitImports, nkImportStmt, a, module
  169. processImplicits implicitIncludes, nkIncludeStmt, a, module
  170. while true:
  171. if graph.stopCompile(): break
  172. var n = parseTopLevelStmt(p)
  173. if n.kind == nkEmpty: break
  174. if {sfNoForward, sfReorder} * module.flags != {}:
  175. # read everything, no streaming possible
  176. var sl = newNodeI(nkStmtList, n.info)
  177. sl.add n
  178. while true:
  179. var n = parseTopLevelStmt(p)
  180. if n.kind == nkEmpty: break
  181. sl.add n
  182. if sfReorder in module.flags:
  183. sl = reorder sl
  184. discard processTopLevelStmt(sl, a)
  185. break
  186. elif not processTopLevelStmt(n, a): break
  187. closeParsers(p)
  188. if s.kind != llsStdIn: break
  189. closePasses(graph, a)
  190. # id synchronization point for more consistent code generation:
  191. idSynchronizationPoint(1000)
  192. else:
  193. openPassesCached(graph, a, module, rd)
  194. var n = loadInitSection(rd)
  195. for i in countup(0, sonsLen(n) - 1):
  196. if graph.stopCompile(): break
  197. processTopLevelStmtCached(n.sons[i], a)
  198. closePassesCached(graph, a)
  199. result = true