store the instantiation cache in the generic symbol

This commit is contained in:
Zahary Karadjov 2012-11-20 23:38:56 +02:00
commit e6f3f46cd9
7 changed files with 95 additions and 71 deletions

View file

@ -588,10 +588,25 @@ type
name*: PRope name*: PRope
path*: PNode # can be a string literal! path*: PNode # can be a string literal!
TInstantiation* = object
sym*: PSym
concreteTypes*: seq[PType]
usedBy*: seq[int32] # list of modules using the generic
# needed in caas mode for purging the cache
# XXX: it's possible to switch to a
# simple ref count here
PInstantiation* = ref TInstantiation
PLib* = ref TLib PLib* = ref TLib
TSym* = object of TIdObj TSym* = object of TIdObj
kind*: TSymKind case kind*: TSymKind
of skType: # generic instantiation caches
typeInstCache*: seq[PType]
of routineKinds:
procInstCache*: seq[PInstantiation]
else: nil
magic*: TMagic magic*: TMagic
typ*: PType typ*: PType
name*: PIdent name*: PIdent
@ -615,8 +630,9 @@ type
# for a conditional: # for a conditional:
# 1 iff the symbol is defined, else 0 # 1 iff the symbol is defined, else 0
# (or not in symbol table) # (or not in symbol table)
# for modules, a unique index corresponding # for modules, an unique index corresponding
# to the order of compilation # to the module's fileIdx
offset*: int # offset of record field offset*: int # offset of record field
loc*: TLoc loc*: TLoc
annex*: PLib # additional fields (seldom used, so we use a annex*: PLib # additional fields (seldom used, so we use a

View file

@ -85,18 +85,6 @@ proc doCRC(fileIdx: int32) =
# echo "FIRST CRC: ", fileIdx.ToFilename # echo "FIRST CRC: ", fileIdx.ToFilename
gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename) gMemCacheData[fileIdx].crc = crcFromFile(fileIdx.toFilename)
proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect.} =
if x == nil: x = @[y]
else: x.add(y)
proc safeAdd*(x: var string, y: char) =
if x == nil: x = ""
x.add(y)
proc safeAdd*(x: var string, y: string) =
if x == nil: x = y
else: x.add(y)
proc addDep(x: Psym, dep: int32) = proc addDep(x: Psym, dep: int32) =
growCache gMemCacheData, dep growCache gMemCacheData, dep
gMemCacheData[x.position].deps.safeAdd(dep) gMemCacheData[x.position].deps.safeAdd(dep)

View file

@ -190,7 +190,7 @@ include semtypes, semtempl, semgnrc, semstmts, semexprs
proc addCodeForGenerics(c: PContext, n: PNode) = proc addCodeForGenerics(c: PContext, n: PNode) =
for i in countup(c.generics.lastGenericIdx, Len(c.generics.generics) - 1): for i in countup(c.generics.lastGenericIdx, Len(c.generics.generics) - 1):
var prc = c.generics.generics[i].instSym var prc = c.generics.generics[i].inst.sym
if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone: if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone:
if prc.ast == nil or prc.ast.sons[bodyPos] == nil: if prc.ast == nil or prc.ast.sons[bodyPos] == nil:
InternalError(prc.info, "no code for " & prc.name.s) InternalError(prc.info, "no code for " & prc.name.s)

View file

@ -36,17 +36,16 @@ type
# in standalone ``except`` and ``finally`` # in standalone ``except`` and ``finally``
next*: PProcCon # used for stacking procedure contexts next*: PProcCon # used for stacking procedure contexts
TInstantiatedSymbol* {.final.} = object TInstantiationPair* = object
genericSym*, instSym*: PSym genericSym*: PSym
concreteTypes*: seq[PType] inst*: PInstantiation
# If we generate an instance of a generic, we'd like to re-use that # If we generate an instance of a generic, we'd like to re-use that
# instance if possible across module boundaries. However, this is not # instance if possible across module boundaries. However, this is not
# possible if the compilation cache is enabled. So we give up then and use # possible if the compilation cache is enabled. So we give up then and use
# the caching of generics only per module, not per project. # the caching of generics only per module, not per project.
TGenericsCache* {.final.} = object TGenericsCache* {.final.} = object
InstTypes*: TIdTable # map PType to PType generics*: seq[TInstantiationPair] # a list of the things to compile
generics*: seq[TInstantiatedSymbol] # a list of the things to compile
lastGenericIdx*: int # used for the generics stack lastGenericIdx*: int # used for the generics stack
PGenericsCache* = ref TGenericsCache PGenericsCache* = ref TGenericsCache
@ -89,13 +88,16 @@ type
var var
gGenericsCache: PGenericsCache # save for modularity gGenericsCache: PGenericsCache # save for modularity
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
result.genericSym = s
result.inst = inst
proc filename*(c: PContext): string = proc filename*(c: PContext): string =
# the module's filename # the module's filename
return c.module.filename return c.module.filename
proc newGenericsCache*(): PGenericsCache = proc newGenericsCache*(): PGenericsCache =
new(result) new(result)
initIdTable(result.InstTypes)
result.generics = @[] result.generics = @[]
proc newContext*(module: PSym): PContext proc newContext*(module: PSym): PContext

View file

@ -11,7 +11,7 @@
# included from sem.nim # included from sem.nim
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
entry: var TInstantiatedSymbol) = entry: var TInstantiation) =
if n.kind != nkGenericParams: if n.kind != nkGenericParams:
InternalError(n.info, "instantiateGenericParamList; no generic params") InternalError(n.info, "instantiateGenericParamList; no generic params")
newSeq(entry.concreteTypes, n.len) newSeq(entry.concreteTypes, n.len)
@ -43,22 +43,18 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
addDecl(c, s) addDecl(c, s)
entry.concreteTypes[i] = t entry.concreteTypes[i] = t
proc sameInstantiation(a, b: TInstantiatedSymbol): bool = proc sameInstantiation(a, b: TInstantiation): bool =
if a.genericSym.id == b.genericSym.id and if a.concreteTypes.len == b.concreteTypes.len:
a.concreteTypes.len == b.concreteTypes.len: for i in 0..a.concreteTypes.high:
for i in 0 .. < a.concreteTypes.len:
if not compareTypes(a.concreteTypes[i], b.concreteTypes[i], if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
flags = {TypeDescExactMatch}): return flags = {TypeDescExactMatch}): return
result = true result = true
proc GenericCacheGet(c: PContext, entry: var TInstantiatedSymbol): PSym = proc GenericCacheGet(genericSym: Psym, entry: TInstantiation): PSym =
for i in countup(0, Len(c.generics.generics) - 1): if genericSym.procInstCache != nil:
if sameInstantiation(entry, c.generics.generics[i]): for inst in genericSym.procInstCache:
result = c.generics.generics[i].instSym if sameInstantiation(entry, inst[]):
# checking for the concrete parameter list is wrong and unnecessary! return inst.sym
#if equalParams(b.typ.n, instSym.typ.n) == paramsEqual:
#echo "found in cache: ", getProcHeader(result)
return
proc removeDefaultParamValues(n: PNode) = proc removeDefaultParamValues(n: PNode) =
# we remove default params, because they cannot be instantiated properly # we remove default params, because they cannot be instantiated properly
@ -110,7 +106,7 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
proc fixupInstantiatedSymbols(c: PContext, s: PSym) = proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
for i in countup(0, Len(c.generics.generics) - 1): for i in countup(0, Len(c.generics.generics) - 1):
if c.generics.generics[i].genericSym.id == s.id: if c.generics.generics[i].genericSym.id == s.id:
var oldPrc = c.generics.generics[i].instSym var oldPrc = c.generics.generics[i].inst.sym
pushInfoContext(oldPrc.info) pushInfoContext(oldPrc.info)
openScope(c.tab) openScope(c.tab)
var n = oldPrc.ast var n = oldPrc.ast
@ -155,10 +151,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
InternalError(n.info, "generateInstance") InternalError(n.info, "generateInstance")
n.sons[namePos] = newSymNode(result) n.sons[namePos] = newSymNode(result)
pushInfoContext(info) pushInfoContext(info)
var entry: TInstantiatedSymbol var entry = TInstantiation.new
entry.instSym = result entry.sym = result
entry.genericSym = fn instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry)
n.sons[genericParamsPos] = ast.emptyNode n.sons[genericParamsPos] = ast.emptyNode
# semantic checking for the parameters: # semantic checking for the parameters:
if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].kind != nkEmpty:
@ -168,9 +163,10 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.typ = newTypeS(tyProc, c) result.typ = newTypeS(tyProc, c)
rawAddSon(result.typ, nil) rawAddSon(result.typ, nil)
result.typ.callConv = fn.typ.callConv result.typ.callConv = fn.typ.callConv
var oldPrc = GenericCacheGet(c, entry) var oldPrc = GenericCacheGet(fn, entry[])
if oldPrc == nil: if oldPrc == nil:
c.generics.generics.add(entry) fn.procInstCache.safeAdd(entry)
c.generics.generics.add(makeInstPair(fn, entry))
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas) pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
if isNil(n.sons[bodyPos]): if isNil(n.sons[bodyPos]):

View file

@ -31,24 +31,31 @@ proc checkConstructedType*(info: TLineInfo, typ: PType) =
if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags: if t.sons[0].kind != tyObject or tfFinal in t.sons[0].flags:
localError(info, errInheritanceOnlyWithNonFinalObjects) localError(info, errInheritanceOnlyWithNonFinalObjects)
proc searchInstTypes(tab: TIdTable, key: PType): PType = proc searchInstTypes(key: PType): PType =
# returns nil if we need to declare this type let genericTyp = key.sons[0]
result = PType(IdTableGet(tab, key)) InternalAssert genericTyp.kind == tyGenericBody and
if result == nil and tab.counter > 0: key.sons[0] == genericTyp and
# we have to do a slow linear search because types may need genericTyp.sym != nil
# to be compared by their structure:
for h in countup(0, high(tab.data)): if genericTyp.sym.typeInstCache == nil:
var t = PType(tab.data[h].key) return
if t != nil:
if key.containerId == t.containerId: for inst in genericTyp.sym.typeInstCache:
var match = true InternalAssert inst.sons.len == key.sons.len + 1
for j in countup(0, sonsLen(t) - 1): if inst.id == key.id: return inst
block MatchType:
for j in 1 .. high(key.sons):
# XXX sameType is not really correct for nested generics? # XXX sameType is not really correct for nested generics?
if not sameType(t.sons[j], key.sons[j]): if not sameType(inst.sons[j], key.sons[j]):
match = false break MatchType
break
if match: return inst
return PType(tab.data[h].val)
proc cacheTypeInst(inst: PType) =
# XXX: add to module's generics
# update the refcount
let genericTyp = inst.sons[0]
genericTyp.sym.typeInstCache.safeAdd(inst)
type type
TReplTypeVars* {.final.} = object TReplTypeVars* {.final.} = object
@ -134,7 +141,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
var header: PType = nil var header: PType = nil
when true: when true:
# search for some instantiation here: # search for some instantiation here:
result = searchInstTypes(cl.c.generics.InstTypes, t) result = searchInstTypes(t)
if result != nil: return if result != nil: return
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
var x = t.sons[i] var x = t.sons[i]
@ -145,7 +152,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
#idTablePut(cl.typeMap, body.sons[i-1], x) #idTablePut(cl.typeMap, body.sons[i-1], x)
if header != nil: if header != nil:
# search again after first pass: # search again after first pass:
result = searchInstTypes(cl.c.generics.InstTypes, header) result = searchInstTypes(header)
if result != nil: return if result != nil: return
else: else:
header = copyType(t, t.owner, false) header = copyType(t, t.owner, false)
@ -153,7 +160,8 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
# we need to add the candidate here, before it's fully instantiated for # we need to add the candidate here, before it's fully instantiated for
# recursive instantions: # recursive instantions:
result = newType(tyGenericInst, t.sons[0].owner) result = newType(tyGenericInst, t.sons[0].owner)
idTablePut(cl.c.generics.InstTypes, header, result) result.rawAddSon(header.sons[0])
cacheTypeInst(result)
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
var x = replaceTypeVarsT(cl, t.sons[i]) var x = replaceTypeVarsT(cl, t.sons[i])
@ -161,7 +169,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
header.sons[i] = x header.sons[i] = x
idTablePut(cl.typeMap, body.sons[i-1], x) idTablePut(cl.typeMap, body.sons[i-1], x)
for i in countup(0, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
# if one of the params is not concrete, we cannot do anything # if one of the params is not concrete, we cannot do anything
# but we already raised an error! # but we already raised an error!
rawAddSon(result, header.sons[i]) rawAddSon(result, header.sons[i])

View file

@ -112,7 +112,7 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
## creates a new object of type ``T`` and returns a safe (traced) ## creates a new object of type ``T`` and returns a safe (traced)
## reference to it in ``a``. ## reference to it in ``a``.
proc new(T: typedesc): ref T = proc new*(T: typedesc): ref T =
## creates a new object of type ``T`` and returns a safe (traced) ## creates a new object of type ``T`` and returns a safe (traced)
## reference to it as result value ## reference to it as result value
new(result) new(result)
@ -2427,3 +2427,17 @@ proc compiles*(x: expr): bool {.magic: "Compiles", noSideEffect.} =
when defined(initDebugger): when defined(initDebugger):
initDebugger() initDebugger()
# XXX: make these the default (or implement the NilObject optimization)
proc safeAdd*[T](x: var seq[T], y: T) {.noSideEffect.} =
if x == nil: x = @[y]
else: x.add(y)
proc safeAdd*(x: var string, y: char) =
if x == nil: x = ""
x.add(y)
proc safeAdd*(x: var string, y: string) =
if x == nil: x = y
else: x.add(y)