store the instantiation cache in the generic symbol
This commit is contained in:
parent
f9bd8cc985
commit
e6f3f46cd9
7 changed files with 95 additions and 71 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]):
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue