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