compilation cache: methods have a chance to work

This commit is contained in:
Araq 2011-10-23 11:24:52 +02:00
commit bd1cb9e77b
7 changed files with 100 additions and 35 deletions

View file

@ -8,7 +8,7 @@
# #
## Thread var support for crappy architectures that lack native support for ## Thread var support for crappy architectures that lack native support for
## thread local storage. ## thread local storage. (**Thank you Mac OS X!**)
proc emulatedThreadVars(): bool {.inline.} = proc emulatedThreadVars(): bool {.inline.} =
result = optThreads in gGlobalOptions result = optThreads in gGlobalOptions
@ -23,9 +23,17 @@ proc AccessThreadLocalVar(p: BProc, s: PSym) =
appcg(p, cpsInit, "NimTV=(NimThreadVars*)#GetThreadLocalVars();$n") appcg(p, cpsInit, "NimTV=(NimThreadVars*)#GetThreadLocalVars();$n")
var var
nimtv: PRope # nimrod thread vars nimtv: PRope # nimrod thread vars; the struct body
nimtvDeps: seq[PType] = @[] nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct
nimtvDeclared = initIntSet() 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) = proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
if emulatedThreadVars(): if emulatedThreadVars():

View file

@ -9,10 +9,11 @@
# This module is responsible for loading of rod files. # This module is responsible for loading of rod files.
# #
# Reading and writing binary files are really hard to debug. Therefore we use # Reading and writing binary files are really hard to debug. Therefore we use
# a special text format. ROD-files are more efficient to process because # a "creative" text/binary hybrid format. ROD-files are more efficient
# symbols are only loaded on demand. # to process because symbols are can be loaded on demand.
# It consists of: #
# A ROD file consists of:
# #
# - a header: # - a header:
# NIM:$fileversion\n # NIM:$fileversion\n
@ -28,7 +29,7 @@
# myfile.inc # myfile.inc
# lib/mymodA # lib/mymodA
# ) # )
# - a include file dependency section: # - an include file dependency section:
# INCLUDES( # INCLUDES(
# <fileidx> <CRC of myfile.inc>\n # fileidx is the LINE in the file section! # <fileidx> <CRC of myfile.inc>\n # fileidx is the LINE in the file section!
# ) # )
@ -48,6 +49,11 @@
# id-diff idx-diff\n # id-diff idx-diff\n
# id-diff idx-diff\n # id-diff idx-diff\n
# ) # )
#
# Since the whole index has to be read in advance, we compress it by
# storing the integer differences to the last entry instead of using the
# real numbers.
#
# - an import index consisting of (ID, moduleID)-pairs: # - an import index consisting of (ID, moduleID)-pairs:
# IMPORTS( # IMPORTS(
# id-diff moduleID-diff\n # id-diff moduleID-diff\n
@ -55,7 +61,12 @@
# ) # )
# - a list of all exported type converters because they are needed for correct # - a list of all exported type converters because they are needed for correct
# semantic checking: # semantic checking:
# CONVERTERS:id id\n # position of the symbol in the DATA section # CONVERTERS:id id\n # symbol ID
#
# - a list of all (private or exported) methods because they are needed for
# correct dispatcher generation:
# METHODS: id id\n # symbol ID
#
# - an AST section that contains the module's AST: # - an AST section that contains the module's AST:
# INIT( # INIT(
# idx\n # position of the node in the DATA section # idx\n # position of the node in the DATA section
@ -72,8 +83,6 @@
# stops immediately after ``DATA(`` and the rest is only loaded on demand # stops immediately after ``DATA(`` and the rest is only loaded on demand
# by using mem'mapped a file. # by using mem'mapped a file.
# #
# We now also do index compression, because an index always needs to be read.
#
import import
os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
@ -116,7 +125,7 @@ type
files: TStringSeq files: TStringSeq
dataIdx: int # offset of start of data section dataIdx: int # offset of start of data section
convertersIdx: int # offset of start of converters section convertersIdx: int # offset of start of converters section
initIdx, interfIdx, compilerProcsIdx: int initIdx, interfIdx, compilerProcsIdx, methodsIdx: int
filename: string filename: string
index, imports: TIndex index, imports: TIndex
readerIndex: int readerIndex: int
@ -125,6 +134,7 @@ type
syms: TIdTable # already processed symbols syms: TIdTable # already processed symbols
memfile: TMemFile # unfortunately there is no point in time where we memfile: TMemFile # unfortunately there is no point in time where we
# can close this! XXX # can close this! XXX
methods*: TSymSeq
PRodReader* = ref TRodReader PRodReader* = ref TRodReader
@ -547,6 +557,9 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
of "CONVERTERS": of "CONVERTERS":
r.convertersIdx = r.pos + 1 r.convertersIdx = r.pos + 1
skipSection(r) skipSection(r)
of "METHODS":
r.methodsIdx = r.pos + 1
skipSection(r)
of "DATA": of "DATA":
r.dataIdx = r.pos + 2 # "(\10" r.dataIdx = r.pos + 2 # "(\10"
# We do not read the DATA section here! We read the needed objects on # We do not read the DATA section here! We read the needed objects on
@ -573,6 +586,7 @@ proc newRodReader(modfilename: string, crc: TCrc32,
new(result) new(result)
result.files = @[] result.files = @[]
result.modDeps = @[] result.modDeps = @[]
result.methods = @[]
var r = result var r = result
r.reason = rrNone r.reason = rrNone
r.pos = 0 r.pos = 0
@ -681,7 +695,8 @@ proc loadInitSection(r: PRodReader): PNode =
r.pos = oldPos r.pos = oldPos
proc loadConverters(r: PRodReader) = proc loadConverters(r: PRodReader) =
# We have to ensure that no exported converter is a stub anymore. # We have to ensure that no exported converter is a stub anymore, and the
# import mechanism takes care of the rest.
if r.convertersIdx == 0 or r.dataIdx == 0: if r.convertersIdx == 0 or r.dataIdx == 0:
InternalError("importConverters") InternalError("importConverters")
r.pos = r.convertersIdx r.pos = r.convertersIdx
@ -690,6 +705,15 @@ proc loadConverters(r: PRodReader) =
discard rrGetSym(r, d, UnknownLineInfo()) discard rrGetSym(r, d, UnknownLineInfo())
if r.s[r.pos] == ' ': inc(r.pos) if r.s[r.pos] == ' ': inc(r.pos)
proc loadMethods(r: PRodReader) =
if r.methodsIdx == 0 or r.dataIdx == 0:
InternalError("loadMethods")
r.pos = r.methodsIdx
while r.s[r.pos] > '\x0A':
var d = decodeVInt(r.s, r.pos)
r.methods.add(rrGetSym(r, d, UnknownLineInfo()))
if r.s[r.pos] == ' ': inc(r.pos)
proc getModuleIdx(filename: string): int = proc getModuleIdx(filename: string): int =
for i in countup(0, high(gMods)): for i in countup(0, high(gMods)):
if sameFile(gMods[i].filename, filename): return i if sameFile(gMods[i].filename, filename): return i
@ -752,6 +776,7 @@ proc handleSymbolFile(module: PSym, filename: string): PRodReader =
processInterf(result, module) processInterf(result, module)
processCompilerProcs(result, module) processCompilerProcs(result, module)
loadConverters(result) loadConverters(result)
loadMethods(result)
else: else:
module.id = getID() module.id = getID()

View file

@ -29,7 +29,7 @@ type
interf: string interf: string
compilerProcs: string compilerProcs: string
index, imports: TIndex index, imports: TIndex
converters: string converters, methods: string
init: string init: string
data: string data: string
filename: string filename: string
@ -85,6 +85,7 @@ proc newRodWriter(modfilename: string, crc: TCrc32, module: PSym): PRodWriter =
result.interf = newStringOfCap(2_000) result.interf = newStringOfCap(2_000)
result.compilerProcs = "" result.compilerProcs = ""
result.converters = "" result.converters = ""
result.methods = ""
result.init = "" result.init = ""
result.data = newStringOfCap(12_000) result.data = newStringOfCap(12_000)
@ -350,6 +351,9 @@ proc symStack(w: PRodWriter) =
if s.kind == skConverter: if s.kind == skConverter:
if w.converters.len != 0: add(w.converters, ' ') if w.converters.len != 0: add(w.converters, ' ')
encodeVInt(s.id, w.converters) encodeVInt(s.id, w.converters)
elif s.kind == skMethod:
if w.methods.len != 0: add(w.methods, ' ')
encodeVInt(s.id, w.methods)
elif IiTableGet(w.imports.tab, s.id) == invalidKey: elif IiTableGet(w.imports.tab, s.id) == invalidKey:
addToIndex(w.imports, s.id, m.id) #if not Contains(debugWritten, s.id): addToIndex(w.imports, s.id, m.id) #if not Contains(debugWritten, s.id):
# MessageOut(w.filename); # MessageOut(w.filename);
@ -456,6 +460,10 @@ proc writeRod(w: PRodWriter) =
f.write(w.converters) f.write(w.converters)
f.write(rodNL) f.write(rodNL)
f.write("METHODS:")
f.write(w.methods)
f.write(rodNL)
f.write("INIT(" & rodNL) f.write("INIT(" & rodNL)
f.write(w.init) f.write(w.init)
f.write(')' & rodNL) f.write(')' & rodNL)
@ -486,8 +494,8 @@ proc process(c: PPassContext, n: PNode): PNode =
of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef: of nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef:
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
if s == nil: InternalError(n.info, "rodwrite.process") if s == nil: InternalError(n.info, "rodwrite.process")
if (n.sons[codePos].kind != nkEmpty) or (s.magic != mNone) or if n.sons[codePos].kind != nkEmpty or s.magic != mNone or
not (sfForward in s.flags): sfForward notin s.flags:
addInterfaceSym(w, s) addInterfaceSym(w, s)
of nkVarSection: of nkVarSection:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):

View file

@ -18,7 +18,7 @@
import import
intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, passes, semfold, magicsys, cgmeth idents, renderer, types, passes, semfold, magicsys, cgmeth, rodread
const const
genPrefix* = ":tmp" # prefix for generated names genPrefix* = ":tmp" # prefix for generated names
@ -726,7 +726,7 @@ proc processTransf(context: PPassContext, n: PNode): PNode =
# Note: For interactive mode we cannot call 'passes.skipCodegen' and skip # Note: For interactive mode we cannot call 'passes.skipCodegen' and skip
# this step! We have to rely that the semantic pass transforms too errornous # this step! We have to rely that the semantic pass transforms too errornous
# nodes into an empty node. # nodes into an empty node.
if passes.skipCodegen(n): return n if passes.skipCodegen(n) or context.fromCache: return n
var c = PTransf(context) var c = PTransf(context)
pushTransCon(c, newTransCon(getCurrOwner(c))) pushTransCon(c, newTransCon(getCurrOwner(c)))
result = PNode(transform(c, n)) result = PNode(transform(c, n))
@ -739,9 +739,15 @@ proc openTransf(module: PSym, filename: string): PPassContext =
n.module = module n.module = module
result = n result = n
proc openTransfCached(module: PSym, filename: string,
rd: PRodReader): PPassContext =
result = openTransf(module, filename)
for m in items(rd.methods): methodDef(m)
proc transfPass(): TPass = proc transfPass(): TPass =
initPass(result) initPass(result)
result.open = openTransf result.open = openTransf
result.openCached = openTransfCached
result.process = processTransf result.process = processTransf
result.close = processTransf # we need to process generics too! result.close = processTransf # we need to process generics too!

View file

@ -1951,7 +1951,9 @@ template spliceImpl(x, start, endp, spliced: expr): stmt =
setLen(x, newLen) setLen(x, newLen)
proc `[]=`*(s: var string, x: TSlice[int], b: string) = proc `[]=`*(s: var string, x: TSlice[int], b: string) =
## slice assignment for strings. Negative indexes are supported. ## slice assignment for strings. Negative indexes are supported. If
## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed.
var a = x.a-|s var a = x.a-|s
var L = x.b-|s - a + 1 var L = x.b-|s - a + 1
if L == b.len: if L == b.len:
@ -1960,15 +1962,15 @@ proc `[]=`*(s: var string, x: TSlice[int], b: string) =
spliceImpl(s, x.a, x.b, b) spliceImpl(s, x.a, x.b, b)
proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[int]): seq[T] = proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[int]): seq[T] =
## slice operation for arrays. Negative indexes are NOT supported because ## slice operation for arrays. Negative indexes are **not** supported
## the array might have negative bounds. ## because the array might have negative bounds.
var L = x.b - x.a + 1 var L = x.b - x.a + 1
newSeq(result, L) newSeq(result, L)
for i in 0.. <L: result[i] = a[i + x.a] for i in 0.. <L: result[i] = a[i + x.a]
proc `[]=`*[Idx, T](a: var array[Idx, T], x: TSlice[int], b: openArray[T]) = proc `[]=`*[Idx, T](a: var array[Idx, T], x: TSlice[int], b: openArray[T]) =
## slice assignment for arrays. Negative indexes are NOT supported because ## slice assignment for arrays. Negative indexes are **not** supported
## the array might have negative bounds. ## because the array might have negative bounds.
var L = x.b - x.a + 1 var L = x.b - x.a + 1
if L == b.len: if L == b.len:
for i in 0 .. <L: a[i+x.a] = b[i] for i in 0 .. <L: a[i+x.a] = b[i]
@ -1976,8 +1978,8 @@ proc `[]=`*[Idx, T](a: var array[Idx, T], x: TSlice[int], b: openArray[T]) =
raise newException(EOutOfRange, "differing lengths for slice assignment") raise newException(EOutOfRange, "differing lengths for slice assignment")
proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[Idx]): seq[T] = proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[Idx]): seq[T] =
## slice operation for arrays. Negative indexes are NOT supported because ## slice operation for arrays. Negative indexes are **not** supported
## the array might have negative bounds. ## because the array might have negative bounds.
var L = ord(x.b) - ord(x.a) + 1 var L = ord(x.b) - ord(x.a) + 1
newSeq(result, L) newSeq(result, L)
var j = x.a var j = x.a
@ -1986,8 +1988,8 @@ proc `[]`*[Idx, T](a: array[Idx, T], x: TSlice[Idx]): seq[T] =
inc(j) inc(j)
proc `[]=`*[Idx, T](a: var array[Idx, T], x: TSlice[Idx], b: openArray[T]) = proc `[]=`*[Idx, T](a: var array[Idx, T], x: TSlice[Idx], b: openArray[T]) =
## slice assignment for arrays. Negative indexes are NOT supported because ## slice assignment for arrays. Negative indexes are **not** supported
## the array might have negative bounds. ## because the array might have negative bounds.
var L = ord(x.b) - ord(x.a) + 1 var L = ord(x.b) - ord(x.a) + 1
if L == b.len: if L == b.len:
var j = x.a var j = x.a
@ -2005,7 +2007,9 @@ proc `[]`*[T](s: seq[T], x: TSlice[int]): seq[T] =
for i in 0.. <L: result[i] = s[i + a] for i in 0.. <L: result[i] = s[i + a]
proc `[]=`*[T](s: var seq[T], x: TSlice[int], b: openArray[T]) = proc `[]=`*[T](s: var seq[T], x: TSlice[int], b: openArray[T]) =
## slice assignment for sequences. Negative indexes are supported. ## slice assignment for sequences. Negative indexes are supported. If
## ``b.len`` is not exactly the number of elements that are referred to
## by `x`, a `splice`:idx: is performed.
var a = x.a-|s var a = x.a-|s
var L = x.b-|s - a + 1 var L = x.b-|s - a + 1
if L == b.len: if L == b.len:

View file

@ -1,25 +1,34 @@
Version 0.8.14 Version 0.8.14
============== ==============
- optimize unused constants away - optimize unused constants away (affected by HLO)
- 'let x = y' - 'let x = y'
- fix actors.nim - fix actors.nim
- make threadvar efficient again on linux after testing - make threadvar efficient again on linux after testing
- test the sort implementation again - test the sort implementation again
- document & test splicing; don't forget to test negative indexes - document & test splicing; don't forget to test negative indexes
- implement lib/pure/memfiles properly - implement lib/pure/memfiles properly
- eval context is per module; this way modularity is kept; global id generation
macro can still be done once macros support basic IO (store current id in
some file)
incremental compilation incremental compilation
----------------------- -----------------------
- adapt thread var implementation to care about the new merge operation - adapt thread var implementation to care about the new merge operation
- write test cases: needs test script support - write test cases: needs test script support
- test type converters - test G, A, B example from the documentation
- test thread var - test thread var
- test method generation - test method generation; could work, needs ROD support though
- test init sections - test init sections
- test DLL interfacing! - test DLL interfacing!
- hallo.rod is missing initial statements: feature or bug? - hallo.rod is missing initial statements: feature or bug?
- automate tests:
- test basic recompilation scheme
- test type converters
- fix remaining bugs - fix remaining bugs
@ -57,6 +66,9 @@ Bugs
result = forward(x) result = forward(x)
- bug: DLL generation is broken - bug: DLL generation is broken
- bug: stress testing basic method example (eval) without ``-d:relase`` leaks
memory; good way to figure out how a fixed amount of stack can hold
an arbitrary number of GC roots!
version 0.9.XX version 0.9.XX

View file

@ -61,6 +61,8 @@ Language Additions
heart's content. heart's content.
- ``bind`` (used for symbol binding in templates and generics) is now a - ``bind`` (used for symbol binding in templates and generics) is now a
declarative statement. declarative statement.
- The slice assignment ``a[i..j] = b`` where ``a`` is a sequence or string
now supports *splicing*.
Compiler Additions Compiler Additions