# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module implements the C code generator. import ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, nversion, nimsets, msgs, std / sha1, bitsets, idents, types, ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases, lowerings, semparallel, tables, sets, ndi import strutils except `%` # collides with ropes.`%` from modulegraphs import ModuleGraph import dynlib when not declared(dynlib.libCandidates): proc libCandidates(s: string, dest: var seq[string]) = ## given a library name pattern `s` write possible library names to `dest`. var le = strutils.find(s, '(') var ri = strutils.find(s, ')', le+1) if le >= 0 and ri > le: var prefix = substr(s, 0, le - 1) var suffix = substr(s, ri + 1) for middle in split(substr(s, le + 1, ri - 1), '|'): libCandidates(prefix & middle & suffix, dest) else: add(dest, s) when options.hasTinyCBackend: import tccgen # implementation proc addForwardedProc(m: BModule, prc: PSym) = m.forwardedProcs.add(prc) inc(m.g.forwardedProcsCounter) proc findPendingModule(m: BModule, s: PSym): BModule = var ms = getModule(s) result = m.g.modules[ms.position] proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) = result.k = k result.storage = s result.lode = lode result.r = nil result.flags = {} proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) = # fills the loc if it is not already initialized if a.k == locNone: a.k = k a.lode = lode a.storage = s if a.r == nil: a.r = r proc t(a: TLoc): PType {.inline.} = if a.lode.kind == nkSym: result = a.lode.sym.typ else: result = a.lode.typ proc lodeTyp(t: PType): PNode = result = newNode(nkEmpty) result.typ = t proc isSimpleConst(typ: PType): bool = let t = skipTypes(typ, abstractVar) result = t.kind notin {tyTuple, tyObject, tyArray, tySet, tySequence} and not (t.kind == tyProc and t.callConv == ccClosure) proc useStringh(m: BModule) = if includesStringh notin m.flags: incl m.flags, includesStringh m.includeHeader("") proc useHeader(m: BModule, sym: PSym) = if lfHeader in sym.loc.flags: assert(sym.annex != nil) let str = getStr(sym.annex.path) m.includeHeader(str) proc cgsym(m: BModule, name: string): Rope proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope = var i = 0 var length = len(frmt) result = nil var num = 0 while i < length: if frmt[i] == '$': inc(i) # skip '$' case frmt[i] of '$': add(result, "$") inc(i) of '#': inc(i) add(result, args[num]) inc(num) of '0'..'9': var j = 0 while true: j = (j * 10) + ord(frmt[i]) - ord('0') inc(i) if i >= length or not (frmt[i] in {'0'..'9'}): break num = j if j > high(args) + 1: internalError("ropes: invalid format string $" & $j) add(result, args[j-1]) of 'n': if optLineDir notin gOptions: add(result, rnl) inc(i) of 'N': add(result, rnl) inc(i) else: internalError("ropes: invalid format string $" & frmt[i]) elif frmt[i] == '#' and frmt[i+1] in IdentStartChars: inc(i) var j = i while frmt[j] in IdentChars: inc(j) var ident = substr(frmt, i, j-1) i = j add(result, cgsym(m, ident)) elif frmt[i] == '#' and frmt[i+1] == '$': inc(i, 2) var j = 0 while frmt[i] in Digits: j = (j * 10) + ord(frmt[i]) - ord('0') inc(i) add(result, cgsym(m, $args[j-1])) var start = i while i < length: if frmt[i] != '$' and frmt[i] != '#': inc(i) else: break if i - 1 >= start: add(result, substr(frmt, start, i - 1)) template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped = ropecg(m, fmt, args) var indent = "\t".rope proc indentLine(p: BProc, r: Rope): Rope = result = r for i in countup(0, p.blocks.len-1): prepend(result, indent) proc appcg(m: BModule, c: var Rope, frmt: FormatStr, args: varargs[Rope]) = add(c, ropecg(m, frmt, args)) proc appcg(m: BModule, s: TCFileSection, frmt: FormatStr, args: varargs[Rope]) = add(m.s[s], ropecg(m, frmt, args)) proc appcg(p: BProc, s: TCProcSection, frmt: FormatStr, args: varargs[Rope]) = add(p.s(s), ropecg(p.module, frmt, args)) proc line(p: BProc, s: TCProcSection, r: Rope) = add(p.s(s), indentLine(p, r)) proc line(p: BProc, s: TCPr
#
#
#           The Nim Compiler
#        (c) Copyright 2017 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## Module that implements ``gorge`` for the compiler.

import msgs, std / sha1, os, osproc, streams, strutils, options,
  lineinfos, pathutils

proc readOutput(p: Process): (string, int) =
  result[0] = ""
  var output = p.outputStream
  while not output.atEnd:
    result[0].add(output.readLine)
    result[0].add("\n")
  if result[0].len > 0:
    result[0].setLen(result[0].len - "\n".len)
  result[1] = p.waitForExit

proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
  let workingDir = parentDir(toFullPath(conf, info))
  if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
    let h = secureHash(cmd & "\t" & input & "\t" & cache)
    let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
    var f: File
    if open(f, filename):
      result = (f.readAll, 0)
      f.close
      return
    var readSuccessful = false
    try:
      var p = startProcess(cmd, workingDir,
                           options={poEvalCommand, poStderrToStdout})
      if input.len != 0:
        p.inputStream.write(input)
        p.inputStream.close()
      result = p.readOutput
      readSuccessful = true
      # only cache successful runs:
      if result[1] == 0:
        writeFile(filename, result[0])
    except IOError, OSError:
      if not readSuccessful: result = ("", -1)
  else:
    try:
      var p = startProcess(cmd, workingDir,
                           options={poEvalCommand, poStderrToStdout})
      if input.len != 0:
        p.inputStream.write(input)
        p.inputStream.close()
      result = p.readOutput
    except IOError, OSError:
      result = ("", -1)
, prc: PSym) = var p = newProc(prc, m) var header = genProcHeader(m, prc) var returnStmt: Rope = nil assert(prc.ast != nil) if sfPure notin prc.flags and prc.typ.sons[0] != nil: if resultPos >= prc.ast.len: internalError(prc.info, "proc has no result symbol") let resNode = prc.ast.sons[resultPos] let res = resNode.sym # get result symbol if not isInvalidReturnType(prc.typ.sons[0]): if sfNoInit in prc.flags: incl(res.flags, sfNoInit) if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil): var decl = localVarDecl(p, resNode) var a: TLoc initLocExprSingleUse(p, val, a) linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a)) else: # declare the result symbol: assignLocalVar(p, resNode) assert(res.loc.r != nil) initLocalVar(p, res, immediateAsgn=false) returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc)) else: fillResult(resNode) assignParam(p, res) resetLoc(p, res.loc) if skipTypes(res.typ, abstractInst).kind == tyArray: #incl(res.loc.flags, lfIndirect) res.loc.storage = OnUnknown for i in countup(1, sonsLen(prc.typ.n) - 1): let param = prc.typ.n.sons[i].sym if param.typ.isCompileTimeOnly: continue assignParam(p, param) closureSetup(p, prc) genStmts(p, prc.getBody) # modifies p.locals, p.init, etc. var generatedProc: Rope if sfNoReturn in prc.flags: if hasDeclspec in extccomp.CC[extccomp.cCompiler].props: header = "__declspec(noreturn) " & header if sfPure in prc.flags: if hasDeclspec in extccomp.CC[extccomp.cCompiler].props: header = "__declspec(naked) " & header generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N", header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)) else: generatedProc = rfmt(nil, "$N$1 {$N", header) add(generatedProc, initGCFrame(p)) if optStackTrace in prc.options: add(generatedProc, p.s(cpsLocals)) var procname = makeCString(prc.name.s) add(generatedProc, initFrame(p, procname, prc.info.quotedFilename)) else: add(generatedProc, p.s(cpsLocals)) if optProfiler in prc.options: # invoke at proc entry for recursion: appcg(p, cpsInit, "\t#nimProfile();$n", []) if p.beforeRetNeeded: add(generatedProc, "{") add(generatedProc, p.s(cpsInit)) add(generatedProc, p.s(cpsStmts)) if p.beforeRetNeeded: add(generatedProc, ~"\t}BeforeRet_: ;$n") add(generatedProc, deinitGCFrame(p)) if optStackTrace in prc.options: add(generatedProc, deinitFrame(p)) add(generatedProc, returnStmt) add(generatedProc, ~"}$N") add(m.s[cfsProcs], generatedProc) proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = result = (sfCompileToCpp in m.module.flags and sfCompileToCpp notin sym.getModule().flags and gCmd != cmdCompileToCpp) or ( sym.flags * {sfImportc, sfInfixCall, sfCompilerProc} == {sfImportc} and sym.magic == mNone and gCmd == cmdCompileToCpp) proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) if lfNoDecl in sym.loc.flags: return if lfDynamicLib in sym.loc.flags: if getModule(sym).id != m.module.id and not containsOrIncl(m.declaredThings, sym.id): add(m.s[cfsVars], rfmt(nil, "extern $1 $2;$n", getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym))) elif not containsOrIncl(m.declaredProtos, sym.id): var header = genProcHeader(m, sym) if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props: header = "__declspec(noreturn) " & header if sym.typ.callConv != ccInline and requiresExternC(m, sym): header = "extern \"C\" " & header if sfPure in sym.flags and hasAttribute in CC[cCompiler].props: header.add(" __attribute__((naked))") if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props: header.add(" __attribute__((noreturn))") add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header)) proc genProcNoForward(m: BModule, prc: PSym) = if lfImportCompilerProc in prc.loc.flags: fillProcLoc(m, prc.ast[namePos]) useHeader(m, prc) # dependency to a compilerproc: discard cgsym(m, prc.name.s) return if lfNoDecl in prc.loc.flags: fillProcLoc(m, prc.ast[namePos]) useHeader(m, prc) genProcPrototype(m, prc) elif prc.typ.callConv == ccInline: # We add inline procs to the calling module to enable C based inlining. # This also means that a check with ``q.declaredThings`` is wrong, we need # a check for ``m.declaredThings``. if not containsOrIncl(m.declaredThings, prc.id): #if prc.loc.k == locNone: fillProcLoc(m, prc.ast[namePos]) #elif {sfExportc, sfImportc} * prc.flags == {}: # # reset name to restore consistency in case of hashing collisions: # echo "resetting ", prc.id, " by ", m.module.name.s # prc.loc.r = nil # prc.loc.r = mangleName(m, prc) useHeader(m, prc) genProcPrototype(m, prc) genProcAux(m, prc) elif lfDynamicLib in prc.loc.flags: var q = findPendingModule(m, prc) fillProcLoc(q, prc.ast[namePos]) useHeader(m, prc) genProcPrototype(m, prc) if q != nil and not containsOrIncl(q.declaredThings, prc.id): symInDynamicLib(q, prc) else: symInDynamicLibPartial(m, prc) elif sfImportc notin prc.flags: var q = findPendingModule(m, prc) fillProcLoc(q, prc.ast[namePos]) useHeader(m, prc) genProcPrototype(m, prc) if q != nil and not containsOrIncl(q.declaredThings, prc.id): genProcAux(q, prc) else: fillProcLoc(m, prc.ast[namePos]) useHeader(m, prc) if sfInfixCall notin prc.flags: genProcPrototype(m, prc) proc requestConstImpl(p: BProc, sym: PSym) = var m = p.module useHeader(m, sym) if sym.loc.k == locNone: fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic) if lfNoDecl in sym.loc.flags: return # declare implementation: var q = findPendingModule(m, sym) if q != nil and not containsOrIncl(q.declaredThings, sym.id): assert q.initProc.module == q addf(q.s[cfsData], "NIM_CONST $1 $2 = $3;$n", [getTypeDesc(q, sym.typ), sym.loc.r, genConstExpr(q.initProc, sym.ast)]) # declare header: if q != m and not containsOrIncl(m.declaredThings, sym.id): assert(sym.loc.r != nil) let headerDecl = "extern NIM_CONST $1 $2;$n" % [getTypeDesc(m, sym.loc.t), sym.loc.r] add(m.s[cfsData], headerDecl) if sfExportc in sym.flags and p.module.g.generatedHeader != nil: add(p.module.g.generatedHeader.s[cfsData], headerDecl) proc isActivated(prc: PSym): bool = prc.typ != nil proc genProc(m: BModule, prc: PSym) = if sfBorrow in prc.flags or not isActivated(prc): return if sfForward in prc.flags: addForwardedProc(m, prc) fillProcLoc(m, prc.ast[namePos]) else: genProcNoForward(m, prc) if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and m.g.generatedHeader != nil and lfNoDecl notin prc.loc.flags: genProcPrototype(m.g.generatedHeader, prc) if prc.typ.callConv == ccInline: if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id): genProcAux(m.g.generatedHeader, prc) proc genVarPrototypeAux(m: BModule, n: PNode) = #assert(sfGlobal in sym.flags) let sym = n.sym useHeader(m, sym) fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap) if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id): return if sym.owner.id != m.module.id: # else we already have the symbol generated! assert(sym.loc.r != nil) if sfThread in sym.flags: declareThreadVar(m, sym, true) else: add(m.s[cfsVars], "extern ") add(m.s[cfsVars], getTypeDesc(m, sym.loc.t)) if lfDynamicLib in sym.loc.flags: add(m.s[cfsVars], "*") if sfRegister in sym.flags: add(m.s[cfsVars], " register") if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile") addf(m.s[cfsVars], " $1;$n", [sym.loc.r]) proc genVarPrototype(m: BModule, n: PNode) = genVarPrototypeAux(m, n) proc addIntTypes(result: var Rope) {.inline.} = addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl & "#define NIM_INTBITS $1" & tnl, [ platform.CPU[targetCPU].intSize.rope]) proc getCopyright(cfile: Cfile): Rope = if optCompileOnly in gGlobalOptions: result = ("/* Generated by Nim Compiler v$1 */$N" & "/* (c) " & copyrightYear & " Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N") % [rope(VersionAsString)] else: result = ("/* Generated by Nim Compiler v$1 */$N" & "/* (c) " & copyrightYear & " Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N" & "/* Compiled for: $2, $3, $4 */$N" & "/* Command for C compiler:$n $5 */$N") % [rope(VersionAsString), rope(platform.OS[targetOS].name), rope(platform.CPU[targetCPU].name), rope(extccomp.CC[extccomp.cCompiler].name), rope(getCompileCFileCmd(cfile))] proc getFileHeader(cfile: Cfile): Rope = result = getCopyright(cfile) addIntTypes(result) proc genFilenames(m: BModule): Rope = discard cgsym(m, "dbgRegisterFilename") result = nil for i in 0..") elif platform.targetOS == osGenode: nimMain = GenodeNimMain otherMain = ComponentConstruct elif optGenDynLib in gGlobalOptions: nimMain = PosixNimDllMain otherMain = PosixCDllMain elif platform.targetOS == osStandalone: nimMain = PosixNimMain otherMain = StandaloneCMain else: nimMain = PosixNimMain otherMain = PosixCMain if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint") if optEndb in gOptions: m.g.breakpoints.add(m.genFilenames) let initStackBottomCall = if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N") inc(m.labels) appcg(m, m.s[cfsProcs], PreMainBody, [ m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit, if emulatedThreadVars() and platform.targetOS != osStandalone: ropecg(m, "\t#initThreadVarsEmulation();$N") else: "".rope, initStackBottomCall]) appcg(m, m.s[cfsProcs], nimMain, [m.g.mainModInit, initStackBottomCall, rope(m.labels)]) if optNoMain notin gGlobalOptions: appcg(m, m.s[cfsProcs], otherMain, []) proc getSomeInitName(m: PSym, suffix: string): Rope = assert m.kind == skModule assert m.owner.kind == skPackage if {sfSystemModule, sfMainModule} * m.flags == {}: result = m.owner.name.s.mangle.rope result.add "_" result.add m.name.s result.add suffix proc getInitName(m: PSym): Rope = if sfMainModule in m.flags: # generate constant name for main module, for "easy" debugging. result = rope"NimMainModule" else: result = getSomeInitName(m, "Init000") proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000") proc registerModuleToMain(g: BModuleList; m: PSym) = var init = m.getInitName datInit = m.getDatInitName addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init]) addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit]) if sfSystemModule notin m.flags: addf(g.mainDatInit, "\t$1();$N", [datInit]) let initCall = "\t$1();$N" % [init] if sfMainModule in m.flags: add(g.mainModInit, initCall) else: add(g.otherModsInit, initCall) proc genInitCode(m: BModule) = var initname = getInitName(m.module) var prc = "NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N" % [initname] if m.typeNodes > 0: appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", [m.typeNodesName, rope(m.typeNodes)]) if m.nimTypes > 0: appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n", [m.nimTypesName, rope(m.nimTypes)]) add(prc, initGCFrame(m.initProc)) add(prc, genSectionStart(cpsLocals)) add(prc, m.preInitProc.s(cpsLocals)) add(prc, m.initProc.s(cpsLocals)) add(prc, m.postInitProc.s(cpsLocals)) add(prc, genSectionEnd(cpsLocals)) if optStackTrace in m.initProc.options and frameDeclared notin m.flags: # BUT: the generated init code might depend on a current frame, so # declare it nevertheless: incl m.flags, frameDeclared if preventStackTrace notin m.flags: var procname = makeCString(m.module.name.s) add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename)) else: add(prc, ~"\tTFrame FR_; FR_.len = 0;$N") add(prc, genSectionStart(cpsInit)) add(prc, m.preInitProc.s(cpsInit)) add(prc, m.initProc.s(cpsInit)) add(prc, m.postInitProc.s(cpsInit)) add(prc, genSectionEnd(cpsInit)) add(prc, genSectionStart(cpsStmts)) add(prc, m.preInitProc.s(cpsStmts)) add(prc, m.initProc.s(cpsStmts)) add(prc, m.postInitProc.s(cpsStmts)) add(prc, genSectionEnd(cpsStmts)) if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: add(prc, deinitFrame(m.initProc)) add(prc, deinitGCFrame(m.initProc)) addf(prc, "}$N$N", []) prc.addf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N", [getDatInitName(m.module)]) for i in cfsTypeInit1..cfsDynLibInit: add(prc, genSectionStart(i)) add(prc, m.s[i]) add(prc, genSectionEnd(i)) addf(prc, "}$N$N", []) # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because # that would lead to a *nesting* of merge sections which the merger does # not support. So we add it to another special section: ``cfsInitProc`` add(m.s[cfsInitProc], prc) for i, el in pairs(m.extensionLoaders): if el != nil: let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % [(i.ord - '0'.ord).rope, el] add(m.s[cfsInitProc], ex) proc genModule(m: BModule, cfile: Cfile): Rope = result = getFileHeader(cfile) result.add(genMergeInfo(m)) generateThreadLocalStorage(m) generateHeaders(m) for i in countup(cfsHeaders, cfsProcs): add(result, genSectionStart(i)) add(result, m.s[i]) add(result, genSectionEnd(i)) add(result, m.s[cfsInitProc]) proc newPreInitProc(m: BModule): BProc = result = newProc(nil, m) # little hack so that unique temporaries are generated: result.labels = 100_000 proc newPostInitProc(m: BModule): BProc = result = newProc(nil, m) # little hack so that unique temporaries are generated: result.labels = 200_000 proc initProcOptions(m: BModule): TOptions = if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule = new(result) result.tmpBase = rope("TM" & $hashOwner(module) & "_") result.headerFiles = @[] result.declaredThings = initIntSet() result.declaredProtos = initIntSet() result.cfilename = filename result.filename = filename result.typeCache = initTable[SigHash, Rope]() result.forwTypeCache = initTable[SigHash, Rope]() result.module = module result.typeInfoMarker = initTable[SigHash, Rope]() result.sigConflicts = initCountTable[SigHash]() result.initProc = newProc(nil, result) result.initProc.options = initProcOptions(result) result.preInitProc = newPreInitProc(result) result.postInitProc = newPostInitProc(result) initNodeTable(result.dataCache) result.typeStack = @[] result.forwardedProcs = @[] result.typeNodesName = getTempName(result) result.nimTypesName = getTempName(result) result.g = g # no line tracing for the init sections of the system module so that we # don't generate a TFrame which can confuse the stack botton initialization: if sfSystemModule in module.flags: incl result.flags, preventStackTrace excl(result.preInitProc.options, optStackTrace) excl(result.postInitProc.options, optStackTrace) let ndiName = if optCDebug in gGlobalOptions: changeFileExt(completeCFilePath(filename), "ndi") else: "" open(result.ndi, ndiName) proc nullify[T](arr: var T) = for i in low(arr)..high(arr): arr[i] = Rope(nil) proc resetModule*(m: BModule) = # between two compilations in CAAS mode, we can throw # away all the data that was written to disk m.headerFiles = @[] m.declaredProtos = initIntSet() m.forwTypeCache = initTable[SigHash, Rope]() m.initProc = newProc(nil, m) m.initProc.options = initProcOptions(m) m.preInitProc = newPreInitProc(m) m.postInitProc = newPostInitProc(m) initNodeTable(m.dataCache) m.typeStack = @[] m.forwardedProcs = @[] m.typeNodesName = getTempName(m) m.nimTypesName = getTempName(m) if sfSystemModule in m.module.flags: incl m.flags, preventStackTrace else: excl m.flags, preventStackTrace nullify m.s m.typeNodes = 0 m.nimTypes = 0 nullify m.extensionLoaders # indicate that this is now cached module # the cache will be invalidated by nullifying gModules #m.fromCache = true m.g = nil # we keep only the "merge info" information for the module # and the properties that can't change: # m.filename # m.cfilename # m.isHeaderFile # m.module ? # m.typeCache # m.declaredThings # m.typeInfoMarker # m.labels # m.FrameDeclared proc resetCgenModules*(g: BModuleList) = for m in cgenModules(g): resetModule(m) proc rawNewModule(g: BModuleList; module: PSym): BModule = result = rawNewModule(g, module, module.position.int32.toFullPath) proc newModule(g: BModuleList; module: PSym): BModule = # we should create only one cgen module for each module sym result = rawNewModule(g, module) growCache g.modules, module.position g.modules[module.position] = result if (optDeadCodeElim in gGlobalOptions): if (sfDeadCodeElim in module.flags): internalError("added pending module twice: " & module.filename) template injectG(config) {.dirty.} = if graph.backend == nil: graph.backend = newModuleList(config) let g = BModuleList(graph.backend) proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext = injectG(graph.config) result = newModule(g, module) if optGenIndex in gGlobalOptions and g.generatedHeader == nil: let f = if graph.config.headerFile.len > 0: graph.config.headerFile else: gProjectFull g.generatedHeader = rawNewModule(g, module, changeFileExt(completeCFilePath(f), hExt)) incl g.generatedHeader.flags, isHeaderFile proc writeHeader(m: BModule) = var result = ("/* Generated by Nim Compiler v$1 */$N" & "/* (c) 2017 Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N") % [rope(VersionAsString)] var guard = "__$1__" % [m.filename.splitFile.name.rope] result.addf("#ifndef $1$n#define $1$n", [guard]) addIntTypes(result) generateHeaders(m) generateThreadLocalStorage(m) for i in countup(cfsHeaders, cfsProcs): add(result, genSectionStart(i)) add(result, m.s[i]) add(result, genSectionEnd(i)) add(result, m.s[cfsInitProc]) if optGenDynLib in gGlobalOptions: result.add("N_LIB_IMPORT ") result.addf("N_CDECL(void, NimMain)(void);$n", []) result.addf("#endif /* $1 */$n", [guard]) writeRope(result, m.filename) proc getCFile(m: BModule): string = let ext = if m.compileToCpp: ".cpp" elif gCmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m" else: ".c" result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), ext) proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = injectG(graph.config) var m = newModule(g, module) readMergeInfo(getCFile(m), m) result = m proc myProcess(b: PPassContext, n: PNode): PNode = result = n if b == nil or passes.skipCodegen(n): return var m = BModule(b) m.initProc.options = initProcOptions(m) softRnl = if optLineDir in gOptions: noRnl else: rnl genStmts(m.initProc, n) proc finishModule(m: BModule) = var i = 0 while i <= high(m.forwardedProcs): # Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use # a ``for`` loop here var prc = m.forwardedProcs[i] if sfForward in prc.flags: internalError(prc.info, "still forwarded: " & prc.name.s) genProcNoForward(m, prc) inc(i) assert(m.g.forwardedProcsCounter >= i) dec(m.g.forwardedProcsCounter, i) setLen(m.forwardedProcs, 0) proc shouldRecompile(code: Rope, cfile: Cfile): bool = result = true if optForceFullMake notin gGlobalOptions: if not equalsFile(code, cfile.cname): if isDefined("nimdiff"): if fileExists(cfile.cname): copyFile(cfile.cname, cfile.cname & ".backup") echo "diff ", cfile.cname, ".backup ", cfile.cname else: echo "new file ", cfile.cname writeRope(code, cfile.cname) return if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname): result = false else: writeRope(code, cfile.cname) # We need 2 different logics here: pending modules (including # 'nim__dat') may require file merging for the combination of dead code # elimination and incremental compilation! Non pending modules need no # such logic and in fact the logic hurts for the main module at least; # it would generate multiple 'main' procs, for instance. proc writeModule(m: BModule, pending: bool) = # generate code for the init statements of the module: let cfile = getCFile(m) if m.rd == nil or optForceFullMake in gGlobalOptions: genInitCode(m) finishTypeDescriptions(m) if sfMainModule in m.module.flags: # generate main file: add(m.s[cfsProcHeaders], m.g.mainModProcs) generateThreadVarsSize(m) var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) var code = genModule(m, cf) when hasTinyCBackend: if gCmd == cmdRun: tccgen.compileCCode($code) return if not shouldRecompile(code, cf): cf.flags = {CfileFlag.Cached} addFileToCompile(cf) elif pending and mergeRequired(m) and sfMainModule notin m.module.flags: let cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) var code = genModule(m, cf) writeRope(code, cfile) addFileToCompile(cf) else: # Consider: first compilation compiles ``system.nim`` and produces # ``system.c`` but then compilation fails due to an error. This means # that ``system.o`` is missing, so we need to call the C compiler for it: var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached} addFileToCompile(cf) close(m.ndi) proc updateCachedModule(m: BModule) = let cfile = getCFile(m) var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {}) if mergeRequired(m) and sfMainModule notin m.module.flags: mergeFiles(cfile, m) genInitCode(m) finishTypeDescriptions(m) var code = genModule(m, cf) writeRope(code, cfile) else: cf.flags = {CfileFlag.Cached} addFileToCompile(cf) proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = result = n if b == nil or passes.skipCodegen(n): return var m = BModule(b) # if the module is cached, we don't regenerate the main proc # nor the dispatchers? But if the dispatchers changed? # XXX emit the dispatchers into its own .c file? if b.rd != nil: return if n != nil: m.initProc.options = initProcOptions(m) genStmts(m.initProc, n) # cached modules need to registered too: registerModuleToMain(m.g, m.module) if sfMainModule in m.module.flags: if m.g.forwardedProcsCounter == 0: incl m.flags, objHasKidsValid let disp = generateMethodDispatchers(graph) for x in disp: genProcAux(m, x.sym) genMainProc(m) proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = let g = BModuleList(backend) # we need to process the transitive closure because recursive module # deps are allowed (and the system module is processed in the wrong # order anyway) g.config = config if g.generatedHeader != nil: finishModule(g.generatedHeader) while g.forwardedProcsCounter > 0: for m in cgenModules(g): if m.rd == nil: finishModule(m) for m in cgenModules(g): if m.rd != nil: m.updateCachedModule else: m.writeModule(pending=true) writeMapping(g.mapping) if g.generatedHeader != nil: writeHeader(g.generatedHeader) const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)