12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466 |
- #
- #
- # The Nim Compiler
- # (c) Copyright 2015 Andreas Rumpf
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## This module implements the C code generator.
- import
- ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
- nversion, nimsets, msgs, securehash, bitsets, idents, types,
- ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
- condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
- lowerings, semparallel, tables, sets, ndi
- import strutils except `%` # collides with ropes.`%`
- from modulegraphs import ModuleGraph
- import dynlib
- when not declared(dynlib.libCandidates):
- proc libCandidates(s: string, dest: var seq[string]) =
- ## given a library name pattern `s` write possible library names to `dest`.
- var le = strutils.find(s, '(')
- var ri = strutils.find(s, ')', le+1)
- if le >= 0 and ri > le:
- var prefix = substr(s, 0, le - 1)
- var suffix = substr(s, ri + 1)
- for middle in split(substr(s, le + 1, ri - 1), '|'):
- libCandidates(prefix & middle & suffix, dest)
- else:
- add(dest, s)
- when options.hasTinyCBackend:
- import tccgen
- # implementation
- proc addForwardedProc(m: BModule, prc: PSym) =
- m.forwardedProcs.add(prc)
- inc(m.g.forwardedProcsCounter)
- proc findPendingModule(m: BModule, s: PSym): BModule =
- var ms = getModule(s)
- result = m.g.modules[ms.position]
- proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
- result.k = k
- result.storage = s
- result.lode = lode
- result.r = nil
- result.flags = {}
- proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) =
- # fills the loc if it is not already initialized
- if a.k == locNone:
- a.k = k
- a.lode = lode
- a.storage = s
- if a.r == nil: a.r = r
- proc t(a: TLoc): PType {.inline.} =
- if a.lode.kind == nkSym:
- result = a.lode.sym.typ
- else:
- result = a.lode.typ
- proc lodeTyp(t: PType): PNode =
- result = newNode(nkEmpty)
- result.typ = t
- proc isSimpleConst(typ: PType): bool =
- let t = skipTypes(typ, abstractVar)
- result = t.kind notin
- {tyTuple, tyObject, tyArray, tySet, tySequence} and not
- (t.kind == tyProc and t.callConv == ccClosure)
- proc useStringh(m: BModule) =
- if includesStringh notin m.flags:
- incl m.flags, includesStringh
- m.includeHeader("<string.h>")
- proc useHeader(m: BModule, sym: PSym) =
- if lfHeader in sym.loc.flags:
- assert(sym.annex != nil)
- let str = getStr(sym.annex.path)
- m.includeHeader(str)
- proc cgsym(m: BModule, name: string): Rope
- proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
- var i = 0
- var length = len(frmt)
- result = nil
- var num = 0
- while i < length:
- if frmt[i] == '$':
- inc(i) # skip '$'
- case frmt[i]
- of '$':
- add(result, "$")
- inc(i)
- of '#':
- inc(i)
- add(result, args[num])
- inc(num)
- of '0'..'9':
- var j = 0
- while true:
- j = (j * 10) + ord(frmt[i]) - ord('0')
- inc(i)
- if i >= length or not (frmt[i] in {'0'..'9'}): break
- num = j
- if j > high(args) + 1:
- internalError("ropes: invalid format string $" & $j)
- add(result, args[j-1])
- of 'n':
- if optLineDir notin gOptions: add(result, rnl)
- inc(i)
- of 'N':
- add(result, rnl)
- inc(i)
- else: internalError("ropes: invalid format string $" & frmt[i])
- elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
- inc(i)
- var j = i
- while frmt[j] in IdentChars: inc(j)
- var ident = substr(frmt, i, j-1)
- i = j
- add(result, cgsym(m, ident))
- elif frmt[i] == '#' and frmt[i+1] == '$':
- inc(i, 2)
- var j = 0
- while frmt[i] in Digits:
- j = (j * 10) + ord(frmt[i]) - ord('0')
- inc(i)
- add(result, cgsym(m, $args[j-1]))
- var start = i
- while i < length:
- if frmt[i] != '$' and frmt[i] != '#': inc(i)
- else: break
- if i - 1 >= start:
- add(result, substr(frmt, start, i - 1))
- template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped =
- ropecg(m, fmt, args)
- var indent = "\t".rope
- proc indentLine(p: BProc, r: Rope): Rope =
- result = r
- for i in countup(0, p.blocks.len-1):
- prepend(result, indent)
- proc appcg(m: BModule, c: var Rope, frmt: FormatStr,
- args: varargs[Rope]) =
- add(c, ropecg(m, frmt, args))
- proc appcg(m: BModule, s: TCFileSection, frmt: FormatStr,
- args: varargs[Rope]) =
- add(m.s[s], ropecg(m, frmt, args))
- proc appcg(p: BProc, s: TCProcSection, frmt: FormatStr,
- args: varargs[Rope]) =
- add(p.s(s), ropecg(p.module, frmt, args))
- proc line(p: BProc, s: TCProcSection, r: Rope) =
- add(p.s(s), indentLine(p, r))
- proc line(p: BProc, s: TCProcSection, r: string) =
- add(p.s(s), indentLine(p, r.rope))
- proc lineF(p: BProc, s: TCProcSection, frmt: FormatStr,
- args: openarray[Rope]) =
- add(p.s(s), indentLine(p, frmt % args))
- proc lineCg(p: BProc, s: TCProcSection, frmt: FormatStr,
- args: varargs[Rope]) =
- add(p.s(s), indentLine(p, ropecg(p.module, frmt, args)))
- proc linefmt(p: BProc, s: TCProcSection, frmt: FormatStr,
- args: varargs[Rope]) =
- add(p.s(s), indentLine(p, 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 genCLineDir(r: var Rope, filename: string, line: int) =
- assert line >= 0
- if optLineDir in gOptions:
- addf(r, "$N#line $2 $1$N",
- [rope(makeSingleLineCString(filename)), rope(line)])
- proc genCLineDir(r: var Rope, info: TLineInfo) =
- genCLineDir(r, info.toFullPath, info.safeLineNm)
- 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
- proc genLineDir(p: BProc, t: PNode) =
- var tt = t
- #while tt.kind in {nkStmtListExpr}+nkCallKinds:
- # tt = tt.lastSon
- if tt.kind in nkCallKinds and tt.len > 1:
- tt = tt.sons[1]
- let line = tt.info.safeLineNm
- if optEmbedOrigSrc in gGlobalOptions:
- add(p.s(cpsStmts), ~"//" & tt.info.sourceLine & rnl)
- genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line)
- if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
- (p.prc == nil or sfPure notin p.prc.flags):
- if freshLineInfo(p, tt.info):
- linefmt(p, cpsStmts, "#endb($1, $2);$N",
- line.rope, makeCString(toFilename(tt.info)))
- elif ({optLineTrace, optStackTrace} * p.options ==
- {optLineTrace, optStackTrace}) and
- (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0:
- if freshLineInfo(p, tt.info):
- linefmt(p, cpsStmts, "nimln_($1, $2);$n",
- line.rope, tt.info.quotedFilename)
- proc postStmtActions(p: BProc) {.inline.} =
- add(p.s(cpsStmts), p.module.injectStmt)
- proc accessThreadLocalVar(p: BProc, s: PSym)
- proc emulatedThreadVars(): bool {.inline.}
- proc genProc(m: BModule, prc: PSym)
- template compileToCpp(m: BModule): untyped =
- gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
- include "ccgtypes.nim"
- # ------------------------------ Manager of temporaries ------------------
- proc rdLoc(a: TLoc): Rope =
- # 'read' location (deref if indirect)
- result = a.r
- if lfIndirect in a.flags: result = "(*$1)" % [result]
- proc addrLoc(a: TLoc): Rope =
- result = a.r
- if lfIndirect notin a.flags and mapType(a.t) != ctArray:
- result = "(&" & result & ")"
- 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]
- proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
- takeAddr: bool) =
- case analyseObjectWithTypeField(t)
- of frNone:
- discard
- of frHeader:
- var r = rdLoc(a)
- if not takeAddr: r = "(*$1)" % [r]
- var s = skipTypes(t, abstractInst)
- if not p.module.compileToCpp:
- while (s.kind == tyObject) and (s.sons[0] != nil):
- add(r, ".Sup")
- s = skipTypes(s.sons[0], skipPtrs)
- linefmt(p, section, "$1.m_type = $2;$n", r, genTypeInfo(p.module, t))
- of frEmbedded:
- # worst case for performance:
- var r = if takeAddr: addrLoc(a) else: rdLoc(a)
- linefmt(p, section, "#objectInit($1, $2);$n", r, genTypeInfo(p.module, t))
- type
- TAssignmentFlag = enum
- needToCopy, afDestIsNil, afDestIsNotNil, afSrcIsNil, afSrcIsNotNil
- TAssignmentFlags = set[TAssignmentFlag]
- proc genRefAssign(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
- proc isComplexValueType(t: PType): bool {.inline.} =
- let t = t.skipTypes(abstractInst)
- result = t.kind in {tyArray, tySet, tyTuple, tyObject} or
- (t.kind == tyProc and t.callConv == ccClosure)
- proc resetLoc(p: BProc, loc: var TLoc) =
- let containsGcRef = containsGarbageCollectedRef(loc.t)
- let typ = skipTypes(loc.t, abstractVarRange)
- if isImportedCppType(typ): return
- if not isComplexValueType(typ):
- if containsGcRef:
- var nilLoc: TLoc
- initLoc(nilLoc, locTemp, loc.lode, OnStack)
- nilLoc.r = rope("NIM_NIL")
- genRefAssign(p, loc, nilLoc, {afSrcIsNil})
- else:
- linefmt(p, cpsStmts, "$1 = 0;$n", rdLoc(loc))
- else:
- if optNilCheck in p.options:
- linefmt(p, cpsStmts, "#chckNil((void*)$1);$n", addrLoc(loc))
- if loc.storage != OnStack:
- linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
- addrLoc(loc), genTypeInfo(p.module, loc.t))
- # XXX: generated reset procs should not touch the m_type
- # field, so disabling this should be safe:
- genObjectInit(p, cpsStmts, loc.t, loc, true)
- else:
- useStringh(p.module)
- linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
- addrLoc(loc), rdLoc(loc))
- # 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, true)
- proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
- let typ = loc.t
- if not isComplexValueType(typ):
- linefmt(p, cpsStmts, "$1 = ($2)0;$n", rdLoc(loc),
- getTypeDesc(p.module, typ))
- else:
- if not isTemp or containsGarbageCollectedRef(loc.t):
- # don't use memset for temporary values for performance if we can
- # avoid it:
- if not isImportedCppType(typ):
- useStringh(p.module)
- linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
- addrLoc(loc), rdLoc(loc))
- genObjectInit(p, cpsStmts, loc.t, loc, true)
- 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, result: var TLoc; needsInit=false) =
- inc(p.labels)
- result.r = "T" & rope(p.labels) & "_"
- linefmt(p, cpsLocals, "$1 $2;$n", getTypeDesc(p.module, t), result.r)
- result.k = locTemp
- result.lode = lodeTyp t
- result.storage = OnStack
- result.flags = {}
- constructLoc(p, result, not needsInit)
- proc getIntTemp(p: BProc, result: var TLoc) =
- inc(p.labels)
- result.r = "T" & rope(p.labels) & "_"
- linefmt(p, cpsLocals, "NI $1;$n", result.r)
- result.k = locTemp
- result.storage = OnStack
- result.lode = lodeTyp getSysType(tyInt)
- result.flags = {}
- proc initGCFrame(p: BProc): Rope =
- if p.gcFrameId > 0: result = "struct {$1} GCFRAME_;$n" % [p.gcFrameType]
- proc deinitGCFrame(p: BProc): Rope =
- if p.gcFrameId > 0:
- result = ropecg(p.module,
- "if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n")
- proc localDebugInfo(p: BProc, s: PSym) =
- if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return
- # XXX work around a bug: No type information for open arrays possible:
- if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return
- var a = "&" & s.loc.r
- if s.kind == skParam and ccgIntroducedPtr(s): a = s.loc.r
- lineF(p, cpsInit,
- "FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n",
- [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a,
- genTypeInfo(p.module, s.loc.t)])
- inc(p.maxFrameLen)
- inc p.blocks[p.blocks.len-1].frameLen
- proc localVarDecl(p: BProc; n: PNode): Rope =
- let s = n.sym
- if s.loc.k == locNone:
- fillLoc(s.loc, locLocalVar, n, mangleLocalName(p, s), OnStack)
- if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
- result = getTypeDesc(p.module, s.typ)
- if s.constraint.isNil:
- if sfRegister in s.flags: add(result, " register")
- #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
- # add(decl, " GC_GUARD")
- if sfVolatile in s.flags: add(result, " volatile")
- add(result, " ")
- add(result, s.loc.r)
- else:
- result = s.cgDeclFrmt % [result, s.loc.r]
- 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 gOptions: "" else: tnl
- let decl = localVarDecl(p, n) & ";" & nl
- line(p, cpsLocals, decl)
- localDebugInfo(p, n.sym)
- include ccgthreadvars
- proc varInDynamicLib(m: BModule, sym: PSym)
- proc mangleDynLibProc(sym: PSym): Rope
- proc assignGlobalVar(p: BProc, n: PNode) =
- let s = n.sym
- if s.loc.k == locNone:
- fillLoc(s.loc, locGlobalVar, n, mangleName(p.module, s), OnHeap)
- 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.r = mangleDynLibProc(s)
- return
- useHeader(p.module, s)
- if lfNoDecl in s.loc.flags: return
- if sfThread in s.flags:
- declareThreadVar(p.module, s, sfImportc in s.flags)
- else:
- var decl: Rope = nil
- var td = getTypeDesc(p.module, s.loc.t)
- if s.constraint.isNil:
- if sfImportc in s.flags: add(decl, "extern ")
- add(decl, td)
- if sfRegister in s.flags: add(decl, " register")
- if sfVolatile in s.flags: add(decl, " volatile")
- addf(decl, " $1;$n", [s.loc.r])
- else:
- decl = (s.cgDeclFrmt & ";$n") % [td, s.loc.r]
- add(p.module.s[cfsVars], decl)
- if p.withinLoop > 0:
- # fixes tests/run/tzeroarray:
- resetLoc(p, s.loc)
- if p.module.module.options * {optStackTrace, optEndb} ==
- {optStackTrace, optEndb}:
- appcg(p.module, p.module.s[cfsDebugInit],
- "#dbgRegisterGlobal($1, &$2, $3);$n",
- [makeCString(normalize(s.owner.name.s & '.' & s.name.s)),
- s.loc.r, genTypeInfo(p.module, s.typ)])
- proc assignParam(p: BProc, s: PSym) =
- assert(s.loc.r != nil)
- scopeMangledParam(p, s)
- localDebugInfo(p, s)
- proc fillProcLoc(m: BModule; n: PNode) =
- let sym = n.sym
- if sym.loc.k == locNone:
- fillLoc(sym.loc, locProc, n, mangleName(m, sym), OnStack)
- proc getLabel(p: BProc): TLabel =
- inc(p.labels)
- result = "LA" & rope(p.labels) & "_"
- proc fixLabel(p: BProc, labl: TLabel) =
- lineF(p, cpsStmts, "$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 genProcPrototype(m: BModule, sym: PSym)
- proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
- proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags)
- proc intLiteral(i: BiggestInt): Rope
- proc genLiteral(p: BProc, n: PNode): Rope
- proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope
- proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
- initLoc(result, locNone, e, OnUnknown)
- expr(p, e, result)
- proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
- initLoc(result, locNone, e, OnUnknown)
- result.flags.incl lfSingleUse
- expr(p, e, result)
- proc lenField(p: BProc): Rope =
- result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
- include ccgcalls, "ccgstmts.nim", "ccgexprs.nim"
- # ----------------------------- 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 == nil)
- lib.name = tmp # BUGFIX: cgsym has awful side-effects
- addf(m.s[cfsVars], "static void* $1;$n", [tmp])
- if lib.path.kind in {nkStrLit..nkTripleStrLit}:
- var s: TStringSeq = @[]
- libCandidates(lib.path.strVal, s)
- rawMessage(hintDependency, lib.path.strVal)
- var loadlib: Rope = nil
- for i in countup(0, high(s)):
- inc(m.labels)
- if i > 0: add(loadlib, "||")
- appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n",
- [tmp, getStrLit(m, s[i])])
- appcg(m, m.s[cfsDynLibInit],
- "if (!($1)) #nimLoadLibraryError((#NimStringDesc*) &$2);$n",
- [loadlib, getStrLit(m, lib.path.strVal)])
- else:
- var p = newProc(nil, m)
- p.options = p.options - {optStackTrace, optEndb}
- var dest: TLoc
- initLocExpr(p, lib.path, dest)
- add(m.s[cfsVars], p.s(cpsLocals))
- add(m.s[cfsDynLibInit], p.s(cpsInit))
- add(m.s[cfsDynLibInit], p.s(cpsStmts))
- appcg(m, m.s[cfsDynLibInit],
- "if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
- [tmp, rdLoc(dest)])
- if lib.name == nil: internalError("loadDynamicLib")
- proc mangleDynLibProc(sym: PSym): Rope =
- if sfCompilerProc in sym.flags:
- # NOTE: sym.loc.r is the external name!
- result = rope(sym.name.s)
- else:
- result = "Dl_$1_" % [rope(sym.id)]
- proc symInDynamicLib(m: BModule, sym: PSym) =
- var lib = sym.annex
- let isCall = isGetProcAddr(lib)
- var extname = sym.loc.r
- if not isCall: loadDynamicLib(m, lib)
- var tmp = mangleDynLibProc(sym)
- sym.loc.r = 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], a)
- var params = rdLoc(a) & "("
- for i in 1 .. n.len-2:
- initLocExpr(m.initProc, n[i], a)
- params.add(rdLoc(a))
- params.add(", ")
- let load = "\t$1 = ($2) ($3$4));$n" %
- [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)]
- var last = lastSon(n)
- if last.kind == nkHiddenStdConv: last = last.sons[1]
- internalAssert(last.kind == nkStrLit)
- let idx = last.strVal
- if idx.len == 0:
- add(m.initProc.s(cpsStmts), load)
- elif idx.len == 1 and idx[0] in {'0'..'9'}:
- add(m.extensionLoaders[idx[0]], load)
- else:
- internalError(sym.info, "wrong index: " & idx)
- else:
- appcg(m, m.s[cfsDynLibInit],
- "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
- [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)])
- addf(m.s[cfsVars], "$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)])
- proc varInDynamicLib(m: BModule, sym: PSym) =
- var lib = sym.annex
- var extname = sym.loc.r
- loadDynamicLib(m, lib)
- incl(sym.loc.flags, lfIndirect)
- var tmp = mangleDynLibProc(sym)
- sym.loc.r = 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), lib.name, makeCString($extname)])
- addf(m.s[cfsVars], "$2* $1;$n",
- [sym.loc.r, getTypeDesc(m, sym.loc.t)])
- proc symInDynamicLibPartial(m: BModule, sym: PSym) =
- sym.loc.r = mangleDynLibProc(sym)
- sym.typ.sym = nil # generate a new name
- proc cgsym(m: BModule, name: string): Rope =
- var sym = magicsys.getCompilerProc(name)
- if sym != nil:
- 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("cgsym: " & name & ": " & $sym.kind)
- else:
- # we used to exclude the system module from this check, but for DLL
- # generation support this sloppyness leads to hard to detect bugs, so
- # we're picky here for the system module too:
- rawMessage(errSystemNeeds, name)
- result = sym.loc.r
- proc generateHeaders(m: BModule) =
- add(m.s[cfsHeaders], tnl & "#include \"nimbase.h\"" & tnl)
- for it in m.headerFiles:
- if it[0] == '#':
- add(m.s[cfsHeaders], rope(it.replace('`', '"') & tnl))
- elif it[0] notin {'\"', '<'}:
- addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)])
- else:
- addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
- add(m.s[cfsHeaders], "#undef LANGUAGE_C" & tnl)
- add(m.s[cfsHeaders], "#undef MIPSEB" & tnl)
- add(m.s[cfsHeaders], "#undef MIPSEL" & tnl)
- add(m.s[cfsHeaders], "#undef PPC" & tnl)
- add(m.s[cfsHeaders], "#undef R3000" & tnl)
- add(m.s[cfsHeaders], "#undef R4000" & tnl)
- add(m.s[cfsHeaders], "#undef i386" & tnl)
- add(m.s[cfsHeaders], "#undef linux" & tnl)
- add(m.s[cfsHeaders], "#undef mips" & tnl)
- add(m.s[cfsHeaders], "#undef near" & tnl)
- add(m.s[cfsHeaders], "#undef powerpc" & tnl)
- add(m.s[cfsHeaders], "#undef unix" & tnl)
- proc initFrame(p: BProc, procname, filename: Rope): Rope =
- discard cgsym(p.module, "nimFrame")
- if p.maxFrameLen > 0:
- discard cgsym(p.module, "VarSlot")
- result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4);$n",
- procname, filename, p.maxFrameLen.rope,
- p.blocks[0].frameLen.rope)
- else:
- result = rfmt(nil, "\tnimfr_($1, $2);$n", procname, filename)
- proc deinitFrame(p: BProc): Rope =
- result = rfmt(p.module, "\t#popFrame();$n")
- 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(prc.info, "closure generation failed")
- var env = ls.sym
- #echo "created environment: ", env.id, " for ", prc.name.s
- assignLocalVar(p, ls)
- # generate cast assignment:
- linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
- rdLoc(env.loc), getTypeDesc(p.module, env.typ))
- proc containsResult(n: PNode): bool =
- if n.kind == nkSym and n.sym.kind == skResult:
- result = true
- else:
- for i in 0..<n.safeLen:
- if containsResult(n[i]): return true
- proc easyResultAsgn(n: PNode): PNode =
- const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt} +
- declarativeDefs
- 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:
- 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
- proc genProcAux(m: BModule, prc: PSym) =
- var p = newProc(prc, m)
- var header = genProcHeader(m, prc)
- var returnStmt: Rope = nil
- assert(prc.ast != nil)
- if sfPure notin prc.flags and prc.typ.sons[0] != nil:
- if resultPos >= prc.ast.len:
- internalError(prc.info, "proc has no result symbol")
- let resNode = prc.ast.sons[resultPos]
- let res = resNode.sym # get result symbol
- if not isInvalidReturnType(prc.typ.sons[0]):
- if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
- if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(prc.getBody); val != nil):
- var decl = localVarDecl(p, resNode)
- var a: TLoc
- initLocExprSingleUse(p, val, a)
- linefmt(p, cpsStmts, "$1 = $2;$n", decl, rdLoc(a))
- else:
- # declare the result symbol:
- assignLocalVar(p, resNode)
- assert(res.loc.r != nil)
- initLocalVar(p, res, immediateAsgn=false)
- returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc))
- else:
- fillResult(resNode)
- assignParam(p, res)
- if skipTypes(res.typ, abstractInst).kind == tyArray:
- #incl(res.loc.flags, lfIndirect)
- res.loc.storage = OnUnknown
- for i in countup(1, sonsLen(prc.typ.n) - 1):
- let param = prc.typ.n.sons[i].sym
- if param.typ.isCompileTimeOnly: continue
- assignParam(p, param)
- closureSetup(p, prc)
- genStmts(p, prc.getBody) # modifies p.locals, p.init, etc.
- var generatedProc: Rope
- if sfNoReturn in prc.flags:
- if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
- header = "__declspec(noreturn) " & header
- if sfPure in prc.flags:
- if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
- header = "__declspec(naked) " & header
- generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N",
- header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
- else:
- generatedProc = rfmt(nil, "$N$1 {$N", header)
- add(generatedProc, initGCFrame(p))
- if optStackTrace in prc.options:
- add(generatedProc, p.s(cpsLocals))
- var procname = makeCString(prc.name.s)
- add(generatedProc, initFrame(p, procname, prc.info.quotedFilename))
- else:
- add(generatedProc, p.s(cpsLocals))
- if optProfiler in prc.options:
- # invoke at proc entry for recursion:
- appcg(p, cpsInit, "\t#nimProfile();$n", [])
- if p.beforeRetNeeded: add(generatedProc, "{")
- add(generatedProc, p.s(cpsInit))
- add(generatedProc, p.s(cpsStmts))
- if p.beforeRetNeeded: add(generatedProc, ~"\t}BeforeRet_: ;$n")
- add(generatedProc, deinitGCFrame(p))
- if optStackTrace in prc.options: add(generatedProc, deinitFrame(p))
- add(generatedProc, returnStmt)
- add(generatedProc, ~"}$N")
- add(m.s[cfsProcs], generatedProc)
- proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
- result = (sfCompileToCpp in m.module.flags and
- sfCompileToCpp notin sym.getModule().flags and
- gCmd != cmdCompileToCpp) or (
- sym.flags * {sfImportc, sfInfixCall, sfCompilerProc} == {sfImportc} and
- sym.magic == mNone and
- gCmd == cmdCompileToCpp)
- proc genProcPrototype(m: BModule, sym: PSym) =
- useHeader(m, sym)
- if lfNoDecl in sym.loc.flags: return
- if lfDynamicLib in sym.loc.flags:
- if getModule(sym).id != m.module.id and
- not containsOrIncl(m.declaredThings, sym.id):
- add(m.s[cfsVars], rfmt(nil, "extern $1 $2;$n",
- getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
- elif not containsOrIncl(m.declaredProtos, sym.id):
- var header = genProcHeader(m, sym)
- if sfNoReturn in sym.flags and hasDeclspec in extccomp.CC[cCompiler].props:
- header = "__declspec(noreturn) " & header
- if sym.typ.callConv != ccInline and requiresExternC(m, sym):
- header = "extern \"C\" " & header
- if sfPure in sym.flags and hasAttribute in CC[cCompiler].props:
- header.add(" __attribute__((naked))")
- if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
- header.add(" __attribute__((noreturn))")
- add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header))
- proc genProcNoForward(m: BModule, prc: PSym) =
- if lfImportCompilerProc in prc.loc.flags:
- fillProcLoc(m, prc.ast[namePos])
- useHeader(m, prc)
- # dependency to a compilerproc:
- discard cgsym(m, prc.name.s)
- return
- if lfNoDecl in prc.loc.flags:
- fillProcLoc(m, prc.ast[namePos])
- useHeader(m, prc)
- genProcPrototype(m, prc)
- elif prc.typ.callConv == ccInline:
- # We add inline procs to the calling module to enable C based inlining.
- # This also means that a check with ``q.declaredThings`` is wrong, we need
- # a check for ``m.declaredThings``.
- if not containsOrIncl(m.declaredThings, prc.id):
- #if prc.loc.k == locNone:
- fillProcLoc(m, prc.ast[namePos])
- #elif {sfExportc, sfImportc} * prc.flags == {}:
- # # reset name to restore consistency in case of hashing collisions:
- # echo "resetting ", prc.id, " by ", m.module.name.s
- # prc.loc.r = nil
- # prc.loc.r = mangleName(m, prc)
- useHeader(m, prc)
- genProcPrototype(m, prc)
- genProcAux(m, prc)
- elif lfDynamicLib in prc.loc.flags:
- var q = findPendingModule(m, prc)
- fillProcLoc(q, prc.ast[namePos])
- useHeader(m, prc)
- genProcPrototype(m, prc)
- if q != nil and not containsOrIncl(q.declaredThings, prc.id):
- symInDynamicLib(q, prc)
- else:
- symInDynamicLibPartial(m, prc)
- elif sfImportc notin prc.flags:
- var q = findPendingModule(m, prc)
- fillProcLoc(q, prc.ast[namePos])
- useHeader(m, prc)
- genProcPrototype(m, prc)
- if q != nil and not containsOrIncl(q.declaredThings, prc.id):
- genProcAux(q, prc)
- else:
- fillProcLoc(m, prc.ast[namePos])
- useHeader(m, prc)
- if sfInfixCall notin prc.flags: genProcPrototype(m, prc)
- proc requestConstImpl(p: BProc, sym: PSym) =
- var m = p.module
- useHeader(m, sym)
- if sym.loc.k == locNone:
- fillLoc(sym.loc, locData, sym.ast, mangleName(p.module, sym), OnStatic)
- if lfNoDecl in sym.loc.flags: return
- # declare implementation:
- var q = findPendingModule(m, sym)
- if q != nil and not containsOrIncl(q.declaredThings, sym.id):
- assert q.initProc.module == q
- addf(q.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
- [getTypeDesc(q, sym.typ), sym.loc.r, genConstExpr(q.initProc, sym.ast)])
- # declare header:
- if q != m and not containsOrIncl(m.declaredThings, sym.id):
- assert(sym.loc.r != nil)
- let headerDecl = "extern NIM_CONST $1 $2;$n" %
- [getTypeDesc(m, sym.loc.t), sym.loc.r]
- add(m.s[cfsData], headerDecl)
- if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
- add(p.module.g.generatedHeader.s[cfsData], headerDecl)
- proc isActivated(prc: PSym): bool = prc.typ != nil
- proc genProc(m: BModule, prc: PSym) =
- if sfBorrow in prc.flags or not isActivated(prc): return
- if sfForward in prc.flags:
- addForwardedProc(m, prc)
- fillProcLoc(m, prc.ast[namePos])
- else:
- genProcNoForward(m, prc)
- if {sfExportc, sfCompilerProc} * prc.flags == {sfExportc} and
- m.g.generatedHeader != nil and lfNoDecl notin prc.loc.flags:
- genProcPrototype(m.g.generatedHeader, prc)
- if prc.typ.callConv == ccInline:
- if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id):
- genProcAux(m.g.generatedHeader, prc)
- proc genVarPrototypeAux(m: BModule, n: PNode) =
- #assert(sfGlobal in sym.flags)
- let sym = n.sym
- useHeader(m, sym)
- fillLoc(sym.loc, locGlobalVar, n, mangleName(m, sym), OnHeap)
- if (lfNoDecl in sym.loc.flags) or containsOrIncl(m.declaredThings, sym.id):
- return
- if sym.owner.id != m.module.id:
- # else we already have the symbol generated!
- assert(sym.loc.r != nil)
- if sfThread in sym.flags:
- declareThreadVar(m, sym, true)
- else:
- add(m.s[cfsVars], "extern ")
- add(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
- if lfDynamicLib in sym.loc.flags: add(m.s[cfsVars], "*")
- if sfRegister in sym.flags: add(m.s[cfsVars], " register")
- if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
- addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
- proc genVarPrototype(m: BModule, n: PNode) =
- genVarPrototypeAux(m, n)
- proc addIntTypes(result: var Rope) {.inline.} =
- addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl &
- "#define NIM_INTBITS $1" & tnl, [
- platform.CPU[targetCPU].intSize.rope])
- proc getCopyright(cfile: Cfile): Rope =
- if optCompileOnly in gGlobalOptions:
- result = ("/* Generated by Nim Compiler v$1 */$N" &
- "/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
- "/* The generated code is subject to the original license. */$N") %
- [rope(VersionAsString)]
- else:
- result = ("/* Generated by Nim Compiler v$1 */$N" &
- "/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
- "/* The generated code is subject to the original license. */$N" &
- "/* Compiled for: $2, $3, $4 */$N" &
- "/* Command for C compiler:$n $5 */$N") %
- [rope(VersionAsString),
- rope(platform.OS[targetOS].name),
- rope(platform.CPU[targetCPU].name),
- rope(extccomp.CC[extccomp.cCompiler].name),
- rope(getCompileCFileCmd(cfile))]
- proc getFileHeader(cfile: Cfile): Rope =
- result = getCopyright(cfile)
- addIntTypes(result)
- proc genFilenames(m: BModule): Rope =
- discard cgsym(m, "dbgRegisterFilename")
- result = nil
- for i in 0.. <fileInfos.len:
- result.addf("dbgRegisterFilename($1);$N", [fileInfos[i].projPath.makeCString])
- proc genMainProc(m: BModule) =
- 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 =
- "void PreMainInner(void) {$N" &
- "\tsystemInit000();$N" &
- "$1" &
- "$2" &
- "$3" &
- "}$N$N" &
- "void PreMain(void) {$N" &
- "\tvoid (*volatile inner)(void);$N" &
- "\tsystemDatInit000();$N" &
- "\tinner = PreMainInner;$N" &
- "$4$5" &
- "\t(*inner)();$N" &
- "}$N$N"
- MainProcs =
- "\tNimMain();$N"
- MainProcsWithResult =
- MainProcs & "\treturn nim_program_result;$N"
- NimMainInner = "N_CDECL(void, NimMainInner)(void) {$N" &
- "$1" &
- "}$N$N"
- NimMainProc =
- "N_CDECL(void, NimMain)(void) {$N" &
- "\tvoid (*volatile inner)(void);$N" &
- "\tPreMain();$N" &
- "\tinner = NimMainInner;$N" &
- "$2" &
- "\t(*inner)();$N" &
- "}$N$N"
- NimMainBody = NimMainInner & NimMainProc
- PosixNimMain =
- "int cmdCount;$N" &
- "char** cmdLine;$N" &
- "char** gEnv;$N" &
- NimMainBody
- 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 & "}$N" &
- "\treturn 1;$N}$N$N"
- PosixNimDllMain = WinNimDllMain
- PosixCDllMain =
- "void NIM_POSIX_INIT NimMainInit(void) {$N" &
- MainProcs &
- "}$N$N"
- GenodeNimMain =
- "Libc::Env *genodeEnv;$N" &
- NimMainBody
- ComponentConstruct =
- "void Libc::Component::construct(Libc::Env &env) {$N" &
- "\tgenodeEnv = &env;$N" &
- "\tLibc::with_libc([&] () {$N\t" &
- MainProcs &
- "\t});$N" &
- "}$N$N"
- var nimMain, otherMain: FormatStr
- if platform.targetOS == osWindows and
- gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}:
- if optGenGuiApp in gGlobalOptions:
- nimMain = WinNimMain
- otherMain = WinCMain
- else:
- nimMain = WinNimDllMain
- otherMain = WinCDllMain
- m.includeHeader("<windows.h>")
- elif platform.targetOS == osGenode:
- nimMain = GenodeNimMain
- otherMain = ComponentConstruct
- elif optGenDynLib in gGlobalOptions:
- nimMain = PosixNimDllMain
- otherMain = PosixCDllMain
- elif platform.targetOS == osStandalone:
- nimMain = PosixNimMain
- otherMain = StandaloneCMain
- else:
- nimMain = PosixNimMain
- otherMain = PosixCMain
- if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
- if optEndb in gOptions:
- m.g.breakpoints.add(m.genFilenames)
- let initStackBottomCall =
- if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope
- else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
- inc(m.labels)
- appcg(m, m.s[cfsProcs], PreMainBody, [
- m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit,
- if emulatedThreadVars() and platform.targetOS != osStandalone:
- ropecg(m, "\t#initThreadVarsEmulation();$N")
- else:
- "".rope,
- initStackBottomCall])
- appcg(m, m.s[cfsProcs], nimMain,
- [m.g.mainModInit, initStackBottomCall, rope(m.labels)])
- if optNoMain notin gGlobalOptions:
- appcg(m, m.s[cfsProcs], otherMain, [])
- proc getSomeInitName(m: PSym, suffix: string): Rope =
- assert m.kind == skModule
- assert m.owner.kind == skPackage
- if {sfSystemModule, sfMainModule} * m.flags == {}:
- result = m.owner.name.s.mangle.rope
- result.add "_"
- result.add m.name.s
- result.add suffix
- proc getInitName(m: PSym): Rope =
- if sfMainModule in m.flags:
- # generate constant name for main module, for "easy" debugging.
- result = rope"NimMainModule"
- else:
- result = getSomeInitName(m, "Init000")
- proc getDatInitName(m: PSym): Rope = getSomeInitName(m, "DatInit000")
- proc registerModuleToMain(g: BModuleList; m: PSym) =
- var
- init = m.getInitName
- datInit = m.getDatInitName
- addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init])
- addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit])
- if sfSystemModule notin m.flags:
- addf(g.mainDatInit, "\t$1();$N", [datInit])
- let initCall = "\t$1();$N" % [init]
- if sfMainModule in m.flags:
- add(g.mainModInit, initCall)
- else:
- add(g.otherModsInit, initCall)
- proc genInitCode(m: BModule) =
- var initname = getInitName(m.module)
- var prc = "NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N" % [initname]
- if m.typeNodes > 0:
- appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
- [m.typeNodesName, rope(m.typeNodes)])
- if m.nimTypes > 0:
- appcg(m, m.s[cfsTypeInit1], "static #TNimType $1[$2];$n",
- [m.nimTypesName, rope(m.nimTypes)])
- add(prc, initGCFrame(m.initProc))
- add(prc, genSectionStart(cpsLocals))
- add(prc, m.preInitProc.s(cpsLocals))
- add(prc, m.initProc.s(cpsLocals))
- add(prc, m.postInitProc.s(cpsLocals))
- add(prc, genSectionEnd(cpsLocals))
- if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
- # BUT: the generated init code might depend on a current frame, so
- # declare it nevertheless:
- incl m.flags, frameDeclared
- if preventStackTrace notin m.flags:
- var procname = makeCString(m.module.name.s)
- add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
- else:
- add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
- add(prc, genSectionStart(cpsInit))
- add(prc, m.preInitProc.s(cpsInit))
- add(prc, m.initProc.s(cpsInit))
- add(prc, m.postInitProc.s(cpsInit))
- add(prc, genSectionEnd(cpsInit))
- add(prc, genSectionStart(cpsStmts))
- add(prc, m.preInitProc.s(cpsStmts))
- add(prc, m.initProc.s(cpsStmts))
- add(prc, m.postInitProc.s(cpsStmts))
- add(prc, genSectionEnd(cpsStmts))
- if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
- add(prc, deinitFrame(m.initProc))
- add(prc, deinitGCFrame(m.initProc))
- addf(prc, "}$N$N", [])
- prc.addf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N",
- [getDatInitName(m.module)])
- for i in cfsTypeInit1..cfsDynLibInit:
- add(prc, genSectionStart(i))
- add(prc, m.s[i])
- add(prc, genSectionEnd(i))
- addf(prc, "}$N$N", [])
- # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
- # that would lead to a *nesting* of merge sections which the merger does
- # not support. So we add it to another special section: ``cfsInitProc``
- add(m.s[cfsInitProc], prc)
- for i, el in pairs(m.extensionLoaders):
- if el != nil:
- let ex = "N_NIMCALL(void, nimLoadProcs$1)(void) {$2}$N$N" %
- [(i.ord - '0'.ord).rope, el]
- add(m.s[cfsInitProc], ex)
- proc genModule(m: BModule, cfile: Cfile): Rope =
- result = getFileHeader(cfile)
- result.add(genMergeInfo(m))
- generateThreadLocalStorage(m)
- generateHeaders(m)
- for i in countup(cfsHeaders, cfsProcs):
- add(result, genSectionStart(i))
- add(result, m.s[i])
- add(result, genSectionEnd(i))
- add(result, m.s[cfsInitProc])
- proc newPreInitProc(m: BModule): BProc =
- result = newProc(nil, m)
- # little hack so that unique temporaries are generated:
- result.labels = 100_000
- proc newPostInitProc(m: BModule): BProc =
- result = newProc(nil, m)
- # little hack so that unique temporaries are generated:
- result.labels = 200_000
- proc initProcOptions(m: BModule): TOptions =
- if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions
- proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
- new(result)
- result.tmpBase = rope("TM" & $hashOwner(module) & "_")
- result.headerFiles = @[]
- result.declaredThings = initIntSet()
- result.declaredProtos = initIntSet()
- result.cfilename = filename
- result.filename = filename
- result.typeCache = initTable[SigHash, Rope]()
- result.forwTypeCache = initTable[SigHash, Rope]()
- result.module = module
- result.typeInfoMarker = initTable[SigHash, Rope]()
- result.sigConflicts = initCountTable[SigHash]()
- result.initProc = newProc(nil, result)
- result.initProc.options = initProcOptions(result)
- result.preInitProc = newPreInitProc(result)
- result.postInitProc = newPostInitProc(result)
- initNodeTable(result.dataCache)
- result.typeStack = @[]
- result.forwardedProcs = @[]
- result.typeNodesName = getTempName(result)
- result.nimTypesName = getTempName(result)
- result.g = g
- # no line tracing for the init sections of the system module so that we
- # don't generate a TFrame which can confuse the stack botton initialization:
- if sfSystemModule in module.flags:
- incl result.flags, preventStackTrace
- excl(result.preInitProc.options, optStackTrace)
- excl(result.postInitProc.options, optStackTrace)
- let ndiName = if optCDebug in gGlobalOptions: changeFileExt(completeCFilePath(filename), "ndi")
- else: ""
- open(result.ndi, ndiName)
- proc nullify[T](arr: var T) =
- for i in low(arr)..high(arr):
- arr[i] = Rope(nil)
- proc resetModule*(m: BModule) =
- # between two compilations in CAAS mode, we can throw
- # away all the data that was written to disk
- m.headerFiles = @[]
- m.declaredProtos = initIntSet()
- m.forwTypeCache = initTable[SigHash, Rope]()
- m.initProc = newProc(nil, m)
- m.initProc.options = initProcOptions(m)
- m.preInitProc = newPreInitProc(m)
- m.postInitProc = newPostInitProc(m)
- initNodeTable(m.dataCache)
- m.typeStack = @[]
- m.forwardedProcs = @[]
- m.typeNodesName = getTempName(m)
- m.nimTypesName = getTempName(m)
- if sfSystemModule in m.module.flags:
- incl m.flags, preventStackTrace
- else:
- excl m.flags, preventStackTrace
- nullify m.s
- m.typeNodes = 0
- m.nimTypes = 0
- nullify m.extensionLoaders
- # indicate that this is now cached module
- # the cache will be invalidated by nullifying gModules
- m.fromCache = true
- m.g = nil
- # we keep only the "merge info" information for the module
- # and the properties that can't change:
- # m.filename
- # m.cfilename
- # m.isHeaderFile
- # m.module ?
- # m.typeCache
- # m.declaredThings
- # m.typeInfoMarker
- # m.labels
- # m.FrameDeclared
- proc resetCgenModules*(g: BModuleList) =
- for m in cgenModules(g): resetModule(m)
- proc rawNewModule(g: BModuleList; module: PSym): BModule =
- result = rawNewModule(g, module, module.position.int32.toFullPath)
- proc newModule(g: BModuleList; module: PSym): BModule =
- # we should create only one cgen module for each module sym
- result = rawNewModule(g, module)
- growCache g.modules, module.position
- g.modules[module.position] = result
- if (optDeadCodeElim in gGlobalOptions):
- if (sfDeadCodeElim in module.flags):
- internalError("added pending module twice: " & module.filename)
- template injectG(config) {.dirty.} =
- if graph.backend == nil:
- graph.backend = newModuleList(config)
- let g = BModuleList(graph.backend)
- proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
- injectG(graph.config)
- result = newModule(g, module)
- if optGenIndex in gGlobalOptions and g.generatedHeader == nil:
- let f = if graph.config.headerFile.len > 0: graph.config.headerFile else: gProjectFull
- g.generatedHeader = rawNewModule(g, module,
- changeFileExt(completeCFilePath(f), hExt))
- incl g.generatedHeader.flags, isHeaderFile
- proc writeHeader(m: BModule) =
- var result = ("/* Generated by Nim Compiler v$1 */$N" &
- "/* (c) " & CompileDate.substr(0, 3) & " Andreas Rumpf */$N" &
- "/* The generated code is subject to the original license. */$N") %
- [rope(VersionAsString)]
- var guard = "__$1__" % [m.filename.splitFile.name.rope]
- result.addf("#ifndef $1$n#define $1$n", [guard])
- addIntTypes(result)
- generateHeaders(m)
- generateThreadLocalStorage(m)
- for i in countup(cfsHeaders, cfsProcs):
- add(result, genSectionStart(i))
- add(result, m.s[i])
- add(result, genSectionEnd(i))
- add(result, m.s[cfsInitProc])
- if optGenDynLib in gGlobalOptions:
- result.add("N_LIB_IMPORT ")
- result.addf("N_CDECL(void, NimMain)(void);$n", [])
- result.addf("#endif /* $1 */$n", [guard])
- writeRope(result, m.filename)
- proc getCFile(m: BModule): string =
- let ext =
- if m.compileToCpp: ".cpp"
- elif gCmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m"
- else: ".c"
- result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), ext)
- proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
- injectG(graph.config)
- assert optSymbolFiles in gGlobalOptions
- var m = newModule(g, module)
- readMergeInfo(getCFile(m), m)
- result = m
- proc myProcess(b: PPassContext, n: PNode): PNode =
- result = n
- if b == nil or passes.skipCodegen(n): return
- var m = BModule(b)
- m.initProc.options = initProcOptions(m)
- softRnl = if optLineDir in gOptions: noRnl else: rnl
- genStmts(m.initProc, n)
- proc finishModule(m: BModule) =
- var i = 0
- while i <= high(m.forwardedProcs):
- # Note: ``genProc`` may add to ``m.forwardedProcs``, so we cannot use
- # a ``for`` loop here
- var prc = m.forwardedProcs[i]
- if sfForward in prc.flags:
- internalError(prc.info, "still forwarded: " & prc.name.s)
- genProcNoForward(m, prc)
- inc(i)
- assert(m.g.forwardedProcsCounter >= i)
- dec(m.g.forwardedProcsCounter, i)
- setLen(m.forwardedProcs, 0)
- proc shouldRecompile(code: Rope, cfile: Cfile): bool =
- result = true
- if optForceFullMake notin gGlobalOptions:
- if not equalsFile(code, cfile.cname):
- if isDefined("nimdiff"):
- if fileExists(cfile.cname):
- copyFile(cfile.cname, cfile.cname & ".backup")
- echo "diff ", cfile.cname, ".backup ", cfile.cname
- else:
- echo "new file ", cfile.cname
- writeRope(code, cfile.cname)
- return
- if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname):
- result = false
- else:
- writeRope(code, cfile.cname)
- # We need 2 different logics here: pending modules (including
- # 'nim__dat') may require file merging for the combination of dead code
- # elimination and incremental compilation! Non pending modules need no
- # such logic and in fact the logic hurts for the main module at least;
- # it would generate multiple 'main' procs, for instance.
- proc writeModule(m: BModule, pending: bool) =
- # generate code for the init statements of the module:
- let cfile = getCFile(m)
- if not m.fromCache or optForceFullMake in gGlobalOptions:
- genInitCode(m)
- finishTypeDescriptions(m)
- if sfMainModule in m.module.flags:
- # generate main file:
- add(m.s[cfsProcHeaders], m.g.mainModProcs)
- generateThreadVarsSize(m)
- var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
- var code = genModule(m, cf)
- when hasTinyCBackend:
- if gCmd == cmdRun:
- tccgen.compileCCode($code)
- return
- if not shouldRecompile(code, cf): cf.flags = {CfileFlag.Cached}
- addFileToCompile(cf)
- elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
- let cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
- mergeFiles(cfile, m)
- genInitCode(m)
- finishTypeDescriptions(m)
- var code = genModule(m, cf)
- writeRope(code, cfile)
- addFileToCompile(cf)
- else:
- # Consider: first compilation compiles ``system.nim`` and produces
- # ``system.c`` but then compilation fails due to an error. This means
- # that ``system.o`` is missing, so we need to call the C compiler for it:
- var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
- if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached}
- addFileToCompile(cf)
- close(m.ndi)
- proc updateCachedModule(m: BModule) =
- let cfile = getCFile(m)
- var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
- if mergeRequired(m) and sfMainModule notin m.module.flags:
- mergeFiles(cfile, m)
- genInitCode(m)
- finishTypeDescriptions(m)
- var code = genModule(m, cf)
- writeRope(code, cfile)
- else:
- cf.flags = {CfileFlag.Cached}
- addFileToCompile(cf)
- proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
- result = n
- if b == nil or passes.skipCodegen(n): return
- var m = BModule(b)
- if n != nil:
- m.initProc.options = initProcOptions(m)
- genStmts(m.initProc, n)
- # cached modules need to registered too:
- registerModuleToMain(m.g, m.module)
- if sfMainModule in m.module.flags:
- if m.g.forwardedProcsCounter == 0:
- incl m.flags, objHasKidsValid
- let disp = generateMethodDispatchers(graph)
- for x in disp: genProcAux(m, x.sym)
- genMainProc(m)
- proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
- let g = BModuleList(backend)
- # we need to process the transitive closure because recursive module
- # deps are allowed (and the system module is processed in the wrong
- # order anyway)
- g.config = config
- if g.generatedHeader != nil: finishModule(g.generatedHeader)
- while g.forwardedProcsCounter > 0:
- for m in cgenModules(g):
- if not m.fromCache:
- finishModule(m)
- for m in cgenModules(g):
- if m.fromCache:
- m.updateCachedModule
- else:
- m.writeModule(pending=true)
- writeMapping(g.mapping)
- if g.generatedHeader != nil: writeHeader(g.generatedHeader)
- const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
|