summary refs log tree commit diff stats
path: root/compiler/ic/cbackend.nim
blob: 21f69e48520a32de32e8563a72e3b4fd6085c019 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#
#
#           The Nim Compiler
#        (c) Copyright 2021 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## New entry point into our C/C++ code generator. Ideally
## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
## to work on packed trees directly and produce the C code as an AST which can
## then be rendered to text in a very simple manner. Unfortunately nobody wrote
## this code. So instead we wrap the existing cgen.nim and its friends so that
## we call directly into the existing code generation logic but avoiding the
## naive, outdated `passes` design. Thus you will see some
## `useAliveDataFromDce in flags` checks in the old code -- the old code is
## also doing cross-module dependency tracking and DCE that we don't need
## anymore. DCE is now done as prepass over the entire packed module graph.

import std/packedsets, algorithm, tables

when defined(nimPreviewSlimSystem):
  import std/assertions

import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
  pathutils, extccomp, msgs, modulepaths]

import packed_ast, ic, dce, rodfiles

proc unpackTree(g: ModuleGraph; thisModule: int;
                tree: PackedTree; n: NodePos): PNode =
  var decoder = initPackedDecoder(g.config, g.cache)
  result = loadNodes(decoder, g.packed, thisModule, tree, n)

proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
  if g.backend == nil:
    g.backend = cgendata.newModuleList(g)
  assert g.backend != nil
  var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
  bmod.idgen = idgenFromLoadedModule(m)

proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
  var bmod = BModuleList(g.backend).modules[m.module.position]
  assert bmod != nil
  bmod.flags.incl useAliveDataFromDce
  bmod.alive = move alive[m.module.position]

  for p in allNodes(m.fromDisk.topLevel):
    let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
    cgen.genTopLevelStmt(bmod, n)

  let disps = finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
  if disps != nil:
    for disp in disps:
      genProcAux(bmod, disp.sym)
  m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)

proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
  for x in mitems(m.fromDisk.emittedTypeInfo):
    #echo "found type ", x, " for file ", int(origin)
    g.emittedTypeInfo[x] = origin

proc addFileToLink(config: ConfigRef; m: PSym) =
  let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
  let ext =
      if config.backend == backendCpp: ".nim.cpp"
      elif config.backend == backendObjc: ".nim.m"
      else: ".nim.c"
  let cfile = changeFileExt(completeCfilePath(config,
                            mangleModuleName(config, filename).AbsoluteFile), ext)
  let objFile = completeCfilePath(config, toObjFile(config, cfile))
  if fileExists(objFile):
    var cf = Cfile(nimname: m.name.s, cname: cfile,
                   obj: objFile,
                   flags: {CfileFlag.Cached})
    addFileToCompile(config, cf)

when defined(debugDce):
  import os, std/packedsets

proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) =
  var f = rodfiles.create(asymFile.string)
  f.storeHeader()
  f.storeSection aliveSymsSection
  f.storeSeq(s)
  close f

template prepare {.dirty.} =
  let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
  var s = newSeqOfCap[int32](alive[position].len)
  for a in items(alive[position]): s.add int32(a)
  sort(s)

proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) =
  prepare()
  storeAliveSymsImpl(asymFile, s)

proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
  prepare()
  var f2 = rodfiles.open(asymFile.string)
  f2.loadHeader()
  f2.loadSection aliveSymsSection
  var oldData: seq[int32]
  f2.loadSeq(oldData)
  f2.close
  if f2.err == ok and oldData == s:
    result = false
  else:
    when defined(debugDce):
      let oldAsSet = toPackedSet[int32](oldData)
      let newAsSet = toPackedSet[int32](s)
      echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err
      echo "in old but not in new ", oldAsSet.difference(newAsSet), " number of entries in old ", oldAsSet.len
      echo "in new but not in old ", newAsSet.difference(oldAsSet), " number of entries in new ", newAsSet.len
      #if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0:
      #  echo "command failed"
    result = true
    storeAliveSymsImpl(asymFile, s)

proc genPackedModule(g: ModuleGraph, i: int; alive: var AliveSyms) =
  # case statement here to enforce exhaustive checks.
  case g.packed[i].status
  of undefined:
    discard "nothing to do"
  of loading, stored:
    assert false
  of storing, outdated:
    storeAliveSyms(g.config, g.packed[i].module.position, alive)
    generateCodeForModule(g, g.packed[i], alive)
    closeRodFile(g, g.packed[i].module)
  of loaded:
    if g.packed[i].loadedButAliveSetChanged:
      generateCodeForModule(g, g.packed[i], alive)
    else:
      addFileToLink(g.config, g.packed[i].module)
      replayTypeInfo(g, g.packed[i], FileIndex(i))

      if g.backend == nil:
        g.backend = cgendata.newModuleList(g)
      registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags)

proc generateCode*(g: ModuleGraph) =
  ## The single entry point, generate C(++) code for the entire
  ## Nim program aka `ModuleGraph`.
  resetForBackend(g)
  var alive = computeAliveSyms(g.packed, g.config)

  when false:
    for i in 0..high(g.packed):
      echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i))

  # First pass: Setup all the backend modules for all the modules that have
  # changed:
  for i in 0..high(g.packed):
    # case statement here to enforce exhaustive checks.
    case g.packed[i].status
    of undefined:
      discard "nothing to do"
    of loading, stored:
      assert false
    of storing, outdated:
      setupBackendModule(g, g.packed[i])
    of loaded:
      # Even though this module didn't change, DCE might trigger a change.
      # Consider this case: Module A uses symbol S from B and B does not use
      # S itself. A is then edited not to use S either. Thus we have to
      # recompile B in order to remove S from the final result.
      if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
        g.packed[i].loadedButAliveSetChanged = true
        setupBackendModule(g, g.packed[i])

  # Second pass: Code generation.
  let mainModuleIdx = g.config.projectMainIdx2.int
  # We need to generate the main module last, because only then
  # all init procs have been registered:
  for i in 0..high(g.packed):
    if i != mainModuleIdx:
      genPackedModule(g, i, alive)
  if mainModuleIdx >= 0:
    genPackedModule(g, mainModuleIdx, alive)