summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/astalgo.nim5
-rw-r--r--compiler/ccgmerge.nim253
-rwxr-xr-xcompiler/cgen.nim145
-rw-r--r--compiler/cgendata.nim116
-rw-r--r--compiler/idgen.nim9
-rwxr-xr-xcompiler/rodutils.nim4
-rwxr-xr-xcompiler/ropes.nim6
-rwxr-xr-xtodo.txt21
8 files changed, 441 insertions, 118 deletions
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index b06194daf..472a8d803 100755
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -720,6 +720,11 @@ proc IdTableGet(t: TIdTable, key: int): PObject =
   var index = IdTableRawGet(t, key)
   if index >= 0: result = t.data[index].val
   else: result = nil
+
+iterator pairs*(t: TIdTable): tuple[key: int, value: PObject] =
+  for i in 0..high(t.data):
+    if t.data[i].key != nil:
+      yield (t.data[i].key.id, t.data[i].val)
   
 proc IdTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: PObject) = 
   var h: THash
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
new file mode 100644
index 000000000..252b11079
--- /dev/null
+++ b/compiler/ccgmerge.nim
@@ -0,0 +1,253 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2011 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements the merge operation of 2 different C files. This
+## is needed for incremental compilation.
+
+import
+  ast, astalgo, ropes, options, strutils, lexbase, msgs, cgendata, rodutils,
+  intsets, platform, llstream
+
+# Careful! Section marks need to contain a tabulator so that they cannot
+# be part of C string literals.
+
+const
+  CFileSectionNames: array[TCFileSection, string] = [
+    cfsMergeInfo: "",
+    cfsHeaders: "NIM_merge_HEADERS",
+    cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
+    cfsTypes: "NIM_merge_TYPES",
+    cfsSeqTypes: "NIM_merge_SEQ_TYPES",
+    cfsFieldInfo: "NIM_merge_FIELD_INFO",
+    cfsTypeInfo: "NIM_merge_TYPE_INFO",
+    cfsProcHeaders: "NIM_merge_PROC_HEADERS",
+    cfsData: "NIM_merge_DATA",
+    cfsVars: "NIM_merge_VARS",
+    cfsProcs: "NIM_merge_PROCS",
+    cfsInitProc: "NIM_merge_INIT_PROC",
+    cfsTypeInit1: "NIM_merge_TYPE_INIT1",
+    cfsTypeInit2: "NIM_merge_TYPE_INIT2",
+    cfsTypeInit3: "NIM_merge_TYPE_INIT3",
+    cfsDebugInit: "NIM_merge_DEBUG_INIT",
+    cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
+    cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
+  ]
+  CProcSectionNames: array[TCProcSection, string] = [
+    cpsLocals: "NIM_merge_PROC_LOCALS",
+    cpsInit: "NIM_merge_PROC_INIT",
+    cpsStmts: "NIM_merge_PROC_BODY"
+  ]
+  
+proc genSectionStart*(fs: TCFileSection): PRope =
+  if optSymbolFiles in gGlobalOptions:
+    result = toRope(tnl)
+    app(result, "/*\t")
+    app(result, CFileSectionNames[fs])
+    app(result, "*/")
+    app(result, tnl)
+
+proc genSectionEnd*(fs: TCFileSection): PRope =
+  if optSymbolFiles in gGlobalOptions:
+    result = toRope("/*\tNIM_merge_END*/" & tnl)
+
+proc genSectionStart*(ps: TCProcSection): PRope =
+  if optSymbolFiles in gGlobalOptions:
+    result = toRope(tnl)
+    app(result, "/*\t")
+    app(result, CProcSectionNames[ps])
+    app(result, "*/")
+    app(result, tnl)
+
+proc genSectionEnd*(ps: TCProcSection): PRope =
+  if optSymbolFiles in gGlobalOptions:
+    result = toRope("/*\tNIM_merge_END*/" & tnl)
+
+proc writeTypeCache(a: TIdTable, s: var string) =
+  var i = 0
+  for id, value in pairs(a):
+    if i == 10:
+      i = 0
+      s.add(tnl)
+    else:
+      s.add(' ')
+    encodeVInt(id, s)
+    s.add(':')
+    encodeStr(PRope(value).ropeToStr, s)
+    inc i
+  s.add('}')
+
+proc writeIntSet(a: TIntSet, s: var string) =
+  var i = 0
+  for x in items(a):
+    if i == 10:
+      i = 0
+      s.add(tnl)
+    else:
+      s.add(' ')
+    encodeVInt(x, s)
+    inc i
+  s.add('}')
+  
+proc genMergeInfo*(m: BModule): PRope =
+  if optSymbolFiles notin gGlobalOptions: return nil
+  var s = "/*\tNIM_merge_INFO:"
+  s.add(tnl)
+  s.add("typeCache:{")
+  writeTypeCache(m.typeCache, s)
+  s.add("declared:{")
+  writeIntSet(m.declaredThings, s)
+  s.add("typeInfo:{")
+  writeIntSet(m.typeInfoMarker, s)
+  s.add("labels:")
+  encodeVInt(m.labels, s)
+  s.add(tnl)
+  s.add("*/")
+  result = s.toRope
+
+template `^`(pos: expr): expr = L.buf[pos]
+
+proc skipWhite(L: var TBaseLexer) =
+  var pos = L.bufpos
+  while true:
+    case ^pos
+    of CR: pos = lexbase.HandleCR(L, pos)
+    of LF: pos = lexbase.HandleLF(L, pos)
+    of ' ': inc pos
+    else: break
+  L.bufpos = pos
+
+proc skipUntilCmd(L: var TBaseLexer) =
+  var pos = L.bufpos
+  while true:
+    case ^pos
+    of CR: pos = lexbase.HandleCR(L, pos)
+    of LF: pos = lexbase.HandleLF(L, pos)
+    of '\0': break
+    of '/': 
+      if ^(pos+1) == '*' and ^(pos+2) == '\t':
+        inc pos, 3
+        break
+      inc pos
+    else: inc pos
+  L.bufpos = pos
+
+proc readVerbatimSection(L: var TBaseLexer): PRope = 
+  const section = "/*\tNIM_merge_END*/"
+  var pos = L.bufpos
+  var buf = L.buf
+  result = newMutableRope(30_000)
+  while true:
+    case buf[pos]
+    of CR:
+      pos = lexbase.HandleCR(L, pos)
+      buf = L.buf
+      result.data.add(tnl)
+    of LF:
+      pos = lexbase.HandleLF(L, pos)
+      buf = L.buf
+      result.data.add(tnl)
+    of '\0': break
+    else: nil
+    if buf[pos] == section[0]:
+      var s = 0
+      while buf[pos+1] == section[s+1]:
+        inc s
+        inc pos
+      if section[s] != '\0':
+        # reset:
+        dec pos, s
+      else:
+        break
+    result.data.add(buf[pos])
+    inc pos
+  L.bufpos = pos
+  result.length = result.data.len
+
+proc readKey(L: var TBaseLexer): string =
+  var pos = L.bufpos
+  var buf = L.buf
+  while buf[pos] in IdentChars:
+    result.add(buf[pos])
+    inc pos
+  if buf[pos] != ':': internalError("ccgmerge: ':' expected")
+  L.bufpos = pos + 1 # skip ':'
+
+proc NewFakeType(id: int): PType = 
+  new(result)
+  result.id = id
+
+proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
+  if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
+  inc L.bufpos
+  while ^L.bufpos != '}':
+    skipWhite(L)
+    var key = decodeVInt(L.buf, L.bufpos)
+    if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
+    inc L.bufpos
+    var value = decodeStr(L.buf, L.bufpos)
+    # XXX little hack: we create a "fake" type object with the correct Id
+    # better would be to adapt the data structure to not even store the
+    # object as key, but only the Id
+    IdTablePut(result, newFakeType(key), value.toRope)
+  inc L.bufpos
+
+proc readIntSet(L: var TBaseLexer, result: var TIntSet) =
+  if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
+  inc L.bufpos
+  while ^L.bufpos != '}':
+    skipWhite(L)
+    var key = decodeVInt(L.buf, L.bufpos)
+    result.incl(key)
+  inc L.bufpos
+
+proc processMergeInfo(L: var TBaseLexer, m: BModule) =
+  while true:
+    skipWhite(L)
+    if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
+      inc(L.bufpos, 2)
+      break
+    var k = readKey(L)
+    case k
+    of "typeCache": readTypeCache(L, m.typeCache)
+    of "declared":  readIntSet(L, m.declaredThings)
+    of "typeInfo":  readIntSet(L, m.typeInfoMarker)
+    of "labels":    m.labels = decodeVInt(L.buf, L.bufpos)
+    else: InternalError("ccgmerge: unkown key: " & k)
+  
+proc readMergeInfo*(cfilename: string, m: BModule) =
+  ## reads the merge information into `m`.
+  var s = LLStreamOpen(cfilename, fmRead)
+  if s == nil: return
+  var L: TBaseLexer
+  openBaseLexer(L, s)
+  while true:
+    skipUntilCmd(L)
+    if ^L.bufpos == '\0': break
+    var k = readKey(L)
+    if k == "NIM_merge_INFO":   
+      processMergeInfo(L, m)
+    elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
+      inc(L.bufpos, 2)
+      # read back into section
+      skipWhite(L)
+      var verbatim = readVerbatimSection(L)
+      skipWhite(L)
+      var sectionA = CFileSectionNames.find(k)
+      if sectionA > 0 and sectionA <= high(TCFileSection).int:
+        m.s[TCFileSection(sectionA)] = verbatim
+      else:
+        var sectionB = CProcSectionNames.find(k)
+        if sectionB >= 0 and sectionB <= high(TCProcSection).int:
+          m.initProc.s[TCProcSection(sectionB)] = verbatim
+        else:
+          InternalError("ccgmerge: unknown section: " & k)
+    else:
+      InternalError("ccgmerge: */ expected")
+  closeBaseLexer(L)
+
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 7cfcbe098..9c0a1d1f7 100755
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -15,7 +15,7 @@ import
   options, intsets,
   nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
   times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
-  rodutils, renderer, idgen
+  rodutils, renderer, idgen, cgendata, ccgmerge
 
 when options.hasTinyCBackend:
   import tccgen
@@ -23,96 +23,6 @@ when options.hasTinyCBackend:
 proc cgenPass*(): TPass
 # implementation
 
-type 
-  TLabel = PRope              # for the C generator a label is just a rope
-  TCFileSection = enum        # the sections a generated C file consists of
-    cfsHeaders,               # section for C include file headers
-    cfsForwardTypes,          # section for C forward typedefs
-    cfsTypes,                 # section for C typedefs
-    cfsSeqTypes,              # section for sequence types only
-                              # this is needed for strange type generation
-                              # reasons
-    cfsFieldInfo,             # section for field information
-    cfsTypeInfo,              # section for type information
-    cfsProcHeaders,           # section for C procs prototypes
-    cfsData,                  # section for C constant data
-    cfsVars,                  # section for C variable declarations
-    cfsProcs,                 # section for C procs that are not inline
-    cfsTypeInit1,             # section 1 for declarations of type information
-    cfsTypeInit2,             # section 2 for init of type information
-    cfsTypeInit3,             # section 3 for init of type information
-    cfsDebugInit,             # section for init of debug information
-    cfsDynLibInit,            # section for init of dynamic library binding
-    cfsDynLibDeinit           # section for deinitialization of dynamic
-                              # libraries
-  TCTypeKind = enum           # describes the type kind of a C type
-    ctVoid, ctChar, ctBool, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64, 
-    ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64, 
-    ctFloat128, ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
-  TCFileSections = array[TCFileSection, PRope] # represents a generated C file
-  TCProcSection = enum        # the sections a generated C proc consists of
-    cpsLocals,                # section of local variables for C proc
-    cpsInit,                  # section for init of variables for C proc
-    cpsStmts                  # section of local statements for C proc
-  TCProcSections = array[TCProcSection, PRope] # represents a generated C proc
-  BModule = ref TCGen
-  BProc = ref TCProc
-  TBlock{.final.} = object 
-    id*: int                  # the ID of the label; positive means that it
-                              # has been used (i.e. the label should be emitted)
-    nestedTryStmts*: int      # how many try statements is it nested into
-  
-  TCProc{.final.} = object   # represents C proc that is currently generated
-    s: TCProcSections        # the procs sections; short name for readability
-    prc: PSym                # the Nimrod proc that this C proc belongs to
-    BeforeRetNeeded: bool    # true iff 'BeforeRet' label for proc is needed
-    ThreadVarAccessed: bool  # true if the proc already accessed some threadvar
-    nestedTryStmts: seq[PNode] # in how many nested try statements we are
-                               # (the vars must be volatile then)
-    labels: Natural          # for generating unique labels in the C proc
-    blocks: seq[TBlock]      # nested blocks
-    options: TOptions        # options that should be used for code
-                             # generation; this is the same as prc.options
-                             # unless prc == nil
-    frameLen: int            # current length of frame descriptor
-    sendClosure: PType       # closure record type that we pass
-    receiveClosure: PType    # closure record type that we get
-    module: BModule          # used to prevent excessive parameter passing
-    withinLoop: int          # > 0 if we are within a loop
-  
-  TTypeSeq = seq[PType]
-  TCGen = object of TPassContext # represents a C source file
-    module*: PSym
-    filename*: string
-    s*: TCFileSections        # sections of the C file
-    PreventStackTrace: bool   # true if stack traces need to be prevented
-    usesThreadVars: bool      # true if the module uses a thread var
-    cfilename*: string        # filename of the module (including path,
-                              # without extension)
-    typeCache*: TIdTable      # cache the generated types
-    forwTypeCache*: TIdTable  # cache for forward declarations of types
-    declaredThings*: TIntSet  # things we have declared in this .c file
-    declaredProtos*: TIntSet  # prototypes we have declared in this .c file
-    headerFiles*: TLinkedList # needed headers to include
-    typeInfoMarker*: TIntSet  # needed for generating type information
-    initProc*: BProc          # code for init procedure
-    typeStack*: TTypeSeq      # used for type generation
-    dataCache*: TNodeTable
-    forwardedProcs*: TSymSeq  # keep forwarded procs here
-    typeNodes*, nimTypes*: int # used for type info generation
-    typeNodesName*, nimTypesName*: PRope # used for type info generation
-    labels*: natural          # for generating unique module-scope names
-
-var 
-  mainModProcs, mainModInit: PRope # parts of the main module
-  gMapping: PRope             # the generated mapping file (if requested)
-  gProcProfile: Natural       # proc profile counter
-  gGeneratedSyms: TIntSet     # set of ID's of generated symbols
-  gPendingModules: seq[BModule] = @[] # list of modules that are not
-                                      # finished with code generation
-  gForwardedProcsCounter: int = 0
-  gNimDat: BModule            # generated global data
-
 proc ropeff(cformat, llvmformat: string, args: openarray[PRope]): PRope = 
   if gCmd == cmdCompileToLLVM: result = ropef(llvmformat, args)
   else: result = ropef(cformat, args)
@@ -157,15 +67,6 @@ proc fillLoc(a: var TLoc, k: TLocKind, typ: PType, r: PRope, s: TStorageLoc) =
     a.s = s
     if a.r == nil: a.r = r
   
-proc newProc(prc: PSym, module: BModule): BProc = 
-  new(result)
-  result.prc = prc
-  result.module = module
-  if prc != nil: result.options = prc.options
-  else: result.options = gOptions
-  result.blocks = @[]
-  result.nestedTryStmts = @[]
-
 proc isSimpleConst(typ: PType): bool = 
   result = not (skipTypes(typ, abstractVar).kind in
       {tyTuple, tyObject, tyArray, tyArrayConstr, tySet, tySequence})
@@ -864,31 +765,53 @@ proc genInitCode(m: BModule) =
     # BUT: the generated init code might depend on a current frame, so
     # declare it nevertheless:
     getFrameDecl(m.initProc)
+  
+  app(prc, genSectionStart(cpsLocals))
+  app(prc, m.initProc.s[cpsLocals])
+  app(prc, genSectionEnd(cpsLocals))
+
+  app(prc, genSectionStart(cfsTypeInit1))
+  app(prc, m.s[cfsTypeInit1])
   if optStackTrace in m.initProc.options and not m.PreventStackTrace: 
-    app(prc, m.initProc.s[cpsLocals])
-    app(prc, m.s[cfsTypeInit1])
     var procname = CStringLit(m.initProc, prc, m.module.name.s)
     var filename = CStringLit(m.initProc, prc, toFilename(m.module.info))
     app(prc, initFrame(m.initProc, procname, filename))
-  else:
-    app(prc, m.initProc.s[cpsLocals])
-    app(prc, m.s[cfsTypeInit1])
-  app(prc, m.s[cfsTypeInit2])
-  app(prc, m.s[cfsTypeInit3])
-  app(prc, m.s[cfsDebugInit])
-  app(prc, m.s[cfsDynLibInit])
+  app(prc, genSectionEnd(cfsTypeInit1))
+  
+  for i in cfsTypeInit2..cfsDynLibInit:
+    app(prc, genSectionStart(i))
+    app(prc, m.s[i])
+    app(prc, genSectionEnd(i))
+  
+  app(prc, genSectionStart(cpsInit))
   app(prc, m.initProc.s[cpsInit])
+  app(prc, genSectionEnd(cpsInit))
+
+  app(prc, genSectionStart(cpsStmts))  
   app(prc, m.initProc.s[cpsStmts])
   if optStackTrace in m.initProc.options and not m.PreventStackTrace: 
     app(prc, deinitFrame(m.initProc))
+  app(prc, genSectionEnd(cpsStmts))
   appf(prc, "}$n$n")
-  app(m.s[cfsProcs], prc)
+  # we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
+  # that would lead to a *nesting* of merge sections which the merger does
+  # not support. So we add it to another special section: ``cfsInitProc``
+  app(m.s[cfsInitProc], prc)
 
 proc genModule(m: BModule, cfilenoext: string): PRope = 
   result = getFileHeader(cfilenoext)
+  result.app(genMergeInfo(m))
+  
+  app(m.s[cfsHeaders], genSectionStart(cfsHeaders))
   generateHeaders(m)
+  app(m.s[cfsHeaders], genSectionEnd(cfsHeaders))
+
   generateThreadLocalStorage(m)
-  for i in countup(low(TCFileSection), cfsProcs): app(result, m.s[i])
+  for i in countup(low(TCFileSection), cfsProcs): 
+    app(result, genSectionStart(i))
+    app(result, m.s[i])
+    app(result, genSectionEnd(i))
+  app(result, m.s[cfsInitProc])
   
 proc rawNewModule(module: PSym, filename: string): BModule = 
   new(result)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
new file mode 100644
index 000000000..a25e7e3db
--- /dev/null
+++ b/compiler/cgendata.nim
@@ -0,0 +1,116 @@
+#
+#
+#           The Nimrod Compiler
+#        (c) Copyright 2011 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module contains the data structures for the C code generation phase.
+
+import 
+  ast, astalgo, ropes, passes, options, intsets, lists, platform
+
+type
+  TLabel* = PRope             # for the C generator a label is just a rope
+  TCFileSection* = enum       # the sections a generated C file consists of
+    cfsMergeInfo,             # section containing merge information
+    cfsHeaders,               # section for C include file headers
+    cfsForwardTypes,          # section for C forward typedefs
+    cfsTypes,                 # section for C typedefs
+    cfsSeqTypes,              # section for sequence types only
+                              # this is needed for strange type generation
+                              # reasons
+    cfsFieldInfo,             # section for field information
+    cfsTypeInfo,              # section for type information
+    cfsProcHeaders,           # section for C procs prototypes
+    cfsData,                  # section for C constant data
+    cfsVars,                  # section for C variable declarations
+    cfsProcs,                 # section for C procs that are not inline
+    cfsInitProc,              # section for the C init proc
+    cfsTypeInit1,             # section 1 for declarations of type information
+    cfsTypeInit2,             # section 2 for init of type information
+    cfsTypeInit3,             # section 3 for init of type information
+    cfsDebugInit,             # section for init of debug information
+    cfsDynLibInit,            # section for init of dynamic library binding
+    cfsDynLibDeinit           # section for deinitialization of dynamic
+                              # libraries
+  TCTypeKind* = enum           # describes the type kind of a C type
+    ctVoid, ctChar, ctBool, ctUInt, ctUInt8, ctUInt16, ctUInt32, ctUInt64, 
+    ctInt, ctInt8, ctInt16, ctInt32, ctInt64, ctFloat, ctFloat32, ctFloat64, 
+    ctFloat128, ctArray, ctStruct, ctPtr, ctNimStr, ctNimSeq, ctProc, ctCString
+  TCFileSections = array[TCFileSection, PRope] # represents a generated C file
+  TCProcSection* = enum       # the sections a generated C proc consists of
+    cpsLocals,                # section of local variables for C proc
+    cpsInit,                  # section for init of variables for C proc
+    cpsStmts                  # section of local statements for C proc
+  TCProcSections = array[TCProcSection, PRope] # represents a generated C proc
+  BModule* = ref TCGen
+  BProc* = ref TCProc
+  TBlock{.final.} = object 
+    id*: int                  # the ID of the label; positive means that it
+                              # has been used (i.e. the label should be emitted)
+    nestedTryStmts*: int      # how many try statements is it nested into
+  
+  TCProc{.final.} = object    # represents C proc that is currently generated
+    s*: TCProcSections        # the procs sections; short name for readability
+    prc*: PSym                # the Nimrod proc that this C proc belongs to
+    BeforeRetNeeded*: bool    # true iff 'BeforeRet' label for proc is needed
+    ThreadVarAccessed*: bool  # true if the proc already accessed some threadvar
+    nestedTryStmts*: seq[PNode] # in how many nested try statements we are
+                                # (the vars must be volatile then)
+    labels*: Natural          # for generating unique labels in the C proc
+    blocks*: seq[TBlock]      # nested blocks
+    options*: TOptions        # options that should be used for code
+                              # generation; this is the same as prc.options
+                              # unless prc == nil
+    frameLen*: int            # current length of frame descriptor
+    sendClosure*: PType       # closure record type that we pass
+    receiveClosure*: PType    # closure record type that we get
+    module*: BModule          # used to prevent excessive parameter passing
+    withinLoop*: int          # > 0 if we are within a loop
+  
+  TTypeSeq* = seq[PType]
+  TCGen = object of TPassContext # represents a C source file
+    module*: PSym
+    filename*: string
+    s*: TCFileSections        # sections of the C file
+    PreventStackTrace*: bool  # true if stack traces need to be prevented
+    usesThreadVars*: bool     # true if the module uses a thread var
+    cfilename*: string        # filename of the module (including path,
+                              # without extension)
+    typeCache*: TIdTable      # cache the generated types
+    forwTypeCache*: TIdTable  # cache for forward declarations of types
+    declaredThings*: TIntSet  # things we have declared in this .c file
+    declaredProtos*: TIntSet  # prototypes we have declared in this .c file
+    headerFiles*: TLinkedList # needed headers to include
+    typeInfoMarker*: TIntSet  # needed for generating type information
+    initProc*: BProc          # code for init procedure
+    typeStack*: TTypeSeq      # used for type generation
+    dataCache*: TNodeTable
+    forwardedProcs*: TSymSeq  # keep forwarded procs here
+    typeNodes*, nimTypes*: int # used for type info generation
+    typeNodesName*, nimTypesName*: PRope # used for type info generation
+    labels*: natural          # for generating unique module-scope names
+
+var
+  mainModProcs*, mainModInit*: PRope # parts of the main module
+  gMapping*: PRope             # the generated mapping file (if requested)
+  gProcProfile*: Natural       # proc profile counter
+  gGeneratedSyms*: TIntSet     # set of ID's of generated symbols
+  gPendingModules*: seq[BModule] = @[] # list of modules that are not
+                                       # finished with code generation
+  gForwardedProcsCounter*: int = 0
+  gNimDat*: BModule            # generated global data
+
+
+proc newProc*(prc: PSym, module: BModule): BProc = 
+  new(result)
+  result.prc = prc
+  result.module = module
+  if prc != nil: result.options = prc.options
+  else: result.options = gOptions
+  result.blocks = @[]
+  result.nestedTryStmts = @[]
+
diff --git a/compiler/idgen.nim b/compiler/idgen.nim
index f5ae8a1f8..8bcfa37b7 100644
--- a/compiler/idgen.nim
+++ b/compiler/idgen.nim
@@ -9,7 +9,7 @@
 
 ## This module contains a simple persistent id generator.
 
-import idents, strutils, os
+import idents, strutils, os, options
 
 var gFrontEndId, gBackendId*: int
 
@@ -40,15 +40,18 @@ proc setId*(id: int) {.inline.} =
 proc IDsynchronizationPoint*(idRange: int) = 
   gFrontEndId = (gFrontEndId div IdRange + 1) * IdRange + 1
 
+proc toGid(f: string): string =
+  result = options.completeGeneratedFilePath(f.addFileExt("gid"))
+
 proc saveMaxIds*(project: string) =
-  var f = open(project.addFileExt("gid"), fmWrite)
+  var f = open(project.toGid, fmWrite)
   f.writeln($gFrontEndId)
   f.writeln($gBackEndId)
   f.close()
   
 proc loadMaxIds*(project: string) =
   var f: TFile
-  if open(f, project.addFileExt("gid"), fmRead):
+  if open(f, project.toGid, fmRead):
     var frontEndId = parseInt(f.readLine)
     var backEndId = parseInt(f.readLine)
     gFrontEndId = max(gFrontEndId, frontEndId)
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index 5b6ffc57f..c6ef85a2b 100755
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -59,8 +59,8 @@ const
   chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
 
 # since negative numbers require a leading '-' they use up 1 byte. Thus we
-# subtract/add vintDelta here to save space for little negative numbers
-# which are common in ROD files: 
+# subtract/add `vintDelta` here to save space for small negative numbers
+# which are common in ROD files:
 const
   vintDelta = 5
 
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 7e0d6c8e2..298d2d517 100755
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -111,6 +111,12 @@ proc newRope(data: string = nil): PRope =
     result.length = len(data)
     result.data = data
 
+proc newMutableRope*(capacity = 30): PRope =
+  ## creates a new rope that supports direct modifications of the rope's
+  ## 'data' and 'length' fields.
+  new(result)
+  result.data = newStringOfCap(capacity)
+
 var 
   cache: PRope                # the root of the cache tree
   misses, hits: int
diff --git a/todo.txt b/todo.txt
index 5d54a6386..eadf9642a 100755
--- a/todo.txt
+++ b/todo.txt
@@ -2,19 +2,33 @@ Version 0.8.14
 ==============
 
 - optimize unused constants away
-- 'let x = y'; const ptr/ref
+- 'let x = y'
 - fix actors.nim
 - make threadvar efficient again on linux after testing
 - test the sort implementation again
 - optional indentation for 'case' statement
 - document & test splicing; don't forget to test negative indexes
 - thread local vs. global raiseHook()
-- incremental compilation (!)
+- make pegs support a compile-time option and make c2nim use regexes instead
+  per default
+
+
+incremental compilation
+-----------------------
+
+- implement C file merge operation
+- adapt thread var implementation to care about the new merge operation
+- write test cases: needs test script support
+- fix remaining bugs
+- write documentation
+- make the compiler output a warning if linking fails with --symbolFiles:on
+  (necessary?)
 
 
 version 0.9.0
 =============
 
+- const ptr/ref
 - unsigned ints and bignums
 - warning for implicit openArray -> varargs convention
 - implement explicit varargs
@@ -24,6 +38,8 @@ version 0.9.0
 - make exceptions compatible with C++ exceptions
 - ``=`` should be overloadable; requires specialization for ``=``
 - 'const' objects including case objects
+- os module should use Windows Unicode versions
+- 64bit build for Windows
 
 Bugs
 ----
@@ -141,5 +157,6 @@ Version 2
   a full blown statement; a ``try`` expression might be a good idea to make
   error handling more light-weight
   people also want ``inc a; inc b``
+  --> solved by providing an expr version of most control structures?