some progress on the VM
This commit is contained in:
parent
c260b22fbc
commit
e8fc470310
103 changed files with 294 additions and 11 deletions
332
compiler/cgen/ccgcalls.nim
Normal file
332
compiler/cgen/ccgcalls.nim
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
#
|
||||
# included from cgen.nim
|
||||
|
||||
proc leftAppearsOnRightSide(le, ri: PNode): bool =
|
||||
if le != nil:
|
||||
for i in 1 .. <ri.len:
|
||||
let r = ri[i]
|
||||
if isPartOf(le, r) != arNo: return true
|
||||
|
||||
proc hasNoInit(call: PNode): bool {.inline.} =
|
||||
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags
|
||||
|
||||
proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
callee, params: PRope) =
|
||||
var pl = con(callee, ~"(", params)
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(typ.sons[0]):
|
||||
if params != nil: pl.app(~", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
|
||||
# Great, we can use 'd':
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
resetLoc(p, d)
|
||||
app(pl, addrLoc(d))
|
||||
app(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ.sons[0], tmp)
|
||||
app(pl, addrLoc(tmp))
|
||||
app(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
app(pl, ~")")
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
app(pl, ~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
|
||||
proc isInCurrentFrame(p: BProc, n: PNode): bool =
|
||||
# checks if `n` is an expression that refers to the current frame;
|
||||
# this does not work reliably because of forwarding + inlining can break it
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil:
|
||||
result = p.prc.id == n.sym.owner.id
|
||||
of nkDotExpr, nkBracketExpr:
|
||||
if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyPtr,tyRef}:
|
||||
result = isInCurrentFrame(p, n.sons[0])
|
||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||
result = isInCurrentFrame(p, n.sons[1])
|
||||
of nkHiddenDeref, nkDerefExpr:
|
||||
# what about: var x = addr(y); callAsOpenArray(x[])?
|
||||
# *shrug* ``addr`` is unsafe anyway.
|
||||
result = false
|
||||
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||
result = isInCurrentFrame(p, n.sons[0])
|
||||
else: discard
|
||||
|
||||
proc openArrayLoc(p: BProc, n: PNode): PRope =
|
||||
var a: TLoc
|
||||
|
||||
let q = skipConv(n)
|
||||
if getMagic(q) == mSlice:
|
||||
# magic: pass slice to openArray:
|
||||
var b, c: TLoc
|
||||
initLocExpr(p, q[1], a)
|
||||
initLocExpr(p, q[2], b)
|
||||
initLocExpr(p, q[3], c)
|
||||
let fmt =
|
||||
case skipTypes(a.t, abstractVar+{tyPtr}).kind
|
||||
of tyOpenArray, tyVarargs, tyArray, tyArrayConstr:
|
||||
"($1)+($2), ($3)-($2)+1"
|
||||
of tyString, tySequence:
|
||||
if skipTypes(n.typ, abstractInst).kind == tyVar:
|
||||
"(*$1)->data+($2), ($3)-($2)+1"
|
||||
else:
|
||||
"$1->data+($2), ($3)-($2)+1"
|
||||
else: (internalError("openArrayLoc: " & typeToString(a.t)); "")
|
||||
result = ropef(fmt, [rdLoc(a), rdLoc(b), rdLoc(c)])
|
||||
else:
|
||||
initLocExpr(p, n, a)
|
||||
case skipTypes(a.t, abstractVar).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
result = ropef("$1, $1Len0", [rdLoc(a)])
|
||||
of tyString, tySequence:
|
||||
if skipTypes(n.typ, abstractInst).kind == tyVar:
|
||||
result = ropef("(*$1)->data, (*$1)->$2", [a.rdLoc, lenField()])
|
||||
else:
|
||||
result = ropef("$1->data, $1->$2", [a.rdLoc, lenField()])
|
||||
of tyArray, tyArrayConstr:
|
||||
result = ropef("$1, $2", [rdLoc(a), toRope(lengthOrd(a.t))])
|
||||
else: internalError("openArrayLoc: " & typeToString(a.t))
|
||||
|
||||
proc genArgStringToCString(p: BProc,
|
||||
n: PNode): PRope {.inline.} =
|
||||
var a: TLoc
|
||||
initLocExpr(p, n.sons[0], a)
|
||||
result = ropef("$1->data", [a.rdLoc])
|
||||
|
||||
proc genArg(p: BProc, n: PNode, param: PSym): PRope =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
result = genArgStringToCString(p, n)
|
||||
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
||||
var n = if n.kind != nkHiddenAddr: n else: n.sons[0]
|
||||
result = openArrayLoc(p, n)
|
||||
elif ccgIntroducedPtr(param):
|
||||
initLocExpr(p, n, a)
|
||||
result = addrLoc(a)
|
||||
else:
|
||||
initLocExpr(p, n, a)
|
||||
result = rdLoc(a)
|
||||
|
||||
proc genArgNoParam(p: BProc, n: PNode): PRope =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
result = genArgStringToCString(p, n)
|
||||
else:
|
||||
initLocExpr(p, n, a)
|
||||
result = rdLoc(a)
|
||||
|
||||
proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var op: TLoc
|
||||
# this is a hotspot in the compiler
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var params: PRope
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
var length = sonsLen(ri)
|
||||
for i in countup(1, length - 1):
|
||||
if ri.sons[i].typ.isCompileTimeOnly: continue
|
||||
if params != nil: app(params, ~", ")
|
||||
if i < sonsLen(typ):
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym))
|
||||
else:
|
||||
app(params, genArgNoParam(p, ri.sons[i]))
|
||||
fixupCall(p, le, ri, d, op.r, params)
|
||||
|
||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
|
||||
proc getRawProcType(p: BProc, t: PType): PRope =
|
||||
result = getClosureType(p.module, t, clHalf)
|
||||
|
||||
proc addComma(r: PRope): PRope =
|
||||
result = if r == nil: r else: con(r, ~", ")
|
||||
|
||||
const PatProc = "$1.ClEnv? $1.ClPrc($3$1.ClEnv):(($4)($1.ClPrc))($2)"
|
||||
const PatIter = "$1.ClPrc($3$1.ClEnv)" # we know the env exists
|
||||
var op: TLoc
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var pl: PRope
|
||||
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
var length = sonsLen(ri)
|
||||
for i in countup(1, length - 1):
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
if i < sonsLen(typ):
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
app(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym))
|
||||
else:
|
||||
app(pl, genArgNoParam(p, ri.sons[i]))
|
||||
if i < length - 1: app(pl, ~", ")
|
||||
|
||||
template genCallPattern {.dirty.} =
|
||||
lineF(p, cpsStmts, callPattern & ";$n", op.r, pl, pl.addComma, rawProc)
|
||||
|
||||
let rawProc = getRawProcType(p, typ)
|
||||
let callPattern = if tfIterator in typ.flags: PatIter else: PatProc
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(typ.sons[0]):
|
||||
if sonsLen(ri) > 1: app(pl, ~", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
|
||||
# Great, we can use 'd':
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
elif d.k notin {locExpr, locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
resetLoc(p, d)
|
||||
app(pl, addrLoc(d))
|
||||
genCallPattern()
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ.sons[0], tmp)
|
||||
app(pl, addrLoc(tmp))
|
||||
genCallPattern()
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.t, OnUnknown)
|
||||
list.r = ropef(callPattern, op.r, pl, pl.addComma, rawProc)
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
genCallPattern()
|
||||
|
||||
proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
var op, a: TLoc
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var pl: PRope = nil
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
var length = sonsLen(ri)
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
|
||||
var param = typ.n.sons[1].sym
|
||||
app(pl, genArg(p, ri.sons[1], param))
|
||||
|
||||
if skipTypes(param.typ, {tyGenericInst}).kind == tyPtr: app(pl, ~"->")
|
||||
else: app(pl, ~".")
|
||||
app(pl, op.r)
|
||||
var params: PRope
|
||||
for i in countup(2, length - 1):
|
||||
if params != nil: params.app(~", ")
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
if i < sonsLen(typ):
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
app(params, genArg(p, ri.sons[i], typ.n.sons[i].sym))
|
||||
else:
|
||||
app(params, genArgNoParam(p, ri.sons[i]))
|
||||
fixupCall(p, le, ri, d, pl, params)
|
||||
|
||||
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
# generates a crappy ObjC call
|
||||
var op, a: TLoc
|
||||
initLocExpr(p, ri.sons[0], op)
|
||||
var pl = ~"["
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri.sons[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
var length = sonsLen(ri)
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
|
||||
if length > 1:
|
||||
app(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym))
|
||||
app(pl, ~" ")
|
||||
app(pl, op.r)
|
||||
if length > 2:
|
||||
app(pl, ~": ")
|
||||
app(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym))
|
||||
for i in countup(3, length-1):
|
||||
assert(sonsLen(typ) == sonsLen(typ.n))
|
||||
if i >= sonsLen(typ):
|
||||
internalError(ri.info, "varargs for objective C method?")
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
var param = typ.n.sons[i].sym
|
||||
app(pl, ~" ")
|
||||
app(pl, param.name.s)
|
||||
app(pl, ~": ")
|
||||
app(pl, genArg(p, ri.sons[i], param))
|
||||
if typ.sons[0] != nil:
|
||||
if isInvalidReturnType(typ.sons[0]):
|
||||
if sonsLen(ri) > 1: app(pl, ~" ")
|
||||
# beware of 'result = p(result)'. We always allocate a temporary:
|
||||
if d.k in {locTemp, locNone}:
|
||||
# We already got a temp. Great, special case it:
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
app(pl, ~"Result: ")
|
||||
app(pl, addrLoc(d))
|
||||
app(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ.sons[0], tmp)
|
||||
app(pl, addrLoc(tmp))
|
||||
app(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
app(pl, ~"]")
|
||||
if d.k == locNone: getTemp(p, typ.sons[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, nil, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
app(pl, ~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
|
||||
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||
if e.sons[0].typ.callConv == ccClosure:
|
||||
genClosureCall(p, nil, e, d)
|
||||
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags and
|
||||
e.len >= 2:
|
||||
genInfixCall(p, nil, e, d)
|
||||
elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags:
|
||||
genNamedParamCall(p, e, d)
|
||||
else:
|
||||
genPrefixCall(p, nil, e, d)
|
||||
postStmtActions(p)
|
||||
when false:
|
||||
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
|
||||
|
||||
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
if ri.sons[0].typ.callConv == ccClosure:
|
||||
genClosureCall(p, le, ri, d)
|
||||
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags and
|
||||
ri.len >= 2:
|
||||
genInfixCall(p, le, ri, d)
|
||||
elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags:
|
||||
genNamedParamCall(p, ri, d)
|
||||
else:
|
||||
genPrefixCall(p, le, ri, d)
|
||||
postStmtActions(p)
|
||||
when false:
|
||||
if d.s == onStack and containsGarbageCollectedRef(d.t): keepAlive(p, d)
|
||||
|
||||
2082
compiler/cgen/ccgexprs.nim
Normal file
2082
compiler/cgen/ccgexprs.nim
Normal file
File diff suppressed because it is too large
Load diff
298
compiler/cgen/ccgmerge.nim
Normal file
298
compiler/cgen/ccgmerge.nim
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements the merge operation of 2 different C files. This
|
||||
## is needed for incremental compilation.
|
||||
|
||||
import
|
||||
ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
|
||||
intsets, platform, llstream
|
||||
|
||||
# Careful! Section marks need to contain a tabulator so that they cannot
|
||||
# be part of C string literals.
|
||||
|
||||
const
|
||||
CFileSectionNames: array[TCFileSection, string] = [
|
||||
cfsMergeInfo: "",
|
||||
cfsHeaders: "NIM_merge_HEADERS",
|
||||
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
|
||||
cfsTypes: "NIM_merge_TYPES",
|
||||
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
|
||||
cfsFieldInfo: "NIM_merge_FIELD_INFO",
|
||||
cfsTypeInfo: "NIM_merge_TYPE_INFO",
|
||||
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
|
||||
cfsData: "NIM_merge_DATA",
|
||||
cfsVars: "NIM_merge_VARS",
|
||||
cfsProcs: "NIM_merge_PROCS",
|
||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
||||
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
|
||||
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
||||
cfsDebugInit: "NIM_merge_DEBUG_INIT",
|
||||
cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
|
||||
cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
|
||||
]
|
||||
CProcSectionNames: array[TCProcSection, string] = [
|
||||
cpsLocals: "NIM_merge_PROC_LOCALS",
|
||||
cpsInit: "NIM_merge_PROC_INIT",
|
||||
cpsStmts: "NIM_merge_PROC_BODY"
|
||||
]
|
||||
NimMergeEndMark = "/*\tNIM_merge_END:*/"
|
||||
|
||||
proc genSectionStart*(fs: TCFileSection): PRope =
|
||||
if compilationCachePresent:
|
||||
result = toRope(tnl)
|
||||
app(result, "/*\t")
|
||||
app(result, CFileSectionNames[fs])
|
||||
app(result, ":*/")
|
||||
app(result, tnl)
|
||||
|
||||
proc genSectionEnd*(fs: TCFileSection): PRope =
|
||||
if compilationCachePresent:
|
||||
result = toRope(NimMergeEndMark & tnl)
|
||||
|
||||
proc genSectionStart*(ps: TCProcSection): PRope =
|
||||
if compilationCachePresent:
|
||||
result = toRope(tnl)
|
||||
app(result, "/*\t")
|
||||
app(result, CProcSectionNames[ps])
|
||||
app(result, ":*/")
|
||||
app(result, tnl)
|
||||
|
||||
proc genSectionEnd*(ps: TCProcSection): PRope =
|
||||
if compilationCachePresent:
|
||||
result = toRope(NimMergeEndMark & tnl)
|
||||
|
||||
proc writeTypeCache(a: TIdTable, s: var string) =
|
||||
var i = 0
|
||||
for id, value in pairs(a):
|
||||
if i == 10:
|
||||
i = 0
|
||||
s.add(tnl)
|
||||
else:
|
||||
s.add(' ')
|
||||
encodeVInt(id, s)
|
||||
s.add(':')
|
||||
encodeStr(PRope(value).ropeToStr, s)
|
||||
inc i
|
||||
s.add('}')
|
||||
|
||||
proc writeIntSet(a: TIntSet, s: var string) =
|
||||
var i = 0
|
||||
for x in items(a):
|
||||
if i == 10:
|
||||
i = 0
|
||||
s.add(tnl)
|
||||
else:
|
||||
s.add(' ')
|
||||
encodeVInt(x, s)
|
||||
inc i
|
||||
s.add('}')
|
||||
|
||||
proc genMergeInfo*(m: BModule): PRope =
|
||||
if optSymbolFiles notin gGlobalOptions: return nil
|
||||
var s = "/*\tNIM_merge_INFO:"
|
||||
s.add(tnl)
|
||||
s.add("typeCache:{")
|
||||
writeTypeCache(m.typeCache, s)
|
||||
s.add("declared:{")
|
||||
writeIntSet(m.declaredThings, s)
|
||||
s.add("typeInfo:{")
|
||||
writeIntSet(m.typeInfoMarker, s)
|
||||
s.add("labels:")
|
||||
encodeVInt(m.labels, s)
|
||||
s.add(" hasframe:")
|
||||
encodeVInt(ord(m.frameDeclared), s)
|
||||
s.add(tnl)
|
||||
s.add("*/")
|
||||
result = s.toRope
|
||||
|
||||
template `^`(pos: expr): expr = L.buf[pos]
|
||||
|
||||
proc skipWhite(L: var TBaseLexer) =
|
||||
var pos = L.bufpos
|
||||
while true:
|
||||
case ^pos
|
||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||
of ' ': inc pos
|
||||
else: break
|
||||
L.bufpos = pos
|
||||
|
||||
proc skipUntilCmd(L: var TBaseLexer) =
|
||||
var pos = L.bufpos
|
||||
while true:
|
||||
case ^pos
|
||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||
of '\0': break
|
||||
of '/':
|
||||
if ^(pos+1) == '*' and ^(pos+2) == '\t':
|
||||
inc pos, 3
|
||||
break
|
||||
inc pos
|
||||
else: inc pos
|
||||
L.bufpos = pos
|
||||
|
||||
proc atEndMark(buf: cstring, pos: int): bool =
|
||||
var s = 0
|
||||
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
||||
result = s == NimMergeEndMark.len
|
||||
|
||||
proc readVerbatimSection(L: var TBaseLexer): PRope =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
var r = newStringOfCap(30_000)
|
||||
while true:
|
||||
case buf[pos]
|
||||
of CR:
|
||||
pos = nimlexbase.handleCR(L, pos)
|
||||
buf = L.buf
|
||||
r.add(tnl)
|
||||
of LF:
|
||||
pos = nimlexbase.handleLF(L, pos)
|
||||
buf = L.buf
|
||||
r.add(tnl)
|
||||
of '\0':
|
||||
internalError("ccgmerge: expected: " & NimMergeEndMark)
|
||||
break
|
||||
else:
|
||||
if atEndMark(buf, pos):
|
||||
inc pos, NimMergeEndMark.len
|
||||
break
|
||||
r.add(buf[pos])
|
||||
inc pos
|
||||
L.bufpos = pos
|
||||
result = r.toRope
|
||||
|
||||
proc readKey(L: var TBaseLexer, result: var string) =
|
||||
var pos = L.bufpos
|
||||
var buf = L.buf
|
||||
setLen(result, 0)
|
||||
while buf[pos] in IdentChars:
|
||||
result.add(buf[pos])
|
||||
inc pos
|
||||
if buf[pos] != ':': internalError("ccgmerge: ':' expected")
|
||||
L.bufpos = pos + 1 # skip ':'
|
||||
|
||||
proc newFakeType(id: int): PType =
|
||||
new(result)
|
||||
result.id = id
|
||||
|
||||
proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
|
||||
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
|
||||
inc L.bufpos
|
||||
while ^L.bufpos != '}':
|
||||
skipWhite(L)
|
||||
var key = decodeVInt(L.buf, L.bufpos)
|
||||
if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
|
||||
inc L.bufpos
|
||||
var value = decodeStr(L.buf, L.bufpos)
|
||||
# XXX little hack: we create a "fake" type object with the correct Id
|
||||
# better would be to adapt the data structure to not even store the
|
||||
# object as key, but only the Id
|
||||
idTablePut(result, newFakeType(key), value.toRope)
|
||||
inc L.bufpos
|
||||
|
||||
proc readIntSet(L: var TBaseLexer, result: var TIntSet) =
|
||||
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
|
||||
inc L.bufpos
|
||||
while ^L.bufpos != '}':
|
||||
skipWhite(L)
|
||||
var key = decodeVInt(L.buf, L.bufpos)
|
||||
result.incl(key)
|
||||
inc L.bufpos
|
||||
|
||||
proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
||||
var k = newStringOfCap("typeCache".len)
|
||||
while true:
|
||||
skipWhite(L)
|
||||
if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||
inc(L.bufpos, 2)
|
||||
break
|
||||
readKey(L, k)
|
||||
case k
|
||||
of "typeCache": readTypeCache(L, m.typeCache)
|
||||
of "declared": readIntSet(L, m.declaredThings)
|
||||
of "typeInfo": readIntSet(L, m.typeInfoMarker)
|
||||
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
|
||||
of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0
|
||||
else: internalError("ccgmerge: unkown key: " & k)
|
||||
|
||||
when not defined(nimhygiene):
|
||||
{.pragma: inject.}
|
||||
|
||||
template withCFile(cfilename: string, body: stmt) {.immediate.} =
|
||||
var s = llStreamOpen(cfilename, fmRead)
|
||||
if s == nil: return
|
||||
var L {.inject.}: TBaseLexer
|
||||
openBaseLexer(L, s)
|
||||
var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
|
||||
while true:
|
||||
skipUntilCmd(L)
|
||||
if ^L.bufpos == '\0': break
|
||||
body
|
||||
closeBaseLexer(L)
|
||||
|
||||
proc readMergeInfo*(cfilename: string, m: BModule) =
|
||||
## reads the merge meta information into `m`.
|
||||
withCFile(cfilename):
|
||||
readKey(L, k)
|
||||
if k == "NIM_merge_INFO":
|
||||
processMergeInfo(L, m)
|
||||
break
|
||||
|
||||
type
|
||||
TMergeSections = object {.pure.}
|
||||
f: TCFileSections
|
||||
p: TCProcSections
|
||||
|
||||
proc readMergeSections(cfilename: string, m: var TMergeSections) =
|
||||
## reads the merge sections into `m`.
|
||||
withCFile(cfilename):
|
||||
readKey(L, k)
|
||||
if k == "NIM_merge_INFO":
|
||||
discard
|
||||
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||
inc(L.bufpos, 2)
|
||||
# read back into section
|
||||
skipWhite(L)
|
||||
var verbatim = readVerbatimSection(L)
|
||||
skipWhite(L)
|
||||
var sectionA = CFileSectionNames.find(k)
|
||||
if sectionA > 0 and sectionA <= high(TCFileSection).int:
|
||||
m.f[TCFileSection(sectionA)] = verbatim
|
||||
else:
|
||||
var sectionB = CProcSectionNames.find(k)
|
||||
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
|
||||
m.p[TCProcSection(sectionB)] = verbatim
|
||||
else:
|
||||
internalError("ccgmerge: unknown section: " & k)
|
||||
else:
|
||||
internalError("ccgmerge: '*/' expected")
|
||||
|
||||
proc mergeRequired*(m: BModule): bool =
|
||||
for i in cfsHeaders..cfsProcs:
|
||||
if m.s[i] != nil:
|
||||
#echo "not empty: ", i, " ", ropeToStr(m.s[i])
|
||||
return true
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
if m.initProc.s(i) != nil:
|
||||
#echo "not empty: ", i, " ", ropeToStr(m.initProc.s[i])
|
||||
return true
|
||||
|
||||
proc mergeFiles*(cfilename: string, m: BModule) =
|
||||
## merges the C file with the old version on hard disc.
|
||||
var old: TMergeSections
|
||||
readMergeSections(cfilename, old)
|
||||
# do the merge; old section before new section:
|
||||
for i in low(TCFileSection)..high(TCFileSection):
|
||||
m.s[i] = con(old.f[i], m.s[i])
|
||||
for i in low(TCProcSection)..high(TCProcSection):
|
||||
m.initProc.s(i) = con(old.p[i], m.initProc.s(i))
|
||||
1006
compiler/cgen/ccgstmts.nim
Normal file
1006
compiler/cgen/ccgstmts.nim
Normal file
File diff suppressed because it is too large
Load diff
62
compiler/cgen/ccgthreadvars.nim
Normal file
62
compiler/cgen/ccgthreadvars.nim
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Thread var support for crappy architectures that lack native support for
|
||||
## thread local storage. (**Thank you Mac OS X!**)
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
proc emulatedThreadVars(): bool =
|
||||
result = {optThreads, optTlsEmulation} <= gGlobalOptions
|
||||
|
||||
proc accessThreadLocalVar(p: BProc, s: PSym) =
|
||||
if emulatedThreadVars() and not p.threadVarAccessed:
|
||||
p.threadVarAccessed = true
|
||||
p.module.usesThreadVars = true
|
||||
appf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n")
|
||||
app(p.procSec(cpsInit),
|
||||
ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
|
||||
|
||||
var
|
||||
nimtv: PRope # nimrod thread vars; the struct body
|
||||
nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct
|
||||
nimtvDeclared = initIntSet() # so that every var/field exists only once
|
||||
# in the struct
|
||||
|
||||
# 'nimtv' is incredibly hard to modularize! Best effort is to store all thread
|
||||
# vars in a ROD section and with their type deps and load them
|
||||
# unconditionally...
|
||||
|
||||
# nimtvDeps is VERY hard to cache because it's not a list of IDs nor can it be
|
||||
# made to be one.
|
||||
|
||||
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
if emulatedThreadVars():
|
||||
# we gather all thread locals var into a struct; we need to allocate
|
||||
# storage for that somehow, can't use the thread local storage
|
||||
# allocator for it :-(
|
||||
if not containsOrIncl(nimtvDeclared, s.id):
|
||||
nimtvDeps.add(s.loc.t)
|
||||
appf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
||||
else:
|
||||
if isExtern: app(m.s[cfsVars], "extern ")
|
||||
if optThreads in gGlobalOptions: app(m.s[cfsVars], "NIM_THREADVAR ")
|
||||
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
|
||||
appf(m.s[cfsVars], " $1;$n", [s.loc.r])
|
||||
|
||||
proc generateThreadLocalStorage(m: BModule) =
|
||||
if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
|
||||
for t in items(nimtvDeps): discard getTypeDesc(m, t)
|
||||
appf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if nimtv != nil:
|
||||
app(m.s[cfsProcs],
|
||||
"NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}" & tnl)
|
||||
|
||||
151
compiler/cgen/ccgtrav.nim
Normal file
151
compiler/cgen/ccgtrav.nim
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Generates traversal procs for the C backend. Traversal procs are only an
|
||||
## optimization; the GC works without them too.
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
type
|
||||
TTraversalClosure = object
|
||||
p: BProc
|
||||
visitorFrmt: string
|
||||
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType)
|
||||
proc genCaseRange(p: BProc, branch: PNode)
|
||||
proc getTemp(p: BProc, t: PType, result: var TLoc)
|
||||
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, n: PNode) =
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
genTraverseProc(c, accessor, n.sons[i])
|
||||
of nkRecCase:
|
||||
if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc")
|
||||
var p = c.p
|
||||
let disc = n.sons[0].sym
|
||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", accessor, disc.loc.r)
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
let branch = n.sons[i]
|
||||
assert branch.kind in {nkOfBranch, nkElse}
|
||||
if branch.kind == nkOfBranch:
|
||||
genCaseRange(c.p, branch)
|
||||
else:
|
||||
lineF(p, cpsStmts, "default:$n")
|
||||
genTraverseProc(c, accessor, lastSon(branch))
|
||||
lineF(p, cpsStmts, "break;$n")
|
||||
lineF(p, cpsStmts, "} $n")
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.loc.t == nil:
|
||||
internalError(n.info, "genTraverseProc()")
|
||||
genTraverseProc(c, ropef("$1.$2", accessor, field.loc.r), field.loc.t)
|
||||
else: internalError(n.info, "genTraverseProc()")
|
||||
|
||||
proc parentObj(accessor: PRope): PRope {.inline.} =
|
||||
if gCmd != cmdCompileToCpp:
|
||||
result = ropef("$1.Sup", accessor)
|
||||
else:
|
||||
result = accessor
|
||||
|
||||
proc genTraverseProc(c: var TTraversalClosure, accessor: PRope, typ: PType) =
|
||||
if typ == nil: return
|
||||
var p = c.p
|
||||
case typ.kind
|
||||
of tyGenericInst, tyGenericBody, tyTypeDesc:
|
||||
genTraverseProc(c, accessor, lastSon(typ))
|
||||
of tyArrayConstr, tyArray:
|
||||
let arraySize = lengthOrd(typ.sons[0])
|
||||
var i: TLoc
|
||||
getTemp(p, getSysType(tyInt), i)
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
i.r, arraySize.toRope)
|
||||
genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
|
||||
lineF(p, cpsStmts, "}$n")
|
||||
of tyObject:
|
||||
for i in countup(0, sonsLen(typ) - 1):
|
||||
genTraverseProc(c, accessor.parentObj, typ.sons[i])
|
||||
if typ.n != nil: genTraverseProc(c, accessor, typ.n)
|
||||
of tyTuple:
|
||||
let typ = getUniqueType(typ)
|
||||
for i in countup(0, sonsLen(typ) - 1):
|
||||
genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.toRope), typ.sons[i])
|
||||
of tyRef, tyString, tySequence:
|
||||
lineCg(p, cpsStmts, c.visitorFrmt, accessor)
|
||||
of tyProc:
|
||||
if typ.callConv == ccClosure:
|
||||
lineCg(p, cpsStmts, c.visitorFrmt, rfmt(nil, "$1.ClEnv", accessor))
|
||||
else:
|
||||
discard
|
||||
|
||||
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: PRope, typ: PType) =
|
||||
var p = c.p
|
||||
assert typ.kind == tySequence
|
||||
var i: TLoc
|
||||
getTemp(p, getSysType(tyInt), i)
|
||||
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
|
||||
i.r, accessor, toRope(if gCmd != cmdCompileToCpp: "Sup.len" else: "len"))
|
||||
genTraverseProc(c, ropef("$1->data[$2]", accessor, i.r), typ.sons[0])
|
||||
lineF(p, cpsStmts, "}$n")
|
||||
|
||||
proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): PRope =
|
||||
var c: TTraversalClosure
|
||||
var p = newProc(nil, m)
|
||||
result = getGlobalTempName()
|
||||
|
||||
case reason
|
||||
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
|
||||
else: assert false
|
||||
|
||||
let header = ropef("N_NIMCALL(void, $1)(void* p, NI op)", result)
|
||||
|
||||
let t = getTypeDesc(m, typ)
|
||||
lineF(p, cpsLocals, "$1 a;$n", t)
|
||||
lineF(p, cpsInit, "a = ($1)p;$n", t)
|
||||
|
||||
c.p = p
|
||||
assert typ.kind != tyTypeDesc
|
||||
if typ.kind == tySequence:
|
||||
genTraverseProcSeq(c, "a".toRope, typ)
|
||||
else:
|
||||
if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}:
|
||||
# C's arrays are broken beyond repair:
|
||||
genTraverseProc(c, "a".toRope, typ.sons[0])
|
||||
else:
|
||||
genTraverseProc(c, "(*a)".toRope, typ.sons[0])
|
||||
|
||||
let generatedProc = ropef("$1 {$n$2$3$4}$n",
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
|
||||
|
||||
m.s[cfsProcHeaders].appf("$1;$n", header)
|
||||
m.s[cfsProcs].app(generatedProc)
|
||||
|
||||
proc genTraverseProcForGlobal(m: BModule, s: PSym): PRope =
|
||||
discard genTypeInfo(m, s.loc.t)
|
||||
|
||||
var c: TTraversalClosure
|
||||
var p = newProc(nil, m)
|
||||
var sLoc = s.loc.r
|
||||
result = getGlobalTempName()
|
||||
|
||||
if sfThread in s.flags and emulatedThreadVars():
|
||||
accessThreadLocalVar(p, s)
|
||||
sLoc = con("NimTV->", sLoc)
|
||||
|
||||
c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
|
||||
c.p = p
|
||||
let header = ropef("N_NIMCALL(void, $1)()", result)
|
||||
genTraverseProc(c, sLoc, s.loc.t)
|
||||
|
||||
let generatedProc = ropef("$1 {$n$2$3$4}$n",
|
||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
|
||||
|
||||
m.s[cfsProcHeaders].appf("$1;$n", header)
|
||||
m.s[cfsProcs].app(generatedProc)
|
||||
942
compiler/cgen/ccgtypes.nim
Normal file
942
compiler/cgen/ccgtypes.nim
Normal file
|
|
@ -0,0 +1,942 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
# ------------------------- Name Mangling --------------------------------
|
||||
|
||||
proc mangleField(name: string): string =
|
||||
result = mangle(name)
|
||||
result[0] = result[0].toUpper # Mangling makes everything lowercase,
|
||||
# but some identifiers are C keywords
|
||||
|
||||
proc isKeyword(w: PIdent): bool =
|
||||
# nimrod and C++ share some keywords
|
||||
# it's more efficient to test the whole nimrod keywords range
|
||||
case w.id
|
||||
of ccgKeywordsLow..ccgKeywordsHigh,
|
||||
nimKeywordsLow..nimKeywordsHigh,
|
||||
ord(wInline): return true
|
||||
else: return false
|
||||
|
||||
proc mangleName(s: PSym): PRope =
|
||||
result = s.loc.r
|
||||
if result == nil:
|
||||
if gCmd == cmdCompileToLLVM:
|
||||
case s.kind
|
||||
of skProc, skMethod, skConverter, skConst, skIterators:
|
||||
result = ~"@"
|
||||
of skVar, skForVar, skResult, skLet:
|
||||
if sfGlobal in s.flags: result = ~"@"
|
||||
else: result = ~"%"
|
||||
of skTemp, skParam, skType, skEnumField, skModule:
|
||||
result = ~"%"
|
||||
else: internalError(s.info, "mangleName")
|
||||
when oKeepVariableNames:
|
||||
let keepOrigName = s.kind in skLocalVars - {skForVar} and
|
||||
{sfFromGeneric, sfGlobal, sfShadowed, sfGenSym} * s.flags == {} and
|
||||
not isKeyword(s.name)
|
||||
# XXX: This is still very experimental
|
||||
#
|
||||
# Even with all these inefficient checks, the bootstrap
|
||||
# time is actually improved. This is probably because so many
|
||||
# rope concatenations are now eliminated.
|
||||
#
|
||||
# Future notes:
|
||||
# sfFromGeneric seems to be needed in order to avoid multiple
|
||||
# definitions of certain variables generated in transf with
|
||||
# names such as:
|
||||
# `r`, `res`
|
||||
# I need to study where these come from.
|
||||
#
|
||||
# about sfShadowed:
|
||||
# consider the following nimrod code:
|
||||
# var x = 10
|
||||
# block:
|
||||
# var x = something(x)
|
||||
# The generated C code will be:
|
||||
# NI x;
|
||||
# x = 10;
|
||||
# {
|
||||
# NI x;
|
||||
# x = something(x); // Oops, x is already shadowed here
|
||||
# }
|
||||
# Right now, we work-around by not keeping the original name
|
||||
# of the shadowed variable, but we can do better - we can
|
||||
# create an alternative reference to it in the outer scope and
|
||||
# use that in the inner scope.
|
||||
#
|
||||
# about isCKeyword:
|
||||
# nimrod variable names can be C keywords.
|
||||
# We need to avoid such names in the generated code.
|
||||
# XXX: Study whether mangleName is called just once per variable.
|
||||
# Otherwise, there might be better place to do this.
|
||||
#
|
||||
# about sfGlobal:
|
||||
# This seems to be harder - a top level extern variable from
|
||||
# another modules can have the same name as a local one.
|
||||
# Maybe we should just implement sfShadowed for them too.
|
||||
#
|
||||
# about skForVar:
|
||||
# These are not properly scoped now - we need to add blocks
|
||||
# around for loops in transf
|
||||
if keepOrigName:
|
||||
result = s.name.s.mangle.newRope
|
||||
else:
|
||||
app(result, newRope(mangle(s.name.s)))
|
||||
app(result, ~"_")
|
||||
app(result, toRope(s.id))
|
||||
else:
|
||||
app(result, newRope(mangle(s.name.s)))
|
||||
app(result, ~"_")
|
||||
app(result, toRope(s.id))
|
||||
s.loc.r = result
|
||||
|
||||
proc typeName(typ: PType): PRope =
|
||||
result = if typ.sym != nil: typ.sym.name.s.mangle.toRope
|
||||
else: ~"TY"
|
||||
|
||||
proc getTypeName(typ: PType): PRope =
|
||||
if (typ.sym != nil) and ({sfImportc, sfExportc} * typ.sym.flags != {}) and
|
||||
(gCmd != cmdCompileToLLVM):
|
||||
result = typ.sym.loc.r
|
||||
else:
|
||||
if typ.loc.r == nil:
|
||||
typ.loc.r = if gCmd != cmdCompileToLLVM: con(typ.typeName, typ.id.toRope)
|
||||
else: con([~"%", typ.typeName, typ.id.toRope])
|
||||
result = typ.loc.r
|
||||
if result == nil: internalError("getTypeName: " & $typ.kind)
|
||||
|
||||
proc mapSetType(typ: PType): TCTypeKind =
|
||||
case int(getSize(typ))
|
||||
of 1: result = ctInt8
|
||||
of 2: result = ctInt16
|
||||
of 4: result = ctInt32
|
||||
of 8: result = ctInt64
|
||||
else: result = ctArray
|
||||
|
||||
proc mapType(typ: PType): TCTypeKind =
|
||||
case typ.kind
|
||||
of tyNone, tyStmt: result = ctVoid
|
||||
of tyBool: result = ctBool
|
||||
of tyChar: result = ctChar
|
||||
of tySet: result = mapSetType(typ)
|
||||
of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray
|
||||
of tyObject, tyTuple: result = ctStruct
|
||||
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
|
||||
tyConst, tyMutable, tyIter, tyTypeDesc:
|
||||
result = mapType(lastSon(typ))
|
||||
of tyEnum:
|
||||
if firstOrd(typ) < 0:
|
||||
result = ctInt32
|
||||
else:
|
||||
case int(getSize(typ))
|
||||
of 1: result = ctUInt8
|
||||
of 2: result = ctUInt16
|
||||
of 4: result = ctInt32
|
||||
of 8: result = ctInt64
|
||||
else: internalError("mapType")
|
||||
of tyRange: result = mapType(typ.sons[0])
|
||||
of tyPtr, tyVar, tyRef:
|
||||
var base = skipTypes(typ.lastSon, typedescInst)
|
||||
case base.kind
|
||||
of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctPtrToArray
|
||||
else: result = ctPtr
|
||||
of tyPointer: result = ctPtr
|
||||
of tySequence: result = ctNimSeq
|
||||
of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct
|
||||
of tyString: result = ctNimStr
|
||||
of tyCString: result = ctCString
|
||||
of tyInt..tyUInt64:
|
||||
result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
|
||||
else: internalError("mapType")
|
||||
|
||||
proc mapReturnType(typ: PType): TCTypeKind =
|
||||
if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
|
||||
else: result = mapType(typ)
|
||||
|
||||
proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope
|
||||
proc needsComplexAssignment(typ: PType): bool =
|
||||
result = containsGarbageCollectedRef(typ)
|
||||
|
||||
proc isInvalidReturnType(rettype: PType): bool =
|
||||
# Arrays and sets cannot be returned by a C procedure, because C is
|
||||
# such a poor programming language.
|
||||
# We exclude records with refs too. This enhances efficiency and
|
||||
# is necessary for proper code generation of assignments.
|
||||
if rettype == nil: result = true
|
||||
else:
|
||||
case mapType(rettype)
|
||||
of ctArray:
|
||||
result = not (skipTypes(rettype, typedescInst).kind in
|
||||
{tyVar, tyRef, tyPtr})
|
||||
of ctStruct:
|
||||
result = needsComplexAssignment(skipTypes(rettype, typedescInst))
|
||||
else: result = false
|
||||
|
||||
const
|
||||
CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL",
|
||||
"N_STDCALL", "N_CDECL", "N_SAFECALL",
|
||||
"N_SYSCALL", # this is probably not correct for all platforms,
|
||||
# but one can #define it to what one wants
|
||||
"N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_CLOSURE", "N_NOCONV"]
|
||||
CallingConvToStrLLVM: array[TCallingConvention, string] = ["fastcc $1",
|
||||
"stdcall $1", "ccc $1", "safecall $1", "syscall $1", "$1 alwaysinline",
|
||||
"$1 noinline", "fastcc $1", "ccc $1", "$1"]
|
||||
|
||||
proc cacheGetType(tab: TIdTable, key: PType): PRope =
|
||||
# returns nil if we need to declare this type
|
||||
# since types are now unique via the ``GetUniqueType`` mechanism, this slow
|
||||
# linear search is not necessary anymore:
|
||||
result = PRope(idTableGet(tab, key))
|
||||
|
||||
proc getTempName(): PRope =
|
||||
result = rfmt(nil, "TMP$1", toRope(backendId()))
|
||||
|
||||
proc getGlobalTempName(): PRope =
|
||||
result = rfmt(nil, "TMP$1", toRope(backendId()))
|
||||
|
||||
proc ccgIntroducedPtr(s: PSym): bool =
|
||||
var pt = skipTypes(s.typ, typedescInst)
|
||||
assert skResult != s.kind
|
||||
if tfByRef in pt.flags: return true
|
||||
elif tfByCopy in pt.flags: return false
|
||||
case pt.kind
|
||||
of tyObject:
|
||||
if (optByRef in s.options) or (getSize(pt) > platform.floatSize * 2):
|
||||
result = true # requested anyway
|
||||
elif (tfFinal in pt.flags) and (pt.sons[0] == nil):
|
||||
result = false # no need, because no subtyping possible
|
||||
else:
|
||||
result = true # ordinary objects are always passed by reference,
|
||||
# otherwise casting doesn't work
|
||||
of tyTuple:
|
||||
result = (getSize(pt) > platform.floatSize*2) or (optByRef in s.options)
|
||||
else: result = false
|
||||
|
||||
proc fillResult(param: PSym) =
|
||||
fillLoc(param.loc, locParam, param.typ, ~"Result",
|
||||
OnStack)
|
||||
if (mapReturnType(param.typ) != ctArray) and isInvalidReturnType(param.typ):
|
||||
incl(param.loc.flags, lfIndirect)
|
||||
param.loc.s = OnUnknown
|
||||
|
||||
proc getParamTypeDesc(m: BModule, t: PType, check: var TIntSet): PRope =
|
||||
when false:
|
||||
if t.Kind in {tyRef, tyPtr, tyVar}:
|
||||
var b = skipTypes(t.lastson, typedescInst)
|
||||
if b.kind == tySet and mapSetType(b) == ctArray:
|
||||
return getTypeDescAux(m, b, check)
|
||||
result = getTypeDescAux(m, t, check)
|
||||
|
||||
proc paramStorageLoc(param: PSym): TStorageLoc =
|
||||
if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {tyArray, tyOpenArray}:
|
||||
result = OnStack
|
||||
else:
|
||||
result = OnUnknown
|
||||
|
||||
proc genProcParams(m: BModule, t: PType, rettype, params: var PRope,
|
||||
check: var TIntSet, declareEnvironment=true) =
|
||||
params = nil
|
||||
if (t.sons[0] == nil) or isInvalidReturnType(t.sons[0]):
|
||||
rettype = ~"void"
|
||||
else:
|
||||
rettype = getTypeDescAux(m, t.sons[0], check)
|
||||
for i in countup(1, sonsLen(t.n) - 1):
|
||||
if t.n.sons[i].kind != nkSym: internalError(t.n.info, "genProcParams")
|
||||
var param = t.n.sons[i].sym
|
||||
if isCompileTimeOnly(param.typ): continue
|
||||
if params != nil: app(params, ~", ")
|
||||
fillLoc(param.loc, locParam, param.typ, mangleName(param),
|
||||
param.paramStorageLoc)
|
||||
app(params, getParamTypeDesc(m, param.typ, check))
|
||||
if ccgIntroducedPtr(param):
|
||||
app(params, ~"*")
|
||||
incl(param.loc.flags, lfIndirect)
|
||||
param.loc.s = OnUnknown
|
||||
app(params, ~" ")
|
||||
app(params, param.loc.r)
|
||||
# declare the len field for open arrays:
|
||||
var arr = param.typ
|
||||
if arr.kind == tyVar: arr = arr.sons[0]
|
||||
var j = 0
|
||||
while arr.kind in {tyOpenArray, tyVarargs}:
|
||||
# this fixes the 'sort' bug:
|
||||
if param.typ.kind == tyVar: param.loc.s = OnUnknown
|
||||
# need to pass hidden parameter:
|
||||
appff(params, ", NI $1Len$2", ", @NI $1Len$2", [param.loc.r, j.toRope])
|
||||
inc(j)
|
||||
arr = arr.sons[0]
|
||||
if (t.sons[0] != nil) and isInvalidReturnType(t.sons[0]):
|
||||
var arr = t.sons[0]
|
||||
if params != nil: app(params, ", ")
|
||||
app(params, getTypeDescAux(m, arr, check))
|
||||
if (mapReturnType(t.sons[0]) != ctArray) or (gCmd == cmdCompileToLLVM):
|
||||
app(params, "*")
|
||||
appff(params, " Result", " @Result", [])
|
||||
if t.callConv == ccClosure and declareEnvironment:
|
||||
if params != nil: app(params, ", ")
|
||||
app(params, "void* ClEnv")
|
||||
if tfVarargs in t.flags:
|
||||
if params != nil: app(params, ", ")
|
||||
app(params, "...")
|
||||
if params == nil and gCmd != cmdCompileToLLVM: app(params, "void)")
|
||||
else: app(params, ")")
|
||||
params = con("(", params)
|
||||
|
||||
proc isImportedType(t: PType): bool =
|
||||
result = (t.sym != nil) and (sfImportc in t.sym.flags)
|
||||
|
||||
proc typeNameOrLiteral(t: PType, literal: string): PRope =
|
||||
if (t.sym != nil) and (sfImportc in t.sym.flags) and (t.sym.magic == mNone):
|
||||
result = getTypeName(t)
|
||||
else:
|
||||
result = toRope(literal)
|
||||
|
||||
proc getSimpleTypeDesc(m: BModule, typ: PType): PRope =
|
||||
const
|
||||
NumericalTypeToStr: array[tyInt..tyUInt64, string] = [
|
||||
"NI", "NI8", "NI16", "NI32", "NI64",
|
||||
"NF", "NF32", "NF64", "NF128",
|
||||
"NU", "NU8", "NU16", "NU32", "NU64",]
|
||||
case typ.kind
|
||||
of tyPointer:
|
||||
result = typeNameOrLiteral(typ, "void*")
|
||||
of tyEnum:
|
||||
if firstOrd(typ) < 0:
|
||||
result = typeNameOrLiteral(typ, "NI32")
|
||||
else:
|
||||
case int(getSize(typ))
|
||||
of 1: result = typeNameOrLiteral(typ, "NU8")
|
||||
of 2: result = typeNameOrLiteral(typ, "NU16")
|
||||
of 4: result = typeNameOrLiteral(typ, "NI32")
|
||||
of 8: result = typeNameOrLiteral(typ, "NI64")
|
||||
else:
|
||||
internalError(typ.sym.info, "getSimpleTypeDesc: " & $(getSize(typ)))
|
||||
result = nil
|
||||
of tyString:
|
||||
discard cgsym(m, "NimStringDesc")
|
||||
result = typeNameOrLiteral(typ, "NimStringDesc*")
|
||||
of tyCString: result = typeNameOrLiteral(typ, "NCSTRING")
|
||||
of tyBool: result = typeNameOrLiteral(typ, "NIM_BOOL")
|
||||
of tyChar: result = typeNameOrLiteral(typ, "NIM_CHAR")
|
||||
of tyNil: result = typeNameOrLiteral(typ, "0")
|
||||
of tyInt..tyUInt64:
|
||||
result = typeNameOrLiteral(typ, NumericalTypeToStr[typ.kind])
|
||||
of tyRange: result = getSimpleTypeDesc(m, typ.sons[0])
|
||||
else: result = nil
|
||||
|
||||
proc getTypePre(m: BModule, typ: PType): PRope =
|
||||
if typ == nil: result = toRope("void")
|
||||
else:
|
||||
result = getSimpleTypeDesc(m, typ)
|
||||
if result == nil: result = cacheGetType(m.typeCache, typ)
|
||||
|
||||
proc structOrUnion(t: PType): PRope =
|
||||
(if tfUnion in t.flags: toRope("union") else: toRope("struct"))
|
||||
|
||||
proc getForwardStructFormat(): string =
|
||||
if gCmd == cmdCompileToCpp: result = "$1 $2;$n"
|
||||
else: result = "typedef $1 $2 $2;$n"
|
||||
|
||||
proc getTypeForward(m: BModule, typ: PType): PRope =
|
||||
result = cacheGetType(m.forwTypeCache, typ)
|
||||
if result != nil: return
|
||||
result = getTypePre(m, typ)
|
||||
if result != nil: return
|
||||
case typ.kind
|
||||
of tySequence, tyTuple, tyObject:
|
||||
result = getTypeName(typ)
|
||||
if not isImportedType(typ):
|
||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||
[structOrUnion(typ), result])
|
||||
idTablePut(m.forwTypeCache, typ, result)
|
||||
else: internalError("getTypeForward(" & $typ.kind & ')')
|
||||
|
||||
proc mangleRecFieldName(field: PSym, rectype: PType): PRope =
|
||||
if (rectype.sym != nil) and
|
||||
({sfImportc, sfExportc} * rectype.sym.flags != {}):
|
||||
result = field.loc.r
|
||||
else:
|
||||
result = toRope(mangleField(field.name.s))
|
||||
if result == nil: internalError(field.info, "mangleRecFieldName")
|
||||
|
||||
proc genRecordFieldsAux(m: BModule, n: PNode,
|
||||
accessExpr: PRope, rectype: PType,
|
||||
check: var TIntSet): PRope =
|
||||
var
|
||||
ae, uname, sname, a: PRope
|
||||
k: PNode
|
||||
field: PSym
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
app(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
|
||||
of nkRecCase:
|
||||
if (n.sons[0].kind != nkSym): internalError(n.info, "genRecordFieldsAux")
|
||||
app(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
|
||||
uname = toRope(mangle(n.sons[0].sym.name.s) & 'U')
|
||||
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, uname])
|
||||
else: ae = uname
|
||||
app(result, "union {" & tnl)
|
||||
for i in countup(1, sonsLen(n) - 1):
|
||||
case n.sons[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
k = lastSon(n.sons[i])
|
||||
if k.kind != nkSym:
|
||||
sname = con("S", toRope(i))
|
||||
a = genRecordFieldsAux(m, k, ropef("$1.$2", [ae, sname]), rectype,
|
||||
check)
|
||||
if a != nil:
|
||||
app(result, "struct {")
|
||||
app(result, a)
|
||||
appf(result, "} $1;$n", [sname])
|
||||
else:
|
||||
app(result, genRecordFieldsAux(m, k, ae, rectype, check))
|
||||
else: internalError("genRecordFieldsAux(record case branch)")
|
||||
appf(result, "} $1;$n", [uname])
|
||||
of nkSym:
|
||||
field = n.sym
|
||||
if field.typ.kind == tyEmpty: return
|
||||
#assert(field.ast == nil)
|
||||
sname = mangleRecFieldName(field, rectype)
|
||||
if accessExpr != nil: ae = ropef("$1.$2", [accessExpr, sname])
|
||||
else: ae = sname
|
||||
fillLoc(field.loc, locField, field.typ, ae, OnUnknown)
|
||||
let fieldType = field.loc.t
|
||||
if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags:
|
||||
appf(result, "$1 $2[SEQ_DECL_SIZE];$n",
|
||||
[getTypeDescAux(m, fieldType.elemType, check), sname])
|
||||
else:
|
||||
appf(result, "$1 $2;$n", [getTypeDescAux(m, fieldType, check), sname])
|
||||
else: internalError(n.info, "genRecordFieldsAux()")
|
||||
|
||||
proc getRecordFields(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||
result = genRecordFieldsAux(m, typ.n, nil, typ, check)
|
||||
|
||||
proc getRecordDesc(m: BModule, typ: PType, name: PRope,
|
||||
check: var TIntSet): PRope =
|
||||
# declare the record:
|
||||
var hasField = false
|
||||
|
||||
var attribute: PRope =
|
||||
if tfPacked in typ.flags: toRope(CC[ccompiler].packedPragma)
|
||||
else: nil
|
||||
|
||||
result = ropecg(m, CC[ccompiler].structStmtFmt,
|
||||
[structOrUnion(typ), name, attribute])
|
||||
|
||||
if typ.kind == tyObject:
|
||||
|
||||
if typ.sons[0] == nil:
|
||||
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
||||
appcg(m, result, " {$n", [])
|
||||
else:
|
||||
appcg(m, result, " {$n#TNimType* m_type;$n", [name, attribute])
|
||||
hasField = true
|
||||
elif gCmd == cmdCompileToCpp:
|
||||
appcg(m, result, " : public $1 {$n",
|
||||
[getTypeDescAux(m, typ.sons[0], check)])
|
||||
hasField = true
|
||||
else:
|
||||
appcg(m, result, " {$n $1 Sup;$n",
|
||||
[getTypeDescAux(m, typ.sons[0], check)])
|
||||
hasField = true
|
||||
else:
|
||||
appf(result, " {$n", [name])
|
||||
|
||||
var desc = getRecordFields(m, typ, check)
|
||||
if (desc == nil) and not hasField:
|
||||
appf(result, "char dummy;$n", [])
|
||||
else:
|
||||
app(result, desc)
|
||||
app(result, "};" & tnl)
|
||||
|
||||
proc getTupleDesc(m: BModule, typ: PType, name: PRope,
|
||||
check: var TIntSet): PRope =
|
||||
result = ropef("$1 $2 {$n", [structOrUnion(typ), name])
|
||||
var desc: PRope = nil
|
||||
for i in countup(0, sonsLen(typ) - 1):
|
||||
appf(desc, "$1 Field$2;$n",
|
||||
[getTypeDescAux(m, typ.sons[i], check), toRope(i)])
|
||||
if (desc == nil): app(result, "char dummy;" & tnl)
|
||||
else: app(result, desc)
|
||||
app(result, "};" & tnl)
|
||||
|
||||
proc pushType(m: BModule, typ: PType) =
|
||||
add(m.typeStack, typ)
|
||||
|
||||
proc getTypeDescAux(m: BModule, typ: PType, check: var TIntSet): PRope =
|
||||
# returns only the type's name
|
||||
var
|
||||
name, rettype, desc, recdesc: PRope
|
||||
n: BiggestInt
|
||||
t, et: PType
|
||||
t = getUniqueType(typ)
|
||||
if t == nil: internalError("getTypeDescAux: t == nil")
|
||||
if t.sym != nil: useHeader(m, t.sym)
|
||||
result = getTypePre(m, t)
|
||||
if result != nil: return
|
||||
if containsOrIncl(check, t.id):
|
||||
internalError("cannot generate C type for: " & typeToString(typ))
|
||||
# XXX: this BUG is hard to fix -> we need to introduce helper structs,
|
||||
# but determining when this needs to be done is hard. We should split
|
||||
# C type generation into an analysis and a code generation phase somehow.
|
||||
case t.kind
|
||||
of tyRef, tyPtr, tyVar:
|
||||
et = getUniqueType(t.lastSon)
|
||||
if et.kind in {tyArrayConstr, tyArray, tyOpenArray, tyVarargs}:
|
||||
# this is correct! sets have no proper base type, so we treat
|
||||
# ``var set[char]`` in `getParamTypeDesc`
|
||||
et = getUniqueType(elemType(et))
|
||||
case et.kind
|
||||
of tyObject, tyTuple:
|
||||
# no restriction! We have a forward declaration for structs
|
||||
name = getTypeForward(m, et)
|
||||
result = con(name, "*")
|
||||
idTablePut(m.typeCache, t, result)
|
||||
pushType(m, et)
|
||||
of tySequence:
|
||||
# no restriction! We have a forward declaration for structs
|
||||
name = getTypeForward(m, et)
|
||||
result = con(name, "**")
|
||||
idTablePut(m.typeCache, t, result)
|
||||
pushType(m, et)
|
||||
else:
|
||||
# else we have a strong dependency :-(
|
||||
result = con(getTypeDescAux(m, et, check), "*")
|
||||
idTablePut(m.typeCache, t, result)
|
||||
of tyOpenArray, tyVarargs:
|
||||
et = getUniqueType(t.sons[0])
|
||||
result = con(getTypeDescAux(m, et, check), "*")
|
||||
idTablePut(m.typeCache, t, result)
|
||||
of tyProc:
|
||||
result = getTypeName(t)
|
||||
idTablePut(m.typeCache, t, result)
|
||||
genProcParams(m, t, rettype, desc, check)
|
||||
if not isImportedType(t):
|
||||
if t.callConv != ccClosure: # procedure vars may need a closure!
|
||||
appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
|
||||
[toRope(CallingConvToStr[t.callConv]), rettype, result, desc])
|
||||
else:
|
||||
appf(m.s[cfsTypes], "typedef struct {$n" &
|
||||
"N_NIMCALL_PTR($2, ClPrc) $3;$n" &
|
||||
"void* ClEnv;$n} $1;$n",
|
||||
[result, rettype, desc])
|
||||
of tySequence:
|
||||
# we cannot use getTypeForward here because then t would be associated
|
||||
# with the name of the struct, not with the pointer to the struct:
|
||||
result = cacheGetType(m.forwTypeCache, t)
|
||||
if result == nil:
|
||||
result = getTypeName(t)
|
||||
if not isImportedType(t):
|
||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||
[structOrUnion(t), result])
|
||||
idTablePut(m.forwTypeCache, t, result)
|
||||
assert(cacheGetType(m.typeCache, t) == nil)
|
||||
idTablePut(m.typeCache, t, con(result, "*"))
|
||||
if not isImportedType(t):
|
||||
if skipTypes(t.sons[0], typedescInst).kind != tyEmpty:
|
||||
const
|
||||
cppSeq = "struct $2 : #TGenericSeq {$n"
|
||||
cSeq = "struct $2 {$n" &
|
||||
" #TGenericSeq Sup;$n"
|
||||
appcg(m, m.s[cfsSeqTypes],
|
||||
(if gCmd == cmdCompileToCpp: cppSeq else: cSeq) &
|
||||
" $1 data[SEQ_DECL_SIZE];$n" &
|
||||
"};$n", [getTypeDescAux(m, t.sons[0], check), result])
|
||||
else:
|
||||
result = toRope("TGenericSeq")
|
||||
app(result, "*")
|
||||
of tyArrayConstr, tyArray:
|
||||
n = lengthOrd(t)
|
||||
if n <= 0:
|
||||
n = 1 # make an array of at least one element
|
||||
result = getTypeName(t)
|
||||
idTablePut(m.typeCache, t, result)
|
||||
if not isImportedType(t):
|
||||
appf(m.s[cfsTypes], "typedef $1 $2[$3];$n",
|
||||
[getTypeDescAux(m, t.sons[1], check), result, toRope(n)])
|
||||
of tyObject, tyTuple:
|
||||
result = cacheGetType(m.forwTypeCache, t)
|
||||
if result == nil:
|
||||
result = getTypeName(t)
|
||||
if not isImportedType(t):
|
||||
appf(m.s[cfsForwardTypes], getForwardStructFormat(),
|
||||
[structOrUnion(t), result])
|
||||
idTablePut(m.forwTypeCache, t, result)
|
||||
idTablePut(m.typeCache, t, result) # always call for sideeffects:
|
||||
if t.kind != tyTuple: recdesc = getRecordDesc(m, t, result, check)
|
||||
else: recdesc = getTupleDesc(m, t, result, check)
|
||||
if not isImportedType(t): app(m.s[cfsTypes], recdesc)
|
||||
of tySet:
|
||||
case int(getSize(t))
|
||||
of 1: result = toRope("NU8")
|
||||
of 2: result = toRope("NU16")
|
||||
of 4: result = toRope("NU32")
|
||||
of 8: result = toRope("NU64")
|
||||
else:
|
||||
result = getTypeName(t)
|
||||
idTablePut(m.typeCache, t, result)
|
||||
if not isImportedType(t):
|
||||
appf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
|
||||
[result, toRope(getSize(t))])
|
||||
of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable,
|
||||
tyIter, tyTypeDesc:
|
||||
result = getTypeDescAux(m, lastSon(t), check)
|
||||
else:
|
||||
internalError("getTypeDescAux(" & $t.kind & ')')
|
||||
result = nil
|
||||
# fixes bug #145:
|
||||
excl(check, t.id)
|
||||
|
||||
proc getTypeDesc(m: BModule, typ: PType): PRope =
|
||||
var check = initIntSet()
|
||||
result = getTypeDescAux(m, typ, check)
|
||||
|
||||
type
|
||||
TClosureTypeKind = enum
|
||||
clHalf, clHalfWithEnv, clFull
|
||||
|
||||
proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): PRope =
|
||||
assert t.kind == tyProc
|
||||
var check = initIntSet()
|
||||
result = getTempName()
|
||||
var rettype, desc: PRope
|
||||
genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
|
||||
if not isImportedType(t):
|
||||
if t.callConv != ccClosure or kind != clFull:
|
||||
appf(m.s[cfsTypes], "typedef $1_PTR($2, $3) $4;$n",
|
||||
[toRope(CallingConvToStr[t.callConv]), rettype, result, desc])
|
||||
else:
|
||||
appf(m.s[cfsTypes], "typedef struct {$n" &
|
||||
"N_NIMCALL_PTR($2, ClPrc) $3;$n" &
|
||||
"void* ClEnv;$n} $1;$n",
|
||||
[result, rettype, desc])
|
||||
|
||||
proc getTypeDesc(m: BModule, magic: string): PRope =
|
||||
var sym = magicsys.getCompilerProc(magic)
|
||||
if sym != nil:
|
||||
result = getTypeDesc(m, sym.typ)
|
||||
else:
|
||||
rawMessage(errSystemNeeds, magic)
|
||||
result = nil
|
||||
|
||||
proc finishTypeDescriptions(m: BModule) =
|
||||
var i = 0
|
||||
while i < len(m.typeStack):
|
||||
discard getTypeDesc(m, m.typeStack[i])
|
||||
inc(i)
|
||||
|
||||
template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
|
||||
|
||||
proc genProcHeader(m: BModule, prc: PSym): PRope =
|
||||
var
|
||||
rettype, params: PRope
|
||||
genCLineDir(result, prc.info)
|
||||
# using static is needed for inline procs
|
||||
if gCmd != cmdCompileToLLVM and lfExportLib in prc.loc.flags:
|
||||
if m.isHeaderFile:
|
||||
result.app "N_LIB_IMPORT "
|
||||
else:
|
||||
result.app "N_LIB_EXPORT "
|
||||
elif prc.typ.callConv == ccInline:
|
||||
result.app "static "
|
||||
var check = initIntSet()
|
||||
fillLoc(prc.loc, locProc, prc.typ, mangleName(prc), OnUnknown)
|
||||
genProcParams(m, prc.typ, rettype, params, check)
|
||||
# careful here! don't access ``prc.ast`` as that could reload large parts of
|
||||
# the object graph!
|
||||
if prc.constraint.isNil:
|
||||
appf(result, "$1($2, $3)$4",
|
||||
[toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r,
|
||||
params])
|
||||
else:
|
||||
result = ropef(prc.cgDeclFrmt, [rettype, prc.loc.r, params])
|
||||
|
||||
# ------------------ type info generation -------------------------------------
|
||||
|
||||
proc genTypeInfo(m: BModule, t: PType): PRope
|
||||
proc getNimNode(m: BModule): PRope =
|
||||
result = ropef("$1[$2]", [m.typeNodesName, toRope(m.typeNodes)])
|
||||
inc(m.typeNodes)
|
||||
|
||||
when false:
|
||||
proc getNimType(m: BModule): PRope =
|
||||
result = ropef("$1[$2]", [m.nimTypesName, toRope(m.nimTypes)])
|
||||
inc(m.nimTypes)
|
||||
|
||||
proc allocMemTI(m: BModule, typ: PType, name: PRope) =
|
||||
var tmp = getNimType(m)
|
||||
appf(m.s[cfsTypeInit2], "$2 = &$1;$n", [tmp, name])
|
||||
|
||||
proc isObjLackingTypeField(typ: PType): bool {.inline.} =
|
||||
result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
|
||||
(typ.sons[0] == nil) or isPureObject(typ))
|
||||
|
||||
proc genTypeInfoAuxBase(m: BModule, typ: PType, name, base: PRope) =
|
||||
var nimtypeKind: int
|
||||
#allocMemTI(m, typ, name)
|
||||
if isObjLackingTypeField(typ):
|
||||
nimtypeKind = ord(tyPureObject)
|
||||
else:
|
||||
nimtypeKind = ord(typ.kind)
|
||||
|
||||
var size: PRope
|
||||
if tfIncompleteStruct in typ.flags: size = toRope"void*"
|
||||
else: size = getTypeDesc(m, typ)
|
||||
appf(m.s[cfsTypeInit3],
|
||||
"$1.size = sizeof($2);$n" & "$1.kind = $3;$n" & "$1.base = $4;$n",
|
||||
[name, size, toRope(nimtypeKind), base])
|
||||
# compute type flags for GC optimization
|
||||
var flags = 0
|
||||
if not containsGarbageCollectedRef(typ): flags = flags or 1
|
||||
if not canFormAcycle(typ): flags = flags or 2
|
||||
#else MessageOut("can contain a cycle: " & typeToString(typ))
|
||||
if flags != 0:
|
||||
appf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, toRope(flags)])
|
||||
discard cgsym(m, "TNimType")
|
||||
appf(m.s[cfsVars], "TNimType $1; /* $2 */$n",
|
||||
[name, toRope(typeToString(typ))])
|
||||
|
||||
proc genTypeInfoAux(m: BModule, typ: PType, name: PRope) =
|
||||
var base: PRope
|
||||
if (sonsLen(typ) > 0) and (typ.sons[0] != nil):
|
||||
base = genTypeInfo(m, typ.sons[0])
|
||||
else:
|
||||
base = toRope("0")
|
||||
genTypeInfoAuxBase(m, typ, name, base)
|
||||
|
||||
proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): PRope =
|
||||
# bugfix: we need to search the type that contains the discriminator:
|
||||
var objtype = objtype
|
||||
while lookupInRecord(objtype.n, d.name) == nil:
|
||||
objtype = objtype.sons[0]
|
||||
if objtype.sym == nil:
|
||||
internalError(d.info, "anonymous obj with discriminator")
|
||||
result = ropef("NimDT_$1_$2", [
|
||||
toRope(objtype.sym.name.s.mangle), toRope(d.name.s.mangle)])
|
||||
|
||||
proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): PRope =
|
||||
discard cgsym(m, "TNimNode")
|
||||
var tmp = discriminatorTableName(m, objtype, d)
|
||||
result = ropef("TNimNode* $1[$2];$n", [tmp, toRope(lengthOrd(d.typ)+1)])
|
||||
|
||||
proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: PRope) =
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
var L = sonsLen(n)
|
||||
if L == 1:
|
||||
genObjectFields(m, typ, n.sons[0], expr)
|
||||
elif L > 0:
|
||||
var tmp = getTempName()
|
||||
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, toRope(L)])
|
||||
for i in countup(0, L-1):
|
||||
var tmp2 = getNimNode(m)
|
||||
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2])
|
||||
genObjectFields(m, typ, n.sons[i], tmp2)
|
||||
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
|
||||
[expr, toRope(L), tmp])
|
||||
else:
|
||||
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n", [expr, toRope(L)])
|
||||
of nkRecCase:
|
||||
assert(n.sons[0].kind == nkSym)
|
||||
var field = n.sons[0].sym
|
||||
var tmp = discriminatorTableName(m, typ, field)
|
||||
var L = lengthOrd(field.typ)
|
||||
assert L > 0
|
||||
appf(m.s[cfsTypeInit3], "$1.kind = 3;$n" &
|
||||
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
|
||||
"$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
|
||||
"$1.len = $7;$n", [expr, getTypeDesc(m, typ), field.loc.r,
|
||||
genTypeInfo(m, field.typ),
|
||||
makeCString(field.name.s),
|
||||
tmp, toRope(L)])
|
||||
appf(m.s[cfsData], "TNimNode* $1[$2];$n", [tmp, toRope(L+1)])
|
||||
for i in countup(1, sonsLen(n)-1):
|
||||
var b = n.sons[i] # branch
|
||||
var tmp2 = getNimNode(m)
|
||||
genObjectFields(m, typ, lastSon(b), tmp2)
|
||||
case b.kind
|
||||
of nkOfBranch:
|
||||
if sonsLen(b) < 2:
|
||||
internalError(b.info, "genObjectFields; nkOfBranch broken")
|
||||
for j in countup(0, sonsLen(b) - 2):
|
||||
if b.sons[j].kind == nkRange:
|
||||
var x = int(getOrdValue(b.sons[j].sons[0]))
|
||||
var y = int(getOrdValue(b.sons[j].sons[1]))
|
||||
while x <= y:
|
||||
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(x), tmp2])
|
||||
inc(x)
|
||||
else:
|
||||
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
|
||||
[tmp, toRope(getOrdValue(b.sons[j])), tmp2])
|
||||
of nkElse:
|
||||
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
|
||||
[tmp, toRope(L), tmp2])
|
||||
else: internalError(n.info, "genObjectFields(nkRecCase)")
|
||||
of nkSym:
|
||||
var field = n.sym
|
||||
appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
|
||||
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
|
||||
"$1.name = $5;$n", [expr, getTypeDesc(m, typ),
|
||||
field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)])
|
||||
else: internalError(n.info, "genObjectFields")
|
||||
|
||||
proc genObjectInfo(m: BModule, typ: PType, name: PRope) =
|
||||
if typ.kind == tyObject: genTypeInfoAux(m, typ, name)
|
||||
else: genTypeInfoAuxBase(m, typ, name, toRope("0"))
|
||||
var tmp = getNimNode(m)
|
||||
genObjectFields(m, typ, typ.n, tmp)
|
||||
appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, tmp])
|
||||
var t = typ.sons[0]
|
||||
while t != nil:
|
||||
t = t.skipTypes(abstractInst)
|
||||
t.flags.incl tfObjHasKids
|
||||
t = t.sons[0]
|
||||
|
||||
proc genTupleInfo(m: BModule, typ: PType, name: PRope) =
|
||||
genTypeInfoAuxBase(m, typ, name, toRope("0"))
|
||||
var expr = getNimNode(m)
|
||||
var length = sonsLen(typ)
|
||||
if length > 0:
|
||||
var tmp = getTempName()
|
||||
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n", [tmp, toRope(length)])
|
||||
for i in countup(0, length - 1):
|
||||
var a = typ.sons[i]
|
||||
var tmp2 = getNimNode(m)
|
||||
appf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n", [tmp, toRope(i), tmp2])
|
||||
appf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
|
||||
"$1.offset = offsetof($2, Field$3);$n" &
|
||||
"$1.typ = $4;$n" &
|
||||
"$1.name = \"Field$3\";$n",
|
||||
[tmp2, getTypeDesc(m, typ), toRope(i), genTypeInfo(m, a)])
|
||||
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
|
||||
[expr, toRope(length), tmp])
|
||||
else:
|
||||
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 2;$n",
|
||||
[expr, toRope(length)])
|
||||
appf(m.s[cfsTypeInit3], "$1.node = &$2;$n", [name, expr])
|
||||
|
||||
proc genEnumInfo(m: BModule, typ: PType, name: PRope) =
|
||||
# Type information for enumerations is quite heavy, so we do some
|
||||
# optimizations here: The ``typ`` field is never set, as it is redundant
|
||||
# anyway. We generate a cstring array and a loop over it. Exceptional
|
||||
# positions will be reset after the loop.
|
||||
genTypeInfoAux(m, typ, name)
|
||||
var nodePtrs = getTempName()
|
||||
var length = sonsLen(typ.n)
|
||||
appf(m.s[cfsTypeInit1], "static TNimNode* $1[$2];$n",
|
||||
[nodePtrs, toRope(length)])
|
||||
var enumNames, specialCases: PRope
|
||||
var firstNimNode = m.typeNodes
|
||||
var hasHoles = false
|
||||
for i in countup(0, length - 1):
|
||||
assert(typ.n.sons[i].kind == nkSym)
|
||||
var field = typ.n.sons[i].sym
|
||||
var elemNode = getNimNode(m)
|
||||
if field.ast == nil:
|
||||
# no explicit string literal for the enum field, so use field.name:
|
||||
app(enumNames, makeCString(field.name.s))
|
||||
else:
|
||||
app(enumNames, makeCString(field.ast.strVal))
|
||||
if i < length - 1: app(enumNames, ", " & tnl)
|
||||
if field.position != i or tfEnumHasHoles in typ.flags:
|
||||
appf(specialCases, "$1.offset = $2;$n", [elemNode, toRope(field.position)])
|
||||
hasHoles = true
|
||||
var enumArray = getTempName()
|
||||
var counter = getTempName()
|
||||
appf(m.s[cfsTypeInit1], "NI $1;$n", [counter])
|
||||
appf(m.s[cfsTypeInit1], "static char* NIM_CONST $1[$2] = {$n$3};$n",
|
||||
[enumArray, toRope(length), enumNames])
|
||||
appf(m.s[cfsTypeInit3], "for ($1 = 0; $1 < $2; $1++) {$n" &
|
||||
"$3[$1+$4].kind = 1;$n" & "$3[$1+$4].offset = $1;$n" &
|
||||
"$3[$1+$4].name = $5[$1];$n" & "$6[$1] = &$3[$1+$4];$n" & "}$n", [counter,
|
||||
toRope(length), m.typeNodesName, toRope(firstNimNode), enumArray, nodePtrs])
|
||||
app(m.s[cfsTypeInit3], specialCases)
|
||||
appf(m.s[cfsTypeInit3],
|
||||
"$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n$4.node = &$1;$n",
|
||||
[getNimNode(m), toRope(length), nodePtrs, name])
|
||||
if hasHoles:
|
||||
# 1 << 2 is {ntfEnumHole}
|
||||
appf(m.s[cfsTypeInit3], "$1.flags = 1<<2;$n", [name])
|
||||
|
||||
proc genSetInfo(m: BModule, typ: PType, name: PRope) =
|
||||
assert(typ.sons[0] != nil)
|
||||
genTypeInfoAux(m, typ, name)
|
||||
var tmp = getNimNode(m)
|
||||
appf(m.s[cfsTypeInit3], "$1.len = $2; $1.kind = 0;$n" & "$3.node = &$1;$n",
|
||||
[tmp, toRope(firstOrd(typ)), name])
|
||||
|
||||
proc genArrayInfo(m: BModule, typ: PType, name: PRope) =
|
||||
genTypeInfoAuxBase(m, typ, name, genTypeInfo(m, typ.sons[1]))
|
||||
|
||||
proc fakeClosureType(owner: PSym): PType =
|
||||
# we generate the same RTTI as for a tuple[pointer, ref tuple[]]
|
||||
result = newType(tyTuple, owner)
|
||||
result.rawAddSon(newType(tyPointer, owner))
|
||||
var r = newType(tyRef, owner)
|
||||
r.rawAddSon(newType(tyTuple, owner))
|
||||
result.rawAddSon(r)
|
||||
|
||||
type
|
||||
TTypeInfoReason = enum ## for what do we need the type info?
|
||||
tiNew, ## for 'new'
|
||||
tiNewSeq, ## for 'newSeq'
|
||||
tiNonVariantAsgn, ## for generic assignment without variants
|
||||
tiVariantAsgn ## for generic assignment with variants
|
||||
|
||||
include ccgtrav
|
||||
|
||||
proc genTypeInfo(m: BModule, t: PType): PRope =
|
||||
var t = getUniqueType(t)
|
||||
result = ropef("NTI$1", [toRope(t.id)])
|
||||
if containsOrIncl(m.typeInfoMarker, t.id):
|
||||
return con("(&".toRope, result, ")".toRope)
|
||||
let owner = t.skipTypes(typedescPtrs).owner.getModule
|
||||
if owner != m.module:
|
||||
# make sure the type info is created in the owner module
|
||||
discard genTypeInfo(owner.bmod, t)
|
||||
# reference the type info as extern here
|
||||
discard cgsym(m, "TNimType")
|
||||
discard cgsym(m, "TNimNode")
|
||||
appf(m.s[cfsVars], "extern TNimType $1; /* $2 */$n",
|
||||
[result, toRope(typeToString(t))])
|
||||
return con("(&".toRope, result, ")".toRope)
|
||||
case t.kind
|
||||
of tyEmpty: result = toRope"0"
|
||||
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:
|
||||
genTypeInfoAuxBase(m, t, result, toRope"0")
|
||||
of tyProc:
|
||||
if t.callConv != ccClosure:
|
||||
genTypeInfoAuxBase(m, t, result, toRope"0")
|
||||
else:
|
||||
genTupleInfo(m, fakeClosureType(t.owner), result)
|
||||
of tySequence, tyRef:
|
||||
genTypeInfoAux(m, t, result)
|
||||
if gSelectedGC >= gcMarkAndSweep:
|
||||
let markerProc = genTraverseProc(m, t, tiNew)
|
||||
appf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc])
|
||||
of tyPtr, tyRange: genTypeInfoAux(m, t, result)
|
||||
of tyArrayConstr, tyArray: genArrayInfo(m, t, result)
|
||||
of tySet: genSetInfo(m, t, result)
|
||||
of tyEnum: genEnumInfo(m, t, result)
|
||||
of tyObject: genObjectInfo(m, t, result)
|
||||
of tyTuple:
|
||||
# if t.n != nil: genObjectInfo(m, t, result)
|
||||
# else:
|
||||
# BUGFIX: use consistently RTTI without proper field names; otherwise
|
||||
# results are not deterministic!
|
||||
genTupleInfo(m, t, result)
|
||||
else: internalError("genTypeInfo(" & $t.kind & ')')
|
||||
result = con("(&".toRope, result, ")".toRope)
|
||||
|
||||
proc genTypeSection(m: BModule, n: PNode) =
|
||||
discard
|
||||
204
compiler/cgen/ccgutils.nim
Normal file
204
compiler/cgen/ccgutils.nim
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# This module declares some helpers for the C code generator.
|
||||
|
||||
import
|
||||
ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
|
||||
platform, trees
|
||||
|
||||
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
||||
case n.kind
|
||||
of nkStmtList:
|
||||
for i in 0 .. < n.len:
|
||||
result = getPragmaStmt(n[i], w)
|
||||
if result != nil: break
|
||||
of nkPragma:
|
||||
for i in 0 .. < n.len:
|
||||
if whichPragma(n[i]) == w: return n[i]
|
||||
else: discard
|
||||
|
||||
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
|
||||
result = getPragmaStmt(n, w) != nil
|
||||
|
||||
proc hashString*(s: string): BiggestInt =
|
||||
# has to be the same algorithm as system.hashString!
|
||||
if CPU[targetCPU].bit == 64:
|
||||
# we have to use the same bitwidth
|
||||
# as the target CPU
|
||||
var b = 0'i64
|
||||
for i in countup(0, len(s) - 1):
|
||||
b = b +% ord(s[i])
|
||||
b = b +% `shl`(b, 10)
|
||||
b = b xor `shr`(b, 6)
|
||||
b = b +% `shl`(b, 3)
|
||||
b = b xor `shr`(b, 11)
|
||||
b = b +% `shl`(b, 15)
|
||||
result = b
|
||||
else:
|
||||
var a = 0'i32
|
||||
for i in countup(0, len(s) - 1):
|
||||
a = a +% ord(s[i]).int32
|
||||
a = a +% `shl`(a, 10'i32)
|
||||
a = a xor `shr`(a, 6'i32)
|
||||
a = a +% `shl`(a, 3'i32)
|
||||
a = a xor `shr`(a, 11'i32)
|
||||
a = a +% `shl`(a, 15'i32)
|
||||
result = a
|
||||
|
||||
var
|
||||
gTypeTable: array[TTypeKind, TIdTable]
|
||||
gCanonicalTypes: array[TTypeKind, PType]
|
||||
|
||||
proc initTypeTables() =
|
||||
for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i])
|
||||
|
||||
proc resetCaches* =
|
||||
## XXX: fix that more properly
|
||||
initTypeTables()
|
||||
for i in low(gCanonicalTypes)..high(gCanonicalTypes):
|
||||
gCanonicalTypes[i] = nil
|
||||
|
||||
when false:
|
||||
proc echoStats*() =
|
||||
for i in countup(low(TTypeKind), high(TTypeKind)):
|
||||
echo i, " ", gTypeTable[i].counter
|
||||
|
||||
proc getUniqueType*(key: PType): PType =
|
||||
# this is a hotspot in the compiler!
|
||||
if key == nil: return
|
||||
var k = key.kind
|
||||
case k
|
||||
of tyBool, tyChar,
|
||||
tyInt..tyUInt64:
|
||||
# no canonicalization for integral types, so that e.g. ``pid_t`` is
|
||||
# produced instead of ``NI``.
|
||||
result = key
|
||||
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
|
||||
tyCString, tyNone, tyBigNum:
|
||||
result = gCanonicalTypes[k]
|
||||
if result == nil:
|
||||
gCanonicalTypes[k] = key
|
||||
result = key
|
||||
of tyTypeDesc, tyTypeClasses, tyGenericParam,
|
||||
tyFromExpr, tyFieldAccessor:
|
||||
internalError("GetUniqueType")
|
||||
of tyGenericInst, tyDistinct, tyOrdinal, tyMutable,
|
||||
tyConst, tyIter, tyStatic:
|
||||
result = getUniqueType(lastSon(key))
|
||||
of tyArrayConstr, tyGenericInvokation, tyGenericBody,
|
||||
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
|
||||
tyPtr, tyRef, tySequence, tyForward, tyVarargs, tyProxy, tyVar:
|
||||
# tuples are quite horrible as C does not support them directly and
|
||||
# tuple[string, string] is a (strange) subtype of
|
||||
# tuple[nameA, nameB: string]. This bites us here, so we
|
||||
# use 'sameBackendType' instead of 'sameType'.
|
||||
|
||||
# we have to do a slow linear search because types may need
|
||||
# to be compared by their structure:
|
||||
if idTableHasObjectAsKey(gTypeTable[k], key): return key
|
||||
for h in countup(0, high(gTypeTable[k].data)):
|
||||
var t = PType(gTypeTable[k].data[h].key)
|
||||
if t != nil and sameBackendType(t, key):
|
||||
return t
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
of tyObject:
|
||||
if tfFromGeneric notin key.flags:
|
||||
# fast case; lookup per id suffices:
|
||||
result = PType(idTableGet(gTypeTable[k], key))
|
||||
if result == nil:
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
else:
|
||||
# ugly slow case: need to compare by structure
|
||||
if idTableHasObjectAsKey(gTypeTable[k], key): return key
|
||||
for h in countup(0, high(gTypeTable[k].data)):
|
||||
var t = PType(gTypeTable[k].data[h].key)
|
||||
if t != nil and sameType(t, key):
|
||||
return t
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
of tyEnum:
|
||||
result = PType(idTableGet(gTypeTable[k], key))
|
||||
if result == nil:
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
of tyProc:
|
||||
if key.callConv != ccClosure:
|
||||
result = key
|
||||
else:
|
||||
# ugh, we need the canon here:
|
||||
if idTableHasObjectAsKey(gTypeTable[k], key): return key
|
||||
for h in countup(0, high(gTypeTable[k].data)):
|
||||
var t = PType(gTypeTable[k].data[h].key)
|
||||
if t != nil and sameBackendType(t, key):
|
||||
return t
|
||||
idTablePut(gTypeTable[k], key, key)
|
||||
result = key
|
||||
|
||||
proc tableGetType*(tab: TIdTable, key: PType): PObject =
|
||||
# returns nil if we need to declare this type
|
||||
result = idTableGet(tab, key)
|
||||
if (result == nil) and (tab.counter > 0):
|
||||
# we have to do a slow linear search because types may need
|
||||
# to be compared by their structure:
|
||||
for h in countup(0, high(tab.data)):
|
||||
var t = PType(tab.data[h].key)
|
||||
if t != nil:
|
||||
if sameType(t, key):
|
||||
return tab.data[h].val
|
||||
|
||||
proc makeSingleLineCString*(s: string): string =
|
||||
result = "\""
|
||||
for c in items(s):
|
||||
result.add(c.toCChar)
|
||||
result.add('\"')
|
||||
|
||||
proc mangle*(name: string): string =
|
||||
## Lowercases the given name and manges any non-alphanumeric characters
|
||||
## so they are represented as `HEX____`. If the name starts with a number,
|
||||
## `N` is prepended
|
||||
result = ""
|
||||
case name[0]
|
||||
of Letters:
|
||||
result.add(name[0].toLower)
|
||||
of Digits:
|
||||
result.add("N" & name[0])
|
||||
else:
|
||||
result = "HEX" & toHex(ord(name[0]), 2)
|
||||
for i in 1..(name.len-1):
|
||||
let c = name[i]
|
||||
case c
|
||||
of 'A'..'Z':
|
||||
add(result, c.toLower)
|
||||
of '_':
|
||||
discard
|
||||
of 'a'..'z', '0'..'9':
|
||||
add(result, c)
|
||||
else:
|
||||
add(result, "HEX" & toHex(ord(c), 2))
|
||||
|
||||
proc makeLLVMString*(s: string): PRope =
|
||||
const MaxLineLength = 64
|
||||
result = nil
|
||||
var res = "c\""
|
||||
for i in countup(0, len(s) - 1):
|
||||
if (i + 1) mod MaxLineLength == 0:
|
||||
app(result, toRope(res))
|
||||
setLen(res, 0)
|
||||
case s[i]
|
||||
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
|
||||
add(res, '\\')
|
||||
add(res, toHex(ord(s[i]), 2))
|
||||
else: add(res, s[i])
|
||||
add(res, "\\00\"")
|
||||
app(result, toRope(res))
|
||||
|
||||
initTypeTables()
|
||||
1411
compiler/cgen/cgen.nim
Normal file
1411
compiler/cgen/cgen.nim
Normal file
File diff suppressed because it is too large
Load diff
156
compiler/cgen/cgendata.nim
Normal file
156
compiler/cgen/cgendata.nim
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module contains the data structures for the C code generation phase.
|
||||
|
||||
import
|
||||
ast, astalgo, ropes, passes, options, intsets, lists, platform
|
||||
|
||||
type
|
||||
TLabel* = PRope # for the C generator a label is just a rope
|
||||
TCFileSection* = enum # the sections a generated C file consists of
|
||||
cfsMergeInfo, # section containing merge information
|
||||
cfsHeaders, # section for C include file headers
|
||||
cfsForwardTypes, # section for C forward typedefs
|
||||
cfsTypes, # section for C typedefs
|
||||
cfsSeqTypes, # section for sequence types only
|
||||
# this is needed for strange type generation
|
||||
# reasons
|
||||
cfsFieldInfo, # section for field information
|
||||
cfsTypeInfo, # section for type information
|
||||
cfsProcHeaders, # section for C procs prototypes
|
||||
cfsData, # section for C constant data
|
||||
cfsVars, # section for C variable declarations
|
||||
cfsProcs, # section for C procs that are not inline
|
||||
cfsInitProc, # section for the C init proc
|
||||
cfsTypeInit1, # section 1 for declarations of type information
|
||||
cfsTypeInit2, # section 2 for init of type information
|
||||
cfsTypeInit3, # section 3 for init of type information
|
||||
cfsDebugInit, # section for init of debug information
|
||||
cfsDynLibInit, # section for init of dynamic library binding
|
||||
cfsDynLibDeinit # section for deinitialization of dynamic
|
||||
# libraries
|
||||
TCTypeKind* = enum # describes the type kind of a C type
|
||||
ctVoid, ctChar, ctBool,
|
||||
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
||||
ctFloat, ctFloat32, ctFloat64, ctFloat128,
|
||||
ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64,
|
||||
ctArray, ctPtrToArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc,
|
||||
ctCString
|
||||
TCFileSections* = array[TCFileSection, PRope] # represents a generated C file
|
||||
TCProcSection* = enum # the sections a generated C proc consists of
|
||||
cpsLocals, # section of local variables for C proc
|
||||
cpsInit, # section for init of variables for C proc
|
||||
cpsStmts # section of local statements for C proc
|
||||
TCProcSections* = array[TCProcSection, PRope] # represents a generated C proc
|
||||
BModule* = ref TCGen
|
||||
BProc* = ref TCProc
|
||||
TBlock*{.final.} = object
|
||||
id*: int # the ID of the label; positive means that it
|
||||
label*: PRope # generated text for the label
|
||||
# nil if label is not used
|
||||
sections*: TCProcSections # the code beloging
|
||||
isLoop*: bool # whether block is a loop
|
||||
nestedTryStmts*: int16 # how many try statements is it nested into
|
||||
nestedExceptStmts*: int16 # how many except statements is it nested into
|
||||
frameLen*: int16
|
||||
|
||||
TCProc{.final.} = object # represents C proc that is currently generated
|
||||
prc*: PSym # the Nimrod proc that this C proc belongs to
|
||||
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
||||
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
|
||||
# (the vars must be volatile then)
|
||||
inExceptBlock*: int # are we currently inside an except block?
|
||||
# leaving such scopes by raise or by return must
|
||||
# execute any applicable finally blocks
|
||||
finallySafePoints*: seq[PRope] # For correctly cleaning up exceptions when
|
||||
# using return in finally statements
|
||||
labels*: Natural # for generating unique labels in the C proc
|
||||
blocks*: seq[TBlock] # nested blocks
|
||||
breakIdx*: int # the block that will be exited
|
||||
# with a regular break
|
||||
options*: TOptions # options that should be used for code
|
||||
# generation; this is the same as prc.options
|
||||
# unless prc == nil
|
||||
maxFrameLen*: int # max length of frame descriptor
|
||||
module*: BModule # used to prevent excessive parameter passing
|
||||
withinLoop*: int # > 0 if we are within a loop
|
||||
gcFrameId*: Natural # for the GC stack marking
|
||||
gcFrameType*: PRope # the struct {} we put the GC markers into
|
||||
|
||||
TTypeSeq* = seq[PType]
|
||||
TCGen = object of TPassContext # represents a C source file
|
||||
module*: PSym
|
||||
filename*: string
|
||||
s*: TCFileSections # sections of the C file
|
||||
preventStackTrace*: bool # true if stack traces need to be prevented
|
||||
usesThreadVars*: bool # true if the module uses a thread var
|
||||
frameDeclared*: bool # hack for ROD support so that we don't declare
|
||||
# a frame var twice in an init proc
|
||||
isHeaderFile*: bool # C source file is the header file
|
||||
includesStringh*: bool # C source file already includes ``<string.h>``
|
||||
objHasKidsValid*: bool # whether we can rely on tfObjHasKids
|
||||
cfilename*: string # filename of the module (including path,
|
||||
# without extension)
|
||||
typeCache*: TIdTable # cache the generated types
|
||||
forwTypeCache*: TIdTable # cache for forward declarations of types
|
||||
declaredThings*: TIntSet # things we have declared in this .c file
|
||||
declaredProtos*: TIntSet # prototypes we have declared in this .c file
|
||||
headerFiles*: TLinkedList # needed headers to include
|
||||
typeInfoMarker*: TIntSet # needed for generating type information
|
||||
initProc*: BProc # code for init procedure
|
||||
postInitProc*: BProc # code to be executed after the init proc
|
||||
preInitProc*: BProc # code executed before the init proc
|
||||
typeStack*: TTypeSeq # used for type generation
|
||||
dataCache*: TNodeTable
|
||||
forwardedProcs*: TSymSeq # keep forwarded procs here
|
||||
typeNodes*, nimTypes*: int # used for type info generation
|
||||
typeNodesName*, nimTypesName*: PRope # used for type info generation
|
||||
labels*: Natural # for generating unique module-scope names
|
||||
extensionLoaders*: array['0'..'9', PRope] # special procs for the
|
||||
# OpenGL wrapper
|
||||
injectStmt*: PRope
|
||||
|
||||
var
|
||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: PRope
|
||||
# varuious parts of the main module
|
||||
gMapping*: PRope # the generated mapping file (if requested)
|
||||
gModules*: seq[BModule] = @[] # list of all compiled modules
|
||||
gForwardedProcsCounter*: int = 0
|
||||
|
||||
proc s*(p: BProc, s: TCProcSection): var PRope {.inline.} =
|
||||
# section in the current block
|
||||
result = p.blocks[p.blocks.len - 1].sections[s]
|
||||
|
||||
proc procSec*(p: BProc, s: TCProcSection): var PRope {.inline.} =
|
||||
# top level proc sections
|
||||
result = p.blocks[0].sections[s]
|
||||
|
||||
proc bmod*(module: PSym): BModule =
|
||||
# obtains the BModule for a given module PSym
|
||||
result = gModules[module.position]
|
||||
|
||||
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
new(result)
|
||||
result.prc = prc
|
||||
result.module = module
|
||||
if prc != nil: result.options = prc.options
|
||||
else: result.options = gOptions
|
||||
newSeq(result.blocks, 1)
|
||||
result.nestedTryStmts = @[]
|
||||
result.finallySafePoints = @[]
|
||||
|
||||
iterator cgenModules*: BModule =
|
||||
for i in 0..high(gModules):
|
||||
# ultimately, we are iterating over the file ids here.
|
||||
# some "files" won't have an associated cgen module (like stdin)
|
||||
# and we must skip over them.
|
||||
if gModules[i] != nil: yield gModules[i]
|
||||
|
||||
690
compiler/cgen/extccomp.nim
Normal file
690
compiler/cgen/extccomp.nim
Normal file
|
|
@ -0,0 +1,690 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2013 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
# module for calling the different external C compilers
|
||||
# some things are read in from the configuration file
|
||||
|
||||
import
|
||||
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
|
||||
|
||||
type
|
||||
TSystemCC* = enum
|
||||
ccNone, ccGcc, ccLLVM_Gcc, ccCLang, ccLcc, ccBcc, ccDmc, ccWcc, ccVcc,
|
||||
ccTcc, ccPcc, ccUcc, ccIcl
|
||||
TInfoCCProp* = enum # properties of the C compiler:
|
||||
hasSwitchRange, # CC allows ranges in switch statements (GNU C)
|
||||
hasComputedGoto, # CC has computed goto (GNU C extension)
|
||||
hasCpp, # CC is/contains a C++ compiler
|
||||
hasAssume, # CC has __assume (Visual C extension)
|
||||
hasGcGuard, # CC supports GC_GUARD to keep stack roots
|
||||
hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
|
||||
hasNakedDeclspec, # CC has __declspec(naked)
|
||||
hasNakedAttribute # CC has __attribute__((naked))
|
||||
TInfoCCProps* = set[TInfoCCProp]
|
||||
TInfoCC* = tuple[
|
||||
name: string, # the short name of the compiler
|
||||
objExt: string, # the compiler's object file extenstion
|
||||
optSpeed: string, # the options for optimization for speed
|
||||
optSize: string, # the options for optimization for size
|
||||
compilerExe: string, # the compiler's executable
|
||||
cppCompiler: string, # name of the C++ compiler's executable (if supported)
|
||||
compileTmpl: string, # the compile command template
|
||||
buildGui: string, # command to build a GUI application
|
||||
buildDll: string, # command to build a shared library
|
||||
buildLib: string, # command to build a static library
|
||||
linkerExe: string, # the linker's executable (if not matching compiler's)
|
||||
linkTmpl: string, # command to link files to produce an exe
|
||||
includeCmd: string, # command to add an include dir
|
||||
linkDirCmd: string, # command to add a lib dir
|
||||
linkLibCmd: string, # command to link an external library
|
||||
debug: string, # flags for debug build
|
||||
pic: string, # command for position independent code
|
||||
# used on some platforms
|
||||
asmStmtFrmt: string, # format of ASM statement
|
||||
structStmtFmt: string, # Format for struct statement
|
||||
packedPragma: string, # Attribute/pragma to make struct packed (1-byte aligned)
|
||||
props: TInfoCCProps] # properties of the C compiler
|
||||
|
||||
|
||||
# Configuration settings for various compilers.
|
||||
# When adding new compilers, the cmake sources could be a good reference:
|
||||
# http://cmake.org/gitweb?p=cmake.git;a=tree;f=Modules/Platform;
|
||||
|
||||
template compiler(name: expr, settings: stmt): stmt {.immediate.} =
|
||||
proc name: TInfoCC {.compileTime.} = settings
|
||||
|
||||
compiler gcc:
|
||||
result = (
|
||||
name: "gcc",
|
||||
objExt: "o",
|
||||
optSpeed: " -O3 -ffast-math ",
|
||||
optSize: " -Os -ffast-math ",
|
||||
compilerExe: "gcc",
|
||||
cppCompiler: "g++",
|
||||
compileTmpl: "-c $options $include -o $objfile $file",
|
||||
buildGui: " -mwindows",
|
||||
buildDll: " -shared",
|
||||
buildLib: "ar rcs $libfile $objfiles",
|
||||
linkerExe: "",
|
||||
linkTmpl: "$buildgui $builddll -o $exefile $objfiles $options",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: " -L",
|
||||
linkLibCmd: " -l$1",
|
||||
debug: "",
|
||||
pic: "-fPIC",
|
||||
asmStmtFrmt: "asm($1);$n",
|
||||
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
||||
packedPragma: "__attribute__((__packed__))",
|
||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||
hasNakedAttribute})
|
||||
|
||||
compiler llvmGcc:
|
||||
result = gcc()
|
||||
|
||||
result.name = "llvm_gcc"
|
||||
result.compilerExe = "llvm-gcc"
|
||||
result.cppCompiler = "llvm-g++"
|
||||
result.buildLib = "llvm-ar rcs $libfile $objfiles"
|
||||
|
||||
compiler clang:
|
||||
result = llvmGcc()
|
||||
|
||||
result.name = "clang"
|
||||
result.compilerExe = "clang"
|
||||
result.cppCompiler = "clang++"
|
||||
|
||||
compiler vcc:
|
||||
result = (
|
||||
name: "vcc",
|
||||
objExt: "obj",
|
||||
optSpeed: " /Ogityb2 /G7 /arch:SSE2 ",
|
||||
optSize: " /O1 /G7 ",
|
||||
compilerExe: "cl",
|
||||
cppCompiler: "cl",
|
||||
compileTmpl: "/c $options $include /Fo$objfile $file",
|
||||
buildGui: " /link /SUBSYSTEM:WINDOWS ",
|
||||
buildDll: " /LD",
|
||||
buildLib: "lib /OUT:$libfile $objfiles",
|
||||
linkerExe: "cl",
|
||||
linkTmpl: "$options $builddll /Fe$exefile $objfiles $buildgui",
|
||||
includeCmd: " /I",
|
||||
linkDirCmd: " /LIBPATH:",
|
||||
linkLibCmd: " $1.lib",
|
||||
debug: " /GZ /Zi ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||
structStmtFmt: "$3$n$1 $2",
|
||||
packedPragma: "#pragma pack(1)",
|
||||
props: {hasCpp, hasAssume, hasNakedDeclspec})
|
||||
|
||||
compiler icl:
|
||||
# Intel compilers try to imitate the native ones (gcc and msvc)
|
||||
when defined(windows):
|
||||
result = vcc()
|
||||
else:
|
||||
result = gcc()
|
||||
|
||||
result.name = "icl"
|
||||
result.compilerExe = "icl"
|
||||
result.linkerExe = "icl"
|
||||
|
||||
compiler lcc:
|
||||
result = (
|
||||
name: "lcc",
|
||||
objExt: "obj",
|
||||
optSpeed: " -O -p6 ",
|
||||
optSize: " -O -p6 ",
|
||||
compilerExe: "lcc",
|
||||
cppCompiler: "",
|
||||
compileTmpl: "$options $include -Fo$objfile $file",
|
||||
buildGui: " -subsystem windows",
|
||||
buildDll: " -dll",
|
||||
buildLib: "", # XXX: not supported yet
|
||||
linkerExe: "lcclnk",
|
||||
linkTmpl: "$options $buildgui $builddll -O $exefile $objfiles",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
debug: " -g5 ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "_asm{$n$1$n}$n",
|
||||
structStmtFmt: "$1 $2",
|
||||
packedPragma: "", # XXX: not supported yet
|
||||
props: {})
|
||||
|
||||
compiler bcc:
|
||||
result = (
|
||||
name: "bcc",
|
||||
objExt: "obj",
|
||||
optSpeed: " -O2 -6 ",
|
||||
optSize: " -O1 -6 ",
|
||||
compilerExe: "bcc32",
|
||||
cppCompiler: "",
|
||||
compileTmpl: "-c $options $include -o$objfile $file",
|
||||
buildGui: " -tW",
|
||||
buildDll: " -tWD",
|
||||
buildLib: "", # XXX: not supported yet
|
||||
linkerExe: "bcc32",
|
||||
linkTmpl: "$options $buildgui $builddll -e$exefile $objfiles",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
debug: "",
|
||||
pic: "",
|
||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||
structStmtFmt: "$1 $2",
|
||||
packedPragma: "", # XXX: not supported yet
|
||||
props: {hasCpp})
|
||||
|
||||
compiler dmc:
|
||||
result = (
|
||||
name: "dmc",
|
||||
objExt: "obj",
|
||||
optSpeed: " -ff -o -6 ",
|
||||
optSize: " -ff -o -6 ",
|
||||
compilerExe: "dmc",
|
||||
cppCompiler: "",
|
||||
compileTmpl: "-c $options $include -o$objfile $file",
|
||||
buildGui: " -L/exet:nt/su:windows",
|
||||
buildDll: " -WD",
|
||||
buildLib: "", # XXX: not supported yet
|
||||
linkerExe: "dmc",
|
||||
linkTmpl: "$options $buildgui $builddll -o$exefile $objfiles",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
debug: " -g ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||
structStmtFmt: "$3$n$1 $2",
|
||||
packedPragma: "#pragma pack(1)",
|
||||
props: {hasCpp})
|
||||
|
||||
compiler wcc:
|
||||
result = (
|
||||
name: "wcc",
|
||||
objExt: "obj",
|
||||
optSpeed: " -ox -on -6 -d0 -fp6 -zW ",
|
||||
optSize: "",
|
||||
compilerExe: "wcl386",
|
||||
cppCompiler: "",
|
||||
compileTmpl: "-c $options $include -fo=$objfile $file",
|
||||
buildGui: " -bw",
|
||||
buildDll: " -bd",
|
||||
buildLib: "", # XXX: not supported yet
|
||||
linkerExe: "wcl386",
|
||||
linkTmpl: "$options $buildgui $builddll -fe=$exefile $objfiles ",
|
||||
includeCmd: " -i=",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
debug: " -d2 ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||
structStmtFmt: "$1 $2",
|
||||
packedPragma: "", # XXX: not supported yet
|
||||
props: {hasCpp})
|
||||
|
||||
compiler tcc:
|
||||
result = (
|
||||
name: "tcc",
|
||||
objExt: "o",
|
||||
optSpeed: "",
|
||||
optSize: "",
|
||||
compilerExe: "tcc",
|
||||
cppCompiler: "",
|
||||
compileTmpl: "-c $options $include -o $objfile $file",
|
||||
buildGui: "UNAVAILABLE!",
|
||||
buildDll: " -shared",
|
||||
buildLib: "", # XXX: not supported yet
|
||||
linkerExe: "tcc",
|
||||
linkTmpl: "-o $exefile $options $buildgui $builddll $objfiles",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
debug: " -g ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||
structStmtFmt: "$1 $2",
|
||||
packedPragma: "", # XXX: not supported yet
|
||||
props: {hasSwitchRange, hasComputedGoto})
|
||||
|
||||
compiler pcc:
|
||||
# Pelles C
|
||||
result = (
|
||||
name: "pcc",
|
||||
objExt: "obj",
|
||||
optSpeed: " -Ox ",
|
||||
optSize: " -Os ",
|
||||
compilerExe: "cc",
|
||||
cppCompiler: "",
|
||||
compileTmpl: "-c $options $include -Fo$objfile $file",
|
||||
buildGui: " -SUBSYSTEM:WINDOWS",
|
||||
buildDll: " -DLL",
|
||||
buildLib: "", # XXX: not supported yet
|
||||
linkerExe: "cc",
|
||||
linkTmpl: "$options $buildgui $builddll -OUT:$exefile $objfiles",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
debug: " -Zi ",
|
||||
pic: "",
|
||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||
structStmtFmt: "$1 $2",
|
||||
packedPragma: "", # XXX: not supported yet
|
||||
props: {})
|
||||
|
||||
compiler ucc:
|
||||
result = (
|
||||
name: "ucc",
|
||||
objExt: "o",
|
||||
optSpeed: " -O3 ",
|
||||
optSize: " -O1 ",
|
||||
compilerExe: "cc",
|
||||
cppCompiler: "",
|
||||
compileTmpl: "-c $options $include -o $objfile $file",
|
||||
buildGui: "",
|
||||
buildDll: " -shared ",
|
||||
buildLib: "", # XXX: not supported yet
|
||||
linkerExe: "cc",
|
||||
linkTmpl: "-o $exefile $buildgui $builddll $objfiles $options",
|
||||
includeCmd: " -I",
|
||||
linkDirCmd: "", # XXX: not supported yet
|
||||
linkLibCmd: "", # XXX: not supported yet
|
||||
debug: "",
|
||||
pic: "",
|
||||
asmStmtFrmt: "__asm{$n$1$n}$n",
|
||||
structStmtFmt: "$1 $2",
|
||||
packedPragma: "", # XXX: not supported yet
|
||||
props: {})
|
||||
|
||||
const
|
||||
CC*: array[succ(low(TSystemCC))..high(TSystemCC), TInfoCC] = [
|
||||
gcc(),
|
||||
llvmGcc(),
|
||||
clang(),
|
||||
lcc(),
|
||||
bcc(),
|
||||
dmc(),
|
||||
wcc(),
|
||||
vcc(),
|
||||
tcc(),
|
||||
pcc(),
|
||||
ucc(),
|
||||
icl()]
|
||||
|
||||
const
|
||||
hExt* = ".h"
|
||||
|
||||
var
|
||||
cCompiler* = ccGcc # the used compiler
|
||||
|
||||
cExt* = ".c" # extension of generated C/C++ files
|
||||
# (can be changed to .cpp later)
|
||||
|
||||
cIncludes*: seq[string] = @[] # directories to search for included files
|
||||
cLibs*: seq[string] = @[] # directories to search for lib files
|
||||
cLinkedLibs*: seq[string] = @[] # libraries to link
|
||||
|
||||
# implementation
|
||||
|
||||
proc libNameTmpl(): string {.inline.} =
|
||||
result = if targetOS == osWindows: "$1.lib" else: "lib$1.a"
|
||||
|
||||
var
|
||||
toLink, toCompile, externalToCompile: TLinkedList
|
||||
linkOptions: string = ""
|
||||
compileOptions: string = ""
|
||||
ccompilerpath: string = ""
|
||||
|
||||
proc nameToCC*(name: string): TSystemCC =
|
||||
for i in countup(succ(ccNone), high(TSystemCC)):
|
||||
if cmpIgnoreStyle(name, CC[i].name) == 0:
|
||||
return i
|
||||
result = ccNone
|
||||
|
||||
proc getConfigVar(c: TSystemCC, suffix: string): string =
|
||||
# use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
|
||||
# for niminst support
|
||||
if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
|
||||
optCompileOnly notin gGlobalOptions:
|
||||
let fullCCname = platform.CPU[targetCPU].name & '.' &
|
||||
platform.OS[targetOS].name & '.' &
|
||||
CC[c].name & suffix
|
||||
result = getConfigVar(fullCCname)
|
||||
if result.len == 0:
|
||||
# not overriden for this cross compilation setting?
|
||||
result = getConfigVar(CC[c].name & suffix)
|
||||
else:
|
||||
result = getConfigVar(CC[c].name & suffix)
|
||||
|
||||
proc setCC*(ccname: string) =
|
||||
cCompiler = nameToCC(ccname)
|
||||
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
|
||||
compileOptions = getConfigVar(cCompiler, ".options.always")
|
||||
linkOptions = getConfigVar(cCompiler, ".options.linker")
|
||||
ccompilerpath = getConfigVar(cCompiler, ".path")
|
||||
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
|
||||
defineSymbol(CC[cCompiler].name)
|
||||
|
||||
proc addOpt(dest: var string, src: string) =
|
||||
if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
|
||||
add(dest, src)
|
||||
|
||||
proc addLinkOption*(option: string) =
|
||||
if find(linkOptions, option, 0) < 0: addOpt(linkOptions, option)
|
||||
|
||||
proc addCompileOption*(option: string) =
|
||||
if strutils.find(compileOptions, option, 0) < 0:
|
||||
addOpt(compileOptions, option)
|
||||
|
||||
proc initVars*() =
|
||||
# we need to define the symbol here, because ``CC`` may have never been set!
|
||||
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
|
||||
defineSymbol(CC[cCompiler].name)
|
||||
if gCmd == cmdCompileToCpp: cExt = ".cpp"
|
||||
elif gCmd == cmdCompileToOC: cExt = ".m"
|
||||
addCompileOption(getConfigVar(cCompiler, ".options.always"))
|
||||
addLinkOption(getConfigVar(cCompiler, ".options.linker"))
|
||||
if len(ccompilerpath) == 0:
|
||||
ccompilerpath = getConfigVar(cCompiler, ".path")
|
||||
|
||||
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
|
||||
result = completeGeneratedFilePath(cfile, createSubDir)
|
||||
|
||||
proc toObjFile*(filenameWithoutExt: string): string =
|
||||
# Object file for compilation
|
||||
result = changeFileExt(filenameWithoutExt, CC[cCompiler].objExt)
|
||||
|
||||
proc addFileToCompile*(filename: string) =
|
||||
appendStr(toCompile, filename)
|
||||
|
||||
proc resetCompilationLists* =
|
||||
initLinkedList(toCompile)
|
||||
## XXX: we must associate these with their originating module
|
||||
# when the module is loaded/unloaded it adds/removes its items
|
||||
# That's because we still need to CRC check the external files
|
||||
# Maybe we can do that in checkDep on the other hand?
|
||||
initLinkedList(externalToCompile)
|
||||
initLinkedList(toLink)
|
||||
|
||||
proc addFileToLink*(filename: string) =
|
||||
prependStr(toLink, filename)
|
||||
# BUGFIX: was ``appendStr``
|
||||
|
||||
proc execExternalProgram*(cmd: string) =
|
||||
if optListCmd in gGlobalOptions or gVerbosity > 0: msgWriteln(cmd)
|
||||
if execCmd(cmd) != 0: rawMessage(errExecutionOfProgramFailed, "")
|
||||
|
||||
proc generateScript(projectFile: string, script: PRope) =
|
||||
let (dir, name, ext) = splitFile(projectFile)
|
||||
writeRope(script, dir / addFileExt("compile_" & name,
|
||||
platform.OS[targetOS].scriptExt))
|
||||
|
||||
proc getOptSpeed(c: TSystemCC): string =
|
||||
result = getConfigVar(c, ".options.speed")
|
||||
if result == "":
|
||||
result = CC[c].optSpeed # use default settings from this file
|
||||
|
||||
proc getDebug(c: TSystemCC): string =
|
||||
result = getConfigVar(c, ".options.debug")
|
||||
if result == "":
|
||||
result = CC[c].debug # use default settings from this file
|
||||
|
||||
proc getOptSize(c: TSystemCC): string =
|
||||
result = getConfigVar(c, ".options.size")
|
||||
if result == "":
|
||||
result = CC[c].optSize # use default settings from this file
|
||||
|
||||
proc noAbsolutePaths: bool {.inline.} =
|
||||
# We used to check current OS != specified OS, but this makes no sense
|
||||
# really: Cross compilation from Linux to Linux for example is entirely
|
||||
# reasonable.
|
||||
# `optGenMapping` is included here for niminst.
|
||||
result = gGlobalOptions * {optGenScript, optGenMapping} != {}
|
||||
|
||||
const
|
||||
specialFileA = 42
|
||||
specialFileB = 42
|
||||
|
||||
var fileCounter: int
|
||||
|
||||
proc add(s: var string, many: openArray[string]) =
|
||||
s.add many.join
|
||||
|
||||
proc cFileSpecificOptions(cfilename: string): string =
|
||||
result = compileOptions
|
||||
var trunk = splitFile(cfilename).name
|
||||
if optCDebug in gGlobalOptions:
|
||||
var key = trunk & ".debug"
|
||||
if existsConfigVar(key): addOpt(result, getConfigVar(key))
|
||||
else: addOpt(result, getDebug(cCompiler))
|
||||
if optOptimizeSpeed in gOptions:
|
||||
var key = trunk & ".speed"
|
||||
if existsConfigVar(key): addOpt(result, getConfigVar(key))
|
||||
else: addOpt(result, getOptSpeed(cCompiler))
|
||||
elif optOptimizeSize in gOptions:
|
||||
var key = trunk & ".size"
|
||||
if existsConfigVar(key): addOpt(result, getConfigVar(key))
|
||||
else: addOpt(result, getOptSize(cCompiler))
|
||||
var key = trunk & ".always"
|
||||
if existsConfigVar(key): addOpt(result, getConfigVar(key))
|
||||
|
||||
proc getCompileOptions: string =
|
||||
result = cFileSpecificOptions("__dummy__")
|
||||
|
||||
proc getLinkOptions: string =
|
||||
result = linkOptions
|
||||
for linkedLib in items(cLinkedLibs):
|
||||
result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
|
||||
for libDir in items(cLibs):
|
||||
result.add([CC[cCompiler].linkDirCmd, libDir.quoteShell])
|
||||
|
||||
proc needsExeExt(): bool {.inline.} =
|
||||
result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
|
||||
(platform.hostOS == osWindows)
|
||||
|
||||
proc getCompilerExe(compiler: TSystemCC): string =
|
||||
result = if gCmd == cmdCompileToCpp: CC[compiler].cppCompiler
|
||||
else: CC[compiler].compilerExe
|
||||
if result.len == 0:
|
||||
rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
|
||||
|
||||
proc getLinkerExe(compiler: TSystemCC): string =
|
||||
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
|
||||
else: compiler.getCompilerExe
|
||||
|
||||
proc getCompileCFileCmd*(cfilename: string, isExternal = false): string =
|
||||
var c = cCompiler
|
||||
var options = cFileSpecificOptions(cfilename)
|
||||
var exe = getConfigVar(c, ".exe")
|
||||
if exe.len == 0: exe = c.getCompilerExe
|
||||
|
||||
if needsExeExt(): exe = addFileExt(exe, "exe")
|
||||
if optGenDynLib in gGlobalOptions and
|
||||
ospNeedsPIC in platform.OS[targetOS].props:
|
||||
add(options, ' ' & CC[c].pic)
|
||||
|
||||
var includeCmd, compilePattern: string
|
||||
if not noAbsolutePaths():
|
||||
# compute include paths:
|
||||
includeCmd = CC[c].includeCmd & quoteShell(libpath)
|
||||
|
||||
for includeDir in items(cIncludes):
|
||||
includeCmd.add([CC[c].includeCmd, includeDir.quoteShell])
|
||||
|
||||
compilePattern = joinPath(ccompilerpath, exe)
|
||||
else:
|
||||
includeCmd = ""
|
||||
compilePattern = c.getCompilerExe
|
||||
|
||||
var cfile = if noAbsolutePaths(): extractFilename(cfilename)
|
||||
else: cfilename
|
||||
var objfile = if not isExternal or noAbsolutePaths():
|
||||
toObjFile(cfile)
|
||||
else:
|
||||
completeCFilePath(toObjFile(cfile))
|
||||
cfile = quoteShell(addFileExt(cfile, cExt))
|
||||
objfile = quoteShell(objfile)
|
||||
result = quoteShell(compilePattern % [
|
||||
"file", cfile, "objfile", objfile, "options", options,
|
||||
"include", includeCmd, "nimrod", getPrefixDir(), "lib", libpath])
|
||||
add(result, ' ')
|
||||
addf(result, CC[c].compileTmpl, [
|
||||
"file", cfile, "objfile", objfile,
|
||||
"options", options, "include", includeCmd,
|
||||
"nimrod", quoteShell(getPrefixDir()),
|
||||
"lib", quoteShell(libpath)])
|
||||
|
||||
proc footprint(filename: string): TCrc32 =
|
||||
result = crcFromFile(filename) ><
|
||||
platform.OS[targetOS].name ><
|
||||
platform.CPU[targetCPU].name ><
|
||||
extccomp.CC[extccomp.cCompiler].name ><
|
||||
getCompileCFileCmd(filename, true)
|
||||
|
||||
proc externalFileChanged(filename: string): bool =
|
||||
var crcFile = toGeneratedFile(filename.withPackageName, "crc")
|
||||
var currentCrc = int(footprint(filename))
|
||||
var f: TFile
|
||||
if open(f, crcFile, fmRead):
|
||||
var line = newStringOfCap(40)
|
||||
if not f.readLine(line): line = "0"
|
||||
close(f)
|
||||
var oldCrc = parseInt(line)
|
||||
result = oldCrc != currentCrc
|
||||
else:
|
||||
result = true
|
||||
if result:
|
||||
if open(f, crcFile, fmWrite):
|
||||
f.writeln($currentCrc)
|
||||
close(f)
|
||||
|
||||
proc addExternalFileToCompile*(filename: string) =
|
||||
if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
|
||||
appendStr(externalToCompile, filename)
|
||||
|
||||
proc compileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
|
||||
isExternal: bool) =
|
||||
var it = PStrEntry(list.head)
|
||||
while it != nil:
|
||||
inc(fileCounter) # call the C compiler for the .c file:
|
||||
var compileCmd = getCompileCFileCmd(it.data, isExternal)
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
add(cmds, compileCmd)
|
||||
if optGenScript in gGlobalOptions:
|
||||
app(script, compileCmd)
|
||||
app(script, tnl)
|
||||
it = PStrEntry(it.next)
|
||||
|
||||
proc callCCompiler*(projectfile: string) =
|
||||
var
|
||||
linkCmd, buildgui, builddll: string
|
||||
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
|
||||
return # speed up that call if only compiling and no script shall be
|
||||
# generated
|
||||
fileCounter = 0
|
||||
var c = cCompiler
|
||||
var script: PRope = nil
|
||||
var cmds: TStringSeq = @[]
|
||||
compileCFile(toCompile, script, cmds, false)
|
||||
compileCFile(externalToCompile, script, cmds, true)
|
||||
if optCompileOnly notin gGlobalOptions:
|
||||
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
|
||||
var res = 0
|
||||
if gNumberOfProcessors <= 1:
|
||||
for i in countup(0, high(cmds)): res = max(execCmd(cmds[i]), res)
|
||||
elif optListCmd in gGlobalOptions or gVerbosity > 0:
|
||||
res = execProcesses(cmds, {poEchoCmd, poUseShell, poParentStreams},
|
||||
gNumberOfProcessors)
|
||||
else:
|
||||
res = execProcesses(cmds, {poUseShell, poParentStreams},
|
||||
gNumberOfProcessors)
|
||||
if res != 0:
|
||||
if gNumberOfProcessors <= 1:
|
||||
rawMessage(errExecutionOfProgramFailed, [])
|
||||
else:
|
||||
rawMessage(errGenerated, " execution of an external program failed; " &
|
||||
"rerun with --parallelBuild:1 to see the error message")
|
||||
if optNoLinking notin gGlobalOptions:
|
||||
# call the linker:
|
||||
var it = PStrEntry(toLink.head)
|
||||
var objfiles = ""
|
||||
while it != nil:
|
||||
let objFile = if noAbsolutePaths(): it.data.extractFilename else: it.data
|
||||
add(objfiles, ' ')
|
||||
add(objfiles, quoteShell(
|
||||
addFileExt(objFile, CC[cCompiler].objExt)))
|
||||
it = PStrEntry(it.next)
|
||||
|
||||
if optGenStaticLib in gGlobalOptions:
|
||||
linkCmd = CC[c].buildLib % ["libfile", (libNameTmpl() % gProjectName),
|
||||
"objfiles", objfiles]
|
||||
if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd)
|
||||
else:
|
||||
var linkerExe = getConfigVar(c, ".linkerexe")
|
||||
if len(linkerExe) == 0: linkerExe = c.getLinkerExe
|
||||
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
|
||||
if noAbsolutePaths(): linkCmd = quoteShell(linkerExe)
|
||||
else: linkCmd = quoteShell(joinPath(ccompilerpath, linkerExe))
|
||||
if optGenGuiApp in gGlobalOptions: buildgui = CC[c].buildGui
|
||||
else: buildgui = ""
|
||||
var exefile: string
|
||||
if optGenDynLib in gGlobalOptions:
|
||||
exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
|
||||
builddll = CC[c].buildDll
|
||||
else:
|
||||
exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
|
||||
builddll = ""
|
||||
if options.outFile.len > 0:
|
||||
exefile = options.outFile.expandTilde
|
||||
if not noAbsolutePaths():
|
||||
if not exefile.isAbsolute():
|
||||
exefile = joinPath(splitFile(projectfile).dir, exefile)
|
||||
exefile = quoteShell(exefile)
|
||||
let linkOptions = getLinkOptions()
|
||||
linkCmd = quoteShell(linkCmd % ["builddll", builddll,
|
||||
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
||||
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath])
|
||||
linkCmd.add ' '
|
||||
addf(linkCmd, CC[c].linkTmpl, ["builddll", builddll,
|
||||
"buildgui", buildgui, "options", linkOptions,
|
||||
"objfiles", objfiles, "exefile", exefile,
|
||||
"nimrod", quoteShell(getPrefixDir()),
|
||||
"lib", quoteShell(libpath)])
|
||||
if optCompileOnly notin gGlobalOptions: execExternalProgram(linkCmd)
|
||||
else:
|
||||
linkCmd = ""
|
||||
if optGenScript in gGlobalOptions:
|
||||
app(script, linkCmd)
|
||||
app(script, tnl)
|
||||
generateScript(projectfile, script)
|
||||
|
||||
proc genMappingFiles(list: TLinkedList): PRope =
|
||||
var it = PStrEntry(list.head)
|
||||
while it != nil:
|
||||
appf(result, "--file:r\"$1\"$N", [toRope(addFileExt(it.data, cExt))])
|
||||
it = PStrEntry(it.next)
|
||||
|
||||
proc writeMapping*(gSymbolMapping: PRope) =
|
||||
if optGenMapping notin gGlobalOptions: return
|
||||
var code = toRope("[C_Files]\n")
|
||||
app(code, genMappingFiles(toCompile))
|
||||
app(code, genMappingFiles(externalToCompile))
|
||||
app(code, "\n[C_Compiler]\nFlags=")
|
||||
app(code, strutils.escape(getCompileOptions()))
|
||||
|
||||
app(code, "\n[Linker]\nFlags=")
|
||||
app(code, strutils.escape(getLinkOptions()))
|
||||
|
||||
app(code, "\n[Environment]\nlibpath=")
|
||||
app(code, strutils.escape(libpath))
|
||||
|
||||
appf(code, "\n[Symbols]$n$1", [gSymbolMapping])
|
||||
writeRope(code, joinPath(gProjectPath, "mapping.txt"))
|
||||
|
||||
78
compiler/cgen/tccgen.nim
Normal file
78
compiler/cgen/tccgen.nim
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2012 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import
|
||||
os, strutils, options, msgs, tinyc
|
||||
|
||||
{.compile: "../tinyc/libtcc.c".}
|
||||
|
||||
proc tinyCErrorHandler(closure: pointer, msg: cstring) {.cdecl.} =
|
||||
rawMessage(errGenerated, $msg)
|
||||
|
||||
proc initTinyCState: PccState =
|
||||
result = openCCState()
|
||||
setErrorFunc(result, nil, tinyCErrorHandler)
|
||||
|
||||
var
|
||||
gTinyC = initTinyCState()
|
||||
libIncluded = false
|
||||
|
||||
proc addFile(filename: string) =
|
||||
if addFile(gTinyC, filename) != 0'i32:
|
||||
rawMessage(errCannotOpenFile, filename)
|
||||
|
||||
proc setupEnvironment =
|
||||
when defined(amd64):
|
||||
defineSymbol(gTinyC, "__x86_64__", nil)
|
||||
elif defined(i386):
|
||||
defineSymbol(gTinyC, "__i386__", nil)
|
||||
when defined(linux):
|
||||
defineSymbol(gTinyC, "__linux__", nil)
|
||||
defineSymbol(gTinyC, "__linux", nil)
|
||||
var nimrodDir = getPrefixDir()
|
||||
|
||||
addIncludePath(gTinyC, libpath)
|
||||
when defined(windows):
|
||||
addSysincludePath(gTinyC, nimrodDir / "tinyc/win32/include")
|
||||
addSysincludePath(gTinyC, nimrodDir / "tinyc/include")
|
||||
when defined(windows):
|
||||
defineSymbol(gTinyC, "_WIN32", nil)
|
||||
# we need Mingw's headers too:
|
||||
var gccbin = getConfigVar("gcc.path") % ["nimrod", nimrodDir]
|
||||
addSysincludePath(gTinyC, gccbin /../ "include")
|
||||
#addFile(nimrodDir / r"tinyc\win32\wincrt1.o")
|
||||
addFile(nimrodDir / r"tinyc\win32\alloca86.o")
|
||||
addFile(nimrodDir / r"tinyc\win32\chkstk.o")
|
||||
#addFile(nimrodDir / r"tinyc\win32\crt1.o")
|
||||
|
||||
#addFile(nimrodDir / r"tinyc\win32\dllcrt1.o")
|
||||
#addFile(nimrodDir / r"tinyc\win32\dllmain.o")
|
||||
addFile(nimrodDir / r"tinyc\win32\libtcc1.o")
|
||||
|
||||
#addFile(nimrodDir / r"tinyc\win32\lib\crt1.c")
|
||||
#addFile(nimrodDir / r"tinyc\lib\libtcc1.c")
|
||||
else:
|
||||
addSysincludePath(gTinyC, "/usr/include")
|
||||
when defined(amd64):
|
||||
addSysincludePath(gTinyC, "/usr/include/x86_64-linux-gnu")
|
||||
|
||||
proc compileCCode*(ccode: string) =
|
||||
if not libIncluded:
|
||||
libIncluded = true
|
||||
setupEnvironment()
|
||||
discard compileString(gTinyC, ccode)
|
||||
|
||||
proc run*() =
|
||||
var a: array[0..1, cstring]
|
||||
a[0] = ""
|
||||
a[1] = ""
|
||||
var err = tinyc.run(gTinyC, 0'i32, cast[cstringArray](addr(a))) != 0'i32
|
||||
closeCCState(gTinyC)
|
||||
if err: rawMessage(errExecutionOfProgramFailed, "")
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue