This revision is intended as comparison point between the old and the new GC The used GC can be switched in mmdisp and various statistics will be gathered during execution (these will be removed/disabled in later revisions)
959 lines
35 KiB
Nim
Executable file
959 lines
35 KiB
Nim
Executable file
#
|
|
#
|
|
# The Nimrod Compiler
|
|
# (c) Copyright 2012 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 =
|
|
case name[0]
|
|
of 'a'..'z':
|
|
result = ""
|
|
add(result, chr(ord(name[0]) - ord('a') + ord('A')))
|
|
of '0'..'9', 'A'..'Z':
|
|
result = ""
|
|
add(result, name[0])
|
|
else: result = "HEX" & toHex(ord(name[0]), 2)
|
|
for i in countup(1, len(name) - 1):
|
|
case name[i]
|
|
of 'A'..'Z':
|
|
add(result, chr(ord(name[i]) - ord('A') + ord('a')))
|
|
of '_':
|
|
nil
|
|
of 'a'..'z', '0'..'9':
|
|
add(result, name[i])
|
|
else:
|
|
add(result, "HEX")
|
|
add(result, toHex(ord(name[i]), 2))
|
|
|
|
proc mangle(name: string): string =
|
|
when false:
|
|
case name[0]
|
|
of 'a'..'z':
|
|
result = ""
|
|
add(result, chr(ord(name[0]) - ord('a') + ord('A')))
|
|
of '0'..'9', 'A'..'Z':
|
|
result = ""
|
|
add(result, name[0])
|
|
else: result = "HEX" & toHex(ord(name[0]), 2)
|
|
result = ""
|
|
for i in countup(0, len(name) - 1):
|
|
case name[i]
|
|
of 'A'..'Z':
|
|
add(result, chr(ord(name[i]) - ord('A') + ord('a')))
|
|
of '_':
|
|
nil
|
|
of 'a'..'z', '0'..'9':
|
|
add(result, name[i])
|
|
else:
|
|
add(result, "HEX")
|
|
add(result, toHex(ord(name[i]), 2))
|
|
|
|
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, skIterator:
|
|
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} * 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 and are now eliminated.
|
|
#
|
|
# Future notes:
|
|
# sfFromGeneric seems to be needed in order to avoid multiple
|
|
# definitions of certain varialbes 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 isCompileTimeOnly(t: PType): bool =
|
|
result = t.kind in {tyTypedesc, tyExpr}
|
|
|
|
proc containsCompileTimeOnly(t: PType): bool =
|
|
if isCompileTimeOnly(t): return true
|
|
if t.sons != nil:
|
|
for i in 0 .. <t.sonsLen:
|
|
if t.sons[i] != nil and isCompileTimeOnly(t.sons[i]):
|
|
return true
|
|
return false
|
|
|
|
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.sons[0], abstractInst)
|
|
case base.kind
|
|
of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray
|
|
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, abstractInst).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, abstractInst).kind in
|
|
{tyVar, tyRef, tyPtr})
|
|
of ctStruct:
|
|
result = needsComplexAssignment(skipTypes(rettype, abstractInst))
|
|
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, abstractInst)
|
|
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.sons[0], abstractInst)
|
|
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}).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 getForwardStructFormat(): string =
|
|
if gCmd == cmdCompileToCpp: result = "struct $1;$n"
|
|
else: result = "typedef struct $1 $1;$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(), [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
|
|
#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)
|
|
appf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, 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
|
|
if typ.kind == tyObject:
|
|
if typ.sons[0] == nil:
|
|
if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags:
|
|
result = ropecg(m, "struct $1 {$n", [name])
|
|
else:
|
|
result = ropecg(m, "struct $1 {$n#TNimType* m_type;$n", [name])
|
|
hasField = true
|
|
elif gCmd == cmdCompileToCpp:
|
|
result = ropecg(m, "struct $1 : public $2 {$n",
|
|
[name, getTypeDescAux(m, typ.sons[0], check)])
|
|
hasField = true
|
|
else:
|
|
result = ropecg(m, "struct $1 {$n $2 Sup;$n",
|
|
[name, getTypeDescAux(m, typ.sons[0], check)])
|
|
hasField = true
|
|
else:
|
|
result = ropef("struct $1 {$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("struct $1 {$n", [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.sons[0])
|
|
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(), [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], abstractInst).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(), [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)
|
|
|
|
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!
|
|
appf(result, "$1($2, $3)$4",
|
|
[toRope(CallingConvToStr[prc.typ.callConv]), rettype, prc.loc.r, params])
|
|
|
|
# ------------------ type info generation -------------------------------------
|
|
|
|
proc genTypeInfo(m: BModule, typ: 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), toRope(d.name.s)])
|
|
|
|
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])
|
|
|
|
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, typ: PType): PRope =
|
|
var t = getUniqueType(typ)
|
|
result = ropef("NTI$1", [toRope(t.id)])
|
|
let owner = typ.skipTypes(abstractPtrs).owner.getModule
|
|
if owner != m.module:
|
|
# make sure the type info is created in the owner module
|
|
discard genTypeInfo(owner.bmod, typ)
|
|
# refenrece 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)
|
|
if ContainsOrIncl(m.typeInfoMarker, t.id):
|
|
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 optRefcGC in gGlobalOptions:
|
|
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) =
|
|
nil
|