cbackend.nim 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2021 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## New entry point into our C/C++ code generator. Ideally
  10. ## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
  11. ## to work on packed trees directly and produce the C code as an AST which can
  12. ## then be rendered to text in a very simple manner. Unfortunately nobody wrote
  13. ## this code. So instead we wrap the existing cgen.nim and its friends so that
  14. ## we call directly into the existing code generation logic but avoiding the
  15. ## naive, outdated `passes` design. Thus you will see some
  16. ## `useAliveDataFromDce in flags` checks in the old code -- the old code is
  17. ## also doing cross-module dependency tracking and DCE that we don't need
  18. ## anymore. DCE is now done as prepass over the entire packed module graph.
  19. import std/packedsets, algorithm, tables
  20. when defined(nimPreviewSlimSystem):
  21. import std/assertions
  22. import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
  23. pathutils, extccomp, msgs, modulepaths]
  24. import packed_ast, ic, dce, rodfiles
  25. proc unpackTree(g: ModuleGraph; thisModule: int;
  26. tree: PackedTree; n: NodePos): PNode =
  27. var decoder = initPackedDecoder(g.config, g.cache)
  28. result = loadNodes(decoder, g.packed, thisModule, tree, n)
  29. proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
  30. if g.backend == nil:
  31. g.backend = cgendata.newModuleList(g)
  32. assert g.backend != nil
  33. var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
  34. bmod.idgen = idgenFromLoadedModule(m)
  35. proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
  36. var bmod = BModuleList(g.backend).modules[m.module.position]
  37. assert bmod != nil
  38. bmod.flags.incl useAliveDataFromDce
  39. bmod.alive = move alive[m.module.position]
  40. for p in allNodes(m.fromDisk.topLevel):
  41. let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
  42. cgen.genTopLevelStmt(bmod, n)
  43. let disps = finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
  44. if disps != nil:
  45. for disp in disps:
  46. genProcAux(bmod, disp.sym)
  47. m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
  48. proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
  49. for x in mitems(m.fromDisk.emittedTypeInfo):
  50. #echo "found type ", x, " for file ", int(origin)
  51. g.emittedTypeInfo[x] = origin
  52. proc addFileToLink(config: ConfigRef; m: PSym) =
  53. let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
  54. let ext =
  55. if config.backend == backendCpp: ".nim.cpp"
  56. elif config.backend == backendObjc: ".nim.m"
  57. else: ".nim.c"
  58. let cfile = changeFileExt(completeCfilePath(config,
  59. mangleModuleName(config, filename).AbsoluteFile), ext)
  60. let objFile = completeCfilePath(config, toObjFile(config, cfile))
  61. if fileExists(objFile):
  62. var cf = Cfile(nimname: m.name.s, cname: cfile,
  63. obj: objFile,
  64. flags: {CfileFlag.Cached})
  65. addFileToCompile(config, cf)
  66. when defined(debugDce):
  67. import os, std/packedsets
  68. proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) =
  69. var f = rodfiles.create(asymFile.string)
  70. f.storeHeader()
  71. f.storeSection aliveSymsSection
  72. f.storeSeq(s)
  73. close f
  74. template prepare {.dirty.} =
  75. let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
  76. var s = newSeqOfCap[int32](alive[position].len)
  77. for a in items(alive[position]): s.add int32(a)
  78. sort(s)
  79. proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) =
  80. prepare()
  81. storeAliveSymsImpl(asymFile, s)
  82. proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
  83. prepare()
  84. var f2 = rodfiles.open(asymFile.string)
  85. f2.loadHeader()
  86. f2.loadSection aliveSymsSection
  87. var oldData: seq[int32]
  88. f2.loadSeq(oldData)
  89. f2.close
  90. if f2.err == ok and oldData == s:
  91. result = false
  92. else:
  93. when defined(debugDce):
  94. let oldAsSet = toPackedSet[int32](oldData)
  95. let newAsSet = toPackedSet[int32](s)
  96. echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err
  97. echo "in old but not in new ", oldAsSet.difference(newAsSet), " number of entries in old ", oldAsSet.len
  98. echo "in new but not in old ", newAsSet.difference(oldAsSet), " number of entries in new ", newAsSet.len
  99. #if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0:
  100. # echo "command failed"
  101. result = true
  102. storeAliveSymsImpl(asymFile, s)
  103. proc genPackedModule(g: ModuleGraph, i: int; alive: var AliveSyms) =
  104. # case statement here to enforce exhaustive checks.
  105. case g.packed[i].status
  106. of undefined:
  107. discard "nothing to do"
  108. of loading, stored:
  109. assert false
  110. of storing, outdated:
  111. storeAliveSyms(g.config, g.packed[i].module.position, alive)
  112. generateCodeForModule(g, g.packed[i], alive)
  113. closeRodFile(g, g.packed[i].module)
  114. of loaded:
  115. if g.packed[i].loadedButAliveSetChanged:
  116. generateCodeForModule(g, g.packed[i], alive)
  117. else:
  118. addFileToLink(g.config, g.packed[i].module)
  119. replayTypeInfo(g, g.packed[i], FileIndex(i))
  120. if g.backend == nil:
  121. g.backend = cgendata.newModuleList(g)
  122. registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags)
  123. proc generateCode*(g: ModuleGraph) =
  124. ## The single entry point, generate C(++) code for the entire
  125. ## Nim program aka `ModuleGraph`.
  126. resetForBackend(g)
  127. var alive = computeAliveSyms(g.packed, g.config)
  128. when false:
  129. for i in 0..high(g.packed):
  130. echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i))
  131. # First pass: Setup all the backend modules for all the modules that have
  132. # changed:
  133. for i in 0..high(g.packed):
  134. # case statement here to enforce exhaustive checks.
  135. case g.packed[i].status
  136. of undefined:
  137. discard "nothing to do"
  138. of loading, stored:
  139. assert false
  140. of storing, outdated:
  141. setupBackendModule(g, g.packed[i])
  142. of loaded:
  143. # Even though this module didn't change, DCE might trigger a change.
  144. # Consider this case: Module A uses symbol S from B and B does not use
  145. # S itself. A is then edited not to use S either. Thus we have to
  146. # recompile B in order to remove S from the final result.
  147. if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
  148. g.packed[i].loadedButAliveSetChanged = true
  149. setupBackendModule(g, g.packed[i])
  150. # Second pass: Code generation.
  151. let mainModuleIdx = g.config.projectMainIdx2.int
  152. # We need to generate the main module last, because only then
  153. # all init procs have been registered:
  154. for i in 0..high(g.packed):
  155. if i != mainModuleIdx:
  156. genPackedModule(g, i, alive)
  157. if mainModuleIdx >= 0:
  158. genPackedModule(g, mainModuleIdx, alive)