summary refs log blame commit diff stats
path: root/compiler/ic/cbackend.nim
blob: 34ee59d525727fa821ed66e949e1d546012f23cf (plain) (tree)



















                                                                                  
                                          

                                                                                               

                            
                                    




















                                                                                       



                                                                             






                                                                                              





                                                                   
 










                                                                



                                                                                                     






                                                                                 








                                         








                                                                                                    
                 
                                   



                                                               
                    










                                                       

                                                                  








                                                                            
                                                    
#
#
#           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]
  # std/intsets would give `UnusedImport`, pending https://github.com/nim-lang/Nim/issues/14246
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
  pathutils, extccomp, msgs]

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 generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
  if g.backend == nil:
    g.backend = cgendata.newModuleList(g)

  var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
  bmod.idgen = idgenFromLoadedModule(m)
  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)

  finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))

proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
  for x in mitems(m.fromDisk.emittedTypeInfo):
    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, withPackageName(config, filename)), 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 std / [os, 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)
      echo "in new but not in old ", newAsSet.difference(oldAsSet)

      if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0:
        echo "command failed"
    result = true
    storeAliveSymsImpl(asymFile, s)

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)

  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:
      assert false
    of storing, outdated:
      generateCodeForModule(g, g.packed[i], alive)
      closeRodFile(g, g.packed[i].module)
      storeAliveSyms(g.config, g.packed[i].module.position, alive)
    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):
        generateCodeForModule(g, g.packed[i], alive)
      else:
        addFileToLink(g.config, g.packed[i].module)
        replayTypeInfo(g, g.packed[i], FileIndex(i))