compilation cache: mostly working; generics not yet

This commit is contained in:
Araq 2011-10-25 15:26:36 +02:00
commit 9fb36bd20c
17 changed files with 160 additions and 2949 deletions

View file

@ -568,7 +568,7 @@ const
ConstantDataTypes*: TTypeKinds = {tyArrayConstr, tyArray, tySet,
tyTuple, tySequence}
ExportableSymKinds* = {skVar, skConst, skProc, skMethod, skType, skIterator,
skMacro, skTemplate, skConverter, skStub}
skMacro, skTemplate, skConverter, skEnumField, skStub}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfAllConst}
namePos* = 0
genericParamsPos* = 1

View file

@ -142,7 +142,7 @@ type
PRodReader* = ref TRodReader
const
FileVersion* = "1024" # modify this if the rod-format changes!
FileVersion* = "1026" # modify this if the rod-format changes!
var rodCompilerprocs*: TStrTable
@ -657,17 +657,26 @@ proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
result = decodeSym(rd, info)
rd.pos = oldPos
proc findSomeWhere(id: int) =
for i in countup(0, high(gMods)):
var rd = gMods[i].rd
if rd != nil:
var d = IITableGet(rd.index.tab, id)
if d != invalidKey:
echo "found id ", id, " in ", gMods[i].filename
proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
result = PSym(IdTableGet(r.syms, id))
if result == nil:
# load the symbol:
var d = IITableGet(r.index.tab, id)
if d == invalidKey:
# import from other module:
var moduleID = IiTableGet(r.imports.tab, id)
if moduleID < 0:
if moduleID < 0:
var x = ""
encodeVInt(id, x)
InternalError(info, "missing from both indexes: +" & x)
InternalError(info, "missing from both indexes: +" & x)
# find the reader with the correct moduleID:
for i in countup(0, high(gMods)):
var rd = gMods[i].rd
@ -680,6 +689,7 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
else:
var x = ""
encodeVInt(id, x)
when false: findSomeWhere(id)
InternalError(info, "rrGetSym: no reader found: +" & x)
else:
#if IiTableGet(rd.index.tab, id) <> invalidKey then

View file

@ -322,7 +322,10 @@ proc addToIndex(w: var TIndex, key, val: int) =
w.lastIdxVal = val
IiTablePut(w.tab, key, val)
#var debugWritten = initIntSet()
const debugWrittenIds = false
when debugWrittenIds:
var debugWritten = initIntSet()
proc symStack(w: PRodWriter) =
var i = 0
@ -335,10 +338,12 @@ proc symStack(w: PRodWriter) =
# put definition in here
var L = w.data.len
addToIndex(w.index, s.id, L)
#intSetIncl(debugWritten, s.id)
when debugWrittenIds: incl(debugWritten, s.id)
encodeSym(w, s, w.data)
add(w.data, rodNL)
if sfExported in s.flags and s.kind in ExportableSymKinds:
# put into interface section if appropriate:
if {sfExported, sfFromGeneric} * s.flags == {sfExported} and
s.kind in ExportableSymKinds:
encodeStr(s.name.s, w.interf)
add(w.interf, ' ')
encodeVInt(s.id, w.interf)
@ -355,12 +360,14 @@ proc symStack(w: PRodWriter) =
if w.methods.len != 0: add(w.methods, ' ')
encodeVInt(s.id, w.methods)
elif IiTableGet(w.imports.tab, s.id) == invalidKey:
addToIndex(w.imports, s.id, m.id) #if not Contains(debugWritten, s.id):
# MessageOut(w.filename);
# debug(s.owner);
# debug(s);
# InternalError('BUG!!!!');
#end
addToIndex(w.imports, s.id, m.id)
when debugWrittenIds:
if not Contains(debugWritten, s.id):
echo(w.filename)
debug(s)
debug(s.owner)
debug(m)
InternalError("BUG!!!!")
inc(i)
setlen(w.sstack, 0)

View file

@ -126,13 +126,13 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
include semtypes, semexprs, semgnrc, semstmts
proc addCodeForGenerics(c: PContext, n: PNode) =
for i in countup(lastGenericIdx, Len(generics) - 1):
var prc = generics[i].instSym
for i in countup(c.generics.lastGenericIdx, Len(c.generics.generics) - 1):
var prc = c.generics.generics[i].instSym
if prc.kind in {skProc, skMethod, skConverter} and prc.magic == mNone:
if prc.ast == nil or prc.ast.sons[codePos] == nil:
InternalError(prc.info, "no code for " & prc.name.s)
addSon(n, prc.ast)
lastGenericIdx = Len(generics)
c.generics.lastGenericIdx = Len(c.generics.generics)
proc semExprNoFlags(c: PContext, n: PNode): PNode {.procvar.} =
result = semExpr(c, n, {})
@ -162,7 +162,7 @@ proc myOpenCached(module: PSym, filename: string,
proc SemStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = semStmt(c, n)
# BUGFIX: process newly generated generics here, not at the end!
if lastGenericIdx < Len(generics):
if c.generics.lastGenericIdx < Len(c.generics.generics):
var a = newNodeI(nkStmtList, n.info)
addCodeForGenerics(c, a)
if sonsLen(a) > 0:

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright.
#
# This module contains the data structures for the semantic checking phase.
## This module contains the data structures for the semantic checking phase.
import
strutils, lists, intsets, options, lexer, ast, astalgo, trees, treetab,
@ -37,10 +37,24 @@ type
genericSym*, instSym*: PSym
concreteTypes*: seq[PType]
# 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
# possible if the compilation cache is enabled. So we give up then and use
# the caching of generics only per module, not per project.
TGenericsCache* {.final.} = object
InstTypes*: TIdTable # map PType to PType
generics*: seq[TInstantiatedSymbol] # a list of the things to compile
lastGenericIdx*: int # used for the generics stack
PGenericsCache* = ref TGenericsCache
PContext* = ref TContext
TContext* = object of TPassContext # a context represents a module
module*: PSym # the module sym belonging to the context
p*: PProcCon # procedure context
generics*: PGenericsCache # may point to a global or module-local structure
friendModule*: PSym # current friend module; may access private data;
# this is used so that generic instantiations can
# access private object fields
InstCounter*: int # to prevent endless instantiations
threadEntries*: TSymSeq # list of thread entries to check
@ -56,10 +70,13 @@ type
filename*: string # the module's filename
userPragmas*: TStrTable
var
gGenericsCache: PGenericsCache # save for modularity
var gInstTypes*: TIdTable # map PType to PType
var generics*: seq[TInstantiatedSymbol] = @[] # a list of the things to compile
var lastGenericIdx*: int # used for the generics stack
proc newGenericsCache: PGenericsCache =
new(result)
initIdTable(result.InstTypes)
result.generics = @[]
proc newContext*(module: PSym, nimfile: string): PContext
@ -126,11 +143,21 @@ proc newContext(module: PSym, nimfile: string): PContext =
initLinkedList(result.libs)
append(result.optionStack, newOptionEntry())
result.module = module
result.friendModule = module
result.threadEntries = @[]
result.converters = @[]
result.filename = nimfile
result.includedFiles = initIntSet()
initStrTable(result.userPragmas)
if optSymbolFiles notin gGlobalOptions:
# re-usage of generic instantiations across module boundaries is
# very nice for code size:
if gGenericsCache == nil: gGenericsCache = newGenericsCache()
result.generics = gGenericsCache
else:
# we have to give up and use a per-module cache for generic instantiations:
result.generics = newGenericsCache()
assert gGenericsCache == nil
proc addConverter(c: PContext, conv: PSym) =
var L = len(c.converters)
@ -189,5 +216,4 @@ proc checkSonsLen*(n: PNode, length: int) =
proc checkMinSonsLen*(n: PNode, length: int) =
if sonsLen(n) < length: illFormedAst(n)
initIdTable(gInstTypes)

View file

@ -53,8 +53,9 @@ proc inlineConst(n: PNode, s: PSym): PNode {.inline.} =
proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
case s.kind
of skProc, skMethod, skIterator, skConverter:
var smoduleId = getModule(s).id
if sfProcVar notin s.flags and s.typ.callConv == ccDefault and
getModule(s).id != c.module.id:
smoduleId != c.module.id and smoduleId != c.friendModule.id:
LocalError(n.info, errXCannotBePassedToProcVar, s.name.s)
result = symChoice(c, n, s)
of skConst:
@ -673,8 +674,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
if ty.sons[0] == nil: break
ty = skipTypes(ty.sons[0], {tyGenericInst})
if f != nil:
if sfExported in f.flags or getModule(f).id == c.module.id:
# is the access to a public field or in the same module?
var fmoduleId = getModule(f).id
if sfExported in f.flags or fmoduleId == c.module.id or
fmoduleId == c.friendModule.id:
# is the access to a public field or in the same module or in a friend?
n.sons[0] = makeDeref(n.sons[0])
n.sons[1] = newSymNode(f) # we now have the correct field
n.typ = f.typ

View file

@ -22,9 +22,9 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
if q.typ.kind notin {tyTypeDesc, tyGenericParam}: continue
var s = newSym(skType, q.name, getCurrOwner())
s.info = q.info
incl(s.flags, sfUsed)
s.flags = s.flags + {sfUsed, sfFromGeneric}
var t = PType(IdTableGet(pt, q.typ))
if t == nil:
if t == nil:
LocalError(a.info, errCannotInstantiateX, s.name.s)
break
if t.kind == tyGenericParam:
@ -45,9 +45,9 @@ proc sameInstantiation(a, b: TInstantiatedSymbol): bool =
result = true
proc GenericCacheGet(c: PContext, entry: var TInstantiatedSymbol): PSym =
for i in countup(0, Len(generics) - 1):
if sameInstantiation(entry, generics[i]):
result = generics[i].instSym
for i in countup(0, Len(c.generics.generics) - 1):
if sameInstantiation(entry, c.generics.generics[i]):
result = c.generics.generics[i].instSym
# checking for the concrete parameter list is wrong and unnecessary!
#if equalParams(b.typ.n, instSym.typ.n) == paramsEqual:
#echo "found in cache: ", getProcHeader(result)
@ -86,9 +86,9 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
popProcCon(c)
proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
for i in countup(0, Len(generics) - 1):
if generics[i].genericSym.id == s.id:
var oldPrc = generics[i].instSym
for i in countup(0, Len(c.generics.generics) - 1):
if c.generics.generics[i].genericSym.id == s.id:
var oldPrc = c.generics.generics[i].instSym
pushInfoContext(oldPrc.info)
openScope(c.tab)
var n = oldPrc.ast
@ -112,10 +112,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
if c.InstCounter > 1000: InternalError(fn.ast.info, "nesting too deep")
inc(c.InstCounter)
# NOTE: for access of private fields within generics from a different module
# and other identifiers we fake the current module temporarily!
# XXX bad hack!
var oldMod = c.module
c.module = getModule(fn)
# we set the friend module:
var oldFriend = c.friendModule
c.friendModule = getModule(fn)
result = copySym(fn, false)
incl(result.flags, sfFromGeneric)
result.owner = getCurrOwner().owner
@ -144,7 +143,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
ParamsTypeCheck(c, result.typ)
var oldPrc = GenericCacheGet(c, entry)
if oldPrc == nil:
generics.add(entry)
c.generics.generics.add(entry)
if n.sons[pragmasPos].kind != nkEmpty:
pragma(c, result, n.sons[pragmasPos], allRoutinePragmas)
instantiateBody(c, n, result)
@ -154,7 +153,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
popInfoContext()
closeScope(c.tab) # close scope for parameters
popOwner()
c.module = oldMod
c.friendModule = oldFriend
dec(c.InstCounter)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =

View file

@ -527,8 +527,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
typ = paramType(c, a.sons[length-2], genericParams, cl)
#if matchType(typ, [(tyVar, 0)], tyGenericInvokation):
# debug a.sons[length-2][0][1]
else:
else:
typ = nil
if a.sons[length-1].kind != nkEmpty:
def = semExprWithType(c, a.sons[length-1])

View file

@ -105,7 +105,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
var header: PType = nil
when true:
# search for some instantiation here:
result = searchInstTypes(gInstTypes, t)
result = searchInstTypes(cl.c.generics.InstTypes, t)
if result != nil: return
for i in countup(1, sonsLen(t) - 1):
var x = t.sons[i]
@ -116,7 +116,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
#idTablePut(cl.typeMap, body.sons[i-1], x)
if header != nil:
# search again after first pass:
result = searchInstTypes(gInstTypes, header)
result = searchInstTypes(cl.c.generics.InstTypes, header)
if result != nil: return
else:
header = copyType(t, t.owner, false)
@ -124,7 +124,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
# we need to add the candidate here, before it's fully instantiated for
# recursive instantions:
result = newType(tyGenericInst, t.sons[0].owner)
idTablePut(gInstTypes, header, result)
idTablePut(cl.c.generics.InstTypes, header, result)
for i in countup(1, sonsLen(t) - 1):
var x = replaceTypeVarsT(cl, t.sons[i])
@ -154,14 +154,14 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
assert x.kind != tyGenericInvokation
idTablePut(cl.typeMap, body.sons[i-1], x)
if header == nil: header = t
result = searchInstTypes(gInstTypes, header)
result = searchInstTypes(cl.c.generics.InstTypes, header)
if result != nil: return
result = newType(tyGenericInst, t.sons[0].owner)
for i in countup(0, sonsLen(t) - 1):
# if one of the params is not concrete, we cannot do anything
# but we already raised an error!
addSon(result, header.sons[i])
idTablePut(gInstTypes, header, result)
idTablePut(cl.c.generics.InstTypes, header, result)
var newbody = ReplaceTypeVarsT(cl, lastSon(body))
newbody.flags = newbody.flags + t.flags + body.flags
newbody.n = ReplaceTypeVarsN(cl, lastSon(body).n)

View file

@ -7,8 +7,8 @@
# distribution, for details about the copyright.
#
# This module implements the signature matching for resolving
# the call to overloaded procs, generic procs and operators.
## This module implements the signature matching for resolving
## the call to overloaded procs, generic procs and operators.
import
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,