1#
2#
3#           The Nim Compiler
4#        (c) Copyright 2015 Andreas Rumpf
5#
6#    See the file "copying.txt", included in this
7#    distribution, for details about the copyright.
8#
9
10## This module implements the C code generator.
11
12import
13  ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
14  nversion, nimsets, msgs, bitsets, idents, types,
15  ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
16  rodutils, renderer, cgendata, aliases,
17  lowerings, tables, sets, ndi, lineinfos, pathutils, transf,
18  injectdestructors, astmsgs
19
20when not defined(leanCompiler):
21  import spawn, semparallel
22
23import strutils except `%` # collides with ropes.`%`
24
25from ic / ic import ModuleBackendFlag
26import dynlib
27
28when not declared(dynlib.libCandidates):
29  proc libCandidates(s: string, dest: var seq[string]) =
30    ## given a library name pattern `s` write possible library names to `dest`.
31    var le = strutils.find(s, '(')
32    var ri = strutils.find(s, ')', le+1)
33    if le >= 0 and ri > le:
34      var prefix = substr(s, 0, le - 1)
35      var suffix = substr(s, ri + 1)
36      for middle in split(substr(s, le + 1, ri - 1), '|'):
37        libCandidates(prefix & middle & suffix, dest)
38    else:
39      dest.add(s)
40
41when options.hasTinyCBackend:
42  import tccgen
43
44proc hcrOn(m: BModule): bool = m.config.hcrOn
45proc hcrOn(p: BProc): bool = p.module.config.hcrOn
46
47proc addForwardedProc(m: BModule, prc: PSym) =
48  m.g.forwardedProcs.add(prc)
49
50proc findPendingModule(m: BModule, s: PSym): BModule =
51  let ms = s.itemId.module  #getModule(s)
52  result = m.g.modules[ms]
53
54proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
55  result.k = k
56  result.storage = s
57  result.lode = lode
58  result.r = nil
59  result.flags = {}
60
61proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) =
62  # fills the loc if it is not already initialized
63  if a.k == locNone:
64    a.k = k
65    a.lode = lode
66    a.storage = s
67    if a.r == nil: a.r = r
68
69proc t(a: TLoc): PType {.inline.} =
70  if a.lode.kind == nkSym:
71    result = a.lode.sym.typ
72  else:
73    result = a.lode.typ
74
75proc lodeTyp(t: PType): PNode =
76  result = newNode(nkEmpty)
77  result.typ = t
78
79proc isSimpleConst(typ: PType): bool =
80  let t = skipTypes(typ, abstractVar)
81  result = t.kind notin
82      {tyTuple, tyObject, tyArray, tySet, tySequence} and not
83      (t.kind == tyProc and t.callConv == ccClosure)
84
85proc useHeader(m: BModule, sym: PSym) =
86  if lfHeader in sym.loc.flags:
87    assert(sym.annex != nil)
88    let str = getStr(sym.annex.path)
89    m.includeHeader(str)
90
91proc cgsym(m: BModule, name: string): Rope
92
93proc getCFile(m: BModule): AbsoluteFile
94
95proc getModuleDllPath(m: BModule): Rope =
96  let (dir, name, ext) = splitFile(getCFile(m))
97  let filename = strutils.`%`(platform.OS[m.g.config.target.targetOS].dllFrmt, [name & ext])
98  result = makeCString(dir.string & "/" & filename)
99
100proc getModuleDllPath(m: BModule, module: int): Rope =
101  result = getModuleDllPath(m.g.modules[module])
102
103proc getModuleDllPath(m: BModule, s: PSym): Rope =
104  result = getModuleDllPath(m.g.modules[s.itemId.module])
105
106import macros
107
108proc cgFormatValue(result: var string; value: Rope) =
109  for str in leaves(value):
110    result.add str
111
112proc cgFormatValue(result: var string; value: string) =
113  result.add value
114
115proc cgFormatValue(result: var string; value: BiggestInt) =
116  result.addInt value
117
118proc cgFormatValue(result: var string; value: Int128) =
119  result.addInt128 value
120
121# TODO: please document
122macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
123  args.expectKind nnkBracket
124  # echo "ropecg ", newLit(frmt).repr, ", ", args.repr
125  var i = 0
126  result = nnkStmtListExpr.newTree()
127
128  result.add quote do:
129    assert `m` != nil
130
131  let resVar = genSym(nskVar, "res")
132  # during `koch boot` the median of all generates strings from this
133  # macro is around 40 bytes in length.
134  result.add newVarStmt(resVar, newCall(bindSym"newStringOfCap", newLit(80)))
135  let formatValue = bindSym"cgFormatValue"
136
137  var num = 0
138  var strLit = ""
139
140  template flushStrLit() =
141    if strLit != "":
142      result.add newCall(ident "add", resVar, newLit(strLit))
143      strLit.setLen 0
144
145  while i < frmt.len:
146    if frmt[i] == '$':
147      inc(i)                  # skip '$'
148      case frmt[i]
149      of '$':
150        strLit.add '$'
151        inc(i)
152      of '#':
153        flushStrLit()
154        inc(i)
155        result.add newCall(formatValue, resVar, args[num])
156        inc(num)
157      of '^':
158        flushStrLit()
159        inc(i)
160        result.add newCall(formatValue, resVar, args[^1])
161        inc(num)
162      of '0'..'9':
163        var j = 0
164        while true:
165          j = (j * 10) + ord(frmt[i]) - ord('0')
166          inc(i)
167          if i >= frmt.len or not (frmt[i] in {'0'..'9'}): break
168        num = j
169        if j > args.len:
170          error("ropes: invalid format string " & newLit(frmt).repr & " args.len: " & $args.len)
171
172        flushStrLit()
173        result.add newCall(formatValue, resVar, args[j-1])
174      of 'n':
175        flushStrLit()
176        result.add quote do:
177          if optLineDir notin `m`.config.options:
178            `resVar`.add("\L")
179        inc(i)
180      of 'N':
181        strLit.add "\L"
182        inc(i)
183      else:
184        error("ropes: invalid format string $" & frmt[i])
185    elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
186      inc(i)
187      var j = i
188      while frmt[j] in IdentChars: inc(j)
189      var ident = newLit(substr(frmt, i, j-1))
190      i = j
191      flushStrLit()
192      result.add newCall(formatValue, resVar, newCall(ident"cgsym", m, ident))
193    elif frmt[i] == '#' and frmt[i+1] == '$':
194      inc(i, 2)
195      var j = 0
196      while frmt[i] in Digits:
197        j = (j * 10) + ord(frmt[i]) - ord('0')
198        inc(i)
199      let ident = args[j-1]
200      flushStrLit()
201      result.add newCall(formatValue, resVar, newCall(ident"cgsym", m, ident))
202    var start = i
203    while i < frmt.len:
204      if frmt[i] != '$' and frmt[i] != '#': inc(i)
205      else: break
206    if i - 1 >= start:
207      strLit.add(substr(frmt, start, i - 1))
208
209  flushStrLit()
210  result.add newCall(ident"rope", resVar)
211
212proc indentLine(p: BProc, r: Rope): Rope =
213  result = r
214  for i in 0..<p.blocks.len:
215    prepend(result, "\t".rope)
216
217template appcg(m: BModule, c: var Rope, frmt: FormatStr,
218           args: untyped) =
219  c.add(ropecg(m, frmt, args))
220
221template appcg(m: BModule, sec: TCFileSection, frmt: FormatStr,
222           args: untyped) =
223  m.s[sec].add(ropecg(m, frmt, args))
224
225template appcg(p: BProc, sec: TCProcSection, frmt: FormatStr,
226           args: untyped) =
227  p.s(sec).add(ropecg(p.module, frmt, args))
228
229template line(p: BProc, sec: TCProcSection, r: Rope) =
230  p.s(sec).add(indentLine(p, r))
231
232template line(p: BProc, sec: TCProcSection, r: string) =
233  p.s(sec).add(indentLine(p, r.rope))
234
235template lineF(p: BProc, sec: TCProcSection, frmt: FormatStr,
236              args: untyped) =
237  p.s(sec).add(indentLine(p, frmt % args))
238
239template lineCg(p: BProc, sec: TCProcSection, frmt: FormatStr,
240               args: untyped) =
241  p.s(sec).add(indentLine(p, ropecg(p.module, frmt, args)))
242
243template linefmt(p: BProc, sec: TCProcSection, frmt: FormatStr,
244             args: untyped) =
245  p.s(sec).add(indentLine(p, ropecg(p.module, frmt, args)))
246
247proc safeLineNm(info: TLineInfo): int =
248  result = toLinenumber(info)
249  if result < 0: result = 0 # negative numbers are not allowed in #line
250
251proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
252  assert line >= 0
253  if optLineDir in conf.options and line > 0:
254    r.addf("$N#line $2 $1$N",
255        [rope(makeSingleLineCString(filename)), rope(line)])
256
257proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
258  genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
259
260proc freshLineInfo(p: BProc; info: TLineInfo): bool =
261  if p.lastLineInfo.line != info.line or
262     p.lastLineInfo.fileIndex != info.fileIndex:
263    p.lastLineInfo.line = info.line
264    p.lastLineInfo.fileIndex = info.fileIndex
265    result = true
266
267proc genLineDir(p: BProc, t: PNode) =
268  let line = t.info.safeLineNm
269
270  if optEmbedOrigSrc in p.config.globalOptions:
271    p.s(cpsStmts).add(~"//" & sourceLine(p.config, t.info) & "\L")
272  genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config)
273  if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
274      (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
275    if freshLineInfo(p, t.info):
276      linefmt(p, cpsStmts, "nimln_($1, $2);$n",
277              [line, quotedFilename(p.config, t.info)])
278
279proc accessThreadLocalVar(p: BProc, s: PSym)
280proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
281proc genProc(m: BModule, prc: PSym)
282proc raiseInstr(p: BProc): Rope
283
284template compileToCpp(m: BModule): untyped =
285  m.config.backend == backendCpp or sfCompileToCpp in m.module.flags
286
287proc getTempName(m: BModule): Rope =
288  result = m.tmpBase & rope(m.labels)
289  inc m.labels
290
291proc rdLoc(a: TLoc): Rope =
292  # 'read' location (deref if indirect)
293  result = a.r
294  if lfIndirect in a.flags: result = "(*$1)" % [result]
295
296proc lenField(p: BProc): Rope =
297  result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
298
299proc lenExpr(p: BProc; a: TLoc): Rope =
300  if optSeqDestructors in p.config.globalOptions:
301    result = rdLoc(a) & ".len"
302  else:
303    result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
304
305proc dataField(p: BProc): Rope =
306  if optSeqDestructors in p.config.globalOptions:
307    result = rope".p->data"
308  else:
309    result = rope"->data"
310
311include ccgliterals
312include ccgtypes
313
314# ------------------------------ Manager of temporaries ------------------
315
316template mapTypeChooser(n: PNode): TSymKind =
317  (if n.kind == nkSym: n.sym.kind else: skVar)
318
319template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
320
321proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
322  result = a.r
323  if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
324    result = "(&" & result & ")"
325
326proc byRefLoc(p: BProc; a: TLoc): Rope =
327  result = a.r
328  if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a)) != ctArray and not
329      p.module.compileToCpp:
330    result = "(&" & result & ")"
331
332proc rdCharLoc(a: TLoc): Rope =
333  # read a location that may need a char-cast:
334  result = rdLoc(a)
335  if skipTypes(a.t, abstractRange).kind == tyChar:
336    result = "((NU8)($1))" % [result]
337
338type
339  TAssignmentFlag = enum
340    needToCopy
341  TAssignmentFlags = set[TAssignmentFlag]
342
343proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
344proc rawConstExpr(p: BProc, n: PNode; d: var TLoc)
345proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
346
347type
348  ObjConstrMode = enum
349    constructObj,
350    constructRefObj
351
352proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc,
353                   mode: ObjConstrMode) =
354  #if optNimV2 in p.config.globalOptions: return
355  case analyseObjectWithTypeField(t)
356  of frNone:
357    discard
358  of frHeader:
359    var r = rdLoc(a)
360    if mode == constructRefObj: r = "(*$1)" % [r]
361    var s = skipTypes(t, abstractInst)
362    if not p.module.compileToCpp:
363      while s.kind == tyObject and s[0] != nil:
364        r.add(".Sup")
365        s = skipTypes(s[0], skipPtrs)
366    if optTinyRtti in p.config.globalOptions:
367      linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV2(p.module, t, a.lode.info)])
368    else:
369      linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
370  of frEmbedded:
371    # inheritance in C++ does not allow struct initialization: bug #18410
372    if not p.module.compileToCpp and optTinyRtti in p.config.globalOptions:
373      var tmp: TLoc
374      if mode == constructRefObj:
375        let objType = t.skipTypes(abstractInst+{tyRef})
376        rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp)
377        linefmt(p, cpsStmts,
378            "#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
379            [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, mapTypeChooser(a))])
380      else:
381        rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp)
382        genAssignment(p, a, tmp, {})
383    else:
384      # worst case for performance:
385      var r = if mode == constructObj: addrLoc(p.config, a) else: rdLoc(a)
386      linefmt(p, section, "#objectInit($1, $2);$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
387
388  if isException(t):
389    var r = rdLoc(a)
390    if mode == constructRefObj: r = "(*$1)" % [r]
391    var s = skipTypes(t, abstractInst)
392    if not p.module.compileToCpp:
393      while s.kind == tyObject and s[0] != nil and s.sym.magic != mException:
394        r.add(".Sup")
395        s = skipTypes(s[0], skipPtrs)
396    linefmt(p, section, "$1.name = $2;$n", [r, makeCString(t.skipTypes(abstractInst).sym.name.s)])
397
398proc genRefAssign(p: BProc, dest, src: TLoc)
399
400proc isComplexValueType(t: PType): bool {.inline.} =
401  let t = t.skipTypes(abstractInst + tyUserTypeClasses)
402  result = t.kind in {tyArray, tySet, tyTuple, tyObject, tyOpenArray} or
403    (t.kind == tyProc and t.callConv == ccClosure)
404
405include ccgreset
406
407proc resetLoc(p: BProc, loc: var TLoc) =
408  let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t)
409  let typ = skipTypes(loc.t, abstractVarRange)
410  if isImportedCppType(typ): return
411  if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
412    assert rdLoc(loc) != nil
413
414    let atyp = skipTypes(loc.t, abstractInst)
415    if atyp.kind in {tyVar, tyLent}:
416      linefmt(p, cpsStmts, "$1->len = 0; $1->p = NIM_NIL;$n", [rdLoc(loc)])
417    else:
418      linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
419  elif not isComplexValueType(typ):
420    if containsGcRef:
421      var nilLoc: TLoc
422      initLoc(nilLoc, locTemp, loc.lode, OnStack)
423      nilLoc.r = rope("NIM_NIL")
424      genRefAssign(p, loc, nilLoc)
425    else:
426      linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)])
427  else:
428    if loc.storage != OnStack and containsGcRef:
429      specializeReset(p, loc)
430      when false:
431        linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
432                [addrLoc(p.config, loc), genTypeInfoV1(p.module, loc.t, loc.lode.info)])
433      # XXX: generated reset procs should not touch the m_type
434      # field, so disabling this should be safe:
435      genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
436    else:
437      # array passed as argument decayed into pointer, bug #7332
438      # so we use getTypeDesc here rather than rdLoc(loc)
439      linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
440              [addrLoc(p.config, loc),
441              getTypeDesc(p.module, loc.t, mapTypeChooser(loc))])
442      # XXX: We can be extra clever here and call memset only
443      # on the bytes following the m_type field?
444      genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
445
446proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
447  let typ = loc.t
448  if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}:
449    linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
450  elif not isComplexValueType(typ):
451    if containsGarbageCollectedRef(loc.t):
452      var nilLoc: TLoc
453      initLoc(nilLoc, locTemp, loc.lode, OnStack)
454      nilLoc.r = rope("NIM_NIL")
455      genRefAssign(p, loc, nilLoc)
456    else:
457      linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
458        getTypeDesc(p.module, typ, mapTypeChooser(loc))])
459  else:
460    if not isTemp or containsGarbageCollectedRef(loc.t):
461      # don't use nimZeroMem for temporary values for performance if we can
462      # avoid it:
463      if not isImportedCppType(typ):
464        linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
465                [addrLoc(p.config, loc), getTypeDesc(p.module, typ, mapTypeChooser(loc))])
466    genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
467
468proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
469  if sfNoInit notin v.flags:
470    # we know it is a local variable and thus on the stack!
471    # If ``not immediateAsgn`` it is not initialized in a binding like
472    # ``var v = X`` and thus we need to init it.
473    # If ``v`` contains a GC-ref we may pass it to ``unsureAsgnRef`` somehow
474    # which requires initialization. However this can really only happen if
475    # ``var v = X()`` gets transformed into ``X(&v)``.
476    # Nowadays the logic in ccgcalls deals with this case however.
477    if not immediateAsgn:
478      constructLoc(p, v.loc)
479
480proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
481  inc(p.labels)
482  result.r = "T" & rope(p.labels) & "_"
483  linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r])
484  result.k = locTemp
485  result.lode = lodeTyp t
486  result.storage = OnStack
487  result.flags = {}
488  constructLoc(p, result, not needsInit)
489  when false:
490    # XXX Introduce a compiler switch in order to detect these easily.
491    if getSize(p.config, t) > 1024 * 1024:
492      if p.prc != nil:
493        echo "ENORMOUS TEMPORARY! ", p.config $ p.prc.info
494      else:
495        echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo
496      writeStackTrace()
497
498proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
499  inc(p.labels)
500  result.r = "T" & rope(p.labels) & "_"
501  linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, skVar), result.r, value])
502  result.k = locTemp
503  result.lode = lodeTyp t
504  result.storage = OnStack
505  result.flags = {}
506
507proc getIntTemp(p: BProc, result: var TLoc) =
508  inc(p.labels)
509  result.r = "T" & rope(p.labels) & "_"
510  linefmt(p, cpsLocals, "NI $1;$n", [result.r])
511  result.k = locTemp
512  result.storage = OnStack
513  result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt)
514  result.flags = {}
515
516proc localVarDecl(p: BProc; n: PNode): Rope =
517  let s = n.sym
518  if s.loc.k == locNone:
519    fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack)
520    if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
521  if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
522    result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
523  result.add getTypeDesc(p.module, s.typ, skVar)
524  if s.constraint.isNil:
525    if sfRegister in s.flags: result.add(" register")
526    #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
527    #  decl.add(" GC_GUARD")
528    if sfVolatile in s.flags: result.add(" volatile")
529    if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
530    result.add(" ")
531    result.add(s.loc.r)
532  else:
533    result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
534
535proc assignLocalVar(p: BProc, n: PNode) =
536  #assert(s.loc.k == locNone) # not yet assigned
537  # this need not be fulfilled for inline procs; they are regenerated
538  # for each module that uses them!
539  let nl = if optLineDir in p.config.options: "" else: "\L"
540  let decl = localVarDecl(p, n) & ";" & nl
541  line(p, cpsLocals, decl)
542
543include ccgthreadvars
544
545proc varInDynamicLib(m: BModule, sym: PSym)
546
547proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool =
548  return m.hcrOn and {sfThread, sfGlobal} * s.flags == {sfGlobal} and
549      ({lfNoDecl, lfHeader} * s.loc.flags == {})
550      # and s.owner.kind == skModule # owner isn't always a module (global pragma on local var)
551      # and s.loc.k == locGlobalVar  # loc isn't always initialized when this proc is used
552
553proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
554  let s = n.sym
555  if s.loc.k == locNone:
556    fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap)
557    if treatGlobalDifferentlyForHCR(p.module, s): incl(s.loc.flags, lfIndirect)
558
559  if lfDynamicLib in s.loc.flags:
560    var q = findPendingModule(p.module, s)
561    if q != nil and not containsOrIncl(q.declaredThings, s.id):
562      varInDynamicLib(q, s)
563    else:
564      s.loc.r = mangleDynLibProc(s)
565    if value != nil:
566      internalError(p.config, n.info, ".dynlib variables cannot have a value")
567    return
568  useHeader(p.module, s)
569  if lfNoDecl in s.loc.flags: return
570  if not containsOrIncl(p.module.declaredThings, s.id):
571    if sfThread in s.flags:
572      declareThreadVar(p.module, s, sfImportc in s.flags)
573      if value != nil:
574        internalError(p.config, n.info, ".threadvar variables cannot have a value")
575    else:
576      var decl: Rope = nil
577      var td = getTypeDesc(p.module, s.loc.t, skVar)
578      if s.constraint.isNil:
579        if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
580          decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
581        if p.hcrOn: decl.add("static ")
582        elif sfImportc in s.flags: decl.add("extern ")
583        elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ")
584        else: decl.add("N_LIB_PRIVATE ")
585        if s.kind == skLet and value != nil: decl.add("NIM_CONST ")
586        decl.add(td)
587        if p.hcrOn: decl.add("*")
588        if sfRegister in s.flags: decl.add(" register")
589        if sfVolatile in s.flags: decl.add(" volatile")
590        if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
591        if value != nil:
592          decl.addf(" $1 = $2;$n", [s.loc.r, value])
593        else:
594          decl.addf(" $1;$n", [s.loc.r])
595      else:
596        if value != nil:
597          decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
598        else:
599          decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
600      p.module.s[cfsVars].add(decl)
601  if p.withinLoop > 0 and value == nil:
602    # fixes tests/run/tzeroarray:
603    resetLoc(p, s.loc)
604
605proc assignParam(p: BProc, s: PSym, retType: PType) =
606  assert(s.loc.r != nil)
607  scopeMangledParam(p, s)
608
609proc fillProcLoc(m: BModule; n: PNode) =
610  let sym = n.sym
611  if sym.loc.k == locNone:
612    fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack)
613
614proc getLabel(p: BProc): TLabel =
615  inc(p.labels)
616  result = "LA" & rope(p.labels) & "_"
617
618proc fixLabel(p: BProc, labl: TLabel) =
619  lineF(p, cpsStmts, "$1: ;$n", [labl])
620
621proc genVarPrototype(m: BModule, n: PNode)
622proc requestConstImpl(p: BProc, sym: PSym)
623proc genStmts(p: BProc, t: PNode)
624proc expr(p: BProc, n: PNode, d: var TLoc)
625proc genProcPrototype(m: BModule, sym: PSym)
626proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
627proc intLiteral(i: BiggestInt): Rope
628proc genLiteral(p: BProc, n: PNode): Rope
629proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
630proc raiseExit(p: BProc)
631
632proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
633  initLoc(result, locNone, e, OnUnknown)
634  expr(p, e, result)
635
636proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
637  initLoc(result, locNone, e, OnUnknown)
638  if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone):
639    # We cannot check for tfNoSideEffect here because of mutable parameters.
640    discard "bug #8202; enforce evaluation order for nested calls for C++ too"
641    # We may need to consider that 'f(g())' cannot be rewritten to 'tmp = g(); f(tmp)'
642    # if 'tmp' lacks a move/assignment operator.
643    if e[0].kind == nkSym and sfCompileToCpp in e[0].sym.flags:
644      result.flags.incl lfSingleUse
645  else:
646    result.flags.incl lfSingleUse
647  expr(p, e, result)
648
649include ccgcalls, "ccgstmts.nim"
650
651proc initFrame(p: BProc, procname, filename: Rope): Rope =
652  const frameDefines = """
653  $1  define nimfr_(proc, file) \
654      TFrame FR_; \
655      FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
656
657  $1  define nimfrs_(proc, file, slots, length) \
658      struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \
659      FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_);
660
661  $1  define nimln_(n, file) \
662      FR_.line = n; FR_.filename = file;
663  """
664  if p.module.s[cfsFrameDefines].len == 0:
665    appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"])
666
667  discard cgsym(p.module, "nimFrame")
668  result = ropecg(p.module, "\tnimfr_($1, $2);$n", [procname, filename])
669
670proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope =
671  discard cgsym(p.module, "nimFrame")
672  p.blocks[0].sections[cpsLocals].addf("TFrame $1;$n", [frame])
673  result = ropecg(p.module, "\t$1.procname = $2; $1.filename = $3; " &
674                      " $1.line = $4; $1.len = -1; nimFrame(&$1);$n",
675                      [frame, procname, filename, line])
676
677proc deinitFrameNoDebug(p: BProc; frame: Rope): Rope =
678  result = ropecg(p.module, "\t#popFrameOfAddr(&$1);$n", [frame])
679
680proc deinitFrame(p: BProc): Rope =
681  result = ropecg(p.module, "\t#popFrame();$n", [])
682
683include ccgexprs
684
685# ----------------------------- dynamic library handling -----------------
686# We don't finalize dynamic libs as the OS does this for us.
687
688proc isGetProcAddr(lib: PLib): bool =
689  let n = lib.path
690  result = n.kind in nkCallKinds and n.typ != nil and
691    n.typ.kind in {tyPointer, tyProc}
692
693proc loadDynamicLib(m: BModule, lib: PLib) =
694  assert(lib != nil)
695  if not lib.generated:
696    lib.generated = true
697    var tmp = getTempName(m)
698    assert(lib.name == nil)
699    lib.name = tmp # BUGFIX: cgsym has awful side-effects
700    m.s[cfsVars].addf("static void* $1;$n", [tmp])
701    if lib.path.kind in {nkStrLit..nkTripleStrLit}:
702      var s: TStringSeq = @[]
703      libCandidates(lib.path.strVal, s)
704      rawMessage(m.config, hintDependency, lib.path.strVal)
705      var loadlib: Rope = nil
706      for i in 0..high(s):
707        inc(m.labels)
708        if i > 0: loadlib.add("||")
709        let n = newStrNode(nkStrLit, s[i])
710        n.info = lib.path.info
711        appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n",
712              [tmp, genStringLiteral(m, n)])
713      appcg(m, m.s[cfsDynLibInit],
714            "if (!($1)) #nimLoadLibraryError($2);$n",
715            [loadlib, genStringLiteral(m, lib.path)])
716    else:
717      var p = newProc(nil, m)
718      p.options.excl optStackTrace
719      p.flags.incl nimErrorFlagDisabled
720      var dest: TLoc
721      initLoc(dest, locTemp, lib.path, OnStack)
722      dest.r = getTempName(m)
723      appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
724           [getTypeDesc(m, lib.path.typ, skVar), rdLoc(dest)])
725      expr(p, lib.path, dest)
726
727      m.s[cfsVars].add(p.s(cpsLocals))
728      m.s[cfsDynLibInit].add(p.s(cpsInit))
729      m.s[cfsDynLibInit].add(p.s(cpsStmts))
730      appcg(m, m.s[cfsDynLibInit],
731           "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
732           [tmp, rdLoc(dest)])
733
734  if lib.name == nil: internalError(m.config, "loadDynamicLib")
735
736proc mangleDynLibProc(sym: PSym): Rope =
737  # we have to build this as a single rope in order not to trip the
738  # optimization in genInfixCall, see test tests/cpp/t8241.nim
739  if sfCompilerProc in sym.flags:
740    # NOTE: sym.loc.r is the external name!
741    result = rope(sym.name.s)
742  else:
743    result = rope(strutils.`%`("Dl_$1_", $sym.id))
744
745proc symInDynamicLib(m: BModule, sym: PSym) =
746  var lib = sym.annex
747  let isCall = isGetProcAddr(lib)
748  var extname = sym.loc.r
749  if not isCall: loadDynamicLib(m, lib)
750  var tmp = mangleDynLibProc(sym)
751  sym.loc.r = tmp             # from now on we only need the internal name
752  sym.typ.sym = nil           # generate a new name
753  inc(m.labels, 2)
754  if isCall:
755    let n = lib.path
756    var a: TLoc
757    initLocExpr(m.initProc, n[0], a)
758    var params = rdLoc(a) & "("
759    for i in 1..<n.len-1:
760      initLocExpr(m.initProc, n[i], a)
761      params.add(rdLoc(a))
762      params.add(", ")
763    let load = "\t$1 = ($2) ($3$4));$n" %
764        [tmp, getTypeDesc(m, sym.typ, skVar), params, makeCString($extname)]
765    var last = lastSon(n)
766    if last.kind == nkHiddenStdConv: last = last[1]
767    internalAssert(m.config, last.kind == nkStrLit)
768    let idx = last.strVal
769    if idx.len == 0:
770      m.initProc.s(cpsStmts).add(load)
771    elif idx.len == 1 and idx[0] in {'0'..'9'}:
772      m.extensionLoaders[idx[0]].add(load)
773    else:
774      internalError(m.config, sym.info, "wrong index: " & idx)
775  else:
776    appcg(m, m.s[cfsDynLibInit],
777        "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
778        [tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)])
779  m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
780
781proc varInDynamicLib(m: BModule, sym: PSym) =
782  var lib = sym.annex
783  var extname = sym.loc.r
784  loadDynamicLib(m, lib)
785  incl(sym.loc.flags, lfIndirect)
786  var tmp = mangleDynLibProc(sym)
787  sym.loc.r = tmp             # from now on we only need the internal name
788  inc(m.labels, 2)
789  appcg(m, m.s[cfsDynLibInit],
790      "$1 = ($2*) #nimGetProcAddr($3, $4);$n",
791      [tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)])
792  m.s[cfsVars].addf("$2* $1;$n",
793      [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
794
795proc symInDynamicLibPartial(m: BModule, sym: PSym) =
796  sym.loc.r = mangleDynLibProc(sym)
797  sym.typ.sym = nil           # generate a new name
798
799proc cgsym(m: BModule, name: string): Rope =
800  let sym = magicsys.getCompilerProc(m.g.graph, name)
801  if sym != nil:
802    case sym.kind
803    of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
804    of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
805    of skType: discard getTypeDesc(m, sym.typ)
806    else: internalError(m.config, "cgsym: " & name & ": " & $sym.kind)
807  else:
808    # we used to exclude the system module from this check, but for DLL
809    # generation support this sloppyness leads to hard to detect bugs, so
810    # we're picky here for the system module too:
811    rawMessage(m.config, errGenerated, "system module needs: " & name)
812  result = sym.loc.r
813  if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
814    result.addActualSuffixForHCR(m.module, sym)
815
816proc generateHeaders(m: BModule) =
817  m.s[cfsHeaders].add("\L#include \"nimbase.h\"\L")
818
819  for it in m.headerFiles:
820    if it[0] == '#':
821      m.s[cfsHeaders].add(rope(it.replace('`', '"') & "\L"))
822    elif it[0] notin {'"', '<'}:
823      m.s[cfsHeaders].addf("#include \"$1\"$N", [rope(it)])
824    else:
825      m.s[cfsHeaders].addf("#include $1$N", [rope(it)])
826  m.s[cfsHeaders].add("""#undef LANGUAGE_C
827#undef MIPSEB
828#undef MIPSEL
829#undef PPC
830#undef R3000
831#undef R4000
832#undef i386
833#undef linux
834#undef mips
835#undef near
836#undef far
837#undef powerpc
838#undef unix
839""")
840
841proc openNamespaceNim(namespace: string): Rope =
842  result.add("namespace ")
843  result.add(namespace)
844  result.add(" {\L")
845
846proc closeNamespaceNim(): Rope =
847  result.add("}\L")
848
849proc closureSetup(p: BProc, prc: PSym) =
850  if tfCapturesEnv notin prc.typ.flags: return
851  # prc.ast[paramsPos].last contains the type we're after:
852  var ls = lastSon(prc.ast[paramsPos])
853  if ls.kind != nkSym:
854    internalError(p.config, prc.info, "closure generation failed")
855  var env = ls.sym
856  #echo "created environment: ", env.id, " for ", prc.name.s
857  assignLocalVar(p, ls)
858  # generate cast assignment:
859  if p.config.selectedGC == gcGo:
860    linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, ($2) ClE_0);$n",
861            [addrLoc(p.config, env.loc), getTypeDesc(p.module, env.typ)])
862  else:
863    linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
864            [rdLoc(env.loc), getTypeDesc(p.module, env.typ)])
865
866proc containsResult(n: PNode): bool =
867  result = false
868  case n.kind
869  of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkFormalParams:
870    discard
871  of nkSym:
872    if n.sym.kind == skResult:
873      result = true
874  else:
875    for i in 0..<n.len:
876      if containsResult(n[i]): return true
877
878const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
879                  nkMacroDef, nkMixinStmt, nkBindStmt} +
880                  declarativeDefs
881
882proc easyResultAsgn(n: PNode): PNode =
883  case n.kind
884  of nkStmtList, nkStmtListExpr:
885    var i = 0
886    while i < n.len and n[i].kind in harmless: inc i
887    if i < n.len: result = easyResultAsgn(n[i])
888  of nkAsgn, nkFastAsgn:
889    if n[0].kind == nkSym and n[0].sym.kind == skResult and not containsResult(n[1]):
890      incl n.flags, nfPreventCg
891      return n[1]
892  of nkReturnStmt:
893    if n.len > 0:
894      result = easyResultAsgn(n[0])
895      if result != nil: incl n.flags, nfPreventCg
896  else: discard
897
898type
899  InitResultEnum = enum Unknown, InitSkippable, InitRequired
900
901proc allPathsAsgnResult(n: PNode): InitResultEnum =
902  # Exceptions coming from calls don't have not be considered here:
903  #
904  # proc bar(): string = raise newException(...)
905  #
906  # proc foo(): string =
907  #   # optimized out: 'reset(result)'
908  #   result = bar()
909  #
910  # try:
911  #   a = foo()
912  # except:
913  #   echo "a was not written to"
914  #
915  template allPathsInBranch(it) =
916    let a = allPathsAsgnResult(it)
917    case a
918    of InitRequired: return InitRequired
919    of InitSkippable: discard
920    of Unknown:
921      # sticky, but can be overwritten by InitRequired:
922      result = Unknown
923
924  result = Unknown
925  case n.kind
926  of nkStmtList, nkStmtListExpr:
927    for it in n:
928      result = allPathsAsgnResult(it)
929      if result != Unknown: return result
930  of nkAsgn, nkFastAsgn:
931    if n[0].kind == nkSym and n[0].sym.kind == skResult:
932      if not containsResult(n[1]): result = InitSkippable
933      else: result = InitRequired
934    elif containsResult(n):
935      result = InitRequired
936  of nkReturnStmt:
937    if n.len > 0:
938      if n[0].kind == nkEmpty and result != InitSkippable:
939        # This is a bare `return` statement, if `result` was not initialized
940        # anywhere else (or if we're not sure about this) let's require it to be
941        # initialized. This avoids cases like #9286 where this heuristic lead to
942        # wrong code being generated.
943        result = InitRequired
944      else: result = allPathsAsgnResult(n[0])
945  of nkIfStmt, nkIfExpr:
946    var exhaustive = false
947    result = InitSkippable
948    for it in n:
949      # Every condition must not use 'result':
950      if it.len == 2 and containsResult(it[0]):
951        return InitRequired
952      if it.len == 1: exhaustive = true
953      allPathsInBranch(it.lastSon)
954    # if the 'if' statement is not exhaustive and yet it touched 'result'
955    # in some way, say Unknown.
956    if not exhaustive: result = Unknown
957  of nkCaseStmt:
958    if containsResult(n[0]): return InitRequired
959    result = InitSkippable
960    var exhaustive = skipTypes(n[0].typ,
961        abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
962    for i in 1..<n.len:
963      let it = n[i]
964      allPathsInBranch(it.lastSon)
965      if it.kind == nkElse: exhaustive = true
966    if not exhaustive: result = Unknown
967  of nkWhileStmt:
968    # some dubious code can assign the result in the 'while'
969    # condition and that would be fine. Everything else isn't:
970    result = allPathsAsgnResult(n[0])
971    if result == Unknown:
972      result = allPathsAsgnResult(n[1])
973      # we cannot assume that the 'while' loop is really executed at least once:
974      if result == InitSkippable: result = Unknown
975  of harmless:
976    result = Unknown
977  of nkGotoState, nkBreakState:
978    # give up for now.
979    result = InitRequired
980  of nkSym:
981    # some path reads from 'result' before it was written to!
982    if n.sym.kind == skResult: result = InitRequired
983  of nkTryStmt, nkHiddenTryStmt:
984    # We need to watch out for the following problem:
985    # try:
986    #   result = stuffThatRaises()
987    # except:
988    #   discard "result was not set"
989    #
990    # So ... even if the assignment to 'result' is the very first
991    # assignment this is not good enough! The only pattern we allow for
992    # is 'finally: result = x'
993    result = InitSkippable
994    allPathsInBranch(n[0])
995    for i in 1..<n.len:
996      if n[i].kind == nkFinally:
997        result = allPathsAsgnResult(n[i].lastSon)
998      else:
999        allPathsInBranch(n[i].lastSon)
1000  else:
1001    for i in 0..<n.safeLen:
1002      allPathsInBranch(n[i])
1003
1004proc getProcTypeCast(m: BModule, prc: PSym): Rope =
1005  result = getTypeDesc(m, prc.loc.t)
1006  if prc.typ.callConv == ccClosure:
1007    var rettype, params: Rope
1008    var check = initIntSet()
1009    genProcParams(m, prc.typ, rettype, params, check)
1010    result = "$1(*)$2" % [rettype, params]
1011
1012proc genProcBody(p: BProc; procBody: PNode) =
1013  genStmts(p, procBody) # modifies p.locals, p.init, etc.
1014  if {nimErrorFlagAccessed, nimErrorFlagDeclared} * p.flags == {nimErrorFlagAccessed}:
1015    p.flags.incl nimErrorFlagDeclared
1016    p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", []))
1017    p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))
1018
1019proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
1020  sfNoReturn in s.flags and m.config.exc != excGoto
1021
1022proc genProcAux(m: BModule, prc: PSym) =
1023  var p = newProc(prc, m)
1024  var header = genProcHeader(m, prc)
1025  var returnStmt: Rope = nil
1026  assert(prc.ast != nil)
1027
1028  var procBody = transformBody(m.g.graph, m.idgen, prc, cache = false)
1029  if sfInjectDestructors in prc.flags:
1030    procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody)
1031
1032  if sfPure notin prc.flags and prc.typ[0] != nil:
1033    if resultPos >= prc.ast.len:
1034      internalError(m.config, prc.info, "proc has no result symbol")
1035    let resNode = prc.ast[resultPos]
1036    let res = resNode.sym # get result symbol
1037    if not isInvalidReturnType(m.config, prc.typ[0]):
1038      if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
1039      if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
1040        var decl = localVarDecl(p, resNode)
1041        var a: TLoc
1042        initLocExprSingleUse(p, val, a)
1043        linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)])
1044      else:
1045        # declare the result symbol:
1046        assignLocalVar(p, resNode)
1047        assert(res.loc.r != nil)
1048        initLocalVar(p, res, immediateAsgn=false)
1049      returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
1050    else:
1051      fillResult(p.config, resNode)
1052      assignParam(p, res, prc.typ[0])
1053      # We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)'
1054      # to 'unsureAsgn(result, x)'
1055      # Sketch why this is correct: If 'result' points to a stack location
1056      # the 'unsureAsgn' is a nop. If it points to a global variable the
1057      # global is either 'nil' or points to valid memory and so the RC operation
1058      # succeeds without touching not-initialized memory.
1059      if sfNoInit in prc.flags: discard
1060      elif allPathsAsgnResult(procBody) == InitSkippable: discard
1061      else:
1062        resetLoc(p, res.loc)
1063      if skipTypes(res.typ, abstractInst).kind == tyArray:
1064        #incl(res.loc.flags, lfIndirect)
1065        res.loc.storage = OnUnknown
1066
1067  for i in 1..<prc.typ.n.len:
1068    let param = prc.typ.n[i].sym
1069    if param.typ.isCompileTimeOnly: continue
1070    assignParam(p, param, prc.typ[0])
1071  closureSetup(p, prc)
1072  genProcBody(p, procBody)
1073
1074  var generatedProc: Rope
1075  generatedProc.genCLineDir prc.info, m.config
1076  if isNoReturn(p.module, prc):
1077    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
1078      header = "__declspec(noreturn) " & header
1079  if sfPure in prc.flags:
1080    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
1081      header = "__declspec(naked) " & header
1082    generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N",
1083                         [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
1084  else:
1085    if m.hcrOn and isReloadable(m, prc):
1086      # Add forward declaration for "_actual"-suffixed functions defined in the same module (or inline).
1087      # This fixes the use of methods and also the case when 2 functions within the same module
1088      # call each other using directly the "_actual" versions (an optimization) - see issue #11608
1089      m.s[cfsProcHeaders].addf("$1;\n", [header])
1090    generatedProc.add ropecg(p.module, "$1 {$n", [header])
1091    if optStackTrace in prc.options:
1092      generatedProc.add(p.s(cpsLocals))
1093      var procname = makeCString(prc.name.s)
1094      generatedProc.add(initFrame(p, procname, quotedFilename(p.config, prc.info)))
1095    else:
1096      generatedProc.add(p.s(cpsLocals))
1097    if optProfiler in prc.options:
1098      # invoke at proc entry for recursion:
1099      appcg(p, cpsInit, "\t#nimProfile();$n", [])
1100    # this pair of {} is required for C++ (C++ is weird with its
1101    # control flow integrity checks):
1102    if beforeRetNeeded in p.flags: generatedProc.add("{")
1103    generatedProc.add(p.s(cpsInit))
1104    generatedProc.add(p.s(cpsStmts))
1105    if beforeRetNeeded in p.flags: generatedProc.add(~"\t}BeforeRet_: ;$n")
1106    if optStackTrace in prc.options: generatedProc.add(deinitFrame(p))
1107    generatedProc.add(returnStmt)
1108    generatedProc.add(~"}$N")
1109  m.s[cfsProcs].add(generatedProc)
1110  if isReloadable(m, prc):
1111    m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
1112         [prc.loc.r, prc.loc.r & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
1113
1114proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
1115  result = (sfCompileToCpp in m.module.flags and
1116           sfCompileToCpp notin sym.getModule().flags and
1117           m.config.backend != backendCpp) or (
1118           sym.flags * {sfInfixCall, sfCompilerProc, sfMangleCpp} == {} and
1119           sym.flags * {sfImportc, sfExportc} != {} and
1120           sym.magic == mNone and
1121           m.config.backend == backendCpp)
1122
1123proc genProcPrototype(m: BModule, sym: PSym) =
1124  useHeader(m, sym)
1125  if lfNoDecl in sym.loc.flags: return
1126  if lfDynamicLib in sym.loc.flags:
1127    if sym.itemId.module != m.module.position and
1128        not containsOrIncl(m.declaredThings, sym.id):
1129      m.s[cfsVars].add(ropecg(m, "$1 $2 $3;$n",
1130                        [(if isReloadable(m, sym): "static" else: "extern"),
1131                        getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)]))
1132      if isReloadable(m, sym):
1133        m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
1134             [mangleDynLibProc(sym), getTypeDesc(m, sym.loc.t), getModuleDllPath(m, sym)])
1135  elif not containsOrIncl(m.declaredProtos, sym.id):
1136    let asPtr = isReloadable(m, sym)
1137    var header = genProcHeader(m, sym, asPtr)
1138    if not asPtr:
1139      if isNoReturn(m, sym) and hasDeclspec in extccomp.CC[m.config.cCompiler].props:
1140        header = "__declspec(noreturn) " & header
1141      if sym.typ.callConv != ccInline and requiresExternC(m, sym):
1142        header = "extern \"C\" " & header
1143      if sfPure in sym.flags and hasAttribute in CC[m.config.cCompiler].props:
1144        header.add(" __attribute__((naked))")
1145      if isNoReturn(m, sym) and hasAttribute in CC[m.config.cCompiler].props:
1146        header.add(" __attribute__((noreturn))")
1147    m.s[cfsProcHeaders].add(ropecg(m, "$1;$N", [header]))
1148
1149# TODO: figure out how to rename this - it DOES generate a forward declaration
1150proc genProcNoForward(m: BModule, prc: PSym) =
1151  if lfImportCompilerProc in prc.loc.flags:
1152    fillProcLoc(m, prc.ast[namePos])
1153    useHeader(m, prc)
1154    # dependency to a compilerproc:
1155    discard cgsym(m, prc.name.s)
1156    return
1157  if lfNoDecl in prc.loc.flags:
1158    fillProcLoc(m, prc.ast[namePos])
1159    genProcPrototype(m, prc)
1160  elif prc.typ.callConv == ccInline:
1161    # We add inline procs to the calling module to enable C based inlining.
1162    # This also means that a check with ``q.declaredThings`` is wrong, we need
1163    # a check for ``m.declaredThings``.
1164    if not containsOrIncl(m.declaredThings, prc.id):
1165      #if prc.loc.k == locNone:
1166      # mangle the inline proc based on the module where it is defined -
1167      # not on the first module that uses it
1168      let m2 = if m.config.symbolFiles != disabledSf: m
1169               else: findPendingModule(m, prc)
1170      fillProcLoc(m2, prc.ast[namePos])
1171      #elif {sfExportc, sfImportc} * prc.flags == {}:
1172      #  # reset name to restore consistency in case of hashing collisions:
1173      #  echo "resetting ", prc.id, " by ", m.module.name.s
1174      #  prc.loc.r = nil
1175      #  prc.loc.r = mangleName(m, prc)
1176      genProcPrototype(m, prc)
1177      genProcAux(m, prc)
1178  elif lfDynamicLib in prc.loc.flags:
1179    var q = findPendingModule(m, prc)
1180    fillProcLoc(q, prc.ast[namePos])
1181    genProcPrototype(m, prc)
1182    if q != nil and not containsOrIncl(q.declaredThings, prc.id):
1183      symInDynamicLib(q, prc)
1184      # register the procedure even though it is in a different dynamic library and will not be
1185      # reloadable (and has no _actual suffix) - other modules will need to be able to get it through
1186      # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
1187      if isReloadable(q, prc):
1188        q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
1189            [prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
1190    else:
1191      symInDynamicLibPartial(m, prc)
1192  elif sfImportc notin prc.flags:
1193    var q = findPendingModule(m, prc)
1194    fillProcLoc(q, prc.ast[namePos])
1195    # generate a getProc call to initialize the pointer for this
1196    # externally-to-the-current-module defined proc, also important
1197    # to do the declaredProtos check before the call to genProcPrototype
1198    if isReloadable(m, prc) and prc.id notin m.declaredProtos and
1199      q != nil and q.module.id != m.module.id:
1200      m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
1201           [prc.loc.r, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
1202    genProcPrototype(m, prc)
1203    if q != nil and not containsOrIncl(q.declaredThings, prc.id):
1204      # make sure there is a "prototype" in the external module
1205      # which will actually become a function pointer
1206      if isReloadable(m, prc):
1207        genProcPrototype(q, prc)
1208      genProcAux(q, prc)
1209  else:
1210    fillProcLoc(m, prc.ast[namePos])
1211    useHeader(m, prc)
1212    if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
1213
1214proc requestConstImpl(p: BProc, sym: PSym) =
1215  if genConstSetup(p, sym):
1216    let m = p.module
1217    # declare implementation:
1218    var q = findPendingModule(m, sym)
1219    if q != nil and not containsOrIncl(q.declaredThings, sym.id):
1220      assert q.initProc.module == q
1221      genConstDefinition(q, p, sym)
1222    # declare header:
1223    if q != m and not containsOrIncl(m.declaredThings, sym.id):
1224      genConstHeader(m, q, p, sym)
1225
1226proc isActivated(prc: PSym): bool = prc.typ != nil
1227
1228proc genProc(m: BModule, prc: PSym) =
1229  if sfBorrow in prc.flags or not isActivated(prc): return
1230  if sfForward in prc.flags:
1231    addForwardedProc(m, prc)
1232    fillProcLoc(m, prc.ast[namePos])
1233  else:
1234    genProcNoForward(m, prc)
1235    if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and
1236        m.g.generatedHeader != nil and lfNoDecl notin prc.loc.flags:
1237      genProcPrototype(m.g.generatedHeader, prc)
1238      if prc.typ.callConv == ccInline:
1239        if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id):
1240          genProcAux(m.g.generatedHeader, prc)
1241
1242proc genVarPrototype(m: BModule, n: PNode) =
1243  #assert(sfGlobal in sym.flags)
1244  let sym = n.sym
1245  useHeader(m, sym)
1246  fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap)
1247  if treatGlobalDifferentlyForHCR(m, sym): incl(sym.loc.flags, lfIndirect)
1248
1249  if (lfNoDecl in sym.loc.flags) or contains(m.declaredThings, sym.id):
1250    return
1251  if sym.owner.id != m.module.id:
1252    # else we already have the symbol generated!
1253    assert(sym.loc.r != nil)
1254    if sfThread in sym.flags:
1255      declareThreadVar(m, sym, true)
1256    else:
1257      incl(m.declaredThings, sym.id)
1258      if sym.kind in {skLet, skVar, skField, skForVar} and sym.alignment > 0:
1259        m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)]
1260      m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ")
1261      m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, skVar))
1262      if m.hcrOn: m.s[cfsVars].add("*")
1263      if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
1264      if sfRegister in sym.flags: m.s[cfsVars].add(" register")
1265      if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
1266      if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS")
1267      m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
1268      if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
1269        "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
1270        getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(m, sym)])
1271
1272proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
1273  result.addf("#define NIM_INTBITS $1\L", [
1274    platform.CPU[conf.target.targetCPU].intSize.rope])
1275  if conf.cppCustomNamespace.len > 0:
1276    result.add("#define USE_NIM_NAMESPACE ")
1277    result.add(conf.cppCustomNamespace)
1278    result.add("\L")
1279  if conf.isDefined("nimEmulateOverflowChecks"):
1280    result.add("#define NIM_EmulateOverflowChecks\L")
1281
1282proc headerTop(): Rope =
1283  result = "/* Generated by Nim Compiler v$1 */$N" % [rope(VersionAsString)]
1284
1285proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
1286  result = headerTop()
1287  if optCompileOnly notin conf.globalOptions:
1288    result.add ("/* Compiled for: $1, $2, $3 */$N" &
1289        "/* Command for C compiler:$n   $4 */$N") %
1290        [rope(platform.OS[conf.target.targetOS].name),
1291        rope(platform.CPU[conf.target.targetCPU].name),
1292        rope(extccomp.CC[conf.cCompiler].name),
1293        rope(getCompileCFileCmd(conf, cfile))]
1294
1295proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
1296  result = getCopyright(conf, cfile)
1297  if conf.hcrOn: result.add("#define NIM_HOT_CODE_RELOADING\L")
1298  addNimDefines(result, conf)
1299
1300proc getSomeNameForModule(m: PSym): Rope =
1301  assert m.kind == skModule
1302  assert m.owner.kind == skPackage
1303  if {sfSystemModule, sfMainModule} * m.flags == {}:
1304    result = m.owner.name.s.mangle.rope
1305    result.add "_"
1306  result.add m.name.s.mangle
1307
1308proc getSomeInitName(m: BModule, suffix: string): Rope =
1309  if not m.hcrOn:
1310    result = getSomeNameForModule(m.module)
1311  result.add suffix
1312
1313proc getInitName(m: BModule): Rope =
1314  if sfMainModule in m.module.flags:
1315    # generate constant name for main module, for "easy" debugging.
1316    result = rope"NimMainModule"
1317  else:
1318    result = getSomeInitName(m, "Init000")
1319
1320proc getDatInitName(m: BModule): Rope = getSomeInitName(m, "DatInit000")
1321proc getHcrInitName(m: BModule): Rope = getSomeInitName(m, "HcrInit000")
1322
1323proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): Rope
1324
1325proc genMainProc(m: BModule) =
1326  ## this function is called in cgenWriteModules after all modules are closed,
1327  ## it means raising dependency on the symbols is too late as it will not propagate
1328  ## into other modules, only simple rope manipulations are allowed
1329
1330  var preMainCode: Rope
1331  if m.hcrOn:
1332    proc loadLib(handle: string, name: string): Rope =
1333      let prc = magicsys.getCompilerProc(m.g.graph, name)
1334      assert prc != nil
1335      let n = newStrNode(nkStrLit, prc.annex.path.strVal)
1336      n.info = prc.annex.path.info
1337      appcg(m, result, "\tif (!($1 = #nimLoadLibrary($2)))$N" &
1338                       "\t\t#nimLoadLibraryError($2);$N",
1339                       [handle, genStringLiteral(m, n)])
1340
1341    preMainCode.add(loadLib("hcr_handle", "hcrGetProc"))
1342    preMainCode.add("\tvoid* rtl_handle;\L")
1343    preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom"))
1344    preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr"))
1345    preMainCode.add("\tinner = PreMain;\L")
1346    preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L")
1347    preMainCode.add("\t(*inner)();\L")
1348  else:
1349    preMainCode.add("\tPreMain();\L")
1350
1351  const
1352    # not a big deal if we always compile these 3 global vars... makes the HCR code easier
1353    PosixCmdLine =
1354      "N_LIB_PRIVATE int cmdCount;$N" &
1355      "N_LIB_PRIVATE char** cmdLine;$N" &
1356      "N_LIB_PRIVATE char** gEnv;$N"
1357
1358    # The use of a volatile function pointer to call Pre/NimMainInner
1359    # prevents inlining of the NimMainInner function and dependent
1360    # functions, which might otherwise merge their stack frames.
1361    PreMainBody = "$N" &
1362      "N_LIB_PRIVATE void PreMainInner(void) {$N" &
1363      "$2" &
1364      "}$N$N" &
1365      PosixCmdLine &
1366      "N_LIB_PRIVATE void PreMain(void) {$N" &
1367      "\tvoid (*volatile inner)(void);$N" &
1368      "\tinner = PreMainInner;$N" &
1369      "$1" &
1370      "\t(*inner)();$N" &
1371      "}$N$N"
1372
1373    MainProcs =
1374      "\t$^NimMain();$N"
1375
1376    MainProcsWithResult =
1377      MainProcs & ("\treturn $1nim_program_result;$N")
1378
1379    NimMainInner = "N_LIB_PRIVATE N_CDECL(void, NimMainInner)(void) {$N" &
1380        "$1" &
1381      "}$N$N"
1382
1383    NimMainProc =
1384      "N_CDECL(void, $5NimMain)(void) {$N" &
1385        "\tvoid (*volatile inner)(void);$N" &
1386        "$4" &
1387        "\tinner = NimMainInner;$N" &
1388        "$2" &
1389        "\t(*inner)();$N" &
1390      "}$N$N"
1391
1392    NimMainBody = NimMainInner & NimMainProc
1393
1394    PosixCMain =
1395      "int main(int argc, char** args, char** env) {$N" &
1396        "\tcmdLine = args;$N" &
1397        "\tcmdCount = argc;$N" &
1398        "\tgEnv = env;$N" &
1399        MainProcsWithResult &
1400      "}$N$N"
1401
1402    StandaloneCMain =
1403      "int main(void) {$N" &
1404        MainProcs &
1405        "\treturn 0;$N" &
1406      "}$N$N"
1407
1408    WinNimMain = NimMainBody
1409
1410    WinCMain = "N_STDCALL(int, WinMain)(HINSTANCE hCurInstance, $N" &
1411      "                        HINSTANCE hPrevInstance, $N" &
1412      "                        LPSTR lpCmdLine, int nCmdShow) {$N" &
1413      MainProcsWithResult & "}$N$N"
1414
1415    WinNimDllMain = NimMainInner & "N_LIB_EXPORT " & NimMainProc
1416
1417    WinCDllMain =
1418      "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
1419      "                    LPVOID lpvReserved) {$N" &
1420      "\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
1421      "\treturn 1;$N}$N$N"
1422
1423    PosixNimDllMain = WinNimDllMain
1424
1425    PosixCDllMain =
1426      "N_LIB_PRIVATE void NIM_POSIX_INIT NimMainInit(void) {$N" &
1427        MainProcs &
1428      "}$N$N"
1429
1430    GenodeNimMain =
1431      "extern Genode::Env *nim_runtime_env;$N" &
1432      "extern \"C\" void nim_component_construct(Genode::Env*);$N$N" &
1433      NimMainBody
1434
1435    ComponentConstruct =
1436      "void Libc::Component::construct(Libc::Env &env) {$N" &
1437      "\t// Set Env used during runtime initialization$N" &
1438      "\tnim_runtime_env = &env;$N" &
1439      "\tLibc::with_libc([&] () {$N\t" &
1440      "\t// Initialize runtime and globals$N" &
1441      MainProcs &
1442      "\t// Call application construct$N" &
1443      "\t\tnim_component_construct(&env);$N" &
1444      "\t});$N" &
1445      "}$N$N"
1446
1447  if m.config.target.targetOS == osWindows and
1448      m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
1449    m.includeHeader("<windows.h>")
1450  elif m.config.target.targetOS == osGenode:
1451    m.includeHeader("<libc/component.h>")
1452
1453  let initStackBottomCall =
1454    if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
1455    else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])
1456  inc(m.labels)
1457  appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit])
1458
1459  if m.config.target.targetOS == osWindows and
1460      m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
1461    if optGenGuiApp in m.config.globalOptions:
1462      const nimMain = WinNimMain
1463      appcg(m, m.s[cfsProcs], nimMain,
1464        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
1465    else:
1466      const nimMain = WinNimDllMain
1467      appcg(m, m.s[cfsProcs], nimMain,
1468        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
1469  elif m.config.target.targetOS == osGenode:
1470    const nimMain = GenodeNimMain
1471    appcg(m, m.s[cfsProcs], nimMain,
1472        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
1473  elif optGenDynLib in m.config.globalOptions:
1474    const nimMain = PosixNimDllMain
1475    appcg(m, m.s[cfsProcs], nimMain,
1476        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
1477  elif m.config.target.targetOS == osStandalone:
1478    const nimMain = NimMainBody
1479    appcg(m, m.s[cfsProcs], nimMain,
1480        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
1481  else:
1482    const nimMain = NimMainBody
1483    appcg(m, m.s[cfsProcs], nimMain,
1484        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
1485
1486  if optNoMain notin m.config.globalOptions:
1487    if m.config.cppCustomNamespace.len > 0:
1488      m.s[cfsProcs].add closeNamespaceNim() & "using namespace " & m.config.cppCustomNamespace & ";\L"
1489
1490    if m.config.target.targetOS == osWindows and
1491        m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
1492      if optGenGuiApp in m.config.globalOptions:
1493        const otherMain = WinCMain
1494        appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix])
1495      else:
1496        const otherMain = WinCDllMain
1497        appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
1498    elif m.config.target.targetOS == osGenode:
1499      const otherMain = ComponentConstruct
1500      appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
1501    elif optGenDynLib in m.config.globalOptions:
1502      const otherMain = PosixCDllMain
1503      appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
1504    elif m.config.target.targetOS == osStandalone:
1505      const otherMain = StandaloneCMain
1506      appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix])
1507    else:
1508      const otherMain = PosixCMain
1509      appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix])
1510
1511    if m.config.cppCustomNamespace.len > 0:
1512      m.s[cfsProcs].add openNamespaceNim(m.config.cppCustomNamespace)
1513
1514proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag]) =
1515  ## Called from the IC backend.
1516  if HasDatInitProc in flags:
1517    let datInit = getSomeNameForModule(m) & "DatInit000"
1518    g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
1519    g.mainDatInit.addf("\t$1();$N", [datInit])
1520  if HasModuleInitProc in flags:
1521    let init = getSomeNameForModule(m) & "Init000"
1522    g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
1523    let initCall = "\t$1();$N" % [init]
1524    if sfMainModule in m.flags:
1525      g.mainModInit.add(initCall)
1526    elif sfSystemModule in m.flags:
1527      g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any
1528    else:
1529      g.otherModsInit.add(initCall)
1530
1531proc whichInitProcs*(m: BModule): set[ModuleBackendFlag] =
1532  # called from IC.
1533  result = {}
1534  if m.hcrOn or m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0:
1535    result.incl HasModuleInitProc
1536  for i in cfsTypeInit1..cfsDynLibInit:
1537    if m.s[i].len != 0:
1538      result.incl HasDatInitProc
1539      break
1540
1541proc registerModuleToMain(g: BModuleList; m: BModule) =
1542  let
1543    init = m.getInitName
1544    datInit = m.getDatInitName
1545
1546  if m.hcrOn:
1547    var hcrModuleMeta = "$nN_LIB_PRIVATE const char* hcr_module_list[] = {$n" % []
1548    let systemModulePath = getModuleDllPath(m, g.modules[g.graph.config.m.systemFileIdx.int].module)
1549    let mainModulePath = getModuleDllPath(m, m.module)
1550    if sfMainModule in m.module.flags:
1551      hcrModuleMeta.addf("\t$1,$n", [systemModulePath])
1552    g.graph.importDeps.withValue(FileIndex(m.module.position), deps):
1553      for curr in deps[]:
1554        hcrModuleMeta.addf("\t$1,$n", [getModuleDllPath(m, g.modules[curr.int].module)])
1555    hcrModuleMeta.addf("\t\"\"};$n", [])
1556    hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(void**, HcrGetImportedModules)() { return (void**)hcr_module_list; }$n", [])
1557    hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(char*, HcrGetSigHash)() { return \"$1\"; }$n$n",
1558                          [($sigHash(m.module)).rope])
1559    if sfMainModule in m.module.flags:
1560      g.mainModProcs.add(hcrModuleMeta)
1561      g.mainModProcs.addf("static void* hcr_handle;$N", [])
1562      g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [init])
1563      g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void);$N", [datInit])
1564      g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void*, N_NIMCALL_PTR(void*, getProcAddr)(void*, char*));$N", [m.getHcrInitName])
1565      g.mainModProcs.addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void);$N", [])
1566      g.mainModInit.addf("\t$1();$N", [init])
1567      g.otherModsInit.addf("\thcrInit((void**)hcr_module_list, $1, $2, $3, hcr_handle, nimGetProcAddr);$n",
1568                            [mainModulePath, systemModulePath, datInit])
1569      g.mainDatInit.addf("\t$1(hcr_handle, nimGetProcAddr);$N", [m.getHcrInitName])
1570      g.mainDatInit.addf("\thcrAddModule($1);\n", [mainModulePath])
1571      g.mainDatInit.addf("\tHcrCreateTypeInfos();$N", [])
1572      # nasty nasty hack to get the command line functionality working with HCR
1573      # register the 2 variables on behalf of the os module which might not even
1574      # be loaded (in which case it will get collected but that is not a problem)
1575      # EDIT: indeed, this hack, in combination with another un-necessary one
1576      # (`makeCString` was doing line wrap of string litterals) was root cause for
1577      # bug #16265.
1578      let osModulePath = ($systemModulePath).replace("stdlib_system", "stdlib_os").rope
1579      g.mainDatInit.addf("\thcrAddModule($1);\n", [osModulePath])
1580      g.mainDatInit.add("\tint* cmd_count;\n")
1581      g.mainDatInit.add("\tchar*** cmd_line;\n")
1582      g.mainDatInit.addf("\thcrRegisterGlobal($1, \"cmdCount\", sizeof(cmd_count), NULL, (void**)&cmd_count);$N", [osModulePath])
1583      g.mainDatInit.addf("\thcrRegisterGlobal($1, \"cmdLine\", sizeof(cmd_line), NULL, (void**)&cmd_line);$N", [osModulePath])
1584      g.mainDatInit.add("\t*cmd_count = cmdCount;\n")
1585      g.mainDatInit.add("\t*cmd_line = cmdLine;\n")
1586    else:
1587      m.s[cfsInitProc].add(hcrModuleMeta)
1588    return
1589
1590  if m.s[cfsDatInitProc].len > 0:
1591    g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
1592    g.mainDatInit.addf("\t$1();$N", [datInit])
1593
1594  # Initialization of TLS and GC should be done in between
1595  # systemDatInit and systemInit calls if any
1596  if sfSystemModule in m.module.flags:
1597    if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
1598      g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", []))
1599    if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
1600      g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
1601
1602  if m.s[cfsInitProc].len > 0:
1603    g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
1604    let initCall = "\t$1();$N" % [init]
1605    if sfMainModule in m.module.flags:
1606      g.mainModInit.add(initCall)
1607    elif sfSystemModule in m.module.flags:
1608      g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any
1609    else:
1610      g.otherModsInit.add(initCall)
1611
1612proc genDatInitCode(m: BModule) =
1613  ## this function is called in cgenWriteModules after all modules are closed,
1614  ## it means raising dependency on the symbols is too late as it will not propagate
1615  ## into other modules, only simple rope manipulations are allowed
1616
1617  var moduleDatInitRequired = m.hcrOn
1618
1619  var prc = "$1 N_NIMCALL(void, $2)(void) {$N" %
1620    [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), getDatInitName(m)]
1621
1622  # we don't want to break into such init code - could happen if a line
1623  # directive from a function written by the user spills after itself
1624  genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
1625
1626  for i in cfsTypeInit1..cfsDynLibInit:
1627    if m.s[i].len != 0:
1628      moduleDatInitRequired = true
1629      prc.add(m.s[i])
1630
1631  prc.addf("}$N$N", [])
1632
1633  if moduleDatInitRequired:
1634    m.s[cfsDatInitProc].add(prc)
1635    #rememberFlag(m.g.graph, m.module, HasDatInitProc)
1636
1637# Very similar to the contents of symInDynamicLib - basically only the
1638# things needed for the hot code reloading runtime procs to be loaded
1639proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): Rope =
1640  let prc = magicsys.getCompilerProc(m.g.graph, sym)
1641  assert prc != nil
1642  fillProcLoc(m, prc.ast[namePos])
1643
1644  var extname = prefix & sym
1645  var tmp = mangleDynLibProc(prc)
1646  prc.loc.r = tmp
1647  prc.typ.sym = nil
1648
1649  if not containsOrIncl(m.declaredThings, prc.id):
1650    m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, skVar)])
1651
1652  result = "\t$1 = ($2) $3($4, $5);$n" %
1653      [tmp, getTypeDesc(m, prc.typ, skVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
1654
1655proc genInitCode(m: BModule) =
1656  ## this function is called in cgenWriteModules after all modules are closed,
1657  ## it means raising dependency on the symbols is too late as it will not propagate
1658  ## into other modules, only simple rope manipulations are allowed
1659  var moduleInitRequired = m.hcrOn
1660  let initname = getInitName(m)
1661  var prc = "$1 N_NIMCALL(void, $2)(void) {$N" %
1662    [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
1663  # we don't want to break into such init code - could happen if a line
1664  # directive from a function written by the user spills after itself
1665  genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
1666  if m.typeNodes > 0:
1667    if m.hcrOn:
1668      appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName])
1669      appcg(m, m.s[cfsTypeInit1], "\thcrRegisterGlobal($3, \"$1_$2\", sizeof(TNimNode) * $2, NULL, (void**)&$1);$N",
1670            [m.typeNodesName, m.typeNodes, getModuleDllPath(m, m.module)])
1671    else:
1672      appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
1673            [m.typeNodesName, m.typeNodes])
1674  if m.nimTypes > 0:
1675    appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
1676          [m.nimTypesName, m.nimTypes])
1677
1678  if m.hcrOn:
1679    prc.addf("\tint* nim_hcr_dummy_ = 0;$n" &
1680              "\tNIM_BOOL nim_hcr_do_init_ = " &
1681                  "hcrRegisterGlobal($1, \"module_initialized_\", 1, NULL, (void**)&nim_hcr_dummy_);$n",
1682      [getModuleDllPath(m, m.module)])
1683
1684  template writeSection(thing: untyped, section: TCProcSection, addHcrGuards = false) =
1685    if m.thing.s(section).len > 0:
1686      moduleInitRequired = true
1687      if addHcrGuards: prc.add("\tif (nim_hcr_do_init_) {\n\n")
1688      prc.add(m.thing.s(section))
1689      if addHcrGuards: prc.add("\n\t} // nim_hcr_do_init_\n")
1690
1691  if m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0:
1692    # Give this small function its own scope
1693    prc.addf("{$N", [])
1694    # Keep a bogus frame in case the code needs one
1695    prc.add(~"\tTFrame FR_; FR_.len = 0;$N")
1696
1697    writeSection(preInitProc, cpsLocals)
1698    writeSection(preInitProc, cpsInit, m.hcrOn)
1699    writeSection(preInitProc, cpsStmts)
1700    prc.addf("}/* preInitProc end */$N", [])
1701    when false:
1702      m.initProc.blocks[0].sections[cpsLocals].add m.preInitProc.s(cpsLocals)
1703      m.initProc.blocks[0].sections[cpsInit].prepend m.preInitProc.s(cpsInit)
1704      m.initProc.blocks[0].sections[cpsStmts].prepend m.preInitProc.s(cpsStmts)
1705
1706  # add new scope for following code, because old vcc compiler need variable
1707  # be defined at the top of the block
1708  prc.addf("{$N", [])
1709  writeSection(initProc, cpsLocals)
1710
1711  if m.initProc.s(cpsInit).len > 0 or m.initProc.s(cpsStmts).len > 0:
1712    moduleInitRequired = true
1713    if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
1714      # BUT: the generated init code might depend on a current frame, so
1715      # declare it nevertheless:
1716      incl m.flags, frameDeclared
1717      if preventStackTrace notin m.flags:
1718        var procname = makeCString(m.module.name.s)
1719        prc.add(initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
1720      else:
1721        prc.add(~"\tTFrame FR_; FR_.len = 0;$N")
1722
1723    writeSection(initProc, cpsInit, m.hcrOn)
1724    writeSection(initProc, cpsStmts)
1725
1726    if beforeRetNeeded in m.initProc.flags:
1727      prc.add(~"\tBeforeRet_: ;$n")
1728
1729    if sfMainModule in m.module.flags and m.config.exc == excGoto:
1730      if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
1731        m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
1732
1733    if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
1734      prc.add(deinitFrame(m.initProc))
1735
1736  prc.addf("}$N", [])
1737
1738  prc.addf("}$N$N", [])
1739
1740  # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
1741  # that would lead to a *nesting* of merge sections which the merger does
1742  # not support. So we add it to another special section: ``cfsInitProc``
1743
1744  if m.hcrOn:
1745    var procsToLoad = @["hcrRegisterProc", "hcrGetProc", "hcrRegisterGlobal", "hcrGetGlobal"]
1746
1747    m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, $1)(void* handle, N_NIMCALL_PTR(void*, getProcAddr)(void*, char*)) {$N", [getHcrInitName(m)])
1748    if sfMainModule in m.module.flags:
1749      # additional procs to load
1750      procsToLoad.add("hcrInit")
1751      procsToLoad.add("hcrAddModule")
1752    # load procs
1753    for curr in procsToLoad:
1754      m.s[cfsInitProc].add(hcrGetProcLoadCode(m, curr, "", "handle", "getProcAddr"))
1755    m.s[cfsInitProc].addf("}$N$N", [])
1756
1757  for i, el in pairs(m.extensionLoaders):
1758    if el != nil:
1759      let ex = "NIM_EXTERNC N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
1760        [(i.ord - '0'.ord).rope, el]
1761      moduleInitRequired = true
1762      prc.add(ex)
1763
1764  if moduleInitRequired or sfMainModule in m.module.flags:
1765    m.s[cfsInitProc].add(prc)
1766    #rememberFlag(m.g.graph, m.module, HasModuleInitProc)
1767
1768  genDatInitCode(m)
1769
1770  if m.hcrOn:
1771    m.s[cfsInitProc].addf("N_LIB_EXPORT N_NIMCALL(void, HcrCreateTypeInfos)(void) {$N", [])
1772    m.s[cfsInitProc].add(m.hcrCreateTypeInfosProc)
1773    m.s[cfsInitProc].addf("}$N$N", [])
1774
1775  registerModuleToMain(m.g, m)
1776
1777proc genModule(m: BModule, cfile: Cfile): Rope =
1778  var moduleIsEmpty = true
1779
1780  result = getFileHeader(m.config, cfile)
1781
1782  generateThreadLocalStorage(m)
1783  generateHeaders(m)
1784  result.add(m.s[cfsHeaders])
1785  if m.config.cppCustomNamespace.len > 0:
1786    result.add openNamespaceNim(m.config.cppCustomNamespace)
1787  if m.s[cfsFrameDefines].len > 0:
1788    result.add(m.s[cfsFrameDefines])
1789  else:
1790    result.add("#define nimfr_(x, y)\n#define nimln_(x, y)\n")
1791
1792  for i in cfsForwardTypes..cfsProcs:
1793    if m.s[i].len > 0:
1794      moduleIsEmpty = false
1795      result.add(m.s[i])
1796
1797  if m.s[cfsInitProc].len > 0:
1798    moduleIsEmpty = false
1799    result.add(m.s[cfsInitProc])
1800  if m.s[cfsDatInitProc].len > 0 or m.hcrOn:
1801    moduleIsEmpty = false
1802    result.add(m.s[cfsDatInitProc])
1803
1804  if m.config.cppCustomNamespace.len > 0:
1805    result.add closeNamespaceNim()
1806
1807  if moduleIsEmpty:
1808    result = nil
1809
1810proc initProcOptions(m: BModule): TOptions =
1811  let opts = m.config.options
1812  if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
1813
1814proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule =
1815  new(result)
1816  result.g = g
1817  result.tmpBase = rope("TM" & $hashOwner(module) & "_")
1818  result.headerFiles = @[]
1819  result.declaredThings = initIntSet()
1820  result.declaredProtos = initIntSet()
1821  result.cfilename = filename
1822  result.filename = filename
1823  result.typeCache = initTable[SigHash, Rope]()
1824  result.forwTypeCache = initTable[SigHash, Rope]()
1825  result.module = module
1826  result.typeInfoMarker = initTable[SigHash, Rope]()
1827  result.sigConflicts = initCountTable[SigHash]()
1828  result.initProc = newProc(nil, result)
1829  result.initProc.options = initProcOptions(result)
1830  result.preInitProc = newProc(nil, result)
1831  result.preInitProc.flags.incl nimErrorFlagDisabled
1832  result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated
1833  initNodeTable(result.dataCache)
1834  result.typeStack = @[]
1835  result.typeNodesName = getTempName(result)
1836  result.nimTypesName = getTempName(result)
1837  # no line tracing for the init sections of the system module so that we
1838  # don't generate a TFrame which can confuse the stack bottom initialization:
1839  if sfSystemModule in module.flags:
1840    incl result.flags, preventStackTrace
1841    excl(result.preInitProc.options, optStackTrace)
1842  let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCfilePath(g.config, filename), "ndi")
1843                else: AbsoluteFile""
1844  open(result.ndi, ndiName, g.config)
1845
1846proc rawNewModule(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
1847  result = rawNewModule(g, module, AbsoluteFile toFullPath(conf, module.position.FileIndex))
1848
1849proc newModule*(g: BModuleList; module: PSym; conf: ConfigRef): BModule =
1850  # we should create only one cgen module for each module sym
1851  result = rawNewModule(g, module, conf)
1852  if module.position >= g.modules.len:
1853    setLen(g.modules, module.position + 1)
1854  #growCache g.modules, module.position
1855  g.modules[module.position] = result
1856
1857template injectG() {.dirty.} =
1858  if graph.backend == nil:
1859    graph.backend = newModuleList(graph)
1860  let g = BModuleList(graph.backend)
1861
1862when not defined(nimHasSinkInference):
1863  {.pragma: nosinks.}
1864
1865proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
1866  injectG()
1867  result = newModule(g, module, graph.config)
1868  result.idgen = idgen
1869  if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil:
1870    let f = if graph.config.headerFile.len > 0: AbsoluteFile graph.config.headerFile
1871            else: graph.config.projectFull
1872    g.generatedHeader = rawNewModule(g, module,
1873      changeFileExt(completeCfilePath(graph.config, f), hExt))
1874    incl g.generatedHeader.flags, isHeaderFile
1875
1876proc writeHeader(m: BModule) =
1877  var result = headerTop()
1878  var guard = "__$1__" % [m.filename.splitFile.name.rope]
1879  result.addf("#ifndef $1$n#define $1$n", [guard])
1880  addNimDefines(result, m.config)
1881  generateHeaders(m)
1882
1883  generateThreadLocalStorage(m)
1884  for i in cfsHeaders..cfsProcs:
1885    result.add(m.s[i])
1886    if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: result.add openNamespaceNim(m.config.cppCustomNamespace)
1887  result.add(m.s[cfsInitProc])
1888
1889  if optGenDynLib in m.config.globalOptions:
1890    result.add("N_LIB_IMPORT ")
1891  result.addf("N_CDECL(void, $1NimMain)(void);$n", [rope m.config.nimMainPrefix])
1892  if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim()
1893  result.addf("#endif /* $1 */$n", [guard])
1894  if not writeRope(result, m.filename):
1895    rawMessage(m.config, errCannotOpenFile, m.filename.string)
1896
1897proc getCFile(m: BModule): AbsoluteFile =
1898  let ext =
1899      if m.compileToCpp: ".nim.cpp"
1900      elif m.config.backend == backendObjc or sfCompileToObjc in m.module.flags: ".nim.m"
1901      else: ".nim.c"
1902  result = changeFileExt(completeCfilePath(m.config, withPackageName(m.config, m.cfilename)), ext)
1903
1904when false:
1905  proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
1906    injectG()
1907    var m = newModule(g, module, graph.config)
1908    readMergeInfo(getCFile(m), m)
1909    result = m
1910
1911proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
1912  if n.kind == nkStmtList:
1913    for child in n:
1914      addHcrInitGuards(p, child, inInitGuard)
1915  else:
1916    let stmtShouldExecute = n.kind in {nkVarSection, nkLetSection} or
1917                            nfExecuteOnReload in n.flags
1918    if inInitGuard:
1919      if stmtShouldExecute:
1920        endBlock(p)
1921        inInitGuard = false
1922    else:
1923      if not stmtShouldExecute:
1924        line(p, cpsStmts, "if (nim_hcr_do_init_)\n")
1925        startBlock(p)
1926        inInitGuard = true
1927
1928    genStmts(p, n)
1929
1930proc genTopLevelStmt*(m: BModule; n: PNode) =
1931  ## Also called from `ic/cbackend.nim`.
1932  if passes.skipCodegen(m.config, n): return
1933  m.initProc.options = initProcOptions(m)
1934  #softRnl = if optLineDir in m.config.options: noRnl else: rnl
1935  # XXX replicate this logic!
1936  var transformedN = transformStmt(m.g.graph, m.idgen, m.module, n)
1937  if sfInjectDestructors in m.module.flags:
1938    transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN)
1939
1940  if m.hcrOn:
1941    addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard)
1942  else:
1943    genProcBody(m.initProc, transformedN)
1944
1945proc myProcess(b: PPassContext, n: PNode): PNode =
1946  result = n
1947  if b != nil:
1948    var m = BModule(b)
1949    genTopLevelStmt(m, n)
1950
1951proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
1952  if optForceFullMake notin m.config.globalOptions:
1953    if not moduleHasChanged(m.g.graph, m.module):
1954      result = false
1955    elif not equalsFile(code, cfile.cname):
1956      when false:
1957        #m.config.symbolFiles == readOnlySf: #isDefined(m.config, "nimdiff"):
1958        if fileExists(cfile.cname):
1959          copyFile(cfile.cname.string, cfile.cname.string & ".backup")
1960          echo "diff ", cfile.cname.string, ".backup ", cfile.cname.string
1961        else:
1962          echo "new file ", cfile.cname.string
1963      if not writeRope(code, cfile.cname):
1964        rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
1965      result = true
1966    elif fileExists(cfile.obj) and os.fileNewer(cfile.obj.string, cfile.cname.string):
1967      result = false
1968    else:
1969      result = true
1970  else:
1971    if not writeRope(code, cfile.cname):
1972      rawMessage(m.config, errCannotOpenFile, cfile.cname.string)
1973    result = true
1974
1975# We need 2 different logics here: pending modules (including
1976# 'nim__dat') may require file merging for the combination of dead code
1977# elimination and incremental compilation! Non pending modules need no
1978# such logic and in fact the logic hurts for the main module at least;
1979# it would generate multiple 'main' procs, for instance.
1980
1981proc writeModule(m: BModule, pending: bool) =
1982  template onExit() = close(m.ndi, m.config)
1983  let cfile = getCFile(m)
1984  if moduleHasChanged(m.g.graph, m.module):
1985    genInitCode(m)
1986    finishTypeDescriptions(m)
1987    if sfMainModule in m.module.flags:
1988      # generate main file:
1989      genMainProc(m)
1990      m.s[cfsProcHeaders].add(m.g.mainModProcs)
1991      generateThreadVarsSize(m)
1992
1993  var cf = Cfile(nimname: m.module.name.s, cname: cfile,
1994                  obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
1995  var code = genModule(m, cf)
1996  if code != nil or m.config.symbolFiles != disabledSf:
1997    when hasTinyCBackend:
1998      if m.config.cmd == cmdTcc:
1999        tccgen.compileCCode($code, m.config)
2000        onExit()
2001        return
2002
2003    if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
2004    addFileToCompile(m.config, cf)
2005  onExit()
2006
2007proc updateCachedModule(m: BModule) =
2008  let cfile = getCFile(m)
2009  var cf = Cfile(nimname: m.module.name.s, cname: cfile,
2010                 obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {})
2011  if sfMainModule notin m.module.flags:
2012    genMainProc(m)
2013  cf.flags = {CfileFlag.Cached}
2014  addFileToCompile(m.config, cf)
2015
2016proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
2017  ## Also called from IC.
2018  if sfMainModule in m.module.flags:
2019    # phase ordering problem here: We need to announce this
2020    # dependency to 'nimTestErrorFlag' before system.c has been written to disk.
2021    if m.config.exc == excGoto and getCompilerProc(graph, "nimTestErrorFlag") != nil:
2022      discard cgsym(m, "nimTestErrorFlag")
2023
2024    if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}:
2025      for i in countdown(high(graph.globalDestructors), 0):
2026        n.add graph.globalDestructors[i]
2027  if passes.skipCodegen(m.config, n): return
2028  if moduleHasChanged(graph, m.module):
2029    # if the module is cached, we don't regenerate the main proc
2030    # nor the dispatchers? But if the dispatchers changed?
2031    # XXX emit the dispatchers into its own .c file?
2032    if n != nil:
2033      m.initProc.options = initProcOptions(m)
2034      genProcBody(m.initProc, n)
2035
2036    if m.hcrOn:
2037      # make sure this is pulled in (meaning hcrGetGlobal() is called for it during init)
2038      discard cgsym(m, "programResult")
2039      if m.inHcrInitGuard:
2040        endBlock(m.initProc)
2041
2042    if sfMainModule in m.module.flags:
2043      if m.hcrOn:
2044        # pull ("define" since they are inline when HCR is on) these functions in the main file
2045        # so it can load the HCR runtime and later pass the library handle to the HCR runtime which
2046        # will in turn pass it to the other modules it initializes so they can initialize the
2047        # register/get procs so they don't have to have the definitions of these functions as well
2048        discard cgsym(m, "nimLoadLibrary")
2049        discard cgsym(m, "nimLoadLibraryError")
2050        discard cgsym(m, "nimGetProcAddr")
2051        discard cgsym(m, "procAddrError")
2052        discard cgsym(m, "rawWrite")
2053
2054      # raise dependencies on behalf of genMainProc
2055      if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
2056        discard cgsym(m, "initStackBottomWith")
2057      if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
2058        discard cgsym(m, "initThreadVarsEmulation")
2059
2060      if m.g.forwardedProcs.len == 0:
2061        incl m.flags, objHasKidsValid
2062      let disp = generateMethodDispatchers(graph)
2063      for x in disp: genProcAux(m, x.sym)
2064
2065  let mm = m
2066  m.g.modulesClosed.add mm
2067
2068
2069proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
2070  result = n
2071  if b == nil: return
2072  finalCodegenActions(graph, BModule(b), n)
2073
2074proc genForwardedProcs(g: BModuleList) =
2075  # Forward declared proc:s lack bodies when first encountered, so they're given
2076  # a second pass here
2077  # Note: ``genProcNoForward`` may add to ``forwardedProcs``
2078  while g.forwardedProcs.len > 0:
2079    let
2080      prc = g.forwardedProcs.pop()
2081      m = g.modules[prc.itemId.module]
2082    if sfForward in prc.flags:
2083      internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
2084
2085    genProcNoForward(m, prc)
2086
2087proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
2088  let g = BModuleList(backend)
2089  g.config = config
2090
2091  # we need to process the transitive closure because recursive module
2092  # deps are allowed (and the system module is processed in the wrong
2093  # order anyway)
2094  genForwardedProcs(g)
2095
2096  for m in cgenModules(g):
2097    m.writeModule(pending=true)
2098  writeMapping(config, g.mapping)
2099  if g.generatedHeader != nil: writeHeader(g.generatedHeader)
2100
2101const cgenPass* = makePass(myOpen, myProcess, myClose)
2102