diff options
Diffstat (limited to 'compiler/cgen.nim')
-rw-r--r-- | compiler/cgen.nim | 2360 |
1 files changed, 2360 insertions, 0 deletions
diff --git a/compiler/cgen.nim b/compiler/cgen.nim new file mode 100644 index 000000000..091f5c842 --- /dev/null +++ b/compiler/cgen.nim @@ -0,0 +1,2360 @@ +# +# +# 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, trees, platform, magicsys, extccomp, options, + nversion, nimsets, msgs, bitsets, idents, types, + ccgutils, ropes, wordrecg, treetab, cgmeth, + rodutils, renderer, cgendata, aliases, + lowerings, ndi, lineinfos, pathutils, transf, + injectdestructors, astmsgs, modulepaths, pushpoppragmas, + mangleutils + +from expanddefaults import caseObjDefaultBranch + +import pipelineutils + +when defined(nimPreviewSlimSystem): + import std/assertions + +when not defined(leanCompiler): + import spawn, semparallel + +import std/strutils except `%`, addf # collides with ropes.`%` + +from ic / ic import ModuleBackendFlag +import std/[dynlib, math, tables, sets, os, intsets, hashes] + +const + # we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass + postprocessDirStart = '\1' + postprocessDirSep = '\31' + postprocessDirEnd = '\23' + +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: + dest.add(s) + +when options.hasTinyCBackend: + import tccgen + +proc hcrOn(m: BModule): bool = m.config.hcrOn +proc hcrOn(p: BProc): bool = p.module.config.hcrOn + +proc addForwardedProc(m: BModule, prc: PSym) = + m.g.forwardedProcs.add(prc) + +proc findPendingModule(m: BModule, s: PSym): BModule = + # TODO fixme + if m.config.symbolFiles == v2Sf: + let ms = s.itemId.module #getModule(s) + result = m.g.modules[ms] + else: + var ms = getModule(s) + result = m.g.modules[ms.position] + +proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc = + result = TLoc(k: k, storage: s, lode: lode, + snippet: "", flags: flags) + +proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} = + # fills the loc if it is not already initialized + if a.k == locNone: + a.k = k + a.lode = lode + a.storage = s + if a.snippet == "": a.snippet = r + +proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} = + # fills the loc if it is not already initialized + if a.k == locNone: + a.k = k + a.lode = lode + a.storage = s + +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 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) +proc cgsymValue(m: BModule, name: string): Rope + +proc getCFile(m: BModule): AbsoluteFile + +proc getModuleDllPath(m: BModule): Rope = + let (dir, name, ext) = splitFile(getCFile(m)) + let filename = strutils.`%`(platform.OS[m.g.config.target.targetOS].dllFrmt, [name & ext]) + result = makeCString(dir.string & "/" & filename) + +proc getModuleDllPath(m: BModule, module: int): Rope = + result = getModuleDllPath(m.g.modules[module]) + +proc getModuleDllPath(m: BModule, s: PSym): Rope = + result = getModuleDllPath(m.g.modules[s.itemId.module]) + +import std/macros + +proc cgFormatValue(result: var string; value: string) = + result.add value + +proc cgFormatValue(result: var string; value: BiggestInt) = + result.addInt value + +proc cgFormatValue(result: var string; value: Int128) = + result.addInt128 value + +# TODO: please document +macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope = + args.expectKind nnkBracket + # echo "ropecg ", newLit(frmt).repr, ", ", args.repr + var i = 0 + result = nnkStmtListExpr.newTree() + + result.add quote do: + assert `m` != nil + + let resVar = genSym(nskVar, "res") + # during `koch boot` the median of all generates strings from this + # macro is around 40 bytes in length. + result.add newVarStmt(resVar, newCall(bindSym"newStringOfCap", newLit(80))) + let formatValue = bindSym"cgFormatValue" + + var num = 0 + var strLit = "" + + template flushStrLit() = + if strLit != "": + result.add newCall(ident "add", resVar, newLit(strLit)) + strLit.setLen 0 + + while i < frmt.len: + if frmt[i] == '$': + inc(i) # skip '$' + case frmt[i] + of '$': + strLit.add '$' + inc(i) + of '#': + flushStrLit() + inc(i) + result.add newCall(formatValue, resVar, args[num]) + inc(num) + of '^': + flushStrLit() + inc(i) + result.add newCall(formatValue, resVar, args[^1]) + inc(num) + of '0'..'9': + var j = 0 + while true: + j = (j * 10) + ord(frmt[i]) - ord('0') + inc(i) + if i >= frmt.len or not (frmt[i] in {'0'..'9'}): break + num = j + if j > args.len: + error("ropes: invalid format string " & newLit(frmt).repr & " args.len: " & $args.len) + + flushStrLit() + result.add newCall(formatValue, resVar, args[j-1]) + of 'n': + flushStrLit() + result.add quote do: + if optLineDir notin `m`.config.options: + `resVar`.add("\L") + inc(i) + of 'N': + strLit.add "\L" + inc(i) + else: + error("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 = newLit(substr(frmt, i, j-1)) + i = j + flushStrLit() + result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", 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) + let ident = args[j-1] + flushStrLit() + result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident)) + elif frmt[i] == '#' and frmt[i+1] == '#': + inc(i, 2) + strLit.add("#") + else: + strLit.add(frmt[i]) + inc(i) + + flushStrLit() + result.add newCall(ident"rope", resVar) + +proc addIndent(p: BProc; result: var Rope) = + var i = result.len + let newLen = i + p.blocks.len + result.setLen newLen + while i < newLen: + result[i] = '\t' + inc i + +template appcg(m: BModule, c: var Rope, frmt: FormatStr, + args: untyped) = + c.add(ropecg(m, frmt, args)) + +template appcg(m: BModule, sec: TCFileSection, frmt: FormatStr, + args: untyped) = + m.s[sec].add(ropecg(m, frmt, args)) + +template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr, + args: untyped) = + p.s(sec).add(ropecg(p.module, frmt, args)) + +template line(p: BProc, sec: TCProcSection, r: string) = + addIndent p, p.s(sec) + p.s(sec).add(r) + +template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr, + args: untyped) = + addIndent p, p.s(sec) + p.s(sec).add(frmt % args) + +template lineCg(p: BProc, sec: TCProcSection, frmt: FormatStr, + args: untyped) = + addIndent p, p.s(sec) + p.s(sec).add(ropecg(p.module, frmt, args)) + +template linefmt(p: BProc, sec: TCProcSection, frmt: FormatStr, + args: untyped) = + addIndent p, p.s(sec) + p.s(sec).add(ropecg(p.module, frmt, args)) + +proc safeLineNm(info: TLineInfo): int = + result = toLinenumber(info) + if result < 0: result = 0 # negative numbers are not allowed in #line + +proc genPostprocessDir(field1, field2, field3: string): string = + result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd + +proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) = + assert line >= 0 + if optLineDir in conf.options and line > 0: + if fileIdx == InvalidFileIdx: + r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n")) + else: + r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n")) + +proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) = + assert line >= 0 + if optLineDir in p.config.options and line > 0: + if fileIdx == InvalidFileIdx: + r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n")) + else: + r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n")) + +proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) = + if optLineDir in conf.options: + genCLineDir(r, info.fileIndex, info.safeLineNm, conf) + +proc freshLineInfo(p: BProc; info: TLineInfo): bool = + if p.lastLineInfo.line != info.line or + p.lastLineInfo.fileIndex != info.fileIndex: + p.lastLineInfo.line = info.line + p.lastLineInfo.fileIndex = info.fileIndex + result = true + else: + result = false + +proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) = + if optLineDir in conf.options: + let lastFileIndex = p.lastLineInfo.fileIndex + if freshLineInfo(p, info): + genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex) + +proc genLineDir(p: BProc, t: PNode) = + if p == p.module.preInitProc: return + let line = t.info.safeLineNm + + if optEmbedOrigSrc in p.config.globalOptions: + p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L") + let lastFileIndex = p.lastLineInfo.fileIndex + let freshLine = freshLineInfo(p, t.info) + if freshLine: + genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex) + if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and + (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx: + if freshLine: + line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32)) + +proc accessThreadLocalVar(p: BProc, s: PSym) +proc emulatedThreadVars(conf: ConfigRef): bool {.inline.} +proc genProc(m: BModule, prc: PSym) +proc raiseInstr(p: BProc; result: var Rope) + +template compileToCpp(m: BModule): untyped = + m.config.backend == backendCpp or sfCompileToCpp in m.module.flags + +proc getTempName(m: BModule): Rope = + result = m.tmpBase & rope(m.labels) + inc m.labels + +proc rdLoc(a: TLoc): Rope = + # 'read' location (deref if indirect) + if lfIndirect in a.flags: + result = "(*" & a.snippet & ")" + else: + result = a.snippet + +proc addRdLoc(a: TLoc; result: var Rope) = + if lfIndirect in a.flags: + result.add "(*" & a.snippet & ")" + else: + result.add a.snippet + +proc lenField(p: BProc): Rope {.inline.} = + result = rope(if p.module.compileToCpp: "len" else: "Sup.len") + +proc lenExpr(p: BProc; a: TLoc): Rope = + if optSeqDestructors in p.config.globalOptions: + result = rdLoc(a) & ".len" + else: + result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)] + +proc dataFieldAccessor(p: BProc, sym: Rope): Rope = + if optSeqDestructors in p.config.globalOptions: + result = "(" & sym & ").p" + else: + result = sym + +proc dataField(p: BProc): Rope = + if optSeqDestructors in p.config.globalOptions: + result = rope".p->data" + else: + result = rope"->data" + +proc genProcPrototype(m: BModule, sym: PSym) + +include cbuilder +include ccgliterals +include ccgtypes + +# ------------------------------ Manager of temporaries ------------------ + +template mapTypeChooser(n: PNode): TSymKind = + (if n.kind == nkSym: n.sym.kind else: skVar) + +template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode) + +proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) = + if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray: + result.add "(&" & a.snippet & ")" + else: + result.add a.snippet + +proc addrLoc(conf: ConfigRef; a: TLoc): Rope = + if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray: + result = "(&" & a.snippet & ")" + else: + result = a.snippet + +proc byRefLoc(p: BProc; a: TLoc): Rope = + if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not + p.module.compileToCpp: + result = "(&" & a.snippet & ")" + else: + result = a.snippet + +proc rdCharLoc(a: TLoc): Rope = + # read a location that may need a char-cast: + result = rdLoc(a) + if skipTypes(a.t, abstractRange).kind == tyChar: + result = "((NU8)($1))" % [result] + +type + TAssignmentFlag = enum + needToCopy + needToCopySinkParam + needTempForOpenArray + needAssignCall + TAssignmentFlags = set[TAssignmentFlag] + +proc genObjConstr(p: BProc, e: PNode, d: var TLoc) +proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) +proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) + +type + ObjConstrMode = enum + constructObj, + constructRefObj + +proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc, + mode: ObjConstrMode) = + #if optNimV2 in p.config.globalOptions: return + case analyseObjectWithTypeField(t) + of frNone: + discard + of frHeader: + var r = rdLoc(a) + if mode == constructRefObj: r = "(*$1)" % [r] + var s = skipTypes(t, abstractInst) + if not p.module.compileToCpp: + while s.kind == tyObject and s[0] != nil: + r.add(".Sup") + s = skipTypes(s[0], skipPtrs) + if optTinyRtti in p.config.globalOptions: + linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV2(p.module, t, a.lode.info)]) + else: + linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)]) + of frEmbedded: + if optTinyRtti in p.config.globalOptions: + var tmp: TLoc = default(TLoc) + if mode == constructRefObj: + let objType = t.skipTypes(abstractInst+{tyRef}) + rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp) + linefmt(p, cpsStmts, + "#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", + [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, descKindFromSymKind mapTypeChooser(a))]) + else: + rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp) + genAssignment(p, a, tmp, {}) + else: + # worst case for performance: + var r = if mode == constructObj: addrLoc(p.config, a) else: rdLoc(a) + linefmt(p, section, "#objectInit($1, $2);$n", [r, genTypeInfoV1(p.module, t, a.lode.info)]) + + if isException(t): + var r = rdLoc(a) + if mode == constructRefObj: r = "(*$1)" % [r] + var s = skipTypes(t, abstractInst) + if not p.module.compileToCpp: + while s.kind == tyObject and s[0] != nil and s.sym.magic != mException: + r.add(".Sup") + s = skipTypes(s[0], skipPtrs) + linefmt(p, section, "$1.name = $2;$n", [r, makeCString(t.skipTypes(abstractInst).sym.name.s)]) + +proc genRefAssign(p: BProc, dest, src: TLoc) + +proc isComplexValueType(t: PType): bool {.inline.} = + let t = t.skipTypes(abstractInst + tyUserTypeClasses) + result = t.kind in {tyArray, tySet, tyTuple, tyObject, tyOpenArray} or + (t.kind == tyProc and t.callConv == ccClosure) + +include ccgreset + +proc resetLoc(p: BProc, loc: var TLoc) = + let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t) + let typ = skipTypes(loc.t, abstractVarRange) + if isImportedCppType(typ): + var didGenTemp = false + linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)]) + return + if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}: + assert loc.snippet != "" + + let atyp = skipTypes(loc.t, abstractInst) + if atyp.kind in {tyVar, tyLent}: + linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)]) + else: + linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) + elif not isComplexValueType(typ): + if containsGcRef: + var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) + nilLoc.snippet = rope("NIM_NIL") + genRefAssign(p, loc, nilLoc) + else: + linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)]) + else: + if loc.storage != OnStack and containsGcRef: + specializeReset(p, loc) + when false: + linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", + [addrLoc(p.config, loc), genTypeInfoV1(p.module, loc.t, loc.lode.info)]) + # XXX: generated reset procs should not touch the m_type + # field, so disabling this should be safe: + genObjectInit(p, cpsStmts, loc.t, loc, constructObj) + else: + # array passed as argument decayed into pointer, bug #7332 + # so we use getTypeDesc here rather than rdLoc(loc) + let tyDesc = getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc)) + if p.module.compileToCpp and isOrHasImportedCppType(typ): + if lfIndirect in loc.flags: + #C++ cant be just zeroed. We need to call the ctors + var tmp = getTemp(p, loc.t) + linefmt(p, cpsStmts,"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", + [addrLoc(p.config, loc), addrLoc(p.config, tmp), tyDesc]) + else: + linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", + [addrLoc(p.config, loc), tyDesc]) + + # XXX: We can be extra clever here and call memset only + # on the bytes following the m_type field? + genObjectInit(p, cpsStmts, loc.t, loc, constructObj) + +proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = + let typ = loc.t + if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}: + linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) + elif not isComplexValueType(typ): + if containsGarbageCollectedRef(loc.t): + var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) + nilLoc.snippet = rope("NIM_NIL") + genRefAssign(p, loc, nilLoc) + else: + linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), + getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))]) + else: + if (not isTemp or containsGarbageCollectedRef(loc.t)) and not hasNoInit(loc.t): + # don't use nimZeroMem for temporary values for performance if we can + # avoid it: + if not isOrHasImportedCppType(typ): + linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", + [addrLoc(p.config, loc), getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))]) + genObjectInit(p, cpsStmts, loc.t, loc, constructObj) + +proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = + if sfNoInit notin v.flags: + # we know it is a local variable and thus on the stack! + # If ``not immediateAsgn`` it is not initialized in a binding like + # ``var v = X`` and thus we need to init it. + # If ``v`` contains a GC-ref we may pass it to ``unsureAsgnRef`` somehow + # which requires initialization. However this can really only happen if + # ``var v = X()`` gets transformed into ``X(&v)``. + # Nowadays the logic in ccgcalls deals with this case however. + if not immediateAsgn: + constructLoc(p, v.loc) + +proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = + inc(p.labels) + result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + storage: OnStack, flags: {}) + if p.module.compileToCpp and isOrHasImportedCppType(t): + var didGenTemp = false + linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet, + genCppInitializer(p.module, p, t, didGenTemp)]) + else: + linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet]) + constructLoc(p, result, not needsInit) + when false: + # XXX Introduce a compiler switch in order to detect these easily. + if getSize(p.config, t) > 1024 * 1024: + if p.prc != nil: + echo "ENORMOUS TEMPORARY! ", p.config $ p.prc.info + else: + echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo + writeStackTrace() + +proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc = + inc(p.labels) + result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + storage: OnStack, flags: {}) + linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value]) + +proc getIntTemp(p: BProc): TLoc = + inc(p.labels) + result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, + storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt), + flags: {}) + linefmt(p, cpsLocals, "NI $1;$n", [result.snippet]) + +proc localVarDecl(p: BProc; n: PNode): Rope = + result = "" + let s = n.sym + if s.loc.k == locNone: + fillLocalName(p, s) + fillLoc(s.loc, locLocalVar, n, OnStack) + if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) + if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: + result.addf("NIM_ALIGN($1) ", [rope(s.alignment)]) + + genCLineDir(result, p, n.info, p.config) + + result.add getTypeDesc(p.module, s.typ, dkVar) + if sfCodegenDecl notin s.flags: + if sfRegister in s.flags: result.add(" register") + #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: + # decl.add(" GC_GUARD") + if sfVolatile in s.flags: result.add(" volatile") + if sfNoalias in s.flags: result.add(" NIM_NOALIAS") + result.add(" ") + result.add(s.loc.snippet) + else: + result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet]) + +proc assignLocalVar(p: BProc, n: PNode) = + #assert(s.loc.k == locNone) # not yet assigned + # this need not be fulfilled for inline procs; they are regenerated + # for each module that uses them! + let nl = if optLineDir in p.config.options: "" else: "\n" + var decl = localVarDecl(p, n) + if p.module.compileToCpp and isOrHasImportedCppType(n.typ): + var didGenTemp = false + decl.add genCppInitializer(p.module, p, n.typ, didGenTemp) + decl.add ";" & nl + line(p, cpsLocals, decl) + +include ccgthreadvars + +proc varInDynamicLib(m: BModule, sym: PSym) + +proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool = + return m.hcrOn and {sfThread, sfGlobal} * s.flags == {sfGlobal} and + ({lfNoDecl, lfHeader} * s.loc.flags == {}) + # and s.owner.kind == skModule # owner isn't always a module (global pragma on local var) + # and s.loc.k == locGlobalVar # loc isn't always initialized when this proc is used + +proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = + let s = n.sym + if sfCodegenDecl notin s.flags: + if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: + decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)] + if p.hcrOn: decl.add("static ") + elif sfImportc in s.flags: decl.add("extern ") + elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ") + else: decl.add("N_LIB_PRIVATE ") + if s.kind == skLet and value != "": decl.add("NIM_CONST ") + decl.add(td) + if p.hcrOn: decl.add("*") + if sfRegister in s.flags: decl.add(" register") + if sfVolatile in s.flags: decl.add(" volatile") + if sfNoalias in s.flags: decl.add(" NIM_NOALIAS") + else: + if value != "": + decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.snippet, value]) + else: + decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet]) + +proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool) + +proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = + let s = n.sym + if s.loc.k == locNone: + fillBackendName(p.module, s) + fillLoc(s.loc, locGlobalVar, n, OnHeap) + if treatGlobalDifferentlyForHCR(p.module, s): incl(s.loc.flags, lfIndirect) + + if lfDynamicLib in s.loc.flags: + var q = findPendingModule(p.module, s) + if q != nil and not containsOrIncl(q.declaredThings, s.id): + varInDynamicLib(q, s) + else: + s.loc.snippet = mangleDynLibProc(s) + if value != "": + internalError(p.config, n.info, ".dynlib variables cannot have a value") + return + useHeader(p.module, s) + if lfNoDecl in s.loc.flags: return + if not containsOrIncl(p.module.declaredThings, s.id): + if sfThread in s.flags: + declareThreadVar(p.module, s, sfImportc in s.flags) + if value != "": + internalError(p.config, n.info, ".threadvar variables cannot have a value") + else: + var decl: Rope = "" + let td = getTypeDesc(p.module, s.loc.t, dkVar) + genGlobalVarDecl(p, n, td, value, decl) + if s.constraint.isNil: + if value != "": + if p.module.compileToCpp and value.startsWith "{{}": + # TODO: taking this branch, re"\{\{\}(,\s\{\})*\}" might be emitted, resulting in + # either warnings (GCC 12.2+) or errors (Clang 15, MSVC 19.3+) of C++11+ compilers **when + # explicit constructors are around** due to overload resolution rules in place [^0][^1][^2] + # *Workaround* here: have C++'s static initialization mechanism do the default init work, + # for us lacking a deeper knowledge of an imported object's constructors' ex-/implicitness + # (so far) *and yet* trying to achieve default initialization. + # Still, generating {}s in genConstObjConstr() just to omit them here is faaaar from ideal; + # need to figure out a better way, possibly by keeping around more data about the + # imported objects' contructors? + # + # [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization + # [^1]: https://cplusplus.github.io/CWG/issues/1518.html + # [^2]: https://eel.is/c++draft/over.match.ctor + decl.addf(" $1;$n", [s.loc.snippet]) + else: + decl.addf(" $1 = $2;$n", [s.loc.snippet, value]) + else: + decl.addf(" $1;$n", [s.loc.snippet]) + + p.module.s[cfsVars].add(decl) + if p.withinLoop > 0 and value == "": + # fixes tests/run/tzeroarray: + resetLoc(p, s.loc) + +proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) = + let s = vn.sym + fillBackendName(p.module, s) + fillLoc(s.loc, locGlobalVar, vn, OnHeap) + var decl: Rope = "" + let td = getTypeDesc(p.module, vn.sym.typ, dkVar) + genGlobalVarDecl(p, vn, td, "", decl) + decl.add " " & $s.loc.snippet + genCppVarForCtor(p, value, decl, didGenTemp) + if didGenTemp: return # generated in the caller + p.module.s[cfsVars].add decl + +proc assignParam(p: BProc, s: PSym, retType: PType) = + assert(s.loc.snippet != "") + scopeMangledParam(p, s) + +proc fillProcLoc(m: BModule; n: PNode) = + let sym = n.sym + if sym.loc.k == locNone: + fillBackendName(m, sym) + fillLoc(sym.loc, locProc, n, OnStack) + +proc getLabel(p: BProc): TLabel = + inc(p.labels) + result = "LA" & rope(p.labels) & "_" + +proc fixLabel(p: BProc, labl: TLabel) = + p.s(cpsStmts).add("$1: ;$n" % [labl]) + +proc genVarPrototype(m: BModule, n: PNode) +proc requestConstImpl(p: BProc, sym: PSym) +proc genStmts(p: BProc, t: PNode) +proc expr(p: BProc, n: PNode, d: var TLoc) + +proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) +proc intLiteral(i: BiggestInt; result: var Rope) +proc genLiteral(p: BProc, n: PNode; result: var Rope) +proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int) +proc raiseExit(p: BProc) +proc raiseExitCleanup(p: BProc, destroy: string) + +proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc = + result = initLoc(locNone, e, OnUnknown, flags) + expr(p, e, result) + +proc initLocExprSingleUse(p: BProc, e: PNode): TLoc = + result = initLoc(locNone, e, OnUnknown) + if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone): + # We cannot check for tfNoSideEffect here because of mutable parameters. + discard "bug #8202; enforce evaluation order for nested calls for C++ too" + # We may need to consider that 'f(g())' cannot be rewritten to 'tmp = g(); f(tmp)' + # if 'tmp' lacks a move/assignment operator. + if e[0].kind == nkSym and sfCompileToCpp in e[0].sym.flags: + result.flags.incl lfSingleUse + else: + result.flags.incl lfSingleUse + expr(p, e, result) + +include ccgcalls, "ccgstmts.nim" + +proc initFrame(p: BProc, procname, filename: Rope): Rope = + const frameDefines = """ +$1define nimfr_(proc, file) \ + TFrame FR_; \ + FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_); + +$1define nimln_(n) \ + FR_.line = n; + +$1define nimlf_(n, file) \ + FR_.line = n; FR_.filename = file; + +""" + if p.module.s[cfsFrameDefines].len == 0: + appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"]) + + cgsym(p.module, "nimFrame") + result = ropecg(p.module, "\tnimfr_($1, $2);$n", [procname, filename]) + +proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope = + cgsym(p.module, "nimFrame") + p.blocks[0].sections[cpsLocals].addf("TFrame $1;$n", [frame]) + result = ropecg(p.module, "\t$1.procname = $2; $1.filename = $3; " & + " $1.line = $4; $1.len = -1; nimFrame(&$1);$n", + [frame, procname, filename, line]) + +proc deinitFrameNoDebug(p: BProc; frame: Rope): Rope = + result = ropecg(p.module, "\t#popFrameOfAddr(&$1);$n", [frame]) + +proc deinitFrame(p: BProc): Rope = + result = ropecg(p.module, "\t#popFrame();$n", []) + +include ccgexprs + +# ----------------------------- dynamic library handling ----------------- +# We don't finalize dynamic libs as the OS does this for us. + +proc isGetProcAddr(lib: PLib): bool = + let n = lib.path + result = n.kind in nkCallKinds and n.typ != nil and + n.typ.kind in {tyPointer, tyProc} + +proc loadDynamicLib(m: BModule, lib: PLib) = + assert(lib != nil) + if not lib.generated: + lib.generated = true + var tmp = getTempName(m) + assert(lib.name == "") + lib.name = tmp # BUGFIX: cgsym has awful side-effects + m.s[cfsVars].addf("static void* $1;$n", [tmp]) + if lib.path.kind in {nkStrLit..nkTripleStrLit}: + var s: TStringSeq = @[] + libCandidates(lib.path.strVal, s) + rawMessage(m.config, hintDependency, lib.path.strVal) + var loadlib: Rope = "" + for i in 0..high(s): + inc(m.labels) + if i > 0: loadlib.add("||") + let n = newStrNode(nkStrLit, s[i]) + n.info = lib.path.info + appcg(m, loadlib, "($1 = #nimLoadLibrary(", [tmp]) + genStringLiteral(m, n, loadlib) + loadlib.addf "))$n", [] + appcg(m, m.s[cfsDynLibInit], + "if (!($1)) #nimLoadLibraryError(", + [loadlib]) + genStringLiteral(m, lib.path, m.s[cfsDynLibInit]) + m.s[cfsDynLibInit].addf ");$n", [] + + else: + var p = newProc(nil, m) + p.options.excl optStackTrace + p.flags.incl nimErrorFlagDisabled + var dest: TLoc = initLoc(locTemp, lib.path, OnStack) + dest.snippet = getTempName(m) + appcg(m, m.s[cfsDynLibInit],"$1 $2;$n", + [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)]) + expr(p, lib.path, dest) + + m.s[cfsVars].add(p.s(cpsLocals)) + m.s[cfsDynLibInit].add(p.s(cpsInit)) + m.s[cfsDynLibInit].add(p.s(cpsStmts)) + appcg(m, m.s[cfsDynLibInit], + "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n", + [tmp, rdLoc(dest)]) + + if lib.name == "": internalError(m.config, "loadDynamicLib") + +proc mangleDynLibProc(sym: PSym): Rope = + # we have to build this as a single rope in order not to trip the + # optimization in genInfixCall, see test tests/cpp/t8241.nim + if sfCompilerProc in sym.flags: + # NOTE: sym.loc.snippet is the external name! + result = rope(sym.name.s) + else: + result = rope(strutils.`%`("Dl_$1_", $sym.id)) + +proc symInDynamicLib(m: BModule, sym: PSym) = + var lib = sym.annex + let isCall = isGetProcAddr(lib) + var extname = sym.loc.snippet + if not isCall: loadDynamicLib(m, lib) + var tmp = mangleDynLibProc(sym) + sym.loc.snippet = tmp # from now on we only need the internal name + sym.typ.sym = nil # generate a new name + inc(m.labels, 2) + if isCall: + let n = lib.path + var a: TLoc = initLocExpr(m.initProc, n[0]) + var params = rdLoc(a) & "(" + for i in 1..<n.len-1: + a = initLocExpr(m.initProc, n[i]) + params.add(rdLoc(a)) + params.add(", ") + let load = "\t$1 = ($2) ($3$4));$n" % + [tmp, getTypeDesc(m, sym.typ, dkVar), params, makeCString($extname)] + var last = lastSon(n) + if last.kind == nkHiddenStdConv: last = last[1] + internalAssert(m.config, last.kind == nkStrLit) + let idx = last.strVal + if idx.len == 0: + m.initProc.s(cpsStmts).add(load) + elif idx.len == 1 and idx[0] in {'0'..'9'}: + m.extensionLoaders[idx[0]].add(load) + else: + internalError(m.config, sym.info, "wrong index: " & idx) + else: + appcg(m, m.s[cfsDynLibInit], + "\t$1 = ($2) #nimGetProcAddr($3, $4);$n", + [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)]) + m.s[cfsVars].addf("$2 $1;$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)]) + +proc varInDynamicLib(m: BModule, sym: PSym) = + var lib = sym.annex + var extname = sym.loc.snippet + loadDynamicLib(m, lib) + incl(sym.loc.flags, lfIndirect) + var tmp = mangleDynLibProc(sym) + sym.loc.snippet = tmp # from now on we only need the internal name + inc(m.labels, 2) + appcg(m, m.s[cfsDynLibInit], + "$1 = ($2*) #nimGetProcAddr($3, $4);$n", + [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)]) + m.s[cfsVars].addf("$2* $1;$n", + [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)]) + +proc symInDynamicLibPartial(m: BModule, sym: PSym) = + sym.loc.snippet = mangleDynLibProc(sym) + sym.typ.sym = nil # generate a new name + +proc cgsymImpl(m: BModule; sym: PSym) {.inline.} = + case sym.kind + of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym) + of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym) + of skType: discard getTypeDesc(m, sym.typ) + else: internalError(m.config, "cgsym: " & $sym.kind) + +proc cgsym(m: BModule, name: string) = + let sym = magicsys.getCompilerProc(m.g.graph, name) + if sym != nil: + cgsymImpl m, sym + else: + rawMessage(m.config, errGenerated, "system module needs: " & name) + +proc cgsymValue(m: BModule, name: string): Rope = + let sym = magicsys.getCompilerProc(m.g.graph, name) + if sym != nil: + cgsymImpl m, sym + else: + rawMessage(m.config, errGenerated, "system module needs: " & name) + result = sym.loc.snippet + if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}: + result.addActualSuffixForHCR(m.module, sym) + +proc generateHeaders(m: BModule) = + var nimbase = m.config.nimbasePattern + if nimbase == "": nimbase = "nimbase.h" + m.s[cfsHeaders].addf("\L#include \"$1\"\L", [nimbase]) + + for it in m.headerFiles: + if it[0] == '#': + m.s[cfsHeaders].add(rope(it.replace('`', '"') & "\L")) + elif it[0] notin {'"', '<'}: + m.s[cfsHeaders].addf("#include \"$1\"$N", [rope(it)]) + else: + m.s[cfsHeaders].addf("#include $1$N", [rope(it)]) + m.s[cfsHeaders].add("""#undef LANGUAGE_C +#undef MIPSEB +#undef MIPSEL +#undef PPC +#undef R3000 +#undef R4000 +#undef i386 +#undef linux +#undef mips +#undef near +#undef far +#undef powerpc +#undef unix +""") + +proc openNamespaceNim(namespace: string; result: var Rope) = + result.add("namespace ") + result.add(namespace) + result.add(" {\L") + +proc closeNamespaceNim(result: var Rope) = + result.add("}\L") + +proc closureSetup(p: BProc, prc: PSym) = + if tfCapturesEnv notin prc.typ.flags: return + # prc.ast[paramsPos].last contains the type we're after: + var ls = lastSon(prc.ast[paramsPos]) + if ls.kind != nkSym: + internalError(p.config, prc.info, "closure generation failed") + var env = ls.sym + #echo "created environment: ", env.id, " for ", prc.name.s + assignLocalVar(p, ls) + # generate cast assignment: + if p.config.selectedGC == gcGo: + linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, ($2) ClE_0);$n", + [addrLoc(p.config, env.loc), getTypeDesc(p.module, env.typ)]) + else: + linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n", + [rdLoc(env.loc), getTypeDesc(p.module, env.typ)]) + +const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef, + nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} + + declarativeDefs + +proc containsResult(n: PNode): bool = + result = false + case n.kind + of succ(nkEmpty)..pred(nkSym), succ(nkSym)..nkNilLit, harmless: + discard + of nkReturnStmt: + for i in 0..<n.len: + if containsResult(n[i]): return true + result = n.len > 0 and n[0].kind == nkEmpty + of nkSym: + if n.sym.kind == skResult: + result = true + else: + for i in 0..<n.len: + if containsResult(n[i]): return true + +proc easyResultAsgn(n: PNode): PNode = + result = nil + case n.kind + of nkStmtList, nkStmtListExpr: + var i = 0 + while i < n.len and n[i].kind in harmless: inc i + if i < n.len: result = easyResultAsgn(n[i]) + of nkAsgn, nkFastAsgn, nkSinkAsgn: + if n[0].kind == nkSym and n[0].sym.kind == skResult and not containsResult(n[1]): + incl n.flags, nfPreventCg + return n[1] + of nkReturnStmt: + if n.len > 0: + result = easyResultAsgn(n[0]) + if result != nil: incl n.flags, nfPreventCg + else: discard + +type + InitResultEnum = enum Unknown, InitSkippable, InitRequired + +proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum = + # Exceptions coming from calls don't have not be considered here: + # + # proc bar(): string = raise newException(...) + # + # proc foo(): string = + # # optimized out: 'reset(result)' + # result = bar() + # + # try: + # a = foo() + # except: + # echo "a was not written to" + # + template allPathsInBranch(it) = + let a = allPathsAsgnResult(p, it) + case a + of InitRequired: return InitRequired + of InitSkippable: discard + of Unknown: + # sticky, but can be overwritten by InitRequired: + result = Unknown + + result = Unknown + case n.kind + of nkStmtList, nkStmtListExpr: + for it in n: + result = allPathsAsgnResult(p, it) + if result != Unknown: return result + of nkAsgn, nkFastAsgn, nkSinkAsgn: + if n[0].kind == nkSym and n[0].sym.kind == skResult: + if not containsResult(n[1]): + if allPathsAsgnResult(p, n[1]) == InitRequired: + result = InitRequired + else: + result = InitSkippable + else: result = InitRequired + elif containsResult(n): + result = InitRequired + else: + result = allPathsAsgnResult(p, n[1]) + of nkReturnStmt: + if n.len > 0: + if n[0].kind == nkEmpty and result != InitSkippable: + # This is a bare `return` statement, if `result` was not initialized + # anywhere else (or if we're not sure about this) let's require it to be + # initialized. This avoids cases like #9286 where this heuristic lead to + # wrong code being generated. + result = InitRequired + else: result = allPathsAsgnResult(p, n[0]) + of nkIfStmt, nkIfExpr: + var exhaustive = false + result = InitSkippable + for it in n: + # Every condition must not use 'result': + if it.len == 2 and containsResult(it[0]): + return InitRequired + if it.len == 1: exhaustive = true + allPathsInBranch(it.lastSon) + # if the 'if' statement is not exhaustive and yet it touched 'result' + # in some way, say Unknown. + if not exhaustive: result = Unknown + of nkCaseStmt: + if containsResult(n[0]): return InitRequired + result = InitSkippable + var exhaustive = skipTypes(n[0].typ, + abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring} + for i in 1..<n.len: + let it = n[i] + allPathsInBranch(it.lastSon) + if it.kind == nkElse: exhaustive = true + if not exhaustive: result = Unknown + of nkWhileStmt: + # some dubious code can assign the result in the 'while' + # condition and that would be fine. Everything else isn't: + result = allPathsAsgnResult(p, n[0]) + if result == Unknown: + result = allPathsAsgnResult(p, n[1]) + # we cannot assume that the 'while' loop is really executed at least once: + if result == InitSkippable: result = Unknown + of harmless: + result = Unknown + of nkGotoState, nkBreakState: + # give up for now. + result = InitRequired + of nkSym: + # some path reads from 'result' before it was written to! + if n.sym.kind == skResult: result = InitRequired + of nkTryStmt, nkHiddenTryStmt: + # We need to watch out for the following problem: + # try: + # result = stuffThatRaises() + # except: + # discard "result was not set" + # + # So ... even if the assignment to 'result' is the very first + # assignment this is not good enough! The only pattern we allow for + # is 'finally: result = x' + result = InitSkippable + allPathsInBranch(n[0]) + for i in 1..<n.len: + if n[i].kind == nkFinally: + result = allPathsAsgnResult(p, n[i].lastSon) + else: + allPathsInBranch(n[i].lastSon) + of nkCallKinds: + if canRaiseDisp(p, n[0]): + result = InitRequired + else: + for i in 0..<n.safeLen: + allPathsInBranch(n[i]) + of nkRaiseStmt: + result = InitRequired + of nkChckRangeF, nkChckRange64, nkChckRange: + # TODO: more checks might need to be covered like overflow, indexDefect etc. + # bug #22852 + result = InitRequired + else: + for i in 0..<n.safeLen: + allPathsInBranch(n[i]) + +proc getProcTypeCast(m: BModule, prc: PSym): Rope = + result = getTypeDesc(m, prc.loc.t) + if prc.typ.callConv == ccClosure: + var rettype, params: Rope = "" + var check = initIntSet() + genProcParams(m, prc.typ, rettype, params, check) + result = "$1(*)$2" % [rettype, params] + +proc genProcBody(p: BProc; procBody: PNode) = + genStmts(p, procBody) # modifies p.locals, p.init, etc. + if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}: + p.flags.incl nimErrorFlagDeclared + p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", [])) + p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", [])) + +proc isNoReturn(m: BModule; s: PSym): bool {.inline.} = + sfNoReturn in s.flags and m.config.exc != excGoto + +proc genProcAux*(m: BModule, prc: PSym) = + var p = newProc(prc, m) + var header = newRopeAppender() + let isCppMember = m.config.backend == backendCpp and sfCppMember * prc.flags != {} + if isCppMember: + genMemberProcHeader(m, prc, header) + else: + genProcHeader(m, prc, header) + var returnStmt: Rope = "" + assert(prc.ast != nil) + + var procBody = transformBody(m.g.graph, m.idgen, prc, {}) + if sfInjectDestructors in prc.flags: + procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody) + + let tmpInfo = prc.info + discard freshLineInfo(p, prc.info) + + if sfPure notin prc.flags and prc.typ.returnType != nil: + if resultPos >= prc.ast.len: + internalError(m.config, prc.info, "proc has no result symbol") + let resNode = prc.ast[resultPos] + let res = resNode.sym # get result symbol + if not isInvalidReturnType(m.config, prc.typ) and sfConstructor notin prc.flags: + if sfNoInit in prc.flags: incl(res.flags, sfNoInit) + if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil): + var decl = localVarDecl(p, resNode) + var a: TLoc = initLocExprSingleUse(p, val) + linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)]) + else: + # declare the result symbol: + assignLocalVar(p, resNode) + assert(res.loc.snippet != "") + if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and + allPathsAsgnResult(p, procBody) == InitSkippable: + # In an ideal world the codegen could rely on injectdestructors doing its job properly + # and then the analysis step would not be required. + discard "result init optimized out" + else: + initLocalVar(p, res, immediateAsgn=false) + returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)]) + elif sfConstructor in prc.flags: + resNode.sym.loc.flags.incl lfIndirect + fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) + prc.loc.snippet = getTypeDesc(m, resNode.sym.loc.t, dkVar) + else: + fillResult(p.config, resNode, prc.typ) + assignParam(p, res, prc.typ.returnType) + # We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)' + # to 'unsureAsgn(result, x)' + # Sketch why this is correct: If 'result' points to a stack location + # the 'unsureAsgn' is a nop. If it points to a global variable the + # global is either 'nil' or points to valid memory and so the RC operation + # succeeds without touching not-initialized memory. + if sfNoInit in prc.flags: discard + elif allPathsAsgnResult(p, procBody) == InitSkippable: discard + else: + resetLoc(p, res.loc) + if skipTypes(res.typ, abstractInst).kind == tyArray: + #incl(res.loc.flags, lfIndirect) + res.loc.storage = OnUnknown + + for i in 1..<prc.typ.n.len: + let param = prc.typ.n[i].sym + if param.typ.isCompileTimeOnly: continue + assignParam(p, param, prc.typ.returnType) + closureSetup(p, prc) + genProcBody(p, procBody) + + prc.info = tmpInfo + + var generatedProc: Rope = "" + generatedProc.genCLineDir prc.info, m.config + if isNoReturn(p.module, prc): + if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember: + header = "__declspec(noreturn) " & header + if sfPure in prc.flags: + if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember: + header = "__declspec(naked) " & header + generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N", + [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) + else: + if m.hcrOn and isReloadable(m, prc): + # Add forward declaration for "_actual"-suffixed functions defined in the same module (or inline). + # This fixes the use of methods and also the case when 2 functions within the same module + # call each other using directly the "_actual" versions (an optimization) - see issue #11608 + m.s[cfsProcHeaders].addf("$1;\n", [header]) + generatedProc.add ropecg(p.module, "$1 {$n", [header]) + if optStackTrace in prc.options: + generatedProc.add(p.s(cpsLocals)) + var procname = makeCString(prc.name.s) + generatedProc.add(initFrame(p, procname, quotedFilename(p.config, prc.info))) + else: + generatedProc.add(p.s(cpsLocals)) + if optProfiler in prc.options: + # invoke at proc entry for recursion: + appcg(p, cpsInit, "\t#nimProfile();$n", []) + # this pair of {} is required for C++ (C++ is weird with its + # control flow integrity checks): + if beforeRetNeeded in p.flags: generatedProc.add("{") + generatedProc.add(p.s(cpsInit)) + generatedProc.add(p.s(cpsStmts)) + if beforeRetNeeded in p.flags: generatedProc.add("\t}BeforeRet_: ;\n") + if optStackTrace in prc.options: generatedProc.add(deinitFrame(p)) + generatedProc.add(returnStmt) + generatedProc.add("}\n") + m.s[cfsProcs].add(generatedProc) + if isReloadable(m, prc): + m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n", + [prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) + +proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = + result = (sfCompileToCpp in m.module.flags and + sfCompileToCpp notin sym.getModule().flags and + m.config.backend != backendCpp) or ( + sym.flags * {sfInfixCall, sfCompilerProc, sfMangleCpp} == {} and + sym.flags * {sfImportc, sfExportc} != {} and + sym.magic == mNone and + m.config.backend == backendCpp) + +proc genProcPrototype(m: BModule, sym: PSym) = + useHeader(m, sym) + if lfNoDecl in sym.loc.flags or sfCppMember * sym.flags != {}: return + if lfDynamicLib in sym.loc.flags: + if sym.itemId.module != m.module.position and + not containsOrIncl(m.declaredThings, sym.id): + m.s[cfsVars].add(ropecg(m, "$1 $2 $3;$n", + [(if isReloadable(m, sym): "static" else: "extern"), + getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)])) + if isReloadable(m, sym): + m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n", + [mangleDynLibProc(sym), getTypeDesc(m, sym.loc.t), getModuleDllPath(m, sym)]) + elif not containsOrIncl(m.declaredProtos, sym.id): + let asPtr = isReloadable(m, sym) + var header = newRopeAppender() + genProcHeader(m, sym, header, asPtr) + if not asPtr: + if isNoReturn(m, sym) and hasDeclspec in extccomp.CC[m.config.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[m.config.cCompiler].props: + header.add(" __attribute__((naked))") + if isNoReturn(m, sym) and hasAttribute in CC[m.config.cCompiler].props: + header.add(" __attribute__((noreturn))") + m.s[cfsProcHeaders].add(ropecg(m, "$1;$N", [header])) + +# TODO: figure out how to rename this - it DOES generate a forward declaration +proc genProcNoForward(m: BModule, prc: PSym) = + if lfImportCompilerProc in prc.loc.flags: + fillProcLoc(m, prc.ast[namePos]) + useHeader(m, prc) + # dependency to a compilerproc: + cgsym(m, prc.name.s) + return + if lfNoDecl in prc.loc.flags: + fillProcLoc(m, prc.ast[namePos]) + genProcPrototype(m, prc) + elif lfDynamicLib in prc.loc.flags: + var q = findPendingModule(m, prc) + fillProcLoc(q, prc.ast[namePos]) + genProcPrototype(m, prc) + if q != nil and not containsOrIncl(q.declaredThings, prc.id): + symInDynamicLib(q, prc) + # register the procedure even though it is in a different dynamic library and will not be + # reloadable (and has no _actual suffix) - other modules will need to be able to get it through + # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded) + if isReloadable(q, prc): + q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n", + [prc.loc.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)]) + else: + symInDynamicLibPartial(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: + # mangle the inline proc based on the module where it is defined - + # not on the first module that uses it + let m2 = if m.config.symbolFiles != disabledSf: m + else: findPendingModule(m, prc) + fillProcLoc(m2, 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.snippet = nil + # prc.loc.snippet = mangleName(m, prc) + genProcPrototype(m, prc) + genProcAux(m, prc) + elif sfImportc notin prc.flags: + var q = findPendingModule(m, prc) + fillProcLoc(q, prc.ast[namePos]) + # generate a getProc call to initialize the pointer for this + # externally-to-the-current-module defined proc, also important + # to do the declaredProtos check before the call to genProcPrototype + if isReloadable(m, prc) and prc.id notin m.declaredProtos and + q != nil and q.module.id != m.module.id: + m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n", + [prc.loc.snippet, getProcTypeCast(m, prc), getModuleDllPath(m, prc)]) + genProcPrototype(m, prc) + if q != nil and not containsOrIncl(q.declaredThings, prc.id): + # make sure there is a "prototype" in the external module + # which will actually become a function pointer + if isReloadable(m, prc): + genProcPrototype(q, prc) + 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) = + if genConstSetup(p, sym): + let m = p.module + # declare implementation: + var q = findPendingModule(m, sym) + if q != nil and not containsOrIncl(q.declaredThings, sym.id): + assert q.initProc.module == q + genConstDefinition(q, p, sym) + # declare header: + if q != m and not containsOrIncl(m.declaredThings, sym.id): + genConstHeader(m, q, p, sym) + +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 genVarPrototype(m: BModule, n: PNode) = + #assert(sfGlobal in sym.flags) + let sym = n.sym + useHeader(m, sym) + fillBackendName(m, sym) + fillLoc(sym.loc, locGlobalVar, n, OnHeap) + if treatGlobalDifferentlyForHCR(m, sym): incl(sym.loc.flags, lfIndirect) + + if (lfNoDecl in sym.loc.flags) or contains(m.declaredThings, sym.id): + return + if sym.owner.id != m.module.id: + # else we already have the symbol generated! + assert(sym.loc.snippet != "") + incl(m.declaredThings, sym.id) + if sfThread in sym.flags: + declareThreadVar(m, sym, true) + else: + if sym.kind in {skLet, skVar, skField, skForVar} and sym.alignment > 0: + m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)] + m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ") + m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, dkVar)) + if m.hcrOn: m.s[cfsVars].add("*") + if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*") + if sfRegister in sym.flags: m.s[cfsVars].add(" register") + if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile") + if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS") + m.s[cfsVars].addf(" $1;$n", [sym.loc.snippet]) + if m.hcrOn: m.initProc.procSec(cpsLocals).addf( + "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet, + getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)]) + +proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} = + result.addf("#define NIM_INTBITS $1\L", [ + platform.CPU[conf.target.targetCPU].intSize.rope]) + if conf.cppCustomNamespace.len > 0: + result.add("#define USE_NIM_NAMESPACE ") + result.add(conf.cppCustomNamespace) + result.add("\L") + if conf.isDefined("nimEmulateOverflowChecks"): + result.add("#define NIM_EmulateOverflowChecks\L") + +proc headerTop(): Rope = + result = "/* Generated by Nim Compiler v$1 */$N" % [rope(VersionAsString)] + +proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope = + result = headerTop() + if optCompileOnly notin conf.globalOptions: + result.add ("/* Compiled for: $1, $2, $3 */$N" & + "/* Command for C compiler:$n $4 */$N") % + [rope(platform.OS[conf.target.targetOS].name), + rope(platform.CPU[conf.target.targetCPU].name), + rope(extccomp.CC[conf.cCompiler].name), + rope(getCompileCFileCmd(conf, cfile))] + +proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = + result = getCopyright(conf, cfile) + if conf.hcrOn: result.add("#define NIM_HOT_CODE_RELOADING\L") + addNimDefines(result, conf) + +proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope = + ## Returns a mangled module name. + result = mangleModuleName(conf, filename).mangle + +proc getSomeNameForModule(m: BModule): Rope = + ## Returns a mangled module name. + assert m.module.kind == skModule + assert m.module.owner.kind == skPackage + result = mangleModuleName(m.g.config, m.filename).mangle + +proc getSomeInitName(m: BModule, suffix: string): Rope = + if not m.hcrOn: + result = getSomeNameForModule(m) + else: + result = "" + result.add suffix + +proc getInitName(m: BModule): Rope = + if sfMainModule in m.module.flags: + # generate constant name for main module, for "easy" debugging. + result = rope(m.config.nimMainPrefix) & rope"NimMainModule" + else: + result = getSomeInitName(m, "Init000") + +proc getDatInitName(m: BModule): Rope = getSomeInitName(m, "DatInit000") +proc getHcrInitName(m: BModule): Rope = getSomeInitName(m, "HcrInit000") + +proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): Rope + +proc genMainProc(m: BModule) = + ## this function is called in cgenWriteModules after all modules are closed, + ## it means raising dependency on the symbols is too late as it will not propagate + ## into other modules, only simple rope manipulations are allowed + var preMainCode: Rope = "" + if m.hcrOn: + proc loadLib(handle: string, name: string): Rope = + result = "" + let prc = magicsys.getCompilerProc(m.g.graph, name) + assert prc != nil + let n = newStrNode(nkStrLit, prc.annex.path.strVal) + n.info = prc.annex.path.info + var strLit = newRopeAppender() + genStringLiteral(m, n, strLit) + appcg(m, result, "\tif (!($1 = #nimLoadLibrary($2)))$N" & + "\t\t#nimLoadLibraryError($2);$N", + [handle, strLit]) + + preMainCode.add(loadLib("hcr_handle", "hcrGetProc")) + if m.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix]) + else: + preMainCode.add("\tvoid* rtl_handle;\L") + preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom")) + preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr")) + preMainCode.add("\tinner = $1PreMain;\L" % [rope m.config.nimMainPrefix]) + preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L") + preMainCode.add("\t(*inner)();\L") + else: + preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix]) + + var posixCmdLine: Rope = "" + if optNoMain notin m.config.globalOptions: + posixCmdLine.add "N_LIB_PRIVATE int cmdCount;\L" + posixCmdLine.add "N_LIB_PRIVATE char** cmdLine;\L" + posixCmdLine.add "N_LIB_PRIVATE char** gEnv;\L" + + const + # The use of a volatile function pointer to call Pre/NimMainInner + # prevents inlining of the NimMainInner function and dependent + # functions, which might otherwise merge their stack frames. + + PreMainBody = "$N" & + "N_LIB_PRIVATE void $3PreMainInner(void) {$N" & + "$2" & + "}$N$N" & + "$4" & + "N_LIB_PRIVATE void $3PreMain(void) {$N" & + "##if $5$N" & # 1 for volatile call, 0 for non-volatile + "\tvoid (*volatile inner)(void);$N" & + "\tinner = $3PreMainInner;$N" & + "$1" & + "\t(*inner)();$N" & + "##else$N" & + "$1" & + "\t$3PreMainInner();$N" & + "##endif$N" & + "}$N$N" + + MainProcs = + "\t$^NimMain();$N" + + MainProcsWithResult = + MainProcs & ("\treturn $1nim_program_result;$N") + + NimMainInner = "N_LIB_PRIVATE N_CDECL(void, $5NimMainInner)(void) {$N" & + "$1" & + "}$N$N" + + NimMainProc = + "N_CDECL(void, $5NimMain)(void) {$N" & + "##if $6$N" & # 1 for volatile call, 0 for non-volatile + "\tvoid (*volatile inner)(void);$N" & + "$4" & + "\tinner = $5NimMainInner;$N" & + "$2" & + "\t(*inner)();$N" & + "##else$N" & + "$4" & + "$2" & + "\t$5NimMainInner();$N" & + "##endif$N" & + "}$N$N" + + NimMainBody = NimMainInner & NimMainProc + + PosixCMain = + "int main(int argc, char** args, char** env) {$N" & + "\tcmdLine = args;$N" & + "\tcmdCount = argc;$N" & + "\tgEnv = env;$N" & + MainProcsWithResult & + "}$N$N" + + StandaloneCMain = + "int main(void) {$N" & + MainProcs & + "\treturn 0;$N" & + "}$N$N" + + WinNimMain = NimMainBody + + WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" & + " HINSTANCE hPrevInstance, $N" & + " LPSTR lpCmdLine, int nCmdShow) {$N" & + MainProcsWithResult & "}$N$N" + + WinNimDllMain = NimMainInner & "N_LIB_EXPORT " & NimMainProc + + WinCDllMain = + "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" & + " LPVOID lpvReserved) {$N" & + "\tif (fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "\t}$N" & + "\treturn 1;$N}$N$N" + + PosixNimDllMain = WinNimDllMain + + PosixCDllMain = + "N_LIB_PRIVATE void NIM_POSIX_INIT NimMainInit(void) {$N" & + MainProcs & + "}$N$N" + + GenodeNimMain = + "extern Genode::Env *nim_runtime_env;$N" & + "extern \"C\" void nim_component_construct(Genode::Env*);$N$N" & + NimMainBody + + ComponentConstruct = + "void Libc::Component::construct(Libc::Env &env) {$N" & + "\t// Set Env used during runtime initialization$N" & + "\tnim_runtime_env = &env;$N" & + "\tLibc::with_libc([&] () {$N\t" & + "\t// Initialize runtime and globals$N" & + MainProcs & + "\t// Call application construct$N" & + "\t\tnim_component_construct(&env);$N" & + "\t});$N" & + "}$N$N" + + if m.config.target.targetOS == osWindows and + m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: + m.includeHeader("<windows.h>") + elif m.config.target.targetOS == osGenode: + m.includeHeader("<libc/component.h>") + + let initStackBottomCall = + if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc}: "".rope + else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []) + inc(m.labels) + + let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: "1" else: "0" + appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine, isVolatile]) + + if m.config.target.targetOS == osWindows and + m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: + if optGenGuiApp in m.config.globalOptions: + const nimMain = WinNimMain + appcg(m, m.s[cfsProcs], nimMain, + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + else: + const nimMain = WinNimDllMain + appcg(m, m.s[cfsProcs], nimMain, + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + elif m.config.target.targetOS == osGenode: + const nimMain = GenodeNimMain + appcg(m, m.s[cfsProcs], nimMain, + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + elif optGenDynLib in m.config.globalOptions: + const nimMain = PosixNimDllMain + appcg(m, m.s[cfsProcs], nimMain, + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + else: + const nimMain = NimMainBody + appcg(m, m.s[cfsProcs], nimMain, + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile]) + + if optNoMain notin m.config.globalOptions: + if m.config.cppCustomNamespace.len > 0: + closeNamespaceNim(m.s[cfsProcs]) + m.s[cfsProcs].add "using namespace " & m.config.cppCustomNamespace & ";\L" + + if m.config.target.targetOS == osWindows and + m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: + if optGenGuiApp in m.config.globalOptions: + const otherMain = WinCMain + appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix]) + else: + const otherMain = WinCDllMain + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + elif m.config.target.targetOS == osGenode: + const otherMain = ComponentConstruct + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + elif optGenDynLib in m.config.globalOptions: + const otherMain = PosixCDllMain + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + elif m.config.target.targetOS == osStandalone: + const otherMain = StandaloneCMain + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) + else: + const otherMain = PosixCMain + appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix]) + + if m.config.cppCustomNamespace.len > 0: + openNamespaceNim(m.config.cppCustomNamespace, m.s[cfsProcs]) + +proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag]) = + ## Called from the IC backend. + if HasDatInitProc in flags: + let datInit = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "DatInit000" + g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit]) + g.mainDatInit.addf("\t$1();$N", [datInit]) + if HasModuleInitProc in flags: + let init = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "Init000" + g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) + let initCall = "\t$1();$N" % [init] + if sfMainModule in m.flags: + g.mainModInit.add(initCall) + elif sfSystemModule in m.flags: + g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any + else: + g.otherModsInit.add(initCall) + +proc whichInitProcs*(m: BModule): set[ModuleBackendFlag] = + # called from IC. + result = {} + if m.hcrOn or m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0: + result.incl HasModuleInitProc + for i in cfsTypeInit1..cfsDynLibInit: + if m.s[i].len != 0: + result.incl HasDatInitProc + break + +proc registerModuleToMain(g: BModuleList; m: BModule) = + let + init = m.getInitName + datInit = m.getDatInitName + + if m.hcrOn: + var hcrModuleMeta = "$nN_LIB_PRIVATE const char* hcr_module_list[] = {$n" % [] + let systemModulePath = getModuleDllPath(m, g.modules[g.graph.config.m.systemFileIdx.int].module) + let mainModulePath = getModuleDllPath(m, m.module) + if sfMainModule in m.module.flags: + hcrModuleMeta.addf("\t$1,$n", [systemModulePath]) + g.graph.importDeps.withValue(FileIndex(m.module.position), deps): + for curr in deps[]: + hcrModuleMeta.addf("\t$1,$n", [getModuleDllPath(m, g.modules[curr.int].module)]) + hcrModuleMeta.addf("\t\"\"};$n", []) + hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(void**, HcrGetImportedModules)() { return (void**)hcr_module_list; }$n", []) + hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(char*, HcrGetSigHash)() { return \"$1\"; }$n$n", + [($sigHash(m.module, m.config)).rope]) + if sfMainModule in m.module.flags: + g.mainModProcs.add(hcrModuleMeta) + g.mainModProcs.addf("static void* hcr_handle;$N", []) + g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [init]) + g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [datInit]) + g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void*, N_NIMCALL_PTR(void*, getProcAddr)(void*, char*));$N", [m.getHcrInitName]) + g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void);$N", []) + g.mainModInit.addf("\t$1();$N", [init]) + g.otherModsInit.addf("\thcrInit((void**)hcr_module_list, $1, $2, $3, hcr_handle, nimGetProcAddr);$n", + [mainModulePath, systemModulePath, datInit]) + g.mainDatInit.addf("\t$1(hcr_handle, nimGetProcAddr);$N", [m.getHcrInitName]) + g.mainDatInit.addf("\thcrAddModule($1);\n", [mainModulePath]) + g.mainDatInit.addf("\tHcrCreateTypeInfos();$N", []) + # nasty nasty hack to get the command line functionality working with HCR + # register the 2 variables on behalf of the os module which might not even + # be loaded (in which case it will get collected but that is not a problem) + # EDIT: indeed, this hack, in combination with another un-necessary one + # (`makeCString` was doing line wrap of string litterals) was root cause for + # bug #16265. + let osModulePath = ($systemModulePath).replace("stdlib_system", "stdlib_os").rope + g.mainDatInit.addf("\thcrAddModule($1);\n", [osModulePath]) + g.mainDatInit.add("\tint* cmd_count;\n") + g.mainDatInit.add("\tchar*** cmd_line;\n") + g.mainDatInit.addf("\thcrRegisterGlobal($1, \"cmdCount\", sizeof(cmd_count), NULL, (void**)&cmd_count);$N", [osModulePath]) + g.mainDatInit.addf("\thcrRegisterGlobal($1, \"cmdLine\", sizeof(cmd_line), NULL, (void**)&cmd_line);$N", [osModulePath]) + g.mainDatInit.add("\t*cmd_count = cmdCount;\n") + g.mainDatInit.add("\t*cmd_line = cmdLine;\n") + else: + m.s[cfsInitProc].add(hcrModuleMeta) + return + + if m.s[cfsDatInitProc].len > 0: + g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit]) + g.mainDatInit.addf("\t$1();$N", [datInit]) + + # Initialization of TLS and GC should be done in between + # systemDatInit and systemInit calls if any + if sfSystemModule in m.module.flags: + if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: + g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", [])) + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: + g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])) + + if m.s[cfsInitProc].len > 0: + g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) + let initCall = "\t$1();$N" % [init] + if sfMainModule in m.module.flags: + g.mainModInit.add(initCall) + elif sfSystemModule in m.module.flags: + g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any + else: + g.otherModsInit.add(initCall) + +proc genDatInitCode(m: BModule) = + ## this function is called in cgenWriteModules after all modules are closed, + ## it means raising dependency on the symbols is too late as it will not propagate + ## into other modules, only simple rope manipulations are allowed + + var moduleDatInitRequired = m.hcrOn + + var prc = "$1 N_NIMCALL(void, $2)(void) {$N" % + [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), getDatInitName(m)] + + # we don't want to break into such init code - could happen if a line + # directive from a function written by the user spills after itself + genCLineDir(prc, InvalidFileIdx, 999999, m.config) + + for i in cfsTypeInit1..cfsDynLibInit: + if m.s[i].len != 0: + moduleDatInitRequired = true + prc.add(m.s[i]) + + prc.addf("}$N$N", []) + + if moduleDatInitRequired: + m.s[cfsDatInitProc].add(prc) + #rememberFlag(m.g.graph, m.module, HasDatInitProc) + +# Very similar to the contents of symInDynamicLib - basically only the +# things needed for the hot code reloading runtime procs to be loaded +proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): Rope = + let prc = magicsys.getCompilerProc(m.g.graph, sym) + assert prc != nil + fillProcLoc(m, prc.ast[namePos]) + + var extname = prefix & sym + var tmp = mangleDynLibProc(prc) + prc.loc.snippet = tmp + prc.typ.sym = nil + + if not containsOrIncl(m.declaredThings, prc.id): + m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, getTypeDesc(m, prc.loc.t, dkVar)]) + + result = "\t$1 = ($2) $3($4, $5);$n" % + [tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)] + +proc genInitCode(m: BModule) = + ## this function is called in cgenWriteModules after all modules are closed, + ## it means raising dependency on the symbols is too late as it will not propagate + ## into other modules, only simple rope manipulations are allowed + var moduleInitRequired = m.hcrOn + let initname = getInitName(m) + var prc = "$1 N_NIMCALL(void, $2)(void) {$N" % + [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname] + # we don't want to break into such init code - could happen if a line + # directive from a function written by the user spills after itself + genCLineDir(prc, InvalidFileIdx, 999999, m.config) + if m.typeNodes > 0: + if m.hcrOn: + appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName]) + appcg(m, m.s[cfsTypeInit1], "\thcrRegisterGlobal($3, \"$1_$2\", sizeof(TNimNode) * $2, NULL, (void**)&$1);$N", + [m.typeNodesName, m.typeNodes, getModuleDllPath(m, m.module)]) + else: + appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n", + [m.typeNodesName, m.typeNodes]) + if m.nimTypes > 0: + appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n", + [m.nimTypesName, m.nimTypes]) + + if m.hcrOn: + prc.addf("\tint* nim_hcr_dummy_ = 0;$n" & + "\tNIM_BOOL nim_hcr_do_init_ = " & + "hcrRegisterGlobal($1, \"module_initialized_\", 1, NULL, (void**)&nim_hcr_dummy_);$n", + [getModuleDllPath(m, m.module)]) + + template writeSection(thing: untyped, section: TCProcSection, addHcrGuards = false) = + if m.thing.s(section).len > 0: + moduleInitRequired = true + if addHcrGuards: prc.add("\tif (nim_hcr_do_init_) {\n\n") + prc.add(m.thing.s(section)) + if addHcrGuards: prc.add("\n\t} // nim_hcr_do_init_\n") + + if m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0: + # Give this small function its own scope + prc.addf("{$N", []) + # Keep a bogus frame in case the code needs one + prc.add("\tTFrame FR_; FR_.len = 0;\n") + + writeSection(preInitProc, cpsLocals) + writeSection(preInitProc, cpsInit, m.hcrOn) + writeSection(preInitProc, cpsStmts) + prc.addf("}/* preInitProc end */$N", []) + when false: + m.initProc.blocks[0].sections[cpsLocals].add m.preInitProc.s(cpsLocals) + m.initProc.blocks[0].sections[cpsInit].prepend m.preInitProc.s(cpsInit) + m.initProc.blocks[0].sections[cpsStmts].prepend m.preInitProc.s(cpsStmts) + + # add new scope for following code, because old vcc compiler need variable + # be defined at the top of the block + prc.addf("{$N", []) + writeSection(initProc, cpsLocals) + + if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0: + moduleInitRequired = true + 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) + prc.add(initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info))) + else: + prc.add("\tTFrame FR_; FR_.len = 0;\n") + + writeSection(initProc, cpsInit, m.hcrOn) + writeSection(initProc, cpsStmts) + + if beforeRetNeeded in m.initProc.flags: + prc.add("\tBeforeRet_: ;\n") + + if m.config.exc == excGoto: + if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil: + m.appcg(prc, "\t#nimTestErrorFlag();$n", []) + + if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: + prc.add(deinitFrame(m.initProc)) + + prc.addf("}$N", []) + + prc.addf("}$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`` + + if m.hcrOn: + var procsToLoad = @["hcrRegisterProc", "hcrGetProc", "hcrRegisterGlobal", "hcrGetGlobal"] + + m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void* handle, N_NIMCALL_PTR(void*, getProcAddr)(void*, char*)) {$N", [getHcrInitName(m)]) + if sfMainModule in m.module.flags: + # additional procs to load + procsToLoad.add("hcrInit") + procsToLoad.add("hcrAddModule") + # load procs + for curr in procsToLoad: + m.s[cfsInitProc].add(hcrGetProcLoadCode(m, curr, "", "handle", "getProcAddr")) + m.s[cfsInitProc].addf("}$N$N", []) + + for i, el in pairs(m.extensionLoaders): + if el != "": + let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" % + [(i.ord - '0'.ord).rope, el] + moduleInitRequired = true + prc.add(ex) + + if moduleInitRequired or sfMainModule in m.module.flags: + m.s[cfsInitProc].add(prc) + #rememberFlag(m.g.graph, m.module, HasModuleInitProc) + + genDatInitCode(m) + + if m.hcrOn: + m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void) {$N", []) + m.s[cfsInitProc].add(m.hcrCreateTypeInfosProc) + m.s[cfsInitProc].addf("}$N$N", []) + + registerModuleToMain(m.g, m) + +proc postprocessCode(conf: ConfigRef, r: var Rope) = + # find the first directive + var f = r.find(postprocessDirStart) + if f == -1: + return + + var + nimlnDirLastF = "" + + var res: Rope = r.substr(0, f - 1) + while f != -1: + var + e = r.find(postprocessDirEnd, f + 1) + dir = r.substr(f + 1, e - 1).split(postprocessDirSep) + case dir[0] + of "nimln": + if dir[2] == nimlnDirLastF: + res.add("nimln_(" & dir[1] & ");") + else: + res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");") + nimlnDirLastF = dir[2] + else: + raiseAssert "unexpected postprocess directive" + + # find the next directive + f = r.find(postprocessDirStart, e + 1) + # copy the code until the next directive + if f != -1: + res.add(r.substr(e + 1, f - 1)) + else: + res.add(r.substr(e + 1)) + + r = res + +proc genModule(m: BModule, cfile: Cfile): Rope = + var moduleIsEmpty = true + + result = getFileHeader(m.config, cfile) + + generateThreadLocalStorage(m) + generateHeaders(m) + result.add(m.s[cfsHeaders]) + if m.config.cppCustomNamespace.len > 0: + openNamespaceNim(m.config.cppCustomNamespace, result) + if m.s[cfsFrameDefines].len > 0: + result.add(m.s[cfsFrameDefines]) + + for i in cfsForwardTypes..cfsProcs: + if m.s[i].len > 0: + moduleIsEmpty = false + result.add(m.s[i]) + + if m.s[cfsInitProc].len > 0: + moduleIsEmpty = false + result.add(m.s[cfsInitProc]) + if m.s[cfsDatInitProc].len > 0 or m.hcrOn: + moduleIsEmpty = false + result.add(m.s[cfsDatInitProc]) + + if m.config.cppCustomNamespace.len > 0: + closeNamespaceNim(result) + + if optLineDir in m.config.options: + var srcFileDefs = "" + for fi in 0..m.config.m.fileInfos.high: + srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n") + result = srcFileDefs & result + + if moduleIsEmpty: + result = "" + + postprocessCode(m.config, result) + +proc initProcOptions(m: BModule): TOptions = + let opts = m.config.options + if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts + +proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule = + new(result) + result.g = g + 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) + for i in low(result.s)..high(result.s): result.s[i] = newRopeAppender() + result.initProc.options = initProcOptions(result) + result.preInitProc = newProc(nil, result) + result.preInitProc.flags.incl nimErrorFlagDisabled + result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated + result.dataCache = initNodeTable() + result.typeStack = @[] + result.typeNodesName = getTempName(result) + result.nimTypesName = getTempName(result) + # no line tracing for the init sections of the system module so that we + # don't generate a TFrame which can confuse the stack bottom initialization: + if sfSystemModule in module.flags: + incl result.flags, preventStackTrace + excl(result.preInitProc.options, optStackTrace) + let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCfilePath(g.config, filename), "ndi") + else: AbsoluteFile"" + open(result.ndi, ndiName, g.config) + +proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule = + result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex)) + +proc newModule*(g: BModuleList; module: PSym; conf: ConfigRef): BModule = + # we should create only one cgen module for each module sym + result = rawNewModule(g, module, conf) + if module.position >= g.modules.len: + setLen(g.modules, module.position + 1) + #growCache g.modules, module.position + g.modules[module.position] = result + +template injectG() {.dirty.} = + if graph.backend == nil: + graph.backend = newModuleList(graph) + let g = BModuleList(graph.backend) + +proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = + injectG() + result = newModule(g, module, graph.config) + result.idgen = idgen + if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil: + let f = if graph.config.headerFile.len > 0: AbsoluteFile graph.config.headerFile + else: graph.config.projectFull + g.generatedHeader = rawNewModule(g, module, + changeFileExt(completeCfilePath(graph.config, f), hExt)) + incl g.generatedHeader.flags, isHeaderFile + +proc writeHeader(m: BModule) = + var result = headerTop() + var guard = "__$1__" % [m.filename.splitFile.name.rope] + result.addf("#ifndef $1$n#define $1$n", [guard]) + addNimDefines(result, m.config) + generateHeaders(m) + + generateThreadLocalStorage(m) + for i in cfsHeaders..cfsProcs: + result.add(m.s[i]) + if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: + openNamespaceNim(m.config.cppCustomNamespace, result) + result.add(m.s[cfsInitProc]) + + if optGenDynLib in m.config.globalOptions: + result.add("N_LIB_IMPORT ") + result.addf("N_CDECL(void, $1NimMain)(void);$n", [rope m.config.nimMainPrefix]) + if m.config.cppCustomNamespace.len > 0: closeNamespaceNim(result) + result.addf("#endif /* $1 */$n", [guard]) + if not writeRope(result, m.filename): + rawMessage(m.config, errCannotOpenFile, m.filename.string) + +proc getCFile(m: BModule): AbsoluteFile = + let ext = + if m.compileToCpp: ".nim.cpp" + elif m.config.backend == backendObjc or sfCompileToObjc in m.module.flags: ".nim.m" + else: ".nim.c" + result = changeFileExt(completeCfilePath(m.config, mangleModuleName(m.config, m.cfilename).AbsoluteFile), ext) + +when false: + proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = + injectG() + var m = newModule(g, module, graph.config) + readMergeInfo(getCFile(m), m) + result = m + +proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) = + if n.kind == nkStmtList: + for child in n: + addHcrInitGuards(p, child, inInitGuard) + else: + let stmtShouldExecute = n.kind in {nkVarSection, nkLetSection} or + nfExecuteOnReload in n.flags + if inInitGuard: + if stmtShouldExecute: + endBlock(p) + inInitGuard = false + else: + if not stmtShouldExecute: + line(p, cpsStmts, "if (nim_hcr_do_init_)\n") + startBlock(p) + inInitGuard = true + + genStmts(p, n) + +proc genTopLevelStmt*(m: BModule; n: PNode) = + ## Also called from `ic/cbackend.nim`. + if pipelineutils.skipCodegen(m.config, n): return + m.initProc.options = initProcOptions(m) + #softRnl = if optLineDir in m.config.options: noRnl else: rnl + # XXX replicate this logic! + var transformedN = transformStmt(m.g.graph, m.idgen, m.module, n) + if sfInjectDestructors in m.module.flags: + transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN) + + if m.hcrOn: + addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard) + else: + genProcBody(m.initProc, transformedN) + +proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = + if optForceFullMake notin m.config.globalOptions: + if not moduleHasChanged(m.g.graph, m.module): + result = false + elif not equalsFile(code, cfile.cname): + when false: + #m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"): + if fileExists(cfile.cname): + copyFile(cfile.cname.string, cfile.cname.string & ".backup") + echo "diff ", cfile.cname.string, ".backup ", cfile.cname.string + else: + echo "new file ", cfile.cname.string + if not writeRope(code, cfile.cname): + rawMessage(m.config, errCannotOpenFile, cfile.cname.string) + result = true + elif fileExists(cfile.obj) and os.fileNewer(cfile.obj.string, cfile.cname.string): + result = false + else: + result = true + else: + if not writeRope(code, cfile.cname): + rawMessage(m.config, errCannotOpenFile, cfile.cname.string) + result = true + +# 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) = + template onExit() = close(m.ndi, m.config) + let cfile = getCFile(m) + if moduleHasChanged(m.g.graph, m.module): + genInitCode(m) + finishTypeDescriptions(m) + if sfMainModule in m.module.flags: + # generate main file: + genMainProc(m) + m.s[cfsProcHeaders].add(m.g.mainModProcs) + generateThreadVarsSize(m) + + var cf = Cfile(nimname: m.module.name.s, cname: cfile, + obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) + var code = genModule(m, cf) + if code != "" or m.config.symbolFiles != disabledSf: + when hasTinyCBackend: + if m.config.cmd == cmdTcc: + tccgen.compileCCode($code, m.config) + onExit() + return + + if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} + addFileToCompile(m.config, cf) + onExit() + +proc updateCachedModule(m: BModule) = + let cfile = getCFile(m) + var cf = Cfile(nimname: m.module.name.s, cname: cfile, + obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) + if sfMainModule notin m.module.flags: + genMainProc(m) + cf.flags = {CfileFlag.Cached} + addFileToCompile(m.config, cf) + +proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym = + let procname = getIdent(graph.cache, "NimDestroyGlobals") + result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info) + result.typ = newProcType(m.module.info, m.idgen, m.module.owner) + result.typ.callConv = ccCDecl + incl result.flags, sfExportc + result.loc.snippet = "NimDestroyGlobals" + if isDynlib: + incl(result.loc.flags, lfExportLib) + + let theProc = newNodeI(nkProcDef, m.module.info, bodyPos+1) + for i in 0..<theProc.len: theProc[i] = newNodeI(nkEmpty, m.module.info) + theProc[namePos] = newSymNode(result) + theProc[bodyPos] = body + result.ast = theProc + +proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = + ## Also called from IC. + if sfMainModule in m.module.flags: + # phase ordering problem here: We need to announce this + # dependency to 'nimTestErrorFlag' before system.c has been written to disk. + if m.config.exc == excGoto and getCompilerProc(graph, "nimTestErrorFlag") != nil: + cgsym(m, "nimTestErrorFlag") + + if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}: + for i in countdown(high(graph.globalDestructors), 0): + n.add graph.globalDestructors[i] + else: + var body = newNodeI(nkStmtList, m.module.info) + for i in countdown(high(graph.globalDestructors), 0): + body.add graph.globalDestructors[i] + body.flags.incl nfTransf # should not be further transformed + let dtor = generateLibraryDestroyGlobals(graph, m, body, optGenDynLib in m.config.globalOptions) + genProcAux(m, dtor) + if pipelineutils.skipCodegen(m.config, n): return + if moduleHasChanged(graph, m.module): + # 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 n != nil: + m.initProc.options = initProcOptions(m) + genProcBody(m.initProc, n) + + if m.hcrOn: + # make sure this is pulled in (meaning hcrGetGlobal() is called for it during init) + let sym = magicsys.getCompilerProc(m.g.graph, "programResult") + # ignore when not available, could be a module imported early in `system` + if sym != nil: + cgsymImpl m, sym + if m.inHcrInitGuard: + endBlock(m.initProc) + + if sfMainModule in m.module.flags: + if m.hcrOn: + # pull ("define" since they are inline when HCR is on) these functions in the main file + # so it can load the HCR runtime and later pass the library handle to the HCR runtime which + # will in turn pass it to the other modules it initializes so they can initialize the + # register/get procs so they don't have to have the definitions of these functions as well + cgsym(m, "nimLoadLibrary") + cgsym(m, "nimLoadLibraryError") + cgsym(m, "nimGetProcAddr") + cgsym(m, "procAddrError") + cgsym(m, "rawWrite") + + # raise dependencies on behalf of genMainProc + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: + cgsym(m, "initStackBottomWith") + if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: + cgsym(m, "initThreadVarsEmulation") + + if m.g.forwardedProcs.len == 0: + incl m.flags, objHasKidsValid + if optMultiMethods in m.g.config.globalOptions or + m.g.config.selectedGC notin {gcArc, gcOrc, gcAtomicArc} or + vtables notin m.g.config.features: + generateIfMethodDispatchers(graph, m.idgen) + + + let mm = m + m.g.modulesClosed.add mm + +proc genForwardedProcs(g: BModuleList) = + # Forward declared proc:s lack bodies when first encountered, so they're given + # a second pass here + # Note: ``genProcNoForward`` may add to ``forwardedProcs`` + while g.forwardedProcs.len > 0: + let + prc = g.forwardedProcs.pop() + m = g.modules[prc.itemId.module] + if sfForward in prc.flags: + internalError(m.config, prc.info, "still forwarded: " & prc.name.s) + + genProcNoForward(m, prc) + +proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = + let g = BModuleList(backend) + g.config = config + + # we need to process the transitive closure because recursive module + # deps are allowed (and the system module is processed in the wrong + # order anyway) + genForwardedProcs(g) + + for m in cgenModules(g): + m.writeModule(pending=true) + writeMapping(config, g.mapping) + if g.generatedHeader != nil: writeHeader(g.generatedHeader) |